All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 0/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-10 13:06 ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Matthias Brugger; +Cc: linux-arm-kernel, linux-mediatek

Hi everyone,

I'm taking over the subject for the time being as Markus is on vacation.

this series is built around the DisplayPort driver. The dpi/dpintf
driver and the added helper functions are required for the DisplayPort
driver to work.

In v6, I've ported fixes from the mediatek team to this series, which can be found in this
vendor tree:
- https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/3252762/5

I haven't taken care of the comment made by Vinod in v5 [1] where he asks to use phy_get()
instead of fiddling with the platform data. This will be addressed in v7, Sorry about that.

Older revisions:
RFC - https://lore.kernel.org/linux-mediatek/20210816192523.1739365-1-msp@baylibre.com/
v1  - https://lore.kernel.org/linux-mediatek/20210906193529.718845-1-msp@baylibre.com/
v2  - https://lore.kernel.org/linux-mediatek/20210920084424.231825-1-msp@baylibre.com/
v3  - https://lore.kernel.org/linux-mediatek/20211001094443.2770169-1-msp@baylibre.com/
v4  - https://lore.kernel.org/linux-mediatek/20211011094624.3416029-1-msp@baylibre.com/
v5  - https://lore.kernel.org/all/20211021092707.3562523-1-msp@baylibre.com/

Functional dependencies are:
- Add Mediatek Soc DRM (vdosys0) support for mt8195
  https://lore.kernel.org/all/20211026155911.17651-1-jason-jh.lin@mediatek.com/
- Add MediaTek SoC DRM (vdosys1) support for mt8195
  https://lore.kernel.org/all/20211029075203.17093-1-nancy.lin@mediatek.com/

[1] https://patchwork.kernel.org/project/linux-mediatek/patch/20211021092707.3562523-7-msp@baylibre.com/#24546569

Markus Schneider-Pargmann (7):
  dt-bindings: mediatek,dpi: Add DP_INTF compatible
  dt-bindings: mediatek,dp: Add Display Port binding
  drm/edid: Add cea_sad helpers for freq/length
  video/hdmi: Add audio_infoframe packing for DP
  drm/mediatek: dpi: Add dpintf support
  phy: phy-mtk-dp: Add driver for DP phy
  drm/mediatek: Add mt8195 DisplayPort driver

 .../display/mediatek/mediatek,dp.yaml         |   87 +
 .../display/mediatek/mediatek,dpi.yaml        |   11 +-
 MAINTAINERS                                   |    1 +
 drivers/gpu/drm/drm_edid.c                    |   76 +-
 drivers/gpu/drm/mediatek/Kconfig              |    7 +
 drivers/gpu/drm/mediatek/Makefile             |    2 +
 drivers/gpu/drm/mediatek/mtk_dp.c             | 3094 +++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_dp_reg.h         |  568 +++
 drivers/gpu/drm/mediatek/mtk_dpi.c            |  296 +-
 drivers/gpu/drm/mediatek/mtk_dpi_regs.h       |   38 +
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c   |    4 +
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h   |    1 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.c        |    6 +-
 drivers/gpu/drm/mediatek/mtk_drm_drv.h        |    1 +
 drivers/phy/mediatek/Kconfig                  |    8 +
 drivers/phy/mediatek/Makefile                 |    1 +
 drivers/phy/mediatek/phy-mtk-dp.c             |  219 ++
 drivers/video/hdmi.c                          |   83 +-
 include/drm/drm_dp_helper.h                   |    2 +
 include/drm/drm_edid.h                        |   18 +-
 include/linux/hdmi.h                          |    7 +-
 include/linux/soc/mediatek/mtk-mmsys.h        |    2 +
 22 files changed, 4452 insertions(+), 80 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
 create mode 100644 drivers/phy/mediatek/phy-mtk-dp.c

-- 
2.32.0


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* [PATCH v6 0/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-10 13:06 ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Matthias Brugger; +Cc: linux-arm-kernel, linux-mediatek

Hi everyone,

I'm taking over the subject for the time being as Markus is on vacation.

this series is built around the DisplayPort driver. The dpi/dpintf
driver and the added helper functions are required for the DisplayPort
driver to work.

In v6, I've ported fixes from the mediatek team to this series, which can be found in this
vendor tree:
- https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/3252762/5

I haven't taken care of the comment made by Vinod in v5 [1] where he asks to use phy_get()
instead of fiddling with the platform data. This will be addressed in v7, Sorry about that.

Older revisions:
RFC - https://lore.kernel.org/linux-mediatek/20210816192523.1739365-1-msp@baylibre.com/
v1  - https://lore.kernel.org/linux-mediatek/20210906193529.718845-1-msp@baylibre.com/
v2  - https://lore.kernel.org/linux-mediatek/20210920084424.231825-1-msp@baylibre.com/
v3  - https://lore.kernel.org/linux-mediatek/20211001094443.2770169-1-msp@baylibre.com/
v4  - https://lore.kernel.org/linux-mediatek/20211011094624.3416029-1-msp@baylibre.com/
v5  - https://lore.kernel.org/all/20211021092707.3562523-1-msp@baylibre.com/

Functional dependencies are:
- Add Mediatek Soc DRM (vdosys0) support for mt8195
  https://lore.kernel.org/all/20211026155911.17651-1-jason-jh.lin@mediatek.com/
- Add MediaTek SoC DRM (vdosys1) support for mt8195
  https://lore.kernel.org/all/20211029075203.17093-1-nancy.lin@mediatek.com/

[1] https://patchwork.kernel.org/project/linux-mediatek/patch/20211021092707.3562523-7-msp@baylibre.com/#24546569

Markus Schneider-Pargmann (7):
  dt-bindings: mediatek,dpi: Add DP_INTF compatible
  dt-bindings: mediatek,dp: Add Display Port binding
  drm/edid: Add cea_sad helpers for freq/length
  video/hdmi: Add audio_infoframe packing for DP
  drm/mediatek: dpi: Add dpintf support
  phy: phy-mtk-dp: Add driver for DP phy
  drm/mediatek: Add mt8195 DisplayPort driver

 .../display/mediatek/mediatek,dp.yaml         |   87 +
 .../display/mediatek/mediatek,dpi.yaml        |   11 +-
 MAINTAINERS                                   |    1 +
 drivers/gpu/drm/drm_edid.c                    |   76 +-
 drivers/gpu/drm/mediatek/Kconfig              |    7 +
 drivers/gpu/drm/mediatek/Makefile             |    2 +
 drivers/gpu/drm/mediatek/mtk_dp.c             | 3094 +++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_dp_reg.h         |  568 +++
 drivers/gpu/drm/mediatek/mtk_dpi.c            |  296 +-
 drivers/gpu/drm/mediatek/mtk_dpi_regs.h       |   38 +
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c   |    4 +
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h   |    1 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.c        |    6 +-
 drivers/gpu/drm/mediatek/mtk_drm_drv.h        |    1 +
 drivers/phy/mediatek/Kconfig                  |    8 +
 drivers/phy/mediatek/Makefile                 |    1 +
 drivers/phy/mediatek/phy-mtk-dp.c             |  219 ++
 drivers/video/hdmi.c                          |   83 +-
 include/drm/drm_dp_helper.h                   |    2 +
 include/drm/drm_edid.h                        |   18 +-
 include/linux/hdmi.h                          |    7 +-
 include/linux/soc/mediatek/mtk-mmsys.h        |    2 +
 22 files changed, 4452 insertions(+), 80 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
 create mode 100644 drivers/phy/mediatek/phy-mtk-dp.c

-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH v6 1/7] dt-bindings: mediatek,dpi: Add DP_INTF compatible
  2021-11-10 13:06 ` Guillaume Ranquet
  (?)
  (?)
@ 2021-11-10 13:06   ` Guillaume Ranquet
  -1 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Chun-Kuang Hu, Philipp Zabel, David Airlie, Daniel Vetter,
	Rob Herring, Matthias Brugger, CK Hu, Jitao shi
  Cc: Markus Schneider-Pargmann, Rob Herring, dri-devel,
	linux-mediatek, devicetree, linux-kernel, linux-arm-kernel

From: Markus Schneider-Pargmann <msp@baylibre.com>

DP_INTF is similar to DPI but does not have the exact same feature set
or register layouts.

DP_INTF is the sink of the display pipeline that is connected to the
DisplayPort controller and encoder unit. It takes the same clocks as
DPI.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/display/mediatek/mediatek,dpi.yaml       | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml
index dd2896a40ff08..53acf9a84f7fb 100644
--- a/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml
@@ -4,16 +4,16 @@
 $id: http://devicetree.org/schemas/display/mediatek/mediatek,dpi.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
-title: mediatek DPI Controller Device Tree Bindings
+title: mediatek DPI/DP_INTF Controller Device Tree Bindings
 
 maintainers:
   - CK Hu <ck.hu@mediatek.com>
   - Jitao shi <jitao.shi@mediatek.com>
 
 description: |
-  The Mediatek DPI function block is a sink of the display subsystem and
-  provides 8-bit RGB/YUV444 or 8/10/10-bit YUV422 pixel data on a parallel
-  output bus.
+  The Mediatek DPI and DP_INTF function blocks are a sink of the display
+  subsystem and provides 8-bit RGB/YUV444 or 8/10/10-bit YUV422 pixel data on a
+  parallel output bus.
 
 properties:
   compatible:
@@ -23,6 +23,7 @@ properties:
       - mediatek,mt8173-dpi
       - mediatek,mt8183-dpi
       - mediatek,mt8192-dpi
+      - mediatek,mt8195-dpintf
 
   reg:
     maxItems: 1
@@ -54,7 +55,7 @@ properties:
     $ref: /schemas/graph.yaml#/properties/port
     description:
       Output port node. This port should be connected to the input port of an
-      attached HDMI or LVDS encoder chip.
+      attached HDMI, LVDS or DisplayPort encoder chip.
 
 required:
   - compatible
-- 
2.32.0


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

* [PATCH v6 1/7] dt-bindings: mediatek,dpi: Add DP_INTF compatible
@ 2021-11-10 13:06   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Chun-Kuang Hu, Philipp Zabel, David Airlie, Daniel Vetter,
	Rob Herring, Matthias Brugger, CK Hu, Jitao shi
  Cc: devicetree, linux-kernel, dri-devel, Markus Schneider-Pargmann,
	linux-mediatek, linux-arm-kernel

From: Markus Schneider-Pargmann <msp@baylibre.com>

DP_INTF is similar to DPI but does not have the exact same feature set
or register layouts.

DP_INTF is the sink of the display pipeline that is connected to the
DisplayPort controller and encoder unit. It takes the same clocks as
DPI.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/display/mediatek/mediatek,dpi.yaml       | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml
index dd2896a40ff08..53acf9a84f7fb 100644
--- a/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml
@@ -4,16 +4,16 @@
 $id: http://devicetree.org/schemas/display/mediatek/mediatek,dpi.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
-title: mediatek DPI Controller Device Tree Bindings
+title: mediatek DPI/DP_INTF Controller Device Tree Bindings
 
 maintainers:
   - CK Hu <ck.hu@mediatek.com>
   - Jitao shi <jitao.shi@mediatek.com>
 
 description: |
-  The Mediatek DPI function block is a sink of the display subsystem and
-  provides 8-bit RGB/YUV444 or 8/10/10-bit YUV422 pixel data on a parallel
-  output bus.
+  The Mediatek DPI and DP_INTF function blocks are a sink of the display
+  subsystem and provides 8-bit RGB/YUV444 or 8/10/10-bit YUV422 pixel data on a
+  parallel output bus.
 
 properties:
   compatible:
@@ -23,6 +23,7 @@ properties:
       - mediatek,mt8173-dpi
       - mediatek,mt8183-dpi
       - mediatek,mt8192-dpi
+      - mediatek,mt8195-dpintf
 
   reg:
     maxItems: 1
@@ -54,7 +55,7 @@ properties:
     $ref: /schemas/graph.yaml#/properties/port
     description:
       Output port node. This port should be connected to the input port of an
-      attached HDMI or LVDS encoder chip.
+      attached HDMI, LVDS or DisplayPort encoder chip.
 
 required:
   - compatible
-- 
2.32.0


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

* [PATCH v6 1/7] dt-bindings: mediatek,dpi: Add DP_INTF compatible
@ 2021-11-10 13:06   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Chun-Kuang Hu, Philipp Zabel, David Airlie, Daniel Vetter,
	Rob Herring, Matthias Brugger, CK Hu, Jitao shi
  Cc: Markus Schneider-Pargmann, Rob Herring, dri-devel,
	linux-mediatek, devicetree, linux-kernel, linux-arm-kernel

From: Markus Schneider-Pargmann <msp@baylibre.com>

DP_INTF is similar to DPI but does not have the exact same feature set
or register layouts.

DP_INTF is the sink of the display pipeline that is connected to the
DisplayPort controller and encoder unit. It takes the same clocks as
DPI.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/display/mediatek/mediatek,dpi.yaml       | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml
index dd2896a40ff08..53acf9a84f7fb 100644
--- a/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml
@@ -4,16 +4,16 @@
 $id: http://devicetree.org/schemas/display/mediatek/mediatek,dpi.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
-title: mediatek DPI Controller Device Tree Bindings
+title: mediatek DPI/DP_INTF Controller Device Tree Bindings
 
 maintainers:
   - CK Hu <ck.hu@mediatek.com>
   - Jitao shi <jitao.shi@mediatek.com>
 
 description: |
-  The Mediatek DPI function block is a sink of the display subsystem and
-  provides 8-bit RGB/YUV444 or 8/10/10-bit YUV422 pixel data on a parallel
-  output bus.
+  The Mediatek DPI and DP_INTF function blocks are a sink of the display
+  subsystem and provides 8-bit RGB/YUV444 or 8/10/10-bit YUV422 pixel data on a
+  parallel output bus.
 
 properties:
   compatible:
@@ -23,6 +23,7 @@ properties:
       - mediatek,mt8173-dpi
       - mediatek,mt8183-dpi
       - mediatek,mt8192-dpi
+      - mediatek,mt8195-dpintf
 
   reg:
     maxItems: 1
@@ -54,7 +55,7 @@ properties:
     $ref: /schemas/graph.yaml#/properties/port
     description:
       Output port node. This port should be connected to the input port of an
-      attached HDMI or LVDS encoder chip.
+      attached HDMI, LVDS or DisplayPort encoder chip.
 
 required:
   - compatible
-- 
2.32.0


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* [PATCH v6 1/7] dt-bindings: mediatek,dpi: Add DP_INTF compatible
@ 2021-11-10 13:06   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Chun-Kuang Hu, Philipp Zabel, David Airlie, Daniel Vetter,
	Rob Herring, Matthias Brugger, CK Hu, Jitao shi
  Cc: Markus Schneider-Pargmann, Rob Herring, dri-devel,
	linux-mediatek, devicetree, linux-kernel, linux-arm-kernel

From: Markus Schneider-Pargmann <msp@baylibre.com>

DP_INTF is similar to DPI but does not have the exact same feature set
or register layouts.

DP_INTF is the sink of the display pipeline that is connected to the
DisplayPort controller and encoder unit. It takes the same clocks as
DPI.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/display/mediatek/mediatek,dpi.yaml       | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml
index dd2896a40ff08..53acf9a84f7fb 100644
--- a/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml
@@ -4,16 +4,16 @@
 $id: http://devicetree.org/schemas/display/mediatek/mediatek,dpi.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
-title: mediatek DPI Controller Device Tree Bindings
+title: mediatek DPI/DP_INTF Controller Device Tree Bindings
 
 maintainers:
   - CK Hu <ck.hu@mediatek.com>
   - Jitao shi <jitao.shi@mediatek.com>
 
 description: |
-  The Mediatek DPI function block is a sink of the display subsystem and
-  provides 8-bit RGB/YUV444 or 8/10/10-bit YUV422 pixel data on a parallel
-  output bus.
+  The Mediatek DPI and DP_INTF function blocks are a sink of the display
+  subsystem and provides 8-bit RGB/YUV444 or 8/10/10-bit YUV422 pixel data on a
+  parallel output bus.
 
 properties:
   compatible:
@@ -23,6 +23,7 @@ properties:
       - mediatek,mt8173-dpi
       - mediatek,mt8183-dpi
       - mediatek,mt8192-dpi
+      - mediatek,mt8195-dpintf
 
   reg:
     maxItems: 1
@@ -54,7 +55,7 @@ properties:
     $ref: /schemas/graph.yaml#/properties/port
     description:
       Output port node. This port should be connected to the input port of an
-      attached HDMI or LVDS encoder chip.
+      attached HDMI, LVDS or DisplayPort encoder chip.
 
 required:
   - compatible
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH v6 2/7] dt-bindings: mediatek,dp: Add Display Port binding
  2021-11-10 13:06 ` Guillaume Ranquet
  (?)
  (?)
@ 2021-11-10 13:06   ` Guillaume Ranquet
  -1 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Chun-Kuang Hu, Philipp Zabel, David Airlie, Daniel Vetter,
	Rob Herring, Matthias Brugger, CK Hu, Jitao shi
  Cc: Markus Schneider-Pargmann, Rob Herring, dri-devel,
	linux-mediatek, devicetree, linux-kernel, linux-arm-kernel

From: Markus Schneider-Pargmann <msp@baylibre.com>

This controller is present on several mediatek hardware. Currently
mt8195 and mt8395 have this controller without a functional difference,
so only one compatible field is added.

The controller can have two forms, as a normal display port and as an
embedded display port.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../display/mediatek/mediatek,dp.yaml         | 87 +++++++++++++++++++
 1 file changed, 87 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml

diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
new file mode 100644
index 0000000000000..068b11d766e21
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
@@ -0,0 +1,87 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/mediatek/mediatek,dp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Mediatek Display Port Controller
+
+maintainers:
+  - CK Hu <ck.hu@mediatek.com>
+  - Jitao shi <jitao.shi@mediatek.com>
+
+description: |
+  Device tree bindings for the Mediatek (embedded) Display Port controller
+  present on some Mediatek SoCs.
+
+properties:
+  compatible:
+    enum:
+      - mediatek,mt8195-dp-tx
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: faxi clock
+
+  clock-names:
+    items:
+      - const: faxi
+
+  power-domains:
+    maxItems: 1
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Input endpoint of the controller, usually dp_intf
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Output endpoint of the controller
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/power/mt8195-power.h>
+    edp_tx: edp_tx@1c500000 {
+        compatible = "mediatek,mt8195-dp-tx";
+        reg = <0 0x1c500000 0 0x8000>;
+        interrupts = <GIC_SPI 676 IRQ_TYPE_LEVEL_HIGH 0>;
+        power-domains = <&spm MT8195_POWER_DOMAIN_EPD_TX>;
+        pinctrl-names = "default";
+        pinctrl-0 = <&edp_pin>;
+
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            port@0 {
+                reg = <0>;
+                edp_in: endpoint {
+                    remote-endpoint = <&dp_intf0_out>;
+                };
+            };
+            port@1 {
+                reg = <1>;
+                edp_out: endpoint {
+                	remote-endpoint = <&panel_in>;
+                };
+            };
+        };
+    };
-- 
2.32.0


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

* [PATCH v6 2/7] dt-bindings: mediatek,dp: Add Display Port binding
@ 2021-11-10 13:06   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Chun-Kuang Hu, Philipp Zabel, David Airlie, Daniel Vetter,
	Rob Herring, Matthias Brugger, CK Hu, Jitao shi
  Cc: devicetree, linux-kernel, dri-devel, Markus Schneider-Pargmann,
	linux-mediatek, linux-arm-kernel

From: Markus Schneider-Pargmann <msp@baylibre.com>

This controller is present on several mediatek hardware. Currently
mt8195 and mt8395 have this controller without a functional difference,
so only one compatible field is added.

The controller can have two forms, as a normal display port and as an
embedded display port.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../display/mediatek/mediatek,dp.yaml         | 87 +++++++++++++++++++
 1 file changed, 87 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml

diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
new file mode 100644
index 0000000000000..068b11d766e21
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
@@ -0,0 +1,87 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/mediatek/mediatek,dp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Mediatek Display Port Controller
+
+maintainers:
+  - CK Hu <ck.hu@mediatek.com>
+  - Jitao shi <jitao.shi@mediatek.com>
+
+description: |
+  Device tree bindings for the Mediatek (embedded) Display Port controller
+  present on some Mediatek SoCs.
+
+properties:
+  compatible:
+    enum:
+      - mediatek,mt8195-dp-tx
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: faxi clock
+
+  clock-names:
+    items:
+      - const: faxi
+
+  power-domains:
+    maxItems: 1
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Input endpoint of the controller, usually dp_intf
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Output endpoint of the controller
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/power/mt8195-power.h>
+    edp_tx: edp_tx@1c500000 {
+        compatible = "mediatek,mt8195-dp-tx";
+        reg = <0 0x1c500000 0 0x8000>;
+        interrupts = <GIC_SPI 676 IRQ_TYPE_LEVEL_HIGH 0>;
+        power-domains = <&spm MT8195_POWER_DOMAIN_EPD_TX>;
+        pinctrl-names = "default";
+        pinctrl-0 = <&edp_pin>;
+
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            port@0 {
+                reg = <0>;
+                edp_in: endpoint {
+                    remote-endpoint = <&dp_intf0_out>;
+                };
+            };
+            port@1 {
+                reg = <1>;
+                edp_out: endpoint {
+                	remote-endpoint = <&panel_in>;
+                };
+            };
+        };
+    };
-- 
2.32.0


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

* [PATCH v6 2/7] dt-bindings: mediatek,dp: Add Display Port binding
@ 2021-11-10 13:06   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Chun-Kuang Hu, Philipp Zabel, David Airlie, Daniel Vetter,
	Rob Herring, Matthias Brugger, CK Hu, Jitao shi
  Cc: Markus Schneider-Pargmann, Rob Herring, dri-devel,
	linux-mediatek, devicetree, linux-kernel, linux-arm-kernel

From: Markus Schneider-Pargmann <msp@baylibre.com>

This controller is present on several mediatek hardware. Currently
mt8195 and mt8395 have this controller without a functional difference,
so only one compatible field is added.

The controller can have two forms, as a normal display port and as an
embedded display port.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../display/mediatek/mediatek,dp.yaml         | 87 +++++++++++++++++++
 1 file changed, 87 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml

diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
new file mode 100644
index 0000000000000..068b11d766e21
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
@@ -0,0 +1,87 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/mediatek/mediatek,dp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Mediatek Display Port Controller
+
+maintainers:
+  - CK Hu <ck.hu@mediatek.com>
+  - Jitao shi <jitao.shi@mediatek.com>
+
+description: |
+  Device tree bindings for the Mediatek (embedded) Display Port controller
+  present on some Mediatek SoCs.
+
+properties:
+  compatible:
+    enum:
+      - mediatek,mt8195-dp-tx
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: faxi clock
+
+  clock-names:
+    items:
+      - const: faxi
+
+  power-domains:
+    maxItems: 1
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Input endpoint of the controller, usually dp_intf
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Output endpoint of the controller
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/power/mt8195-power.h>
+    edp_tx: edp_tx@1c500000 {
+        compatible = "mediatek,mt8195-dp-tx";
+        reg = <0 0x1c500000 0 0x8000>;
+        interrupts = <GIC_SPI 676 IRQ_TYPE_LEVEL_HIGH 0>;
+        power-domains = <&spm MT8195_POWER_DOMAIN_EPD_TX>;
+        pinctrl-names = "default";
+        pinctrl-0 = <&edp_pin>;
+
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            port@0 {
+                reg = <0>;
+                edp_in: endpoint {
+                    remote-endpoint = <&dp_intf0_out>;
+                };
+            };
+            port@1 {
+                reg = <1>;
+                edp_out: endpoint {
+                	remote-endpoint = <&panel_in>;
+                };
+            };
+        };
+    };
-- 
2.32.0


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* [PATCH v6 2/7] dt-bindings: mediatek,dp: Add Display Port binding
@ 2021-11-10 13:06   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Chun-Kuang Hu, Philipp Zabel, David Airlie, Daniel Vetter,
	Rob Herring, Matthias Brugger, CK Hu, Jitao shi
  Cc: Markus Schneider-Pargmann, Rob Herring, dri-devel,
	linux-mediatek, devicetree, linux-kernel, linux-arm-kernel

From: Markus Schneider-Pargmann <msp@baylibre.com>

This controller is present on several mediatek hardware. Currently
mt8195 and mt8395 have this controller without a functional difference,
so only one compatible field is added.

The controller can have two forms, as a normal display port and as an
embedded display port.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../display/mediatek/mediatek,dp.yaml         | 87 +++++++++++++++++++
 1 file changed, 87 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml

diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
new file mode 100644
index 0000000000000..068b11d766e21
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
@@ -0,0 +1,87 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/mediatek/mediatek,dp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Mediatek Display Port Controller
+
+maintainers:
+  - CK Hu <ck.hu@mediatek.com>
+  - Jitao shi <jitao.shi@mediatek.com>
+
+description: |
+  Device tree bindings for the Mediatek (embedded) Display Port controller
+  present on some Mediatek SoCs.
+
+properties:
+  compatible:
+    enum:
+      - mediatek,mt8195-dp-tx
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: faxi clock
+
+  clock-names:
+    items:
+      - const: faxi
+
+  power-domains:
+    maxItems: 1
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Input endpoint of the controller, usually dp_intf
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Output endpoint of the controller
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/power/mt8195-power.h>
+    edp_tx: edp_tx@1c500000 {
+        compatible = "mediatek,mt8195-dp-tx";
+        reg = <0 0x1c500000 0 0x8000>;
+        interrupts = <GIC_SPI 676 IRQ_TYPE_LEVEL_HIGH 0>;
+        power-domains = <&spm MT8195_POWER_DOMAIN_EPD_TX>;
+        pinctrl-names = "default";
+        pinctrl-0 = <&edp_pin>;
+
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            port@0 {
+                reg = <0>;
+                edp_in: endpoint {
+                    remote-endpoint = <&dp_intf0_out>;
+                };
+            };
+            port@1 {
+                reg = <1>;
+                edp_out: endpoint {
+                	remote-endpoint = <&panel_in>;
+                };
+            };
+        };
+    };
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH v6 3/7] drm/edid: Add cea_sad helpers for freq/length
  2021-11-10 13:06 ` Guillaume Ranquet
@ 2021-11-10 13:06   ` Guillaume Ranquet
  -1 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter
  Cc: Markus Schneider-Pargmann, dri-devel, linux-kernel

From: Markus Schneider-Pargmann <msp@baylibre.com>

This patch adds two helper functions that extract the frequency and word
length from a struct cea_sad.

For these helper functions new defines are added that help translate the
'freq' and 'byte2' fields into real numbers.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
---
 drivers/gpu/drm/drm_edid.c | 74 ++++++++++++++++++++++++++++++++++++++
 include/drm/drm_edid.h     | 18 ++++++++--
 2 files changed, 90 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 12893e7be89bb..500279a82167a 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -4747,6 +4747,80 @@ int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
 }
 EXPORT_SYMBOL(drm_edid_to_speaker_allocation);
 
+/**
+ * drm_cea_sad_get_sample_rate - Extract the sample rate from cea_sad
+ * @sad: Pointer to the cea_sad struct
+ *
+ * Extracts the cea_sad frequency field and returns the sample rate in Hz.
+ *
+ * Return: Sample rate in Hz or a negative errno if parsing failed.
+ */
+int drm_cea_sad_get_sample_rate(const struct cea_sad *sad)
+{
+	switch (sad->freq) {
+	case DRM_CEA_SAD_FREQ_32KHZ:
+		return 32000;
+	case DRM_CEA_SAD_FREQ_44KHZ:
+		return 44100;
+	case DRM_CEA_SAD_FREQ_48KHZ:
+		return 48000;
+	case DRM_CEA_SAD_FREQ_88KHZ:
+		return 88200;
+	case DRM_CEA_SAD_FREQ_96KHZ:
+		return 96000;
+	case DRM_CEA_SAD_FREQ_176KHZ:
+		return 176400;
+	case DRM_CEA_SAD_FREQ_192KHZ:
+		return 192000;
+	default:
+		return -EINVAL;
+	}
+}
+EXPORT_SYMBOL(drm_cea_sad_get_sample_rate);
+
+static bool drm_cea_sad_is_uncompressed(const struct cea_sad *sad)
+{
+	switch (sad->format) {
+	case HDMI_AUDIO_CODING_TYPE_STREAM:
+	case HDMI_AUDIO_CODING_TYPE_PCM:
+		return true;
+	default:
+		return false;
+	}
+}
+
+/**
+ * drm_cea_sad_get_uncompressed_word_length - Extract word length
+ * @sad: Pointer to the cea_sad struct
+ *
+ * Extracts the cea_sad byte2 field and returns the word length for an
+ * uncompressed stream.
+ *
+ * Note: This function may only be called for uncompressed audio.
+ *
+ * Return: Word length in bits or a negative errno if parsing failed.
+ */
+int drm_cea_sad_get_uncompressed_word_length(const struct cea_sad *sad)
+{
+	if (!drm_cea_sad_is_uncompressed(sad)) {
+		DRM_WARN("Unable to get the uncompressed word length for a compressed format: %u\n",
+			 sad->format);
+		return -EINVAL;
+	}
+
+	switch (sad->byte2) {
+	case DRM_CEA_SAD_UNCOMPRESSED_WORD_16BIT:
+		return 16;
+	case DRM_CEA_SAD_UNCOMPRESSED_WORD_20BIT:
+		return 20;
+	case DRM_CEA_SAD_UNCOMPRESSED_WORD_24BIT:
+		return 24;
+	default:
+		return -EINVAL;
+	}
+}
+EXPORT_SYMBOL(drm_cea_sad_get_uncompressed_word_length);
+
 /**
  * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync delay
  * @connector: connector associated with the HDMI/DP sink
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index 18f6c700f6d02..a30452b313979 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -361,12 +361,24 @@ struct edid {
 
 /* Short Audio Descriptor */
 struct cea_sad {
-	u8 format;
+	u8 format; /* See HDMI_AUDIO_CODING_TYPE_* */
 	u8 channels; /* max number of channels - 1 */
-	u8 freq;
+	u8 freq; /* See CEA_SAD_FREQ_* */
 	u8 byte2; /* meaning depends on format */
 };
 
+#define DRM_CEA_SAD_FREQ_32KHZ  BIT(0)
+#define DRM_CEA_SAD_FREQ_44KHZ  BIT(1)
+#define DRM_CEA_SAD_FREQ_48KHZ  BIT(2)
+#define DRM_CEA_SAD_FREQ_88KHZ  BIT(3)
+#define DRM_CEA_SAD_FREQ_96KHZ  BIT(4)
+#define DRM_CEA_SAD_FREQ_176KHZ BIT(5)
+#define DRM_CEA_SAD_FREQ_192KHZ BIT(6)
+
+#define DRM_CEA_SAD_UNCOMPRESSED_WORD_16BIT BIT(0)
+#define DRM_CEA_SAD_UNCOMPRESSED_WORD_20BIT BIT(1)
+#define DRM_CEA_SAD_UNCOMPRESSED_WORD_24BIT BIT(2)
+
 struct drm_encoder;
 struct drm_connector;
 struct drm_connector_state;
@@ -374,6 +386,8 @@ struct drm_display_mode;
 
 int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads);
 int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb);
+int drm_cea_sad_get_sample_rate(const struct cea_sad *sad);
+int drm_cea_sad_get_uncompressed_word_length(const struct cea_sad *sad);
 int drm_av_sync_delay(struct drm_connector *connector,
 		      const struct drm_display_mode *mode);
 
-- 
2.32.0


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

* [PATCH v6 3/7] drm/edid: Add cea_sad helpers for freq/length
@ 2021-11-10 13:06   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter
  Cc: linux-kernel, dri-devel, Markus Schneider-Pargmann

From: Markus Schneider-Pargmann <msp@baylibre.com>

This patch adds two helper functions that extract the frequency and word
length from a struct cea_sad.

For these helper functions new defines are added that help translate the
'freq' and 'byte2' fields into real numbers.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
---
 drivers/gpu/drm/drm_edid.c | 74 ++++++++++++++++++++++++++++++++++++++
 include/drm/drm_edid.h     | 18 ++++++++--
 2 files changed, 90 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 12893e7be89bb..500279a82167a 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -4747,6 +4747,80 @@ int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
 }
 EXPORT_SYMBOL(drm_edid_to_speaker_allocation);
 
+/**
+ * drm_cea_sad_get_sample_rate - Extract the sample rate from cea_sad
+ * @sad: Pointer to the cea_sad struct
+ *
+ * Extracts the cea_sad frequency field and returns the sample rate in Hz.
+ *
+ * Return: Sample rate in Hz or a negative errno if parsing failed.
+ */
+int drm_cea_sad_get_sample_rate(const struct cea_sad *sad)
+{
+	switch (sad->freq) {
+	case DRM_CEA_SAD_FREQ_32KHZ:
+		return 32000;
+	case DRM_CEA_SAD_FREQ_44KHZ:
+		return 44100;
+	case DRM_CEA_SAD_FREQ_48KHZ:
+		return 48000;
+	case DRM_CEA_SAD_FREQ_88KHZ:
+		return 88200;
+	case DRM_CEA_SAD_FREQ_96KHZ:
+		return 96000;
+	case DRM_CEA_SAD_FREQ_176KHZ:
+		return 176400;
+	case DRM_CEA_SAD_FREQ_192KHZ:
+		return 192000;
+	default:
+		return -EINVAL;
+	}
+}
+EXPORT_SYMBOL(drm_cea_sad_get_sample_rate);
+
+static bool drm_cea_sad_is_uncompressed(const struct cea_sad *sad)
+{
+	switch (sad->format) {
+	case HDMI_AUDIO_CODING_TYPE_STREAM:
+	case HDMI_AUDIO_CODING_TYPE_PCM:
+		return true;
+	default:
+		return false;
+	}
+}
+
+/**
+ * drm_cea_sad_get_uncompressed_word_length - Extract word length
+ * @sad: Pointer to the cea_sad struct
+ *
+ * Extracts the cea_sad byte2 field and returns the word length for an
+ * uncompressed stream.
+ *
+ * Note: This function may only be called for uncompressed audio.
+ *
+ * Return: Word length in bits or a negative errno if parsing failed.
+ */
+int drm_cea_sad_get_uncompressed_word_length(const struct cea_sad *sad)
+{
+	if (!drm_cea_sad_is_uncompressed(sad)) {
+		DRM_WARN("Unable to get the uncompressed word length for a compressed format: %u\n",
+			 sad->format);
+		return -EINVAL;
+	}
+
+	switch (sad->byte2) {
+	case DRM_CEA_SAD_UNCOMPRESSED_WORD_16BIT:
+		return 16;
+	case DRM_CEA_SAD_UNCOMPRESSED_WORD_20BIT:
+		return 20;
+	case DRM_CEA_SAD_UNCOMPRESSED_WORD_24BIT:
+		return 24;
+	default:
+		return -EINVAL;
+	}
+}
+EXPORT_SYMBOL(drm_cea_sad_get_uncompressed_word_length);
+
 /**
  * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync delay
  * @connector: connector associated with the HDMI/DP sink
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index 18f6c700f6d02..a30452b313979 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -361,12 +361,24 @@ struct edid {
 
 /* Short Audio Descriptor */
 struct cea_sad {
-	u8 format;
+	u8 format; /* See HDMI_AUDIO_CODING_TYPE_* */
 	u8 channels; /* max number of channels - 1 */
-	u8 freq;
+	u8 freq; /* See CEA_SAD_FREQ_* */
 	u8 byte2; /* meaning depends on format */
 };
 
+#define DRM_CEA_SAD_FREQ_32KHZ  BIT(0)
+#define DRM_CEA_SAD_FREQ_44KHZ  BIT(1)
+#define DRM_CEA_SAD_FREQ_48KHZ  BIT(2)
+#define DRM_CEA_SAD_FREQ_88KHZ  BIT(3)
+#define DRM_CEA_SAD_FREQ_96KHZ  BIT(4)
+#define DRM_CEA_SAD_FREQ_176KHZ BIT(5)
+#define DRM_CEA_SAD_FREQ_192KHZ BIT(6)
+
+#define DRM_CEA_SAD_UNCOMPRESSED_WORD_16BIT BIT(0)
+#define DRM_CEA_SAD_UNCOMPRESSED_WORD_20BIT BIT(1)
+#define DRM_CEA_SAD_UNCOMPRESSED_WORD_24BIT BIT(2)
+
 struct drm_encoder;
 struct drm_connector;
 struct drm_connector_state;
@@ -374,6 +386,8 @@ struct drm_display_mode;
 
 int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads);
 int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb);
+int drm_cea_sad_get_sample_rate(const struct cea_sad *sad);
+int drm_cea_sad_get_uncompressed_word_length(const struct cea_sad *sad);
 int drm_av_sync_delay(struct drm_connector *connector,
 		      const struct drm_display_mode *mode);
 
-- 
2.32.0


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

* [PATCH v6 4/7] video/hdmi: Add audio_infoframe packing for DP
  2021-11-10 13:06 ` Guillaume Ranquet
@ 2021-11-10 13:06   ` Guillaume Ranquet
  -1 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter
  Cc: Markus Schneider-Pargmann, dri-devel, linux-fbdev, linux-kernel

From: Markus Schneider-Pargmann <msp@baylibre.com>

Similar to HDMI, DP uses audio infoframes as well which are structured
very similar to the HDMI ones.

This patch adds a helper function to pack the HDMI audio infoframe for
DP, called hdmi_audio_infoframe_pack_for_dp().
hdmi_audio_infoframe_pack_only() is split into two parts. One of them
packs the payload only and can be used for HDMI and DP.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
---
 drivers/video/hdmi.c        | 83 ++++++++++++++++++++++++++++---------
 include/drm/drm_dp_helper.h |  2 +
 include/linux/hdmi.h        |  7 +++-
 3 files changed, 72 insertions(+), 20 deletions(-)

diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
index 947be761dfa40..63e74d9fd210e 100644
--- a/drivers/video/hdmi.c
+++ b/drivers/video/hdmi.c
@@ -21,6 +21,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
+#include <drm/drm_dp_helper.h>
 #include <linux/bitops.h>
 #include <linux/bug.h>
 #include <linux/errno.h>
@@ -381,12 +382,34 @@ static int hdmi_audio_infoframe_check_only(const struct hdmi_audio_infoframe *fr
  *
  * Returns 0 on success or a negative error code on failure.
  */
-int hdmi_audio_infoframe_check(struct hdmi_audio_infoframe *frame)
+int hdmi_audio_infoframe_check(const struct hdmi_audio_infoframe *frame)
 {
 	return hdmi_audio_infoframe_check_only(frame);
 }
 EXPORT_SYMBOL(hdmi_audio_infoframe_check);
 
+static void
+hdmi_audio_infoframe_pack_payload(const struct hdmi_audio_infoframe *frame,
+				  u8 *buffer)
+{
+	u8 channels;
+
+	if (frame->channels >= 2)
+		channels = frame->channels - 1;
+	else
+		channels = 0;
+
+	buffer[0] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7);
+	buffer[1] = ((frame->sample_frequency & 0x7) << 2) |
+		 (frame->sample_size & 0x3);
+	buffer[2] = frame->coding_type_ext & 0x1f;
+	buffer[3] = frame->channel_allocation;
+	buffer[4] = (frame->level_shift_value & 0xf) << 3;
+
+	if (frame->downmix_inhibit)
+		buffer[4] |= BIT(7);
+}
+
 /**
  * hdmi_audio_infoframe_pack_only() - write HDMI audio infoframe to binary buffer
  * @frame: HDMI audio infoframe
@@ -404,7 +427,6 @@ EXPORT_SYMBOL(hdmi_audio_infoframe_check);
 ssize_t hdmi_audio_infoframe_pack_only(const struct hdmi_audio_infoframe *frame,
 				       void *buffer, size_t size)
 {
-	unsigned char channels;
 	u8 *ptr = buffer;
 	size_t length;
 	int ret;
@@ -420,28 +442,13 @@ ssize_t hdmi_audio_infoframe_pack_only(const struct hdmi_audio_infoframe *frame,
 
 	memset(buffer, 0, size);
 
-	if (frame->channels >= 2)
-		channels = frame->channels - 1;
-	else
-		channels = 0;
-
 	ptr[0] = frame->type;
 	ptr[1] = frame->version;
 	ptr[2] = frame->length;
 	ptr[3] = 0; /* checksum */
 
-	/* start infoframe payload */
-	ptr += HDMI_INFOFRAME_HEADER_SIZE;
-
-	ptr[0] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7);
-	ptr[1] = ((frame->sample_frequency & 0x7) << 2) |
-		 (frame->sample_size & 0x3);
-	ptr[2] = frame->coding_type_ext & 0x1f;
-	ptr[3] = frame->channel_allocation;
-	ptr[4] = (frame->level_shift_value & 0xf) << 3;
-
-	if (frame->downmix_inhibit)
-		ptr[4] |= BIT(7);
+	hdmi_audio_infoframe_pack_payload(frame,
+					  ptr + HDMI_INFOFRAME_HEADER_SIZE);
 
 	hdmi_infoframe_set_checksum(buffer, length);
 
@@ -479,6 +486,44 @@ ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame,
 }
 EXPORT_SYMBOL(hdmi_audio_infoframe_pack);
 
+/**
+ * hdmi_audio_infoframe_pack_for_dp - Pack a HDMI Audio infoframe for
+ *                                    displayport
+ *
+ * @frame HDMI Audio infoframe
+ * @sdp secondary data packet for display port. This is filled with the
+ * appropriate data
+ * @dp_version Display Port version to be encoded in the header
+ *
+ * Packs a HDMI Audio Infoframe to be sent over Display Port. This function
+ * fills the secondary data packet to be used for Display Port.
+ *
+ * Return: Number of total written bytes or a negative errno on failure.
+ */
+ssize_t
+hdmi_audio_infoframe_pack_for_dp(const struct hdmi_audio_infoframe *frame,
+				 struct dp_sdp *sdp, u8 dp_version)
+{
+	int ret;
+
+	ret = hdmi_audio_infoframe_check(frame);
+	if (ret)
+		return ret;
+
+	memset(sdp->db, 0, sizeof(sdp->db));
+
+	// Secondary-data packet header
+	sdp->sdp_header.HB0 = 0;
+	sdp->sdp_header.HB1 = frame->type;
+	sdp->sdp_header.HB2 = DP_SDP_AUDIO_INFOFRAME_HB2;
+	sdp->sdp_header.HB3 = (dp_version & 0x3f) << 2;
+
+	hdmi_audio_infoframe_pack_payload(frame, sdp->db);
+
+	return frame->length + 4;
+}
+EXPORT_SYMBOL(hdmi_audio_infoframe_pack_for_dp);
+
 /**
  * hdmi_vendor_infoframe_init() - initialize an HDMI vendor infoframe
  * @frame: HDMI vendor infoframe
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index b52df4db3e8fe..d1527175716aa 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -1550,6 +1550,8 @@ int drm_dp_bw_code_to_link_rate(u8 link_bw);
 #define DP_SDP_VSC_EXT_CEA		0x21 /* DP 1.4 */
 /* 0x80+ CEA-861 infoframe types */
 
+#define DP_SDP_AUDIO_INFOFRAME_HB2	0x1b
+
 /**
  * struct dp_sdp_header - DP secondary data packet header
  * @HB0: Secondary Data Packet ID
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
index c8ec982ff4984..2f4dcc8d060e3 100644
--- a/include/linux/hdmi.h
+++ b/include/linux/hdmi.h
@@ -336,7 +336,12 @@ ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame,
 				  void *buffer, size_t size);
 ssize_t hdmi_audio_infoframe_pack_only(const struct hdmi_audio_infoframe *frame,
 				       void *buffer, size_t size);
-int hdmi_audio_infoframe_check(struct hdmi_audio_infoframe *frame);
+int hdmi_audio_infoframe_check(const struct hdmi_audio_infoframe *frame);
+
+struct dp_sdp;
+ssize_t
+hdmi_audio_infoframe_pack_for_dp(const struct hdmi_audio_infoframe *frame,
+				 struct dp_sdp *sdp, u8 dp_version);
 
 enum hdmi_3d_structure {
 	HDMI_3D_STRUCTURE_INVALID = -1,
-- 
2.32.0


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

* [PATCH v6 4/7] video/hdmi: Add audio_infoframe packing for DP
@ 2021-11-10 13:06   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter
  Cc: linux-fbdev, linux-kernel, dri-devel, Markus Schneider-Pargmann

From: Markus Schneider-Pargmann <msp@baylibre.com>

Similar to HDMI, DP uses audio infoframes as well which are structured
very similar to the HDMI ones.

This patch adds a helper function to pack the HDMI audio infoframe for
DP, called hdmi_audio_infoframe_pack_for_dp().
hdmi_audio_infoframe_pack_only() is split into two parts. One of them
packs the payload only and can be used for HDMI and DP.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
---
 drivers/video/hdmi.c        | 83 ++++++++++++++++++++++++++++---------
 include/drm/drm_dp_helper.h |  2 +
 include/linux/hdmi.h        |  7 +++-
 3 files changed, 72 insertions(+), 20 deletions(-)

diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
index 947be761dfa40..63e74d9fd210e 100644
--- a/drivers/video/hdmi.c
+++ b/drivers/video/hdmi.c
@@ -21,6 +21,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
+#include <drm/drm_dp_helper.h>
 #include <linux/bitops.h>
 #include <linux/bug.h>
 #include <linux/errno.h>
@@ -381,12 +382,34 @@ static int hdmi_audio_infoframe_check_only(const struct hdmi_audio_infoframe *fr
  *
  * Returns 0 on success or a negative error code on failure.
  */
-int hdmi_audio_infoframe_check(struct hdmi_audio_infoframe *frame)
+int hdmi_audio_infoframe_check(const struct hdmi_audio_infoframe *frame)
 {
 	return hdmi_audio_infoframe_check_only(frame);
 }
 EXPORT_SYMBOL(hdmi_audio_infoframe_check);
 
+static void
+hdmi_audio_infoframe_pack_payload(const struct hdmi_audio_infoframe *frame,
+				  u8 *buffer)
+{
+	u8 channels;
+
+	if (frame->channels >= 2)
+		channels = frame->channels - 1;
+	else
+		channels = 0;
+
+	buffer[0] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7);
+	buffer[1] = ((frame->sample_frequency & 0x7) << 2) |
+		 (frame->sample_size & 0x3);
+	buffer[2] = frame->coding_type_ext & 0x1f;
+	buffer[3] = frame->channel_allocation;
+	buffer[4] = (frame->level_shift_value & 0xf) << 3;
+
+	if (frame->downmix_inhibit)
+		buffer[4] |= BIT(7);
+}
+
 /**
  * hdmi_audio_infoframe_pack_only() - write HDMI audio infoframe to binary buffer
  * @frame: HDMI audio infoframe
@@ -404,7 +427,6 @@ EXPORT_SYMBOL(hdmi_audio_infoframe_check);
 ssize_t hdmi_audio_infoframe_pack_only(const struct hdmi_audio_infoframe *frame,
 				       void *buffer, size_t size)
 {
-	unsigned char channels;
 	u8 *ptr = buffer;
 	size_t length;
 	int ret;
@@ -420,28 +442,13 @@ ssize_t hdmi_audio_infoframe_pack_only(const struct hdmi_audio_infoframe *frame,
 
 	memset(buffer, 0, size);
 
-	if (frame->channels >= 2)
-		channels = frame->channels - 1;
-	else
-		channels = 0;
-
 	ptr[0] = frame->type;
 	ptr[1] = frame->version;
 	ptr[2] = frame->length;
 	ptr[3] = 0; /* checksum */
 
-	/* start infoframe payload */
-	ptr += HDMI_INFOFRAME_HEADER_SIZE;
-
-	ptr[0] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7);
-	ptr[1] = ((frame->sample_frequency & 0x7) << 2) |
-		 (frame->sample_size & 0x3);
-	ptr[2] = frame->coding_type_ext & 0x1f;
-	ptr[3] = frame->channel_allocation;
-	ptr[4] = (frame->level_shift_value & 0xf) << 3;
-
-	if (frame->downmix_inhibit)
-		ptr[4] |= BIT(7);
+	hdmi_audio_infoframe_pack_payload(frame,
+					  ptr + HDMI_INFOFRAME_HEADER_SIZE);
 
 	hdmi_infoframe_set_checksum(buffer, length);
 
@@ -479,6 +486,44 @@ ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame,
 }
 EXPORT_SYMBOL(hdmi_audio_infoframe_pack);
 
+/**
+ * hdmi_audio_infoframe_pack_for_dp - Pack a HDMI Audio infoframe for
+ *                                    displayport
+ *
+ * @frame HDMI Audio infoframe
+ * @sdp secondary data packet for display port. This is filled with the
+ * appropriate data
+ * @dp_version Display Port version to be encoded in the header
+ *
+ * Packs a HDMI Audio Infoframe to be sent over Display Port. This function
+ * fills the secondary data packet to be used for Display Port.
+ *
+ * Return: Number of total written bytes or a negative errno on failure.
+ */
+ssize_t
+hdmi_audio_infoframe_pack_for_dp(const struct hdmi_audio_infoframe *frame,
+				 struct dp_sdp *sdp, u8 dp_version)
+{
+	int ret;
+
+	ret = hdmi_audio_infoframe_check(frame);
+	if (ret)
+		return ret;
+
+	memset(sdp->db, 0, sizeof(sdp->db));
+
+	// Secondary-data packet header
+	sdp->sdp_header.HB0 = 0;
+	sdp->sdp_header.HB1 = frame->type;
+	sdp->sdp_header.HB2 = DP_SDP_AUDIO_INFOFRAME_HB2;
+	sdp->sdp_header.HB3 = (dp_version & 0x3f) << 2;
+
+	hdmi_audio_infoframe_pack_payload(frame, sdp->db);
+
+	return frame->length + 4;
+}
+EXPORT_SYMBOL(hdmi_audio_infoframe_pack_for_dp);
+
 /**
  * hdmi_vendor_infoframe_init() - initialize an HDMI vendor infoframe
  * @frame: HDMI vendor infoframe
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index b52df4db3e8fe..d1527175716aa 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -1550,6 +1550,8 @@ int drm_dp_bw_code_to_link_rate(u8 link_bw);
 #define DP_SDP_VSC_EXT_CEA		0x21 /* DP 1.4 */
 /* 0x80+ CEA-861 infoframe types */
 
+#define DP_SDP_AUDIO_INFOFRAME_HB2	0x1b
+
 /**
  * struct dp_sdp_header - DP secondary data packet header
  * @HB0: Secondary Data Packet ID
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
index c8ec982ff4984..2f4dcc8d060e3 100644
--- a/include/linux/hdmi.h
+++ b/include/linux/hdmi.h
@@ -336,7 +336,12 @@ ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame,
 				  void *buffer, size_t size);
 ssize_t hdmi_audio_infoframe_pack_only(const struct hdmi_audio_infoframe *frame,
 				       void *buffer, size_t size);
-int hdmi_audio_infoframe_check(struct hdmi_audio_infoframe *frame);
+int hdmi_audio_infoframe_check(const struct hdmi_audio_infoframe *frame);
+
+struct dp_sdp;
+ssize_t
+hdmi_audio_infoframe_pack_for_dp(const struct hdmi_audio_infoframe *frame,
+				 struct dp_sdp *sdp, u8 dp_version);
 
 enum hdmi_3d_structure {
 	HDMI_3D_STRUCTURE_INVALID = -1,
-- 
2.32.0


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

* [PATCH v6 5/7] drm/mediatek: dpi: Add dpintf support
  2021-11-10 13:06 ` Guillaume Ranquet
  (?)
  (?)
@ 2021-11-10 13:06   ` Guillaume Ranquet
  -1 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Chun-Kuang Hu, Philipp Zabel, David Airlie, Daniel Vetter,
	Matthias Brugger
  Cc: Markus Schneider-Pargmann, dri-devel, linux-mediatek,
	linux-arm-kernel, linux-kernel

From: Markus Schneider-Pargmann <msp@baylibre.com>

dpintf is the displayport interface hardware unit. This unit is similar
to dpi and can reuse most of the code.

This patch adds support for mt8195-dpintf to this dpi driver. Main
differences are:
 - Some features/functional components are not available for dpintf
   which are now excluded from code execution once is_dpintf is set
 - dpintf can and needs to choose between different clockdividers based
   on the clockspeed. This is done by choosing a different clock parent.
 - There are two additional clocks that need to be managed. These are
   only set for dpintf and will be set to NULL if not supplied. The
   clk_* calls handle these as normal clocks then.
 - Some register contents differ slightly between the two components. To
   work around this I added register bits/masks with a DPINTF_ prefix
   and use them where different.

Based on a separate driver for dpintf created by
Jason-JH.Lin <jason-jh.lin@mediatek.com>.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
---
 drivers/gpu/drm/mediatek/mtk_dpi.c          | 199 +++++++++++++++-----
 drivers/gpu/drm/mediatek/mtk_dpi_regs.h     |  12 ++
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |   4 +
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |   1 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.c      |   5 +-
 include/linux/soc/mediatek/mtk-mmsys.h      |   2 +
 6 files changed, 176 insertions(+), 47 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
index 4554e2de14309..384074f69111b 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
@@ -125,6 +125,17 @@ struct mtk_dpi_conf {
 	bool edge_sel_en;
 	const u32 *output_fmts;
 	u32 num_output_fmts;
+	bool is_ck_de_pol;
+	bool is_dpintf;
+	bool csc_support;
+	bool swap_input_support;
+	// Mask used for HWIDTH, HPORCH, VSYNC_WIDTH and VSYNC_PORCH (no shift)
+	u32 dimension_mask;
+	// Mask used for HSIZE and VSIZE (no shift)
+	u32 hvsize_mask;
+	u32 channel_swap_shift;
+	u32 yuv422_en_bit;
+	const struct mtk_dpi_yc_limit *limit;
 };
 
 static void mtk_dpi_mask(struct mtk_dpi *dpi, u32 offset, u32 val, u32 mask)
@@ -153,30 +164,30 @@ static void mtk_dpi_disable(struct mtk_dpi *dpi)
 static void mtk_dpi_config_hsync(struct mtk_dpi *dpi,
 				 struct mtk_dpi_sync_param *sync)
 {
-	mtk_dpi_mask(dpi, DPI_TGEN_HWIDTH,
-		     sync->sync_width << HPW, HPW_MASK);
-	mtk_dpi_mask(dpi, DPI_TGEN_HPORCH,
-		     sync->back_porch << HBP, HBP_MASK);
+	mtk_dpi_mask(dpi, DPI_TGEN_HWIDTH, sync->sync_width << HPW,
+		     dpi->conf->dimension_mask << HPW);
+	mtk_dpi_mask(dpi, DPI_TGEN_HPORCH, sync->back_porch << HBP,
+		     dpi->conf->dimension_mask << HBP);
 	mtk_dpi_mask(dpi, DPI_TGEN_HPORCH, sync->front_porch << HFP,
-		     HFP_MASK);
+		     dpi->conf->dimension_mask << HFP);
 }
 
 static void mtk_dpi_config_vsync(struct mtk_dpi *dpi,
 				 struct mtk_dpi_sync_param *sync,
 				 u32 width_addr, u32 porch_addr)
 {
-	mtk_dpi_mask(dpi, width_addr,
-		     sync->sync_width << VSYNC_WIDTH_SHIFT,
-		     VSYNC_WIDTH_MASK);
 	mtk_dpi_mask(dpi, width_addr,
 		     sync->shift_half_line << VSYNC_HALF_LINE_SHIFT,
 		     VSYNC_HALF_LINE_MASK);
+	mtk_dpi_mask(dpi, width_addr,
+		     sync->sync_width << VSYNC_WIDTH_SHIFT,
+		     dpi->conf->dimension_mask << VSYNC_WIDTH_SHIFT);
 	mtk_dpi_mask(dpi, porch_addr,
 		     sync->back_porch << VSYNC_BACK_PORCH_SHIFT,
-		     VSYNC_BACK_PORCH_MASK);
+		     dpi->conf->dimension_mask << VSYNC_BACK_PORCH_SHIFT);
 	mtk_dpi_mask(dpi, porch_addr,
 		     sync->front_porch << VSYNC_FRONT_PORCH_SHIFT,
-		     VSYNC_FRONT_PORCH_MASK);
+		     dpi->conf->dimension_mask << VSYNC_FRONT_PORCH_SHIFT);
 }
 
 static void mtk_dpi_config_vsync_lodd(struct mtk_dpi *dpi,
@@ -210,13 +221,20 @@ static void mtk_dpi_config_pol(struct mtk_dpi *dpi,
 			       struct mtk_dpi_polarities *dpi_pol)
 {
 	unsigned int pol;
+	unsigned int mask;
 
-	pol = (dpi_pol->ck_pol == MTK_DPI_POLARITY_RISING ? 0 : CK_POL) |
-	      (dpi_pol->de_pol == MTK_DPI_POLARITY_RISING ? 0 : DE_POL) |
-	      (dpi_pol->hsync_pol == MTK_DPI_POLARITY_RISING ? 0 : HSYNC_POL) |
+	mask = HSYNC_POL | VSYNC_POL;
+	pol = (dpi_pol->hsync_pol == MTK_DPI_POLARITY_RISING ? 0 : HSYNC_POL) |
 	      (dpi_pol->vsync_pol == MTK_DPI_POLARITY_RISING ? 0 : VSYNC_POL);
-	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, pol,
-		     CK_POL | DE_POL | HSYNC_POL | VSYNC_POL);
+	if (dpi->conf->is_ck_de_pol) {
+		mask |= CK_POL | DE_POL;
+		pol |= (dpi_pol->ck_pol == MTK_DPI_POLARITY_RISING ?
+			0 : CK_POL) |
+		       (dpi_pol->de_pol == MTK_DPI_POLARITY_RISING ?
+			0 : DE_POL);
+	}
+
+	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, pol, mask);
 }
 
 static void mtk_dpi_config_3d(struct mtk_dpi *dpi, bool en_3d)
@@ -231,13 +249,16 @@ static void mtk_dpi_config_interface(struct mtk_dpi *dpi, bool inter)
 
 static void mtk_dpi_config_fb_size(struct mtk_dpi *dpi, u32 width, u32 height)
 {
-	mtk_dpi_mask(dpi, DPI_SIZE, width << HSIZE, HSIZE_MASK);
-	mtk_dpi_mask(dpi, DPI_SIZE, height << VSIZE, VSIZE_MASK);
+	mtk_dpi_mask(dpi, DPI_SIZE, width << HSIZE,
+		     dpi->conf->hvsize_mask << HSIZE);
+	mtk_dpi_mask(dpi, DPI_SIZE, height << VSIZE,
+		     dpi->conf->hvsize_mask << VSIZE);
 }
 
-static void mtk_dpi_config_channel_limit(struct mtk_dpi *dpi,
-					 struct mtk_dpi_yc_limit *limit)
+static void mtk_dpi_config_channel_limit(struct mtk_dpi *dpi)
 {
+	const struct mtk_dpi_yc_limit *limit = dpi->conf->limit;
+
 	mtk_dpi_mask(dpi, DPI_Y_LIMIT, limit->y_bottom << Y_LIMINT_BOT,
 		     Y_LIMINT_BOT_MASK);
 	mtk_dpi_mask(dpi, DPI_Y_LIMIT, limit->y_top << Y_LIMINT_TOP,
@@ -332,12 +353,14 @@ static void mtk_dpi_config_channel_swap(struct mtk_dpi *dpi,
 		break;
 	}
 
-	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, val << CH_SWAP, CH_SWAP_MASK);
+	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, val << CH_SWAP,
+		     CH_SWAP_MASK << dpi->conf->channel_swap_shift);
 }
 
 static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
 {
-	mtk_dpi_mask(dpi, DPI_CON, enable ? YUV422_EN : 0, YUV422_EN);
+	mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->yuv422_en_bit : 0,
+		     dpi->conf->yuv422_en_bit);
 }
 
 static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
@@ -367,19 +390,25 @@ static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
 	if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
 	    (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
 		mtk_dpi_config_yuv422_enable(dpi, false);
-		mtk_dpi_config_csc_enable(dpi, true);
-		mtk_dpi_config_swap_input(dpi, false);
+		if (dpi->conf->csc_support)
+			mtk_dpi_config_csc_enable(dpi, true);
+		if (dpi->conf->swap_input_support)
+			mtk_dpi_config_swap_input(dpi, false);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
 	} else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
 		   (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
 		mtk_dpi_config_yuv422_enable(dpi, true);
-		mtk_dpi_config_csc_enable(dpi, true);
-		mtk_dpi_config_swap_input(dpi, true);
+		if (dpi->conf->csc_support)
+			mtk_dpi_config_csc_enable(dpi, true);
+		if (dpi->conf->swap_input_support)
+			mtk_dpi_config_swap_input(dpi, true);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
 	} else {
 		mtk_dpi_config_yuv422_enable(dpi, false);
-		mtk_dpi_config_csc_enable(dpi, false);
-		mtk_dpi_config_swap_input(dpi, false);
+		if (dpi->conf->csc_support)
+			mtk_dpi_config_csc_enable(dpi, false);
+		if (dpi->conf->swap_input_support)
+			mtk_dpi_config_swap_input(dpi, false);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
 	}
 }
@@ -449,7 +478,6 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
 static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 				    struct drm_display_mode *mode)
 {
-	struct mtk_dpi_yc_limit limit;
 	struct mtk_dpi_polarities dpi_pol;
 	struct mtk_dpi_sync_param hsync;
 	struct mtk_dpi_sync_param vsync_lodd = { 0 };
@@ -472,32 +500,34 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 	pll_rate = clk_get_rate(dpi->tvd_clk);
 
 	vm.pixelclock = pll_rate / factor;
-	if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
-	    (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
+	if (dpi->conf->is_dpintf)
+		clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
+	else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
+		 (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
 		clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
 	else
 		clk_set_rate(dpi->pixel_clk, vm.pixelclock);
 
-
 	vm.pixelclock = clk_get_rate(dpi->pixel_clk);
 
 	dev_dbg(dpi->dev, "Got  PLL %lu Hz, pixel clock %lu Hz\n",
 		pll_rate, vm.pixelclock);
 
-	limit.c_bottom = 0x0010;
-	limit.c_top = 0x0FE0;
-	limit.y_bottom = 0x0010;
-	limit.y_top = 0x0FE0;
-
 	dpi_pol.ck_pol = MTK_DPI_POLARITY_FALLING;
 	dpi_pol.de_pol = MTK_DPI_POLARITY_RISING;
 	dpi_pol.hsync_pol = vm.flags & DISPLAY_FLAGS_HSYNC_HIGH ?
 			    MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING;
 	dpi_pol.vsync_pol = vm.flags & DISPLAY_FLAGS_VSYNC_HIGH ?
 			    MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING;
-	hsync.sync_width = vm.hsync_len;
-	hsync.back_porch = vm.hback_porch;
-	hsync.front_porch = vm.hfront_porch;
+	if (dpi->conf->is_dpintf) {
+		hsync.sync_width = vm.hsync_len / 4;
+		hsync.back_porch = vm.hback_porch / 4;
+		hsync.front_porch = vm.hfront_porch / 4;
+	} else {
+		hsync.sync_width = vm.hsync_len;
+		hsync.back_porch = vm.hback_porch;
+		hsync.front_porch = vm.hfront_porch;
+	}
 	hsync.shift_half_line = false;
 	vsync_lodd.sync_width = vm.vsync_len;
 	vsync_lodd.back_porch = vm.vback_porch;
@@ -536,14 +566,19 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 	else
 		mtk_dpi_config_fb_size(dpi, vm.hactive, vm.vactive);
 
-	mtk_dpi_config_channel_limit(dpi, &limit);
+	mtk_dpi_config_channel_limit(dpi);
 	mtk_dpi_config_bit_num(dpi, dpi->bit_num);
 	mtk_dpi_config_channel_swap(dpi, dpi->channel_swap);
-	mtk_dpi_config_yc_map(dpi, dpi->yc_map);
 	mtk_dpi_config_color_format(dpi, dpi->color_format);
-	mtk_dpi_config_2n_h_fre(dpi);
-	mtk_dpi_dual_edge(dpi);
-	mtk_dpi_config_disable_edge(dpi);
+	if (dpi->conf->is_dpintf) {
+		mtk_dpi_mask(dpi, DPI_CON, DPINTF_INPUT_2P_EN,
+			     DPINTF_INPUT_2P_EN);
+	} else {
+		mtk_dpi_config_yc_map(dpi, dpi->yc_map);
+		mtk_dpi_config_2n_h_fre(dpi);
+		mtk_dpi_dual_edge(dpi);
+		mtk_dpi_config_disable_edge(dpi);
+	}
 	mtk_dpi_sw_reset(dpi, false);
 
 	return 0;
@@ -667,7 +702,7 @@ mtk_dpi_bridge_mode_valid(struct drm_bridge *bridge,
 {
 	struct mtk_dpi *dpi = bridge_to_dpi(bridge);
 
-	if (mode->clock > dpi->conf->max_clock_khz)
+	if (dpi->conf->max_clock_khz && mode->clock > dpi->conf->max_clock_khz)
 		return MODE_CLOCK_HIGH;
 
 	return MODE_OK;
@@ -781,6 +816,16 @@ static unsigned int mt8183_calculate_factor(int clock)
 		return 2;
 }
 
+static unsigned int mt8195_dpintf_calculate_factor(int clock)
+{
+	if (clock < 70000)
+		return 4;
+	else if (clock < 200000)
+		return 2;
+	else
+		return 1;
+}
+
 static const u32 mt8173_output_fmts[] = {
 	MEDIA_BUS_FMT_RGB888_1X24,
 };
@@ -790,12 +835,34 @@ static const u32 mt8183_output_fmts[] = {
 	MEDIA_BUS_FMT_RGB888_2X12_BE,
 };
 
+static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
+	.c_bottom = 0x0010,
+	.c_top = 0x0FE0,
+	.y_bottom = 0x0010,
+	.y_top = 0x0FE0,
+};
+
+static const struct mtk_dpi_yc_limit mtk_dpintf_limit = {
+	.c_bottom = 0x0000,
+	.c_top = 0xFFF,
+	.y_bottom = 0x0000,
+	.y_top = 0xFFF,
+};
+
 static const struct mtk_dpi_conf mt8173_conf = {
 	.cal_factor = mt8173_calculate_factor,
 	.reg_h_fre_con = 0xe0,
 	.max_clock_khz = 300000,
 	.output_fmts = mt8173_output_fmts,
 	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.is_ck_de_pol = true,
+	.csc_support = true,
+	.swap_input_support = true,
+	.dimension_mask = HPW_MASK,
+	.hvsize_mask = HSIZE_MASK,
+	.channel_swap_shift = CH_SWAP,
+	.yuv422_en_bit = YUV422_EN,
+	.limit = &mtk_dpi_limit,
 };
 
 static const struct mtk_dpi_conf mt2701_conf = {
@@ -805,6 +872,14 @@ static const struct mtk_dpi_conf mt2701_conf = {
 	.max_clock_khz = 150000,
 	.output_fmts = mt8173_output_fmts,
 	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.is_ck_de_pol = true,
+	.csc_support = true,
+	.swap_input_support = true,
+	.dimension_mask = HPW_MASK,
+	.hvsize_mask = HSIZE_MASK,
+	.channel_swap_shift = CH_SWAP,
+	.yuv422_en_bit = YUV422_EN,
+	.limit = &mtk_dpi_limit,
 };
 
 static const struct mtk_dpi_conf mt8183_conf = {
@@ -813,6 +888,14 @@ static const struct mtk_dpi_conf mt8183_conf = {
 	.max_clock_khz = 100000,
 	.output_fmts = mt8183_output_fmts,
 	.num_output_fmts = ARRAY_SIZE(mt8183_output_fmts),
+	.is_ck_de_pol = true,
+	.csc_support = true,
+	.swap_input_support = true,
+	.dimension_mask = HPW_MASK,
+	.hvsize_mask = HSIZE_MASK,
+	.channel_swap_shift = CH_SWAP,
+	.yuv422_en_bit = YUV422_EN,
+	.limit = &mtk_dpi_limit,
 };
 
 static const struct mtk_dpi_conf mt8192_conf = {
@@ -821,6 +904,26 @@ static const struct mtk_dpi_conf mt8192_conf = {
 	.max_clock_khz = 150000,
 	.output_fmts = mt8173_output_fmts,
 	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.is_ck_de_pol = true,
+	.csc_support = true,
+	.swap_input_support = true,
+	.dimension_mask = HPW_MASK,
+	.hvsize_mask = HSIZE_MASK,
+	.channel_swap_shift = CH_SWAP,
+	.yuv422_en_bit = YUV422_EN,
+	.limit = &mtk_dpi_limit,
+};
+
+static const struct mtk_dpi_conf mt8195_dpintf_conf = {
+	.cal_factor = mt8195_dpintf_calculate_factor,
+	.output_fmts = mt8173_output_fmts,
+	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.is_dpintf = true,
+	.dimension_mask = DPINTF_HPW_MASK,
+	.hvsize_mask = DPINTF_HSIZE_MASK,
+	.channel_swap_shift = DPINTF_CH_SWAP,
+	.yuv422_en_bit = DPINTF_YUV422_EN,
+	.limit = &mtk_dpintf_limit,
 };
 
 static int mtk_dpi_probe(struct platform_device *pdev)
@@ -870,7 +973,8 @@ static int mtk_dpi_probe(struct platform_device *pdev)
 	if (IS_ERR(dpi->engine_clk)) {
 		ret = PTR_ERR(dpi->engine_clk);
 		if (ret != -EPROBE_DEFER)
-			dev_err(dev, "Failed to get engine clock: %d\n", ret);
+			dev_err(dev, "Failed to get engine clock: %d\n",
+				ret);
 
 		return ret;
 	}
@@ -945,6 +1049,9 @@ static const struct of_device_id mtk_dpi_of_ids[] = {
 	{ .compatible = "mediatek,mt8192-dpi",
 	  .data = &mt8192_conf,
 	},
+	{ .compatible = "mediatek,mt8195-dpintf",
+	  .data = &mt8195_dpintf_conf,
+	},
 	{ },
 };
 MODULE_DEVICE_TABLE(of, mtk_dpi_of_ids);
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi_regs.h b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
index 3a02fabe16627..72efe6ee25842 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
+++ b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
@@ -40,10 +40,14 @@
 #define FAKE_DE_LEVEN			BIT(21)
 #define FAKE_DE_RODD			BIT(22)
 #define FAKE_DE_REVEN			BIT(23)
+#define DPINTF_YUV422_EN		BIT(24)
+#define DPINTF_INPUT_2P_EN		BIT(29)
 
 #define DPI_OUTPUT_SETTING	0x14
 #define CH_SWAP				0
+#define DPINTF_CH_SWAP			BIT(1)
 #define CH_SWAP_MASK			(0x7 << 0)
+#define DPINTF_CH_SWAP_MASK		(0x7 << 1)
 #define SWAP_RGB			0x00
 #define SWAP_GBR			0x01
 #define SWAP_BRG			0x02
@@ -80,8 +84,10 @@
 #define DPI_SIZE		0x18
 #define HSIZE				0
 #define HSIZE_MASK			(0x1FFF << 0)
+#define DPINTF_HSIZE_MASK		(0xFFFF << 0)
 #define VSIZE				16
 #define VSIZE_MASK			(0x1FFF << 16)
+#define DPINTF_VSIZE_MASK		(0xFFFF << 16)
 
 #define DPI_DDR_SETTING		0x1C
 #define DDR_EN				BIT(0)
@@ -93,24 +99,30 @@
 #define DPI_TGEN_HWIDTH		0x20
 #define HPW				0
 #define HPW_MASK			(0xFFF << 0)
+#define DPINTF_HPW_MASK			(0xFFFF << 0)
 
 #define DPI_TGEN_HPORCH		0x24
 #define HBP				0
 #define HBP_MASK			(0xFFF << 0)
+#define DPINTF_HBP_MASK			(0xFFFF << 0)
 #define HFP				16
 #define HFP_MASK			(0xFFF << 16)
+#define DPINTF_HFP_MASK			(0xFFFF << 16)
 
 #define DPI_TGEN_VWIDTH		0x28
 #define DPI_TGEN_VPORCH		0x2C
 
 #define VSYNC_WIDTH_SHIFT		0
 #define VSYNC_WIDTH_MASK		(0xFFF << 0)
+#define DPINTF_VSYNC_WIDTH_MASK		(0xFFFF << 0)
 #define VSYNC_HALF_LINE_SHIFT		16
 #define VSYNC_HALF_LINE_MASK		BIT(16)
 #define VSYNC_BACK_PORCH_SHIFT		0
 #define VSYNC_BACK_PORCH_MASK		(0xFFF << 0)
+#define DPINTF_VSYNC_BACK_PORCH_MASK	(0xFFFF << 0)
 #define VSYNC_FRONT_PORCH_SHIFT		16
 #define VSYNC_FRONT_PORCH_MASK		(0xFFF << 16)
+#define DPINTF_VSYNC_FRONT_PORCH_MASK	(0xFFFF << 16)
 
 #define DPI_BG_HCNTL		0x30
 #define BG_RIGHT			(0x1FFF << 0)
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
index 99cbf44463e40..da9e059312a5d 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -317,6 +317,7 @@ static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
 	[MTK_DISP_UFOE] = "ufoe",
 	[MTK_DSI] = "dsi",
 	[MTK_DPI] = "dpi",
+	[MTK_DP_INTF] = "dp-intf",
 	[MTK_DISP_PWM] = "pwm",
 	[MTK_DISP_MUTEX] = "mutex",
 	[MTK_DISP_OD] = "od",
@@ -339,6 +340,8 @@ static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
 	[DDP_COMPONENT_DITHER]	= { MTK_DISP_DITHER,	0, &ddp_dither },
 	[DDP_COMPONENT_DPI0]	= { MTK_DPI,		0, &ddp_dpi },
 	[DDP_COMPONENT_DPI1]	= { MTK_DPI,		1, &ddp_dpi },
+	[DDP_COMPONENT_DP_INTF0]= { MTK_DP_INTF,	0, &ddp_dpi },
+	[DDP_COMPONENT_DP_INTF1]= { MTK_DP_INTF,	1, &ddp_dpi },
 	[DDP_COMPONENT_DSI0]	= { MTK_DSI,		0, &ddp_dsi },
 	[DDP_COMPONENT_DSI1]	= { MTK_DSI,		1, &ddp_dsi },
 	[DDP_COMPONENT_DSI2]	= { MTK_DSI,		2, &ddp_dsi },
@@ -476,6 +479,7 @@ int mtk_ddp_comp_init(struct device_node *node, struct mtk_ddp_comp *comp,
 	    type == MTK_DISP_COLOR ||
 	    type == MTK_DISP_GAMMA ||
 	    type == MTK_DPI ||
+	    type == MTK_DP_INTF ||
 	    type == MTK_DSI ||
 	    type == MTK_DISP_OVL ||
 	    type == MTK_DISP_OVL_2L ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
index bb914d976cf5d..ee9d853cfa1c1 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
@@ -30,6 +30,7 @@ enum mtk_ddp_comp_type {
 	MTK_DISP_UFOE,
 	MTK_DSI,
 	MTK_DPI,
+	MTK_DP_INTF,
 	MTK_DISP_PWM,
 	MTK_DISP_MUTEX,
 	MTK_DISP_OD,
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index aec39724ebeb6..1ff4e31c86345 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -459,6 +459,8 @@ static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
 	  .data = (void *)MTK_DPI },
 	{ .compatible = "mediatek,mt8183-dpi",
 	  .data = (void *)MTK_DPI },
+	{ .compatible = "mediatek,mt8195-dpintf",
+	  .data = (void *)MTK_DP_INTF },
 	{ .compatible = "mediatek,mt2701-disp-mutex",
 	  .data = (void *)MTK_DISP_MUTEX },
 	{ .compatible = "mediatek,mt2712-disp-mutex",
@@ -569,7 +571,8 @@ static int mtk_drm_probe(struct platform_device *pdev)
 		    comp_type == MTK_DISP_OVL_2L ||
 		    comp_type == MTK_DISP_RDMA ||
 		    comp_type == MTK_DSI ||
-		    comp_type == MTK_DPI) {
+		    comp_type == MTK_DPI ||
+		    comp_type == MTK_DP_INTF) {
 			dev_info(dev, "Adding component match for %pOF\n",
 				 node);
 			drm_of_component_match_add(dev, &match, compare_of,
diff --git a/include/linux/soc/mediatek/mtk-mmsys.h b/include/linux/soc/mediatek/mtk-mmsys.h
index 4bba275e235ac..56ed2fa5f59e8 100644
--- a/include/linux/soc/mediatek/mtk-mmsys.h
+++ b/include/linux/soc/mediatek/mtk-mmsys.h
@@ -19,6 +19,8 @@ enum mtk_ddp_comp_id {
 	DDP_COMPONENT_DITHER,
 	DDP_COMPONENT_DPI0,
 	DDP_COMPONENT_DPI1,
+	DDP_COMPONENT_DP_INTF0,
+	DDP_COMPONENT_DP_INTF1,
 	DDP_COMPONENT_DSI0,
 	DDP_COMPONENT_DSI1,
 	DDP_COMPONENT_DSI2,
-- 
2.32.0


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

* [PATCH v6 5/7] drm/mediatek: dpi: Add dpintf support
@ 2021-11-10 13:06   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Chun-Kuang Hu, Philipp Zabel, David Airlie, Daniel Vetter,
	Matthias Brugger
  Cc: linux-kernel, linux-mediatek, linux-arm-kernel, dri-devel,
	Markus Schneider-Pargmann

From: Markus Schneider-Pargmann <msp@baylibre.com>

dpintf is the displayport interface hardware unit. This unit is similar
to dpi and can reuse most of the code.

This patch adds support for mt8195-dpintf to this dpi driver. Main
differences are:
 - Some features/functional components are not available for dpintf
   which are now excluded from code execution once is_dpintf is set
 - dpintf can and needs to choose between different clockdividers based
   on the clockspeed. This is done by choosing a different clock parent.
 - There are two additional clocks that need to be managed. These are
   only set for dpintf and will be set to NULL if not supplied. The
   clk_* calls handle these as normal clocks then.
 - Some register contents differ slightly between the two components. To
   work around this I added register bits/masks with a DPINTF_ prefix
   and use them where different.

Based on a separate driver for dpintf created by
Jason-JH.Lin <jason-jh.lin@mediatek.com>.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
---
 drivers/gpu/drm/mediatek/mtk_dpi.c          | 199 +++++++++++++++-----
 drivers/gpu/drm/mediatek/mtk_dpi_regs.h     |  12 ++
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |   4 +
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |   1 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.c      |   5 +-
 include/linux/soc/mediatek/mtk-mmsys.h      |   2 +
 6 files changed, 176 insertions(+), 47 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
index 4554e2de14309..384074f69111b 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
@@ -125,6 +125,17 @@ struct mtk_dpi_conf {
 	bool edge_sel_en;
 	const u32 *output_fmts;
 	u32 num_output_fmts;
+	bool is_ck_de_pol;
+	bool is_dpintf;
+	bool csc_support;
+	bool swap_input_support;
+	// Mask used for HWIDTH, HPORCH, VSYNC_WIDTH and VSYNC_PORCH (no shift)
+	u32 dimension_mask;
+	// Mask used for HSIZE and VSIZE (no shift)
+	u32 hvsize_mask;
+	u32 channel_swap_shift;
+	u32 yuv422_en_bit;
+	const struct mtk_dpi_yc_limit *limit;
 };
 
 static void mtk_dpi_mask(struct mtk_dpi *dpi, u32 offset, u32 val, u32 mask)
@@ -153,30 +164,30 @@ static void mtk_dpi_disable(struct mtk_dpi *dpi)
 static void mtk_dpi_config_hsync(struct mtk_dpi *dpi,
 				 struct mtk_dpi_sync_param *sync)
 {
-	mtk_dpi_mask(dpi, DPI_TGEN_HWIDTH,
-		     sync->sync_width << HPW, HPW_MASK);
-	mtk_dpi_mask(dpi, DPI_TGEN_HPORCH,
-		     sync->back_porch << HBP, HBP_MASK);
+	mtk_dpi_mask(dpi, DPI_TGEN_HWIDTH, sync->sync_width << HPW,
+		     dpi->conf->dimension_mask << HPW);
+	mtk_dpi_mask(dpi, DPI_TGEN_HPORCH, sync->back_porch << HBP,
+		     dpi->conf->dimension_mask << HBP);
 	mtk_dpi_mask(dpi, DPI_TGEN_HPORCH, sync->front_porch << HFP,
-		     HFP_MASK);
+		     dpi->conf->dimension_mask << HFP);
 }
 
 static void mtk_dpi_config_vsync(struct mtk_dpi *dpi,
 				 struct mtk_dpi_sync_param *sync,
 				 u32 width_addr, u32 porch_addr)
 {
-	mtk_dpi_mask(dpi, width_addr,
-		     sync->sync_width << VSYNC_WIDTH_SHIFT,
-		     VSYNC_WIDTH_MASK);
 	mtk_dpi_mask(dpi, width_addr,
 		     sync->shift_half_line << VSYNC_HALF_LINE_SHIFT,
 		     VSYNC_HALF_LINE_MASK);
+	mtk_dpi_mask(dpi, width_addr,
+		     sync->sync_width << VSYNC_WIDTH_SHIFT,
+		     dpi->conf->dimension_mask << VSYNC_WIDTH_SHIFT);
 	mtk_dpi_mask(dpi, porch_addr,
 		     sync->back_porch << VSYNC_BACK_PORCH_SHIFT,
-		     VSYNC_BACK_PORCH_MASK);
+		     dpi->conf->dimension_mask << VSYNC_BACK_PORCH_SHIFT);
 	mtk_dpi_mask(dpi, porch_addr,
 		     sync->front_porch << VSYNC_FRONT_PORCH_SHIFT,
-		     VSYNC_FRONT_PORCH_MASK);
+		     dpi->conf->dimension_mask << VSYNC_FRONT_PORCH_SHIFT);
 }
 
 static void mtk_dpi_config_vsync_lodd(struct mtk_dpi *dpi,
@@ -210,13 +221,20 @@ static void mtk_dpi_config_pol(struct mtk_dpi *dpi,
 			       struct mtk_dpi_polarities *dpi_pol)
 {
 	unsigned int pol;
+	unsigned int mask;
 
-	pol = (dpi_pol->ck_pol == MTK_DPI_POLARITY_RISING ? 0 : CK_POL) |
-	      (dpi_pol->de_pol == MTK_DPI_POLARITY_RISING ? 0 : DE_POL) |
-	      (dpi_pol->hsync_pol == MTK_DPI_POLARITY_RISING ? 0 : HSYNC_POL) |
+	mask = HSYNC_POL | VSYNC_POL;
+	pol = (dpi_pol->hsync_pol == MTK_DPI_POLARITY_RISING ? 0 : HSYNC_POL) |
 	      (dpi_pol->vsync_pol == MTK_DPI_POLARITY_RISING ? 0 : VSYNC_POL);
-	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, pol,
-		     CK_POL | DE_POL | HSYNC_POL | VSYNC_POL);
+	if (dpi->conf->is_ck_de_pol) {
+		mask |= CK_POL | DE_POL;
+		pol |= (dpi_pol->ck_pol == MTK_DPI_POLARITY_RISING ?
+			0 : CK_POL) |
+		       (dpi_pol->de_pol == MTK_DPI_POLARITY_RISING ?
+			0 : DE_POL);
+	}
+
+	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, pol, mask);
 }
 
 static void mtk_dpi_config_3d(struct mtk_dpi *dpi, bool en_3d)
@@ -231,13 +249,16 @@ static void mtk_dpi_config_interface(struct mtk_dpi *dpi, bool inter)
 
 static void mtk_dpi_config_fb_size(struct mtk_dpi *dpi, u32 width, u32 height)
 {
-	mtk_dpi_mask(dpi, DPI_SIZE, width << HSIZE, HSIZE_MASK);
-	mtk_dpi_mask(dpi, DPI_SIZE, height << VSIZE, VSIZE_MASK);
+	mtk_dpi_mask(dpi, DPI_SIZE, width << HSIZE,
+		     dpi->conf->hvsize_mask << HSIZE);
+	mtk_dpi_mask(dpi, DPI_SIZE, height << VSIZE,
+		     dpi->conf->hvsize_mask << VSIZE);
 }
 
-static void mtk_dpi_config_channel_limit(struct mtk_dpi *dpi,
-					 struct mtk_dpi_yc_limit *limit)
+static void mtk_dpi_config_channel_limit(struct mtk_dpi *dpi)
 {
+	const struct mtk_dpi_yc_limit *limit = dpi->conf->limit;
+
 	mtk_dpi_mask(dpi, DPI_Y_LIMIT, limit->y_bottom << Y_LIMINT_BOT,
 		     Y_LIMINT_BOT_MASK);
 	mtk_dpi_mask(dpi, DPI_Y_LIMIT, limit->y_top << Y_LIMINT_TOP,
@@ -332,12 +353,14 @@ static void mtk_dpi_config_channel_swap(struct mtk_dpi *dpi,
 		break;
 	}
 
-	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, val << CH_SWAP, CH_SWAP_MASK);
+	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, val << CH_SWAP,
+		     CH_SWAP_MASK << dpi->conf->channel_swap_shift);
 }
 
 static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
 {
-	mtk_dpi_mask(dpi, DPI_CON, enable ? YUV422_EN : 0, YUV422_EN);
+	mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->yuv422_en_bit : 0,
+		     dpi->conf->yuv422_en_bit);
 }
 
 static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
@@ -367,19 +390,25 @@ static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
 	if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
 	    (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
 		mtk_dpi_config_yuv422_enable(dpi, false);
-		mtk_dpi_config_csc_enable(dpi, true);
-		mtk_dpi_config_swap_input(dpi, false);
+		if (dpi->conf->csc_support)
+			mtk_dpi_config_csc_enable(dpi, true);
+		if (dpi->conf->swap_input_support)
+			mtk_dpi_config_swap_input(dpi, false);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
 	} else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
 		   (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
 		mtk_dpi_config_yuv422_enable(dpi, true);
-		mtk_dpi_config_csc_enable(dpi, true);
-		mtk_dpi_config_swap_input(dpi, true);
+		if (dpi->conf->csc_support)
+			mtk_dpi_config_csc_enable(dpi, true);
+		if (dpi->conf->swap_input_support)
+			mtk_dpi_config_swap_input(dpi, true);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
 	} else {
 		mtk_dpi_config_yuv422_enable(dpi, false);
-		mtk_dpi_config_csc_enable(dpi, false);
-		mtk_dpi_config_swap_input(dpi, false);
+		if (dpi->conf->csc_support)
+			mtk_dpi_config_csc_enable(dpi, false);
+		if (dpi->conf->swap_input_support)
+			mtk_dpi_config_swap_input(dpi, false);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
 	}
 }
@@ -449,7 +478,6 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
 static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 				    struct drm_display_mode *mode)
 {
-	struct mtk_dpi_yc_limit limit;
 	struct mtk_dpi_polarities dpi_pol;
 	struct mtk_dpi_sync_param hsync;
 	struct mtk_dpi_sync_param vsync_lodd = { 0 };
@@ -472,32 +500,34 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 	pll_rate = clk_get_rate(dpi->tvd_clk);
 
 	vm.pixelclock = pll_rate / factor;
-	if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
-	    (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
+	if (dpi->conf->is_dpintf)
+		clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
+	else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
+		 (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
 		clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
 	else
 		clk_set_rate(dpi->pixel_clk, vm.pixelclock);
 
-
 	vm.pixelclock = clk_get_rate(dpi->pixel_clk);
 
 	dev_dbg(dpi->dev, "Got  PLL %lu Hz, pixel clock %lu Hz\n",
 		pll_rate, vm.pixelclock);
 
-	limit.c_bottom = 0x0010;
-	limit.c_top = 0x0FE0;
-	limit.y_bottom = 0x0010;
-	limit.y_top = 0x0FE0;
-
 	dpi_pol.ck_pol = MTK_DPI_POLARITY_FALLING;
 	dpi_pol.de_pol = MTK_DPI_POLARITY_RISING;
 	dpi_pol.hsync_pol = vm.flags & DISPLAY_FLAGS_HSYNC_HIGH ?
 			    MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING;
 	dpi_pol.vsync_pol = vm.flags & DISPLAY_FLAGS_VSYNC_HIGH ?
 			    MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING;
-	hsync.sync_width = vm.hsync_len;
-	hsync.back_porch = vm.hback_porch;
-	hsync.front_porch = vm.hfront_porch;
+	if (dpi->conf->is_dpintf) {
+		hsync.sync_width = vm.hsync_len / 4;
+		hsync.back_porch = vm.hback_porch / 4;
+		hsync.front_porch = vm.hfront_porch / 4;
+	} else {
+		hsync.sync_width = vm.hsync_len;
+		hsync.back_porch = vm.hback_porch;
+		hsync.front_porch = vm.hfront_porch;
+	}
 	hsync.shift_half_line = false;
 	vsync_lodd.sync_width = vm.vsync_len;
 	vsync_lodd.back_porch = vm.vback_porch;
@@ -536,14 +566,19 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 	else
 		mtk_dpi_config_fb_size(dpi, vm.hactive, vm.vactive);
 
-	mtk_dpi_config_channel_limit(dpi, &limit);
+	mtk_dpi_config_channel_limit(dpi);
 	mtk_dpi_config_bit_num(dpi, dpi->bit_num);
 	mtk_dpi_config_channel_swap(dpi, dpi->channel_swap);
-	mtk_dpi_config_yc_map(dpi, dpi->yc_map);
 	mtk_dpi_config_color_format(dpi, dpi->color_format);
-	mtk_dpi_config_2n_h_fre(dpi);
-	mtk_dpi_dual_edge(dpi);
-	mtk_dpi_config_disable_edge(dpi);
+	if (dpi->conf->is_dpintf) {
+		mtk_dpi_mask(dpi, DPI_CON, DPINTF_INPUT_2P_EN,
+			     DPINTF_INPUT_2P_EN);
+	} else {
+		mtk_dpi_config_yc_map(dpi, dpi->yc_map);
+		mtk_dpi_config_2n_h_fre(dpi);
+		mtk_dpi_dual_edge(dpi);
+		mtk_dpi_config_disable_edge(dpi);
+	}
 	mtk_dpi_sw_reset(dpi, false);
 
 	return 0;
@@ -667,7 +702,7 @@ mtk_dpi_bridge_mode_valid(struct drm_bridge *bridge,
 {
 	struct mtk_dpi *dpi = bridge_to_dpi(bridge);
 
-	if (mode->clock > dpi->conf->max_clock_khz)
+	if (dpi->conf->max_clock_khz && mode->clock > dpi->conf->max_clock_khz)
 		return MODE_CLOCK_HIGH;
 
 	return MODE_OK;
@@ -781,6 +816,16 @@ static unsigned int mt8183_calculate_factor(int clock)
 		return 2;
 }
 
+static unsigned int mt8195_dpintf_calculate_factor(int clock)
+{
+	if (clock < 70000)
+		return 4;
+	else if (clock < 200000)
+		return 2;
+	else
+		return 1;
+}
+
 static const u32 mt8173_output_fmts[] = {
 	MEDIA_BUS_FMT_RGB888_1X24,
 };
@@ -790,12 +835,34 @@ static const u32 mt8183_output_fmts[] = {
 	MEDIA_BUS_FMT_RGB888_2X12_BE,
 };
 
+static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
+	.c_bottom = 0x0010,
+	.c_top = 0x0FE0,
+	.y_bottom = 0x0010,
+	.y_top = 0x0FE0,
+};
+
+static const struct mtk_dpi_yc_limit mtk_dpintf_limit = {
+	.c_bottom = 0x0000,
+	.c_top = 0xFFF,
+	.y_bottom = 0x0000,
+	.y_top = 0xFFF,
+};
+
 static const struct mtk_dpi_conf mt8173_conf = {
 	.cal_factor = mt8173_calculate_factor,
 	.reg_h_fre_con = 0xe0,
 	.max_clock_khz = 300000,
 	.output_fmts = mt8173_output_fmts,
 	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.is_ck_de_pol = true,
+	.csc_support = true,
+	.swap_input_support = true,
+	.dimension_mask = HPW_MASK,
+	.hvsize_mask = HSIZE_MASK,
+	.channel_swap_shift = CH_SWAP,
+	.yuv422_en_bit = YUV422_EN,
+	.limit = &mtk_dpi_limit,
 };
 
 static const struct mtk_dpi_conf mt2701_conf = {
@@ -805,6 +872,14 @@ static const struct mtk_dpi_conf mt2701_conf = {
 	.max_clock_khz = 150000,
 	.output_fmts = mt8173_output_fmts,
 	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.is_ck_de_pol = true,
+	.csc_support = true,
+	.swap_input_support = true,
+	.dimension_mask = HPW_MASK,
+	.hvsize_mask = HSIZE_MASK,
+	.channel_swap_shift = CH_SWAP,
+	.yuv422_en_bit = YUV422_EN,
+	.limit = &mtk_dpi_limit,
 };
 
 static const struct mtk_dpi_conf mt8183_conf = {
@@ -813,6 +888,14 @@ static const struct mtk_dpi_conf mt8183_conf = {
 	.max_clock_khz = 100000,
 	.output_fmts = mt8183_output_fmts,
 	.num_output_fmts = ARRAY_SIZE(mt8183_output_fmts),
+	.is_ck_de_pol = true,
+	.csc_support = true,
+	.swap_input_support = true,
+	.dimension_mask = HPW_MASK,
+	.hvsize_mask = HSIZE_MASK,
+	.channel_swap_shift = CH_SWAP,
+	.yuv422_en_bit = YUV422_EN,
+	.limit = &mtk_dpi_limit,
 };
 
 static const struct mtk_dpi_conf mt8192_conf = {
@@ -821,6 +904,26 @@ static const struct mtk_dpi_conf mt8192_conf = {
 	.max_clock_khz = 150000,
 	.output_fmts = mt8173_output_fmts,
 	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.is_ck_de_pol = true,
+	.csc_support = true,
+	.swap_input_support = true,
+	.dimension_mask = HPW_MASK,
+	.hvsize_mask = HSIZE_MASK,
+	.channel_swap_shift = CH_SWAP,
+	.yuv422_en_bit = YUV422_EN,
+	.limit = &mtk_dpi_limit,
+};
+
+static const struct mtk_dpi_conf mt8195_dpintf_conf = {
+	.cal_factor = mt8195_dpintf_calculate_factor,
+	.output_fmts = mt8173_output_fmts,
+	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.is_dpintf = true,
+	.dimension_mask = DPINTF_HPW_MASK,
+	.hvsize_mask = DPINTF_HSIZE_MASK,
+	.channel_swap_shift = DPINTF_CH_SWAP,
+	.yuv422_en_bit = DPINTF_YUV422_EN,
+	.limit = &mtk_dpintf_limit,
 };
 
 static int mtk_dpi_probe(struct platform_device *pdev)
@@ -870,7 +973,8 @@ static int mtk_dpi_probe(struct platform_device *pdev)
 	if (IS_ERR(dpi->engine_clk)) {
 		ret = PTR_ERR(dpi->engine_clk);
 		if (ret != -EPROBE_DEFER)
-			dev_err(dev, "Failed to get engine clock: %d\n", ret);
+			dev_err(dev, "Failed to get engine clock: %d\n",
+				ret);
 
 		return ret;
 	}
@@ -945,6 +1049,9 @@ static const struct of_device_id mtk_dpi_of_ids[] = {
 	{ .compatible = "mediatek,mt8192-dpi",
 	  .data = &mt8192_conf,
 	},
+	{ .compatible = "mediatek,mt8195-dpintf",
+	  .data = &mt8195_dpintf_conf,
+	},
 	{ },
 };
 MODULE_DEVICE_TABLE(of, mtk_dpi_of_ids);
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi_regs.h b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
index 3a02fabe16627..72efe6ee25842 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
+++ b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
@@ -40,10 +40,14 @@
 #define FAKE_DE_LEVEN			BIT(21)
 #define FAKE_DE_RODD			BIT(22)
 #define FAKE_DE_REVEN			BIT(23)
+#define DPINTF_YUV422_EN		BIT(24)
+#define DPINTF_INPUT_2P_EN		BIT(29)
 
 #define DPI_OUTPUT_SETTING	0x14
 #define CH_SWAP				0
+#define DPINTF_CH_SWAP			BIT(1)
 #define CH_SWAP_MASK			(0x7 << 0)
+#define DPINTF_CH_SWAP_MASK		(0x7 << 1)
 #define SWAP_RGB			0x00
 #define SWAP_GBR			0x01
 #define SWAP_BRG			0x02
@@ -80,8 +84,10 @@
 #define DPI_SIZE		0x18
 #define HSIZE				0
 #define HSIZE_MASK			(0x1FFF << 0)
+#define DPINTF_HSIZE_MASK		(0xFFFF << 0)
 #define VSIZE				16
 #define VSIZE_MASK			(0x1FFF << 16)
+#define DPINTF_VSIZE_MASK		(0xFFFF << 16)
 
 #define DPI_DDR_SETTING		0x1C
 #define DDR_EN				BIT(0)
@@ -93,24 +99,30 @@
 #define DPI_TGEN_HWIDTH		0x20
 #define HPW				0
 #define HPW_MASK			(0xFFF << 0)
+#define DPINTF_HPW_MASK			(0xFFFF << 0)
 
 #define DPI_TGEN_HPORCH		0x24
 #define HBP				0
 #define HBP_MASK			(0xFFF << 0)
+#define DPINTF_HBP_MASK			(0xFFFF << 0)
 #define HFP				16
 #define HFP_MASK			(0xFFF << 16)
+#define DPINTF_HFP_MASK			(0xFFFF << 16)
 
 #define DPI_TGEN_VWIDTH		0x28
 #define DPI_TGEN_VPORCH		0x2C
 
 #define VSYNC_WIDTH_SHIFT		0
 #define VSYNC_WIDTH_MASK		(0xFFF << 0)
+#define DPINTF_VSYNC_WIDTH_MASK		(0xFFFF << 0)
 #define VSYNC_HALF_LINE_SHIFT		16
 #define VSYNC_HALF_LINE_MASK		BIT(16)
 #define VSYNC_BACK_PORCH_SHIFT		0
 #define VSYNC_BACK_PORCH_MASK		(0xFFF << 0)
+#define DPINTF_VSYNC_BACK_PORCH_MASK	(0xFFFF << 0)
 #define VSYNC_FRONT_PORCH_SHIFT		16
 #define VSYNC_FRONT_PORCH_MASK		(0xFFF << 16)
+#define DPINTF_VSYNC_FRONT_PORCH_MASK	(0xFFFF << 16)
 
 #define DPI_BG_HCNTL		0x30
 #define BG_RIGHT			(0x1FFF << 0)
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
index 99cbf44463e40..da9e059312a5d 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -317,6 +317,7 @@ static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
 	[MTK_DISP_UFOE] = "ufoe",
 	[MTK_DSI] = "dsi",
 	[MTK_DPI] = "dpi",
+	[MTK_DP_INTF] = "dp-intf",
 	[MTK_DISP_PWM] = "pwm",
 	[MTK_DISP_MUTEX] = "mutex",
 	[MTK_DISP_OD] = "od",
@@ -339,6 +340,8 @@ static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
 	[DDP_COMPONENT_DITHER]	= { MTK_DISP_DITHER,	0, &ddp_dither },
 	[DDP_COMPONENT_DPI0]	= { MTK_DPI,		0, &ddp_dpi },
 	[DDP_COMPONENT_DPI1]	= { MTK_DPI,		1, &ddp_dpi },
+	[DDP_COMPONENT_DP_INTF0]= { MTK_DP_INTF,	0, &ddp_dpi },
+	[DDP_COMPONENT_DP_INTF1]= { MTK_DP_INTF,	1, &ddp_dpi },
 	[DDP_COMPONENT_DSI0]	= { MTK_DSI,		0, &ddp_dsi },
 	[DDP_COMPONENT_DSI1]	= { MTK_DSI,		1, &ddp_dsi },
 	[DDP_COMPONENT_DSI2]	= { MTK_DSI,		2, &ddp_dsi },
@@ -476,6 +479,7 @@ int mtk_ddp_comp_init(struct device_node *node, struct mtk_ddp_comp *comp,
 	    type == MTK_DISP_COLOR ||
 	    type == MTK_DISP_GAMMA ||
 	    type == MTK_DPI ||
+	    type == MTK_DP_INTF ||
 	    type == MTK_DSI ||
 	    type == MTK_DISP_OVL ||
 	    type == MTK_DISP_OVL_2L ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
index bb914d976cf5d..ee9d853cfa1c1 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
@@ -30,6 +30,7 @@ enum mtk_ddp_comp_type {
 	MTK_DISP_UFOE,
 	MTK_DSI,
 	MTK_DPI,
+	MTK_DP_INTF,
 	MTK_DISP_PWM,
 	MTK_DISP_MUTEX,
 	MTK_DISP_OD,
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index aec39724ebeb6..1ff4e31c86345 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -459,6 +459,8 @@ static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
 	  .data = (void *)MTK_DPI },
 	{ .compatible = "mediatek,mt8183-dpi",
 	  .data = (void *)MTK_DPI },
+	{ .compatible = "mediatek,mt8195-dpintf",
+	  .data = (void *)MTK_DP_INTF },
 	{ .compatible = "mediatek,mt2701-disp-mutex",
 	  .data = (void *)MTK_DISP_MUTEX },
 	{ .compatible = "mediatek,mt2712-disp-mutex",
@@ -569,7 +571,8 @@ static int mtk_drm_probe(struct platform_device *pdev)
 		    comp_type == MTK_DISP_OVL_2L ||
 		    comp_type == MTK_DISP_RDMA ||
 		    comp_type == MTK_DSI ||
-		    comp_type == MTK_DPI) {
+		    comp_type == MTK_DPI ||
+		    comp_type == MTK_DP_INTF) {
 			dev_info(dev, "Adding component match for %pOF\n",
 				 node);
 			drm_of_component_match_add(dev, &match, compare_of,
diff --git a/include/linux/soc/mediatek/mtk-mmsys.h b/include/linux/soc/mediatek/mtk-mmsys.h
index 4bba275e235ac..56ed2fa5f59e8 100644
--- a/include/linux/soc/mediatek/mtk-mmsys.h
+++ b/include/linux/soc/mediatek/mtk-mmsys.h
@@ -19,6 +19,8 @@ enum mtk_ddp_comp_id {
 	DDP_COMPONENT_DITHER,
 	DDP_COMPONENT_DPI0,
 	DDP_COMPONENT_DPI1,
+	DDP_COMPONENT_DP_INTF0,
+	DDP_COMPONENT_DP_INTF1,
 	DDP_COMPONENT_DSI0,
 	DDP_COMPONENT_DSI1,
 	DDP_COMPONENT_DSI2,
-- 
2.32.0


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

* [PATCH v6 5/7] drm/mediatek: dpi: Add dpintf support
@ 2021-11-10 13:06   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Chun-Kuang Hu, Philipp Zabel, David Airlie, Daniel Vetter,
	Matthias Brugger
  Cc: Markus Schneider-Pargmann, dri-devel, linux-mediatek,
	linux-arm-kernel, linux-kernel

From: Markus Schneider-Pargmann <msp@baylibre.com>

dpintf is the displayport interface hardware unit. This unit is similar
to dpi and can reuse most of the code.

This patch adds support for mt8195-dpintf to this dpi driver. Main
differences are:
 - Some features/functional components are not available for dpintf
   which are now excluded from code execution once is_dpintf is set
 - dpintf can and needs to choose between different clockdividers based
   on the clockspeed. This is done by choosing a different clock parent.
 - There are two additional clocks that need to be managed. These are
   only set for dpintf and will be set to NULL if not supplied. The
   clk_* calls handle these as normal clocks then.
 - Some register contents differ slightly between the two components. To
   work around this I added register bits/masks with a DPINTF_ prefix
   and use them where different.

Based on a separate driver for dpintf created by
Jason-JH.Lin <jason-jh.lin@mediatek.com>.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
---
 drivers/gpu/drm/mediatek/mtk_dpi.c          | 199 +++++++++++++++-----
 drivers/gpu/drm/mediatek/mtk_dpi_regs.h     |  12 ++
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |   4 +
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |   1 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.c      |   5 +-
 include/linux/soc/mediatek/mtk-mmsys.h      |   2 +
 6 files changed, 176 insertions(+), 47 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
index 4554e2de14309..384074f69111b 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
@@ -125,6 +125,17 @@ struct mtk_dpi_conf {
 	bool edge_sel_en;
 	const u32 *output_fmts;
 	u32 num_output_fmts;
+	bool is_ck_de_pol;
+	bool is_dpintf;
+	bool csc_support;
+	bool swap_input_support;
+	// Mask used for HWIDTH, HPORCH, VSYNC_WIDTH and VSYNC_PORCH (no shift)
+	u32 dimension_mask;
+	// Mask used for HSIZE and VSIZE (no shift)
+	u32 hvsize_mask;
+	u32 channel_swap_shift;
+	u32 yuv422_en_bit;
+	const struct mtk_dpi_yc_limit *limit;
 };
 
 static void mtk_dpi_mask(struct mtk_dpi *dpi, u32 offset, u32 val, u32 mask)
@@ -153,30 +164,30 @@ static void mtk_dpi_disable(struct mtk_dpi *dpi)
 static void mtk_dpi_config_hsync(struct mtk_dpi *dpi,
 				 struct mtk_dpi_sync_param *sync)
 {
-	mtk_dpi_mask(dpi, DPI_TGEN_HWIDTH,
-		     sync->sync_width << HPW, HPW_MASK);
-	mtk_dpi_mask(dpi, DPI_TGEN_HPORCH,
-		     sync->back_porch << HBP, HBP_MASK);
+	mtk_dpi_mask(dpi, DPI_TGEN_HWIDTH, sync->sync_width << HPW,
+		     dpi->conf->dimension_mask << HPW);
+	mtk_dpi_mask(dpi, DPI_TGEN_HPORCH, sync->back_porch << HBP,
+		     dpi->conf->dimension_mask << HBP);
 	mtk_dpi_mask(dpi, DPI_TGEN_HPORCH, sync->front_porch << HFP,
-		     HFP_MASK);
+		     dpi->conf->dimension_mask << HFP);
 }
 
 static void mtk_dpi_config_vsync(struct mtk_dpi *dpi,
 				 struct mtk_dpi_sync_param *sync,
 				 u32 width_addr, u32 porch_addr)
 {
-	mtk_dpi_mask(dpi, width_addr,
-		     sync->sync_width << VSYNC_WIDTH_SHIFT,
-		     VSYNC_WIDTH_MASK);
 	mtk_dpi_mask(dpi, width_addr,
 		     sync->shift_half_line << VSYNC_HALF_LINE_SHIFT,
 		     VSYNC_HALF_LINE_MASK);
+	mtk_dpi_mask(dpi, width_addr,
+		     sync->sync_width << VSYNC_WIDTH_SHIFT,
+		     dpi->conf->dimension_mask << VSYNC_WIDTH_SHIFT);
 	mtk_dpi_mask(dpi, porch_addr,
 		     sync->back_porch << VSYNC_BACK_PORCH_SHIFT,
-		     VSYNC_BACK_PORCH_MASK);
+		     dpi->conf->dimension_mask << VSYNC_BACK_PORCH_SHIFT);
 	mtk_dpi_mask(dpi, porch_addr,
 		     sync->front_porch << VSYNC_FRONT_PORCH_SHIFT,
-		     VSYNC_FRONT_PORCH_MASK);
+		     dpi->conf->dimension_mask << VSYNC_FRONT_PORCH_SHIFT);
 }
 
 static void mtk_dpi_config_vsync_lodd(struct mtk_dpi *dpi,
@@ -210,13 +221,20 @@ static void mtk_dpi_config_pol(struct mtk_dpi *dpi,
 			       struct mtk_dpi_polarities *dpi_pol)
 {
 	unsigned int pol;
+	unsigned int mask;
 
-	pol = (dpi_pol->ck_pol == MTK_DPI_POLARITY_RISING ? 0 : CK_POL) |
-	      (dpi_pol->de_pol == MTK_DPI_POLARITY_RISING ? 0 : DE_POL) |
-	      (dpi_pol->hsync_pol == MTK_DPI_POLARITY_RISING ? 0 : HSYNC_POL) |
+	mask = HSYNC_POL | VSYNC_POL;
+	pol = (dpi_pol->hsync_pol == MTK_DPI_POLARITY_RISING ? 0 : HSYNC_POL) |
 	      (dpi_pol->vsync_pol == MTK_DPI_POLARITY_RISING ? 0 : VSYNC_POL);
-	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, pol,
-		     CK_POL | DE_POL | HSYNC_POL | VSYNC_POL);
+	if (dpi->conf->is_ck_de_pol) {
+		mask |= CK_POL | DE_POL;
+		pol |= (dpi_pol->ck_pol == MTK_DPI_POLARITY_RISING ?
+			0 : CK_POL) |
+		       (dpi_pol->de_pol == MTK_DPI_POLARITY_RISING ?
+			0 : DE_POL);
+	}
+
+	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, pol, mask);
 }
 
 static void mtk_dpi_config_3d(struct mtk_dpi *dpi, bool en_3d)
@@ -231,13 +249,16 @@ static void mtk_dpi_config_interface(struct mtk_dpi *dpi, bool inter)
 
 static void mtk_dpi_config_fb_size(struct mtk_dpi *dpi, u32 width, u32 height)
 {
-	mtk_dpi_mask(dpi, DPI_SIZE, width << HSIZE, HSIZE_MASK);
-	mtk_dpi_mask(dpi, DPI_SIZE, height << VSIZE, VSIZE_MASK);
+	mtk_dpi_mask(dpi, DPI_SIZE, width << HSIZE,
+		     dpi->conf->hvsize_mask << HSIZE);
+	mtk_dpi_mask(dpi, DPI_SIZE, height << VSIZE,
+		     dpi->conf->hvsize_mask << VSIZE);
 }
 
-static void mtk_dpi_config_channel_limit(struct mtk_dpi *dpi,
-					 struct mtk_dpi_yc_limit *limit)
+static void mtk_dpi_config_channel_limit(struct mtk_dpi *dpi)
 {
+	const struct mtk_dpi_yc_limit *limit = dpi->conf->limit;
+
 	mtk_dpi_mask(dpi, DPI_Y_LIMIT, limit->y_bottom << Y_LIMINT_BOT,
 		     Y_LIMINT_BOT_MASK);
 	mtk_dpi_mask(dpi, DPI_Y_LIMIT, limit->y_top << Y_LIMINT_TOP,
@@ -332,12 +353,14 @@ static void mtk_dpi_config_channel_swap(struct mtk_dpi *dpi,
 		break;
 	}
 
-	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, val << CH_SWAP, CH_SWAP_MASK);
+	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, val << CH_SWAP,
+		     CH_SWAP_MASK << dpi->conf->channel_swap_shift);
 }
 
 static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
 {
-	mtk_dpi_mask(dpi, DPI_CON, enable ? YUV422_EN : 0, YUV422_EN);
+	mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->yuv422_en_bit : 0,
+		     dpi->conf->yuv422_en_bit);
 }
 
 static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
@@ -367,19 +390,25 @@ static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
 	if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
 	    (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
 		mtk_dpi_config_yuv422_enable(dpi, false);
-		mtk_dpi_config_csc_enable(dpi, true);
-		mtk_dpi_config_swap_input(dpi, false);
+		if (dpi->conf->csc_support)
+			mtk_dpi_config_csc_enable(dpi, true);
+		if (dpi->conf->swap_input_support)
+			mtk_dpi_config_swap_input(dpi, false);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
 	} else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
 		   (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
 		mtk_dpi_config_yuv422_enable(dpi, true);
-		mtk_dpi_config_csc_enable(dpi, true);
-		mtk_dpi_config_swap_input(dpi, true);
+		if (dpi->conf->csc_support)
+			mtk_dpi_config_csc_enable(dpi, true);
+		if (dpi->conf->swap_input_support)
+			mtk_dpi_config_swap_input(dpi, true);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
 	} else {
 		mtk_dpi_config_yuv422_enable(dpi, false);
-		mtk_dpi_config_csc_enable(dpi, false);
-		mtk_dpi_config_swap_input(dpi, false);
+		if (dpi->conf->csc_support)
+			mtk_dpi_config_csc_enable(dpi, false);
+		if (dpi->conf->swap_input_support)
+			mtk_dpi_config_swap_input(dpi, false);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
 	}
 }
@@ -449,7 +478,6 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
 static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 				    struct drm_display_mode *mode)
 {
-	struct mtk_dpi_yc_limit limit;
 	struct mtk_dpi_polarities dpi_pol;
 	struct mtk_dpi_sync_param hsync;
 	struct mtk_dpi_sync_param vsync_lodd = { 0 };
@@ -472,32 +500,34 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 	pll_rate = clk_get_rate(dpi->tvd_clk);
 
 	vm.pixelclock = pll_rate / factor;
-	if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
-	    (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
+	if (dpi->conf->is_dpintf)
+		clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
+	else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
+		 (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
 		clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
 	else
 		clk_set_rate(dpi->pixel_clk, vm.pixelclock);
 
-
 	vm.pixelclock = clk_get_rate(dpi->pixel_clk);
 
 	dev_dbg(dpi->dev, "Got  PLL %lu Hz, pixel clock %lu Hz\n",
 		pll_rate, vm.pixelclock);
 
-	limit.c_bottom = 0x0010;
-	limit.c_top = 0x0FE0;
-	limit.y_bottom = 0x0010;
-	limit.y_top = 0x0FE0;
-
 	dpi_pol.ck_pol = MTK_DPI_POLARITY_FALLING;
 	dpi_pol.de_pol = MTK_DPI_POLARITY_RISING;
 	dpi_pol.hsync_pol = vm.flags & DISPLAY_FLAGS_HSYNC_HIGH ?
 			    MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING;
 	dpi_pol.vsync_pol = vm.flags & DISPLAY_FLAGS_VSYNC_HIGH ?
 			    MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING;
-	hsync.sync_width = vm.hsync_len;
-	hsync.back_porch = vm.hback_porch;
-	hsync.front_porch = vm.hfront_porch;
+	if (dpi->conf->is_dpintf) {
+		hsync.sync_width = vm.hsync_len / 4;
+		hsync.back_porch = vm.hback_porch / 4;
+		hsync.front_porch = vm.hfront_porch / 4;
+	} else {
+		hsync.sync_width = vm.hsync_len;
+		hsync.back_porch = vm.hback_porch;
+		hsync.front_porch = vm.hfront_porch;
+	}
 	hsync.shift_half_line = false;
 	vsync_lodd.sync_width = vm.vsync_len;
 	vsync_lodd.back_porch = vm.vback_porch;
@@ -536,14 +566,19 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 	else
 		mtk_dpi_config_fb_size(dpi, vm.hactive, vm.vactive);
 
-	mtk_dpi_config_channel_limit(dpi, &limit);
+	mtk_dpi_config_channel_limit(dpi);
 	mtk_dpi_config_bit_num(dpi, dpi->bit_num);
 	mtk_dpi_config_channel_swap(dpi, dpi->channel_swap);
-	mtk_dpi_config_yc_map(dpi, dpi->yc_map);
 	mtk_dpi_config_color_format(dpi, dpi->color_format);
-	mtk_dpi_config_2n_h_fre(dpi);
-	mtk_dpi_dual_edge(dpi);
-	mtk_dpi_config_disable_edge(dpi);
+	if (dpi->conf->is_dpintf) {
+		mtk_dpi_mask(dpi, DPI_CON, DPINTF_INPUT_2P_EN,
+			     DPINTF_INPUT_2P_EN);
+	} else {
+		mtk_dpi_config_yc_map(dpi, dpi->yc_map);
+		mtk_dpi_config_2n_h_fre(dpi);
+		mtk_dpi_dual_edge(dpi);
+		mtk_dpi_config_disable_edge(dpi);
+	}
 	mtk_dpi_sw_reset(dpi, false);
 
 	return 0;
@@ -667,7 +702,7 @@ mtk_dpi_bridge_mode_valid(struct drm_bridge *bridge,
 {
 	struct mtk_dpi *dpi = bridge_to_dpi(bridge);
 
-	if (mode->clock > dpi->conf->max_clock_khz)
+	if (dpi->conf->max_clock_khz && mode->clock > dpi->conf->max_clock_khz)
 		return MODE_CLOCK_HIGH;
 
 	return MODE_OK;
@@ -781,6 +816,16 @@ static unsigned int mt8183_calculate_factor(int clock)
 		return 2;
 }
 
+static unsigned int mt8195_dpintf_calculate_factor(int clock)
+{
+	if (clock < 70000)
+		return 4;
+	else if (clock < 200000)
+		return 2;
+	else
+		return 1;
+}
+
 static const u32 mt8173_output_fmts[] = {
 	MEDIA_BUS_FMT_RGB888_1X24,
 };
@@ -790,12 +835,34 @@ static const u32 mt8183_output_fmts[] = {
 	MEDIA_BUS_FMT_RGB888_2X12_BE,
 };
 
+static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
+	.c_bottom = 0x0010,
+	.c_top = 0x0FE0,
+	.y_bottom = 0x0010,
+	.y_top = 0x0FE0,
+};
+
+static const struct mtk_dpi_yc_limit mtk_dpintf_limit = {
+	.c_bottom = 0x0000,
+	.c_top = 0xFFF,
+	.y_bottom = 0x0000,
+	.y_top = 0xFFF,
+};
+
 static const struct mtk_dpi_conf mt8173_conf = {
 	.cal_factor = mt8173_calculate_factor,
 	.reg_h_fre_con = 0xe0,
 	.max_clock_khz = 300000,
 	.output_fmts = mt8173_output_fmts,
 	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.is_ck_de_pol = true,
+	.csc_support = true,
+	.swap_input_support = true,
+	.dimension_mask = HPW_MASK,
+	.hvsize_mask = HSIZE_MASK,
+	.channel_swap_shift = CH_SWAP,
+	.yuv422_en_bit = YUV422_EN,
+	.limit = &mtk_dpi_limit,
 };
 
 static const struct mtk_dpi_conf mt2701_conf = {
@@ -805,6 +872,14 @@ static const struct mtk_dpi_conf mt2701_conf = {
 	.max_clock_khz = 150000,
 	.output_fmts = mt8173_output_fmts,
 	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.is_ck_de_pol = true,
+	.csc_support = true,
+	.swap_input_support = true,
+	.dimension_mask = HPW_MASK,
+	.hvsize_mask = HSIZE_MASK,
+	.channel_swap_shift = CH_SWAP,
+	.yuv422_en_bit = YUV422_EN,
+	.limit = &mtk_dpi_limit,
 };
 
 static const struct mtk_dpi_conf mt8183_conf = {
@@ -813,6 +888,14 @@ static const struct mtk_dpi_conf mt8183_conf = {
 	.max_clock_khz = 100000,
 	.output_fmts = mt8183_output_fmts,
 	.num_output_fmts = ARRAY_SIZE(mt8183_output_fmts),
+	.is_ck_de_pol = true,
+	.csc_support = true,
+	.swap_input_support = true,
+	.dimension_mask = HPW_MASK,
+	.hvsize_mask = HSIZE_MASK,
+	.channel_swap_shift = CH_SWAP,
+	.yuv422_en_bit = YUV422_EN,
+	.limit = &mtk_dpi_limit,
 };
 
 static const struct mtk_dpi_conf mt8192_conf = {
@@ -821,6 +904,26 @@ static const struct mtk_dpi_conf mt8192_conf = {
 	.max_clock_khz = 150000,
 	.output_fmts = mt8173_output_fmts,
 	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.is_ck_de_pol = true,
+	.csc_support = true,
+	.swap_input_support = true,
+	.dimension_mask = HPW_MASK,
+	.hvsize_mask = HSIZE_MASK,
+	.channel_swap_shift = CH_SWAP,
+	.yuv422_en_bit = YUV422_EN,
+	.limit = &mtk_dpi_limit,
+};
+
+static const struct mtk_dpi_conf mt8195_dpintf_conf = {
+	.cal_factor = mt8195_dpintf_calculate_factor,
+	.output_fmts = mt8173_output_fmts,
+	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.is_dpintf = true,
+	.dimension_mask = DPINTF_HPW_MASK,
+	.hvsize_mask = DPINTF_HSIZE_MASK,
+	.channel_swap_shift = DPINTF_CH_SWAP,
+	.yuv422_en_bit = DPINTF_YUV422_EN,
+	.limit = &mtk_dpintf_limit,
 };
 
 static int mtk_dpi_probe(struct platform_device *pdev)
@@ -870,7 +973,8 @@ static int mtk_dpi_probe(struct platform_device *pdev)
 	if (IS_ERR(dpi->engine_clk)) {
 		ret = PTR_ERR(dpi->engine_clk);
 		if (ret != -EPROBE_DEFER)
-			dev_err(dev, "Failed to get engine clock: %d\n", ret);
+			dev_err(dev, "Failed to get engine clock: %d\n",
+				ret);
 
 		return ret;
 	}
@@ -945,6 +1049,9 @@ static const struct of_device_id mtk_dpi_of_ids[] = {
 	{ .compatible = "mediatek,mt8192-dpi",
 	  .data = &mt8192_conf,
 	},
+	{ .compatible = "mediatek,mt8195-dpintf",
+	  .data = &mt8195_dpintf_conf,
+	},
 	{ },
 };
 MODULE_DEVICE_TABLE(of, mtk_dpi_of_ids);
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi_regs.h b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
index 3a02fabe16627..72efe6ee25842 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
+++ b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
@@ -40,10 +40,14 @@
 #define FAKE_DE_LEVEN			BIT(21)
 #define FAKE_DE_RODD			BIT(22)
 #define FAKE_DE_REVEN			BIT(23)
+#define DPINTF_YUV422_EN		BIT(24)
+#define DPINTF_INPUT_2P_EN		BIT(29)
 
 #define DPI_OUTPUT_SETTING	0x14
 #define CH_SWAP				0
+#define DPINTF_CH_SWAP			BIT(1)
 #define CH_SWAP_MASK			(0x7 << 0)
+#define DPINTF_CH_SWAP_MASK		(0x7 << 1)
 #define SWAP_RGB			0x00
 #define SWAP_GBR			0x01
 #define SWAP_BRG			0x02
@@ -80,8 +84,10 @@
 #define DPI_SIZE		0x18
 #define HSIZE				0
 #define HSIZE_MASK			(0x1FFF << 0)
+#define DPINTF_HSIZE_MASK		(0xFFFF << 0)
 #define VSIZE				16
 #define VSIZE_MASK			(0x1FFF << 16)
+#define DPINTF_VSIZE_MASK		(0xFFFF << 16)
 
 #define DPI_DDR_SETTING		0x1C
 #define DDR_EN				BIT(0)
@@ -93,24 +99,30 @@
 #define DPI_TGEN_HWIDTH		0x20
 #define HPW				0
 #define HPW_MASK			(0xFFF << 0)
+#define DPINTF_HPW_MASK			(0xFFFF << 0)
 
 #define DPI_TGEN_HPORCH		0x24
 #define HBP				0
 #define HBP_MASK			(0xFFF << 0)
+#define DPINTF_HBP_MASK			(0xFFFF << 0)
 #define HFP				16
 #define HFP_MASK			(0xFFF << 16)
+#define DPINTF_HFP_MASK			(0xFFFF << 16)
 
 #define DPI_TGEN_VWIDTH		0x28
 #define DPI_TGEN_VPORCH		0x2C
 
 #define VSYNC_WIDTH_SHIFT		0
 #define VSYNC_WIDTH_MASK		(0xFFF << 0)
+#define DPINTF_VSYNC_WIDTH_MASK		(0xFFFF << 0)
 #define VSYNC_HALF_LINE_SHIFT		16
 #define VSYNC_HALF_LINE_MASK		BIT(16)
 #define VSYNC_BACK_PORCH_SHIFT		0
 #define VSYNC_BACK_PORCH_MASK		(0xFFF << 0)
+#define DPINTF_VSYNC_BACK_PORCH_MASK	(0xFFFF << 0)
 #define VSYNC_FRONT_PORCH_SHIFT		16
 #define VSYNC_FRONT_PORCH_MASK		(0xFFF << 16)
+#define DPINTF_VSYNC_FRONT_PORCH_MASK	(0xFFFF << 16)
 
 #define DPI_BG_HCNTL		0x30
 #define BG_RIGHT			(0x1FFF << 0)
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
index 99cbf44463e40..da9e059312a5d 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -317,6 +317,7 @@ static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
 	[MTK_DISP_UFOE] = "ufoe",
 	[MTK_DSI] = "dsi",
 	[MTK_DPI] = "dpi",
+	[MTK_DP_INTF] = "dp-intf",
 	[MTK_DISP_PWM] = "pwm",
 	[MTK_DISP_MUTEX] = "mutex",
 	[MTK_DISP_OD] = "od",
@@ -339,6 +340,8 @@ static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
 	[DDP_COMPONENT_DITHER]	= { MTK_DISP_DITHER,	0, &ddp_dither },
 	[DDP_COMPONENT_DPI0]	= { MTK_DPI,		0, &ddp_dpi },
 	[DDP_COMPONENT_DPI1]	= { MTK_DPI,		1, &ddp_dpi },
+	[DDP_COMPONENT_DP_INTF0]= { MTK_DP_INTF,	0, &ddp_dpi },
+	[DDP_COMPONENT_DP_INTF1]= { MTK_DP_INTF,	1, &ddp_dpi },
 	[DDP_COMPONENT_DSI0]	= { MTK_DSI,		0, &ddp_dsi },
 	[DDP_COMPONENT_DSI1]	= { MTK_DSI,		1, &ddp_dsi },
 	[DDP_COMPONENT_DSI2]	= { MTK_DSI,		2, &ddp_dsi },
@@ -476,6 +479,7 @@ int mtk_ddp_comp_init(struct device_node *node, struct mtk_ddp_comp *comp,
 	    type == MTK_DISP_COLOR ||
 	    type == MTK_DISP_GAMMA ||
 	    type == MTK_DPI ||
+	    type == MTK_DP_INTF ||
 	    type == MTK_DSI ||
 	    type == MTK_DISP_OVL ||
 	    type == MTK_DISP_OVL_2L ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
index bb914d976cf5d..ee9d853cfa1c1 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
@@ -30,6 +30,7 @@ enum mtk_ddp_comp_type {
 	MTK_DISP_UFOE,
 	MTK_DSI,
 	MTK_DPI,
+	MTK_DP_INTF,
 	MTK_DISP_PWM,
 	MTK_DISP_MUTEX,
 	MTK_DISP_OD,
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index aec39724ebeb6..1ff4e31c86345 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -459,6 +459,8 @@ static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
 	  .data = (void *)MTK_DPI },
 	{ .compatible = "mediatek,mt8183-dpi",
 	  .data = (void *)MTK_DPI },
+	{ .compatible = "mediatek,mt8195-dpintf",
+	  .data = (void *)MTK_DP_INTF },
 	{ .compatible = "mediatek,mt2701-disp-mutex",
 	  .data = (void *)MTK_DISP_MUTEX },
 	{ .compatible = "mediatek,mt2712-disp-mutex",
@@ -569,7 +571,8 @@ static int mtk_drm_probe(struct platform_device *pdev)
 		    comp_type == MTK_DISP_OVL_2L ||
 		    comp_type == MTK_DISP_RDMA ||
 		    comp_type == MTK_DSI ||
-		    comp_type == MTK_DPI) {
+		    comp_type == MTK_DPI ||
+		    comp_type == MTK_DP_INTF) {
 			dev_info(dev, "Adding component match for %pOF\n",
 				 node);
 			drm_of_component_match_add(dev, &match, compare_of,
diff --git a/include/linux/soc/mediatek/mtk-mmsys.h b/include/linux/soc/mediatek/mtk-mmsys.h
index 4bba275e235ac..56ed2fa5f59e8 100644
--- a/include/linux/soc/mediatek/mtk-mmsys.h
+++ b/include/linux/soc/mediatek/mtk-mmsys.h
@@ -19,6 +19,8 @@ enum mtk_ddp_comp_id {
 	DDP_COMPONENT_DITHER,
 	DDP_COMPONENT_DPI0,
 	DDP_COMPONENT_DPI1,
+	DDP_COMPONENT_DP_INTF0,
+	DDP_COMPONENT_DP_INTF1,
 	DDP_COMPONENT_DSI0,
 	DDP_COMPONENT_DSI1,
 	DDP_COMPONENT_DSI2,
-- 
2.32.0


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* [PATCH v6 5/7] drm/mediatek: dpi: Add dpintf support
@ 2021-11-10 13:06   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Chun-Kuang Hu, Philipp Zabel, David Airlie, Daniel Vetter,
	Matthias Brugger
  Cc: Markus Schneider-Pargmann, dri-devel, linux-mediatek,
	linux-arm-kernel, linux-kernel

From: Markus Schneider-Pargmann <msp@baylibre.com>

dpintf is the displayport interface hardware unit. This unit is similar
to dpi and can reuse most of the code.

This patch adds support for mt8195-dpintf to this dpi driver. Main
differences are:
 - Some features/functional components are not available for dpintf
   which are now excluded from code execution once is_dpintf is set
 - dpintf can and needs to choose between different clockdividers based
   on the clockspeed. This is done by choosing a different clock parent.
 - There are two additional clocks that need to be managed. These are
   only set for dpintf and will be set to NULL if not supplied. The
   clk_* calls handle these as normal clocks then.
 - Some register contents differ slightly between the two components. To
   work around this I added register bits/masks with a DPINTF_ prefix
   and use them where different.

Based on a separate driver for dpintf created by
Jason-JH.Lin <jason-jh.lin@mediatek.com>.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
---
 drivers/gpu/drm/mediatek/mtk_dpi.c          | 199 +++++++++++++++-----
 drivers/gpu/drm/mediatek/mtk_dpi_regs.h     |  12 ++
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |   4 +
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |   1 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.c      |   5 +-
 include/linux/soc/mediatek/mtk-mmsys.h      |   2 +
 6 files changed, 176 insertions(+), 47 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
index 4554e2de14309..384074f69111b 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
@@ -125,6 +125,17 @@ struct mtk_dpi_conf {
 	bool edge_sel_en;
 	const u32 *output_fmts;
 	u32 num_output_fmts;
+	bool is_ck_de_pol;
+	bool is_dpintf;
+	bool csc_support;
+	bool swap_input_support;
+	// Mask used for HWIDTH, HPORCH, VSYNC_WIDTH and VSYNC_PORCH (no shift)
+	u32 dimension_mask;
+	// Mask used for HSIZE and VSIZE (no shift)
+	u32 hvsize_mask;
+	u32 channel_swap_shift;
+	u32 yuv422_en_bit;
+	const struct mtk_dpi_yc_limit *limit;
 };
 
 static void mtk_dpi_mask(struct mtk_dpi *dpi, u32 offset, u32 val, u32 mask)
@@ -153,30 +164,30 @@ static void mtk_dpi_disable(struct mtk_dpi *dpi)
 static void mtk_dpi_config_hsync(struct mtk_dpi *dpi,
 				 struct mtk_dpi_sync_param *sync)
 {
-	mtk_dpi_mask(dpi, DPI_TGEN_HWIDTH,
-		     sync->sync_width << HPW, HPW_MASK);
-	mtk_dpi_mask(dpi, DPI_TGEN_HPORCH,
-		     sync->back_porch << HBP, HBP_MASK);
+	mtk_dpi_mask(dpi, DPI_TGEN_HWIDTH, sync->sync_width << HPW,
+		     dpi->conf->dimension_mask << HPW);
+	mtk_dpi_mask(dpi, DPI_TGEN_HPORCH, sync->back_porch << HBP,
+		     dpi->conf->dimension_mask << HBP);
 	mtk_dpi_mask(dpi, DPI_TGEN_HPORCH, sync->front_porch << HFP,
-		     HFP_MASK);
+		     dpi->conf->dimension_mask << HFP);
 }
 
 static void mtk_dpi_config_vsync(struct mtk_dpi *dpi,
 				 struct mtk_dpi_sync_param *sync,
 				 u32 width_addr, u32 porch_addr)
 {
-	mtk_dpi_mask(dpi, width_addr,
-		     sync->sync_width << VSYNC_WIDTH_SHIFT,
-		     VSYNC_WIDTH_MASK);
 	mtk_dpi_mask(dpi, width_addr,
 		     sync->shift_half_line << VSYNC_HALF_LINE_SHIFT,
 		     VSYNC_HALF_LINE_MASK);
+	mtk_dpi_mask(dpi, width_addr,
+		     sync->sync_width << VSYNC_WIDTH_SHIFT,
+		     dpi->conf->dimension_mask << VSYNC_WIDTH_SHIFT);
 	mtk_dpi_mask(dpi, porch_addr,
 		     sync->back_porch << VSYNC_BACK_PORCH_SHIFT,
-		     VSYNC_BACK_PORCH_MASK);
+		     dpi->conf->dimension_mask << VSYNC_BACK_PORCH_SHIFT);
 	mtk_dpi_mask(dpi, porch_addr,
 		     sync->front_porch << VSYNC_FRONT_PORCH_SHIFT,
-		     VSYNC_FRONT_PORCH_MASK);
+		     dpi->conf->dimension_mask << VSYNC_FRONT_PORCH_SHIFT);
 }
 
 static void mtk_dpi_config_vsync_lodd(struct mtk_dpi *dpi,
@@ -210,13 +221,20 @@ static void mtk_dpi_config_pol(struct mtk_dpi *dpi,
 			       struct mtk_dpi_polarities *dpi_pol)
 {
 	unsigned int pol;
+	unsigned int mask;
 
-	pol = (dpi_pol->ck_pol == MTK_DPI_POLARITY_RISING ? 0 : CK_POL) |
-	      (dpi_pol->de_pol == MTK_DPI_POLARITY_RISING ? 0 : DE_POL) |
-	      (dpi_pol->hsync_pol == MTK_DPI_POLARITY_RISING ? 0 : HSYNC_POL) |
+	mask = HSYNC_POL | VSYNC_POL;
+	pol = (dpi_pol->hsync_pol == MTK_DPI_POLARITY_RISING ? 0 : HSYNC_POL) |
 	      (dpi_pol->vsync_pol == MTK_DPI_POLARITY_RISING ? 0 : VSYNC_POL);
-	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, pol,
-		     CK_POL | DE_POL | HSYNC_POL | VSYNC_POL);
+	if (dpi->conf->is_ck_de_pol) {
+		mask |= CK_POL | DE_POL;
+		pol |= (dpi_pol->ck_pol == MTK_DPI_POLARITY_RISING ?
+			0 : CK_POL) |
+		       (dpi_pol->de_pol == MTK_DPI_POLARITY_RISING ?
+			0 : DE_POL);
+	}
+
+	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, pol, mask);
 }
 
 static void mtk_dpi_config_3d(struct mtk_dpi *dpi, bool en_3d)
@@ -231,13 +249,16 @@ static void mtk_dpi_config_interface(struct mtk_dpi *dpi, bool inter)
 
 static void mtk_dpi_config_fb_size(struct mtk_dpi *dpi, u32 width, u32 height)
 {
-	mtk_dpi_mask(dpi, DPI_SIZE, width << HSIZE, HSIZE_MASK);
-	mtk_dpi_mask(dpi, DPI_SIZE, height << VSIZE, VSIZE_MASK);
+	mtk_dpi_mask(dpi, DPI_SIZE, width << HSIZE,
+		     dpi->conf->hvsize_mask << HSIZE);
+	mtk_dpi_mask(dpi, DPI_SIZE, height << VSIZE,
+		     dpi->conf->hvsize_mask << VSIZE);
 }
 
-static void mtk_dpi_config_channel_limit(struct mtk_dpi *dpi,
-					 struct mtk_dpi_yc_limit *limit)
+static void mtk_dpi_config_channel_limit(struct mtk_dpi *dpi)
 {
+	const struct mtk_dpi_yc_limit *limit = dpi->conf->limit;
+
 	mtk_dpi_mask(dpi, DPI_Y_LIMIT, limit->y_bottom << Y_LIMINT_BOT,
 		     Y_LIMINT_BOT_MASK);
 	mtk_dpi_mask(dpi, DPI_Y_LIMIT, limit->y_top << Y_LIMINT_TOP,
@@ -332,12 +353,14 @@ static void mtk_dpi_config_channel_swap(struct mtk_dpi *dpi,
 		break;
 	}
 
-	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, val << CH_SWAP, CH_SWAP_MASK);
+	mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, val << CH_SWAP,
+		     CH_SWAP_MASK << dpi->conf->channel_swap_shift);
 }
 
 static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
 {
-	mtk_dpi_mask(dpi, DPI_CON, enable ? YUV422_EN : 0, YUV422_EN);
+	mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->yuv422_en_bit : 0,
+		     dpi->conf->yuv422_en_bit);
 }
 
 static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
@@ -367,19 +390,25 @@ static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
 	if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
 	    (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
 		mtk_dpi_config_yuv422_enable(dpi, false);
-		mtk_dpi_config_csc_enable(dpi, true);
-		mtk_dpi_config_swap_input(dpi, false);
+		if (dpi->conf->csc_support)
+			mtk_dpi_config_csc_enable(dpi, true);
+		if (dpi->conf->swap_input_support)
+			mtk_dpi_config_swap_input(dpi, false);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
 	} else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
 		   (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
 		mtk_dpi_config_yuv422_enable(dpi, true);
-		mtk_dpi_config_csc_enable(dpi, true);
-		mtk_dpi_config_swap_input(dpi, true);
+		if (dpi->conf->csc_support)
+			mtk_dpi_config_csc_enable(dpi, true);
+		if (dpi->conf->swap_input_support)
+			mtk_dpi_config_swap_input(dpi, true);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
 	} else {
 		mtk_dpi_config_yuv422_enable(dpi, false);
-		mtk_dpi_config_csc_enable(dpi, false);
-		mtk_dpi_config_swap_input(dpi, false);
+		if (dpi->conf->csc_support)
+			mtk_dpi_config_csc_enable(dpi, false);
+		if (dpi->conf->swap_input_support)
+			mtk_dpi_config_swap_input(dpi, false);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
 	}
 }
@@ -449,7 +478,6 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
 static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 				    struct drm_display_mode *mode)
 {
-	struct mtk_dpi_yc_limit limit;
 	struct mtk_dpi_polarities dpi_pol;
 	struct mtk_dpi_sync_param hsync;
 	struct mtk_dpi_sync_param vsync_lodd = { 0 };
@@ -472,32 +500,34 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 	pll_rate = clk_get_rate(dpi->tvd_clk);
 
 	vm.pixelclock = pll_rate / factor;
-	if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
-	    (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
+	if (dpi->conf->is_dpintf)
+		clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
+	else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
+		 (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
 		clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
 	else
 		clk_set_rate(dpi->pixel_clk, vm.pixelclock);
 
-
 	vm.pixelclock = clk_get_rate(dpi->pixel_clk);
 
 	dev_dbg(dpi->dev, "Got  PLL %lu Hz, pixel clock %lu Hz\n",
 		pll_rate, vm.pixelclock);
 
-	limit.c_bottom = 0x0010;
-	limit.c_top = 0x0FE0;
-	limit.y_bottom = 0x0010;
-	limit.y_top = 0x0FE0;
-
 	dpi_pol.ck_pol = MTK_DPI_POLARITY_FALLING;
 	dpi_pol.de_pol = MTK_DPI_POLARITY_RISING;
 	dpi_pol.hsync_pol = vm.flags & DISPLAY_FLAGS_HSYNC_HIGH ?
 			    MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING;
 	dpi_pol.vsync_pol = vm.flags & DISPLAY_FLAGS_VSYNC_HIGH ?
 			    MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING;
-	hsync.sync_width = vm.hsync_len;
-	hsync.back_porch = vm.hback_porch;
-	hsync.front_porch = vm.hfront_porch;
+	if (dpi->conf->is_dpintf) {
+		hsync.sync_width = vm.hsync_len / 4;
+		hsync.back_porch = vm.hback_porch / 4;
+		hsync.front_porch = vm.hfront_porch / 4;
+	} else {
+		hsync.sync_width = vm.hsync_len;
+		hsync.back_porch = vm.hback_porch;
+		hsync.front_porch = vm.hfront_porch;
+	}
 	hsync.shift_half_line = false;
 	vsync_lodd.sync_width = vm.vsync_len;
 	vsync_lodd.back_porch = vm.vback_porch;
@@ -536,14 +566,19 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 	else
 		mtk_dpi_config_fb_size(dpi, vm.hactive, vm.vactive);
 
-	mtk_dpi_config_channel_limit(dpi, &limit);
+	mtk_dpi_config_channel_limit(dpi);
 	mtk_dpi_config_bit_num(dpi, dpi->bit_num);
 	mtk_dpi_config_channel_swap(dpi, dpi->channel_swap);
-	mtk_dpi_config_yc_map(dpi, dpi->yc_map);
 	mtk_dpi_config_color_format(dpi, dpi->color_format);
-	mtk_dpi_config_2n_h_fre(dpi);
-	mtk_dpi_dual_edge(dpi);
-	mtk_dpi_config_disable_edge(dpi);
+	if (dpi->conf->is_dpintf) {
+		mtk_dpi_mask(dpi, DPI_CON, DPINTF_INPUT_2P_EN,
+			     DPINTF_INPUT_2P_EN);
+	} else {
+		mtk_dpi_config_yc_map(dpi, dpi->yc_map);
+		mtk_dpi_config_2n_h_fre(dpi);
+		mtk_dpi_dual_edge(dpi);
+		mtk_dpi_config_disable_edge(dpi);
+	}
 	mtk_dpi_sw_reset(dpi, false);
 
 	return 0;
@@ -667,7 +702,7 @@ mtk_dpi_bridge_mode_valid(struct drm_bridge *bridge,
 {
 	struct mtk_dpi *dpi = bridge_to_dpi(bridge);
 
-	if (mode->clock > dpi->conf->max_clock_khz)
+	if (dpi->conf->max_clock_khz && mode->clock > dpi->conf->max_clock_khz)
 		return MODE_CLOCK_HIGH;
 
 	return MODE_OK;
@@ -781,6 +816,16 @@ static unsigned int mt8183_calculate_factor(int clock)
 		return 2;
 }
 
+static unsigned int mt8195_dpintf_calculate_factor(int clock)
+{
+	if (clock < 70000)
+		return 4;
+	else if (clock < 200000)
+		return 2;
+	else
+		return 1;
+}
+
 static const u32 mt8173_output_fmts[] = {
 	MEDIA_BUS_FMT_RGB888_1X24,
 };
@@ -790,12 +835,34 @@ static const u32 mt8183_output_fmts[] = {
 	MEDIA_BUS_FMT_RGB888_2X12_BE,
 };
 
+static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
+	.c_bottom = 0x0010,
+	.c_top = 0x0FE0,
+	.y_bottom = 0x0010,
+	.y_top = 0x0FE0,
+};
+
+static const struct mtk_dpi_yc_limit mtk_dpintf_limit = {
+	.c_bottom = 0x0000,
+	.c_top = 0xFFF,
+	.y_bottom = 0x0000,
+	.y_top = 0xFFF,
+};
+
 static const struct mtk_dpi_conf mt8173_conf = {
 	.cal_factor = mt8173_calculate_factor,
 	.reg_h_fre_con = 0xe0,
 	.max_clock_khz = 300000,
 	.output_fmts = mt8173_output_fmts,
 	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.is_ck_de_pol = true,
+	.csc_support = true,
+	.swap_input_support = true,
+	.dimension_mask = HPW_MASK,
+	.hvsize_mask = HSIZE_MASK,
+	.channel_swap_shift = CH_SWAP,
+	.yuv422_en_bit = YUV422_EN,
+	.limit = &mtk_dpi_limit,
 };
 
 static const struct mtk_dpi_conf mt2701_conf = {
@@ -805,6 +872,14 @@ static const struct mtk_dpi_conf mt2701_conf = {
 	.max_clock_khz = 150000,
 	.output_fmts = mt8173_output_fmts,
 	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.is_ck_de_pol = true,
+	.csc_support = true,
+	.swap_input_support = true,
+	.dimension_mask = HPW_MASK,
+	.hvsize_mask = HSIZE_MASK,
+	.channel_swap_shift = CH_SWAP,
+	.yuv422_en_bit = YUV422_EN,
+	.limit = &mtk_dpi_limit,
 };
 
 static const struct mtk_dpi_conf mt8183_conf = {
@@ -813,6 +888,14 @@ static const struct mtk_dpi_conf mt8183_conf = {
 	.max_clock_khz = 100000,
 	.output_fmts = mt8183_output_fmts,
 	.num_output_fmts = ARRAY_SIZE(mt8183_output_fmts),
+	.is_ck_de_pol = true,
+	.csc_support = true,
+	.swap_input_support = true,
+	.dimension_mask = HPW_MASK,
+	.hvsize_mask = HSIZE_MASK,
+	.channel_swap_shift = CH_SWAP,
+	.yuv422_en_bit = YUV422_EN,
+	.limit = &mtk_dpi_limit,
 };
 
 static const struct mtk_dpi_conf mt8192_conf = {
@@ -821,6 +904,26 @@ static const struct mtk_dpi_conf mt8192_conf = {
 	.max_clock_khz = 150000,
 	.output_fmts = mt8173_output_fmts,
 	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.is_ck_de_pol = true,
+	.csc_support = true,
+	.swap_input_support = true,
+	.dimension_mask = HPW_MASK,
+	.hvsize_mask = HSIZE_MASK,
+	.channel_swap_shift = CH_SWAP,
+	.yuv422_en_bit = YUV422_EN,
+	.limit = &mtk_dpi_limit,
+};
+
+static const struct mtk_dpi_conf mt8195_dpintf_conf = {
+	.cal_factor = mt8195_dpintf_calculate_factor,
+	.output_fmts = mt8173_output_fmts,
+	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.is_dpintf = true,
+	.dimension_mask = DPINTF_HPW_MASK,
+	.hvsize_mask = DPINTF_HSIZE_MASK,
+	.channel_swap_shift = DPINTF_CH_SWAP,
+	.yuv422_en_bit = DPINTF_YUV422_EN,
+	.limit = &mtk_dpintf_limit,
 };
 
 static int mtk_dpi_probe(struct platform_device *pdev)
@@ -870,7 +973,8 @@ static int mtk_dpi_probe(struct platform_device *pdev)
 	if (IS_ERR(dpi->engine_clk)) {
 		ret = PTR_ERR(dpi->engine_clk);
 		if (ret != -EPROBE_DEFER)
-			dev_err(dev, "Failed to get engine clock: %d\n", ret);
+			dev_err(dev, "Failed to get engine clock: %d\n",
+				ret);
 
 		return ret;
 	}
@@ -945,6 +1049,9 @@ static const struct of_device_id mtk_dpi_of_ids[] = {
 	{ .compatible = "mediatek,mt8192-dpi",
 	  .data = &mt8192_conf,
 	},
+	{ .compatible = "mediatek,mt8195-dpintf",
+	  .data = &mt8195_dpintf_conf,
+	},
 	{ },
 };
 MODULE_DEVICE_TABLE(of, mtk_dpi_of_ids);
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi_regs.h b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
index 3a02fabe16627..72efe6ee25842 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
+++ b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
@@ -40,10 +40,14 @@
 #define FAKE_DE_LEVEN			BIT(21)
 #define FAKE_DE_RODD			BIT(22)
 #define FAKE_DE_REVEN			BIT(23)
+#define DPINTF_YUV422_EN		BIT(24)
+#define DPINTF_INPUT_2P_EN		BIT(29)
 
 #define DPI_OUTPUT_SETTING	0x14
 #define CH_SWAP				0
+#define DPINTF_CH_SWAP			BIT(1)
 #define CH_SWAP_MASK			(0x7 << 0)
+#define DPINTF_CH_SWAP_MASK		(0x7 << 1)
 #define SWAP_RGB			0x00
 #define SWAP_GBR			0x01
 #define SWAP_BRG			0x02
@@ -80,8 +84,10 @@
 #define DPI_SIZE		0x18
 #define HSIZE				0
 #define HSIZE_MASK			(0x1FFF << 0)
+#define DPINTF_HSIZE_MASK		(0xFFFF << 0)
 #define VSIZE				16
 #define VSIZE_MASK			(0x1FFF << 16)
+#define DPINTF_VSIZE_MASK		(0xFFFF << 16)
 
 #define DPI_DDR_SETTING		0x1C
 #define DDR_EN				BIT(0)
@@ -93,24 +99,30 @@
 #define DPI_TGEN_HWIDTH		0x20
 #define HPW				0
 #define HPW_MASK			(0xFFF << 0)
+#define DPINTF_HPW_MASK			(0xFFFF << 0)
 
 #define DPI_TGEN_HPORCH		0x24
 #define HBP				0
 #define HBP_MASK			(0xFFF << 0)
+#define DPINTF_HBP_MASK			(0xFFFF << 0)
 #define HFP				16
 #define HFP_MASK			(0xFFF << 16)
+#define DPINTF_HFP_MASK			(0xFFFF << 16)
 
 #define DPI_TGEN_VWIDTH		0x28
 #define DPI_TGEN_VPORCH		0x2C
 
 #define VSYNC_WIDTH_SHIFT		0
 #define VSYNC_WIDTH_MASK		(0xFFF << 0)
+#define DPINTF_VSYNC_WIDTH_MASK		(0xFFFF << 0)
 #define VSYNC_HALF_LINE_SHIFT		16
 #define VSYNC_HALF_LINE_MASK		BIT(16)
 #define VSYNC_BACK_PORCH_SHIFT		0
 #define VSYNC_BACK_PORCH_MASK		(0xFFF << 0)
+#define DPINTF_VSYNC_BACK_PORCH_MASK	(0xFFFF << 0)
 #define VSYNC_FRONT_PORCH_SHIFT		16
 #define VSYNC_FRONT_PORCH_MASK		(0xFFF << 16)
+#define DPINTF_VSYNC_FRONT_PORCH_MASK	(0xFFFF << 16)
 
 #define DPI_BG_HCNTL		0x30
 #define BG_RIGHT			(0x1FFF << 0)
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
index 99cbf44463e40..da9e059312a5d 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -317,6 +317,7 @@ static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
 	[MTK_DISP_UFOE] = "ufoe",
 	[MTK_DSI] = "dsi",
 	[MTK_DPI] = "dpi",
+	[MTK_DP_INTF] = "dp-intf",
 	[MTK_DISP_PWM] = "pwm",
 	[MTK_DISP_MUTEX] = "mutex",
 	[MTK_DISP_OD] = "od",
@@ -339,6 +340,8 @@ static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
 	[DDP_COMPONENT_DITHER]	= { MTK_DISP_DITHER,	0, &ddp_dither },
 	[DDP_COMPONENT_DPI0]	= { MTK_DPI,		0, &ddp_dpi },
 	[DDP_COMPONENT_DPI1]	= { MTK_DPI,		1, &ddp_dpi },
+	[DDP_COMPONENT_DP_INTF0]= { MTK_DP_INTF,	0, &ddp_dpi },
+	[DDP_COMPONENT_DP_INTF1]= { MTK_DP_INTF,	1, &ddp_dpi },
 	[DDP_COMPONENT_DSI0]	= { MTK_DSI,		0, &ddp_dsi },
 	[DDP_COMPONENT_DSI1]	= { MTK_DSI,		1, &ddp_dsi },
 	[DDP_COMPONENT_DSI2]	= { MTK_DSI,		2, &ddp_dsi },
@@ -476,6 +479,7 @@ int mtk_ddp_comp_init(struct device_node *node, struct mtk_ddp_comp *comp,
 	    type == MTK_DISP_COLOR ||
 	    type == MTK_DISP_GAMMA ||
 	    type == MTK_DPI ||
+	    type == MTK_DP_INTF ||
 	    type == MTK_DSI ||
 	    type == MTK_DISP_OVL ||
 	    type == MTK_DISP_OVL_2L ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
index bb914d976cf5d..ee9d853cfa1c1 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
@@ -30,6 +30,7 @@ enum mtk_ddp_comp_type {
 	MTK_DISP_UFOE,
 	MTK_DSI,
 	MTK_DPI,
+	MTK_DP_INTF,
 	MTK_DISP_PWM,
 	MTK_DISP_MUTEX,
 	MTK_DISP_OD,
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index aec39724ebeb6..1ff4e31c86345 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -459,6 +459,8 @@ static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
 	  .data = (void *)MTK_DPI },
 	{ .compatible = "mediatek,mt8183-dpi",
 	  .data = (void *)MTK_DPI },
+	{ .compatible = "mediatek,mt8195-dpintf",
+	  .data = (void *)MTK_DP_INTF },
 	{ .compatible = "mediatek,mt2701-disp-mutex",
 	  .data = (void *)MTK_DISP_MUTEX },
 	{ .compatible = "mediatek,mt2712-disp-mutex",
@@ -569,7 +571,8 @@ static int mtk_drm_probe(struct platform_device *pdev)
 		    comp_type == MTK_DISP_OVL_2L ||
 		    comp_type == MTK_DISP_RDMA ||
 		    comp_type == MTK_DSI ||
-		    comp_type == MTK_DPI) {
+		    comp_type == MTK_DPI ||
+		    comp_type == MTK_DP_INTF) {
 			dev_info(dev, "Adding component match for %pOF\n",
 				 node);
 			drm_of_component_match_add(dev, &match, compare_of,
diff --git a/include/linux/soc/mediatek/mtk-mmsys.h b/include/linux/soc/mediatek/mtk-mmsys.h
index 4bba275e235ac..56ed2fa5f59e8 100644
--- a/include/linux/soc/mediatek/mtk-mmsys.h
+++ b/include/linux/soc/mediatek/mtk-mmsys.h
@@ -19,6 +19,8 @@ enum mtk_ddp_comp_id {
 	DDP_COMPONENT_DITHER,
 	DDP_COMPONENT_DPI0,
 	DDP_COMPONENT_DPI1,
+	DDP_COMPONENT_DP_INTF0,
+	DDP_COMPONENT_DP_INTF1,
 	DDP_COMPONENT_DSI0,
 	DDP_COMPONENT_DSI1,
 	DDP_COMPONENT_DSI2,
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH v6 6/7] phy: phy-mtk-dp: Add driver for DP phy
  2021-11-10 13:06 ` Guillaume Ranquet
                     ` (2 preceding siblings ...)
  (?)
@ 2021-11-10 13:06   ` Guillaume Ranquet
  -1 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Chunfeng Yun, Kishon Vijay Abraham I, Vinod Koul, Chun-Kuang Hu,
	Philipp Zabel, Matthias Brugger
  Cc: Markus Schneider-Pargmann, linux-kernel, linux-arm-kernel,
	linux-mediatek, linux-phy, dri-devel

From: Markus Schneider-Pargmann <msp@baylibre.com>

This is a new driver that supports the integrated DisplayPort phy for
mediatek SoCs, especially the mt8195. The phy is integrated into the
DisplayPort controller and will be created by the mtk-dp driver. This
driver expects a struct regmap to be able to work on the same registers
as the DisplayPort controller. It sets the device data to be the struct
phy so that the DisplayPort controller can easily work with it.

The driver does not have any devicetree bindings because the datasheet
does not list the controller and the phy as distinct units.

The interaction with the controller can be covered by the configure
callback of the phy framework and its displayport parameters.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
---
 MAINTAINERS                       |   1 +
 drivers/phy/mediatek/Kconfig      |   8 ++
 drivers/phy/mediatek/Makefile     |   1 +
 drivers/phy/mediatek/phy-mtk-dp.c | 219 ++++++++++++++++++++++++++++++
 4 files changed, 229 insertions(+)
 create mode 100644 drivers/phy/mediatek/phy-mtk-dp.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 170bbbeefc3f4..f9a71b6d2a4a9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6367,6 +6367,7 @@ L:	linux-mediatek@lists.infradead.org (moderated for non-subscribers)
 S:	Supported
 F:	Documentation/devicetree/bindings/display/mediatek/
 F:	drivers/gpu/drm/mediatek/
+F:	drivers/phy/mediatek/phy-mtk-dp.c
 F:	drivers/phy/mediatek/phy-mtk-hdmi*
 F:	drivers/phy/mediatek/phy-mtk-mipi*
 
diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig
index 55f8e6c048ab3..f7ec860590492 100644
--- a/drivers/phy/mediatek/Kconfig
+++ b/drivers/phy/mediatek/Kconfig
@@ -55,3 +55,11 @@ config PHY_MTK_MIPI_DSI
 	select GENERIC_PHY
 	help
 	  Support MIPI DSI for Mediatek SoCs.
+
+config PHY_MTK_DP
+	tristate "MediaTek DP-PHY Driver"
+	depends on ARCH_MEDIATEK || COMPILE_TEST
+	depends on OF
+	select GENERIC_PHY
+	help
+	  Support DisplayPort PHY for Mediatek SoCs.
diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile
index ace660fbed3a1..4ba1e06504346 100644
--- a/drivers/phy/mediatek/Makefile
+++ b/drivers/phy/mediatek/Makefile
@@ -3,6 +3,7 @@
 # Makefile for the phy drivers.
 #
 
+obj-$(CONFIG_PHY_MTK_DP)		+= phy-mtk-dp.o
 obj-$(CONFIG_PHY_MTK_TPHY)		+= phy-mtk-tphy.o
 obj-$(CONFIG_PHY_MTK_UFS)		+= phy-mtk-ufs.o
 obj-$(CONFIG_PHY_MTK_XSPHY)		+= phy-mtk-xsphy.o
diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
new file mode 100644
index 0000000000000..4f8d26ec0346b
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-dp.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 BayLibre
+ * Author: Markus Schneider-Pargmann <msp@baylibre.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define PHY_OFFSET 0x1000
+
+#define MTK_DP_PHY_DIG_PLL_CTL_1		(PHY_OFFSET + 0x014)
+# define TPLL_SSC_EN				BIT(3)
+
+#define MTK_DP_PHY_DIG_BIT_RATE			(PHY_OFFSET + 0x03C)
+# define BIT_RATE_RBR				0
+# define BIT_RATE_HBR				1
+# define BIT_RATE_HBR2				2
+# define BIT_RATE_HBR3				3
+
+#define MTK_DP_PHY_DIG_SW_RST			(PHY_OFFSET + 0x038)
+# define DP_GLB_SW_RST_PHYD			BIT(0)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_3		(PHY_OFFSET + 0x138)
+#define MTK_DP_LANE1_DRIVING_PARAM_3		(PHY_OFFSET + 0x238)
+#define MTK_DP_LANE2_DRIVING_PARAM_3		(PHY_OFFSET + 0x338)
+#define MTK_DP_LANE3_DRIVING_PARAM_3		(PHY_OFFSET + 0x438)
+# define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT	0x10
+# define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT	(0x14 << 8)
+# define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT	(0x18 << 16)
+# define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT	(0x20 << 24)
+# define DRIVING_PARAM_3_DEFAULT		(XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_4		(PHY_OFFSET + 0x13C)
+#define MTK_DP_LANE1_DRIVING_PARAM_4		(PHY_OFFSET + 0x23C)
+#define MTK_DP_LANE2_DRIVING_PARAM_4		(PHY_OFFSET + 0x33C)
+#define MTK_DP_LANE3_DRIVING_PARAM_4		(PHY_OFFSET + 0x43C)
+# define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT	0x18
+# define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT	(0x1e << 8)
+# define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT	(0x24 << 16)
+# define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT	(0x20 << 24)
+# define DRIVING_PARAM_4_DEFAULT		(XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_5		(PHY_OFFSET + 0x140)
+#define MTK_DP_LANE1_DRIVING_PARAM_5		(PHY_OFFSET + 0x240)
+#define MTK_DP_LANE2_DRIVING_PARAM_5		(PHY_OFFSET + 0x340)
+#define MTK_DP_LANE3_DRIVING_PARAM_5		(PHY_OFFSET + 0x440)
+# define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT	0x28
+# define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT	(0x30 << 8)
+# define DRIVING_PARAM_5_DEFAULT		(XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_6		(PHY_OFFSET + 0x144)
+#define MTK_DP_LANE1_DRIVING_PARAM_6		(PHY_OFFSET + 0x244)
+#define MTK_DP_LANE2_DRIVING_PARAM_6		(PHY_OFFSET + 0x344)
+#define MTK_DP_LANE3_DRIVING_PARAM_6		(PHY_OFFSET + 0x444)
+# define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT	0x00
+# define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT	(0x04 << 8)
+# define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT	(0x08 << 16)
+# define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT	(0x10 << 24)
+# define DRIVING_PARAM_6_DEFAULT		(XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_7		(PHY_OFFSET + 0x148)
+#define MTK_DP_LANE1_DRIVING_PARAM_7		(PHY_OFFSET + 0x248)
+#define MTK_DP_LANE2_DRIVING_PARAM_7		(PHY_OFFSET + 0x348)
+#define MTK_DP_LANE3_DRIVING_PARAM_7		(PHY_OFFSET + 0x448)
+# define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT	0x00
+# define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT	(0x06 << 8)
+# define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT	(0x0c << 16)
+# define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT	(0x00 << 24)
+# define DRIVING_PARAM_7_DEFAULT		(XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_8		(PHY_OFFSET + 0x14C)
+#define MTK_DP_LANE1_DRIVING_PARAM_8		(PHY_OFFSET + 0x24C)
+#define MTK_DP_LANE2_DRIVING_PARAM_8		(PHY_OFFSET + 0x34C)
+#define MTK_DP_LANE3_DRIVING_PARAM_8		(PHY_OFFSET + 0x44C)
+# define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT	0x08
+# define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT	(0x00 << 8)
+# define DRIVING_PARAM_8_DEFAULT		(XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT)
+
+struct mtk_dp_phy {
+	struct regmap *regs;
+};
+
+static int mtk_dp_phy_init(struct phy *phy)
+{
+	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+	u32 driving_params[] = {
+		DRIVING_PARAM_3_DEFAULT,
+		DRIVING_PARAM_4_DEFAULT,
+		DRIVING_PARAM_5_DEFAULT,
+		DRIVING_PARAM_6_DEFAULT,
+		DRIVING_PARAM_7_DEFAULT,
+		DRIVING_PARAM_8_DEFAULT
+	};
+
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+
+	return 0;
+}
+
+static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+	u32 val;
+
+	if (opts->dp.set_rate) {
+		switch (opts->dp.link_rate) {
+		default:
+			dev_err(&phy->dev,
+				"Implementation error, unknown linkrate %x\n",
+				opts->dp.link_rate);
+			return -EINVAL;
+		case 1620:
+			val = BIT_RATE_RBR;
+			break;
+		case 2700:
+			val = BIT_RATE_HBR;
+			break;
+		case 5400:
+			val = BIT_RATE_HBR2;
+			break;
+		case 8100:
+			val = BIT_RATE_HBR3;
+			break;
+		}
+		regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE, val);
+	}
+
+	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
+			   TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN : 0);
+
+	return 0;
+}
+
+static int mtk_dp_phy_reset(struct phy *phy)
+{
+	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+
+	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
+			   DP_GLB_SW_RST_PHYD, 0);
+	usleep_range(50, 200);
+	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
+			   DP_GLB_SW_RST_PHYD, 1);
+
+	return 0;
+}
+
+static const struct phy_ops mtk_dp_phy_dev_ops = {
+	.init = mtk_dp_phy_init,
+	.configure = mtk_dp_phy_configure,
+	.reset = mtk_dp_phy_reset,
+	.owner = THIS_MODULE,
+};
+
+static int mtk_dp_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_dp_phy *dp_phy;
+	struct phy *phy;
+
+	dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
+	if (!dp_phy)
+		return -ENOMEM;
+
+	dp_phy->regs = *(struct regmap **)dev->platform_data;
+	if (!dp_phy->regs) {
+		dev_err(dev, "No data passed, requires struct regmap**\n");
+		return -EINVAL;
+	}
+
+	phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
+	if (IS_ERR(phy)) {
+		dev_err(dev, "Failed to create DP PHY: %ld\n", PTR_ERR(phy));
+		return PTR_ERR(phy);
+	}
+	phy_set_drvdata(phy, dp_phy);
+
+	// Set device data to the phy so that mtk-dp can get it easily
+	dev_set_drvdata(dev, phy);
+
+	return 0;
+}
+
+struct platform_driver mtk_dp_phy_driver = {
+	.probe = mtk_dp_phy_probe,
+	.driver = {
+		.name = "mediatek-dp-phy",
+	},
+};
+module_platform_driver(mtk_dp_phy_driver);
+
+MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
+MODULE_DESCRIPTION("MediaTek DP PHY Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.32.0


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

* [PATCH v6 6/7] phy: phy-mtk-dp: Add driver for DP phy
@ 2021-11-10 13:06   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Chunfeng Yun, Kishon Vijay Abraham I, Vinod Koul, Chun-Kuang Hu,
	Philipp Zabel, Matthias Brugger
  Cc: linux-kernel, dri-devel, Markus Schneider-Pargmann,
	linux-mediatek, linux-phy, linux-arm-kernel

From: Markus Schneider-Pargmann <msp@baylibre.com>

This is a new driver that supports the integrated DisplayPort phy for
mediatek SoCs, especially the mt8195. The phy is integrated into the
DisplayPort controller and will be created by the mtk-dp driver. This
driver expects a struct regmap to be able to work on the same registers
as the DisplayPort controller. It sets the device data to be the struct
phy so that the DisplayPort controller can easily work with it.

The driver does not have any devicetree bindings because the datasheet
does not list the controller and the phy as distinct units.

The interaction with the controller can be covered by the configure
callback of the phy framework and its displayport parameters.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
---
 MAINTAINERS                       |   1 +
 drivers/phy/mediatek/Kconfig      |   8 ++
 drivers/phy/mediatek/Makefile     |   1 +
 drivers/phy/mediatek/phy-mtk-dp.c | 219 ++++++++++++++++++++++++++++++
 4 files changed, 229 insertions(+)
 create mode 100644 drivers/phy/mediatek/phy-mtk-dp.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 170bbbeefc3f4..f9a71b6d2a4a9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6367,6 +6367,7 @@ L:	linux-mediatek@lists.infradead.org (moderated for non-subscribers)
 S:	Supported
 F:	Documentation/devicetree/bindings/display/mediatek/
 F:	drivers/gpu/drm/mediatek/
+F:	drivers/phy/mediatek/phy-mtk-dp.c
 F:	drivers/phy/mediatek/phy-mtk-hdmi*
 F:	drivers/phy/mediatek/phy-mtk-mipi*
 
diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig
index 55f8e6c048ab3..f7ec860590492 100644
--- a/drivers/phy/mediatek/Kconfig
+++ b/drivers/phy/mediatek/Kconfig
@@ -55,3 +55,11 @@ config PHY_MTK_MIPI_DSI
 	select GENERIC_PHY
 	help
 	  Support MIPI DSI for Mediatek SoCs.
+
+config PHY_MTK_DP
+	tristate "MediaTek DP-PHY Driver"
+	depends on ARCH_MEDIATEK || COMPILE_TEST
+	depends on OF
+	select GENERIC_PHY
+	help
+	  Support DisplayPort PHY for Mediatek SoCs.
diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile
index ace660fbed3a1..4ba1e06504346 100644
--- a/drivers/phy/mediatek/Makefile
+++ b/drivers/phy/mediatek/Makefile
@@ -3,6 +3,7 @@
 # Makefile for the phy drivers.
 #
 
+obj-$(CONFIG_PHY_MTK_DP)		+= phy-mtk-dp.o
 obj-$(CONFIG_PHY_MTK_TPHY)		+= phy-mtk-tphy.o
 obj-$(CONFIG_PHY_MTK_UFS)		+= phy-mtk-ufs.o
 obj-$(CONFIG_PHY_MTK_XSPHY)		+= phy-mtk-xsphy.o
diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
new file mode 100644
index 0000000000000..4f8d26ec0346b
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-dp.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 BayLibre
+ * Author: Markus Schneider-Pargmann <msp@baylibre.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define PHY_OFFSET 0x1000
+
+#define MTK_DP_PHY_DIG_PLL_CTL_1		(PHY_OFFSET + 0x014)
+# define TPLL_SSC_EN				BIT(3)
+
+#define MTK_DP_PHY_DIG_BIT_RATE			(PHY_OFFSET + 0x03C)
+# define BIT_RATE_RBR				0
+# define BIT_RATE_HBR				1
+# define BIT_RATE_HBR2				2
+# define BIT_RATE_HBR3				3
+
+#define MTK_DP_PHY_DIG_SW_RST			(PHY_OFFSET + 0x038)
+# define DP_GLB_SW_RST_PHYD			BIT(0)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_3		(PHY_OFFSET + 0x138)
+#define MTK_DP_LANE1_DRIVING_PARAM_3		(PHY_OFFSET + 0x238)
+#define MTK_DP_LANE2_DRIVING_PARAM_3		(PHY_OFFSET + 0x338)
+#define MTK_DP_LANE3_DRIVING_PARAM_3		(PHY_OFFSET + 0x438)
+# define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT	0x10
+# define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT	(0x14 << 8)
+# define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT	(0x18 << 16)
+# define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT	(0x20 << 24)
+# define DRIVING_PARAM_3_DEFAULT		(XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_4		(PHY_OFFSET + 0x13C)
+#define MTK_DP_LANE1_DRIVING_PARAM_4		(PHY_OFFSET + 0x23C)
+#define MTK_DP_LANE2_DRIVING_PARAM_4		(PHY_OFFSET + 0x33C)
+#define MTK_DP_LANE3_DRIVING_PARAM_4		(PHY_OFFSET + 0x43C)
+# define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT	0x18
+# define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT	(0x1e << 8)
+# define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT	(0x24 << 16)
+# define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT	(0x20 << 24)
+# define DRIVING_PARAM_4_DEFAULT		(XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_5		(PHY_OFFSET + 0x140)
+#define MTK_DP_LANE1_DRIVING_PARAM_5		(PHY_OFFSET + 0x240)
+#define MTK_DP_LANE2_DRIVING_PARAM_5		(PHY_OFFSET + 0x340)
+#define MTK_DP_LANE3_DRIVING_PARAM_5		(PHY_OFFSET + 0x440)
+# define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT	0x28
+# define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT	(0x30 << 8)
+# define DRIVING_PARAM_5_DEFAULT		(XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_6		(PHY_OFFSET + 0x144)
+#define MTK_DP_LANE1_DRIVING_PARAM_6		(PHY_OFFSET + 0x244)
+#define MTK_DP_LANE2_DRIVING_PARAM_6		(PHY_OFFSET + 0x344)
+#define MTK_DP_LANE3_DRIVING_PARAM_6		(PHY_OFFSET + 0x444)
+# define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT	0x00
+# define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT	(0x04 << 8)
+# define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT	(0x08 << 16)
+# define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT	(0x10 << 24)
+# define DRIVING_PARAM_6_DEFAULT		(XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_7		(PHY_OFFSET + 0x148)
+#define MTK_DP_LANE1_DRIVING_PARAM_7		(PHY_OFFSET + 0x248)
+#define MTK_DP_LANE2_DRIVING_PARAM_7		(PHY_OFFSET + 0x348)
+#define MTK_DP_LANE3_DRIVING_PARAM_7		(PHY_OFFSET + 0x448)
+# define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT	0x00
+# define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT	(0x06 << 8)
+# define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT	(0x0c << 16)
+# define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT	(0x00 << 24)
+# define DRIVING_PARAM_7_DEFAULT		(XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_8		(PHY_OFFSET + 0x14C)
+#define MTK_DP_LANE1_DRIVING_PARAM_8		(PHY_OFFSET + 0x24C)
+#define MTK_DP_LANE2_DRIVING_PARAM_8		(PHY_OFFSET + 0x34C)
+#define MTK_DP_LANE3_DRIVING_PARAM_8		(PHY_OFFSET + 0x44C)
+# define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT	0x08
+# define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT	(0x00 << 8)
+# define DRIVING_PARAM_8_DEFAULT		(XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT)
+
+struct mtk_dp_phy {
+	struct regmap *regs;
+};
+
+static int mtk_dp_phy_init(struct phy *phy)
+{
+	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+	u32 driving_params[] = {
+		DRIVING_PARAM_3_DEFAULT,
+		DRIVING_PARAM_4_DEFAULT,
+		DRIVING_PARAM_5_DEFAULT,
+		DRIVING_PARAM_6_DEFAULT,
+		DRIVING_PARAM_7_DEFAULT,
+		DRIVING_PARAM_8_DEFAULT
+	};
+
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+
+	return 0;
+}
+
+static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+	u32 val;
+
+	if (opts->dp.set_rate) {
+		switch (opts->dp.link_rate) {
+		default:
+			dev_err(&phy->dev,
+				"Implementation error, unknown linkrate %x\n",
+				opts->dp.link_rate);
+			return -EINVAL;
+		case 1620:
+			val = BIT_RATE_RBR;
+			break;
+		case 2700:
+			val = BIT_RATE_HBR;
+			break;
+		case 5400:
+			val = BIT_RATE_HBR2;
+			break;
+		case 8100:
+			val = BIT_RATE_HBR3;
+			break;
+		}
+		regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE, val);
+	}
+
+	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
+			   TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN : 0);
+
+	return 0;
+}
+
+static int mtk_dp_phy_reset(struct phy *phy)
+{
+	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+
+	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
+			   DP_GLB_SW_RST_PHYD, 0);
+	usleep_range(50, 200);
+	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
+			   DP_GLB_SW_RST_PHYD, 1);
+
+	return 0;
+}
+
+static const struct phy_ops mtk_dp_phy_dev_ops = {
+	.init = mtk_dp_phy_init,
+	.configure = mtk_dp_phy_configure,
+	.reset = mtk_dp_phy_reset,
+	.owner = THIS_MODULE,
+};
+
+static int mtk_dp_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_dp_phy *dp_phy;
+	struct phy *phy;
+
+	dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
+	if (!dp_phy)
+		return -ENOMEM;
+
+	dp_phy->regs = *(struct regmap **)dev->platform_data;
+	if (!dp_phy->regs) {
+		dev_err(dev, "No data passed, requires struct regmap**\n");
+		return -EINVAL;
+	}
+
+	phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
+	if (IS_ERR(phy)) {
+		dev_err(dev, "Failed to create DP PHY: %ld\n", PTR_ERR(phy));
+		return PTR_ERR(phy);
+	}
+	phy_set_drvdata(phy, dp_phy);
+
+	// Set device data to the phy so that mtk-dp can get it easily
+	dev_set_drvdata(dev, phy);
+
+	return 0;
+}
+
+struct platform_driver mtk_dp_phy_driver = {
+	.probe = mtk_dp_phy_probe,
+	.driver = {
+		.name = "mediatek-dp-phy",
+	},
+};
+module_platform_driver(mtk_dp_phy_driver);
+
+MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
+MODULE_DESCRIPTION("MediaTek DP PHY Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.32.0


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

* [PATCH v6 6/7] phy: phy-mtk-dp: Add driver for DP phy
@ 2021-11-10 13:06   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Chunfeng Yun, Kishon Vijay Abraham I, Vinod Koul, Chun-Kuang Hu,
	Philipp Zabel, Matthias Brugger
  Cc: Markus Schneider-Pargmann, linux-kernel, linux-arm-kernel,
	linux-mediatek, linux-phy, dri-devel

From: Markus Schneider-Pargmann <msp@baylibre.com>

This is a new driver that supports the integrated DisplayPort phy for
mediatek SoCs, especially the mt8195. The phy is integrated into the
DisplayPort controller and will be created by the mtk-dp driver. This
driver expects a struct regmap to be able to work on the same registers
as the DisplayPort controller. It sets the device data to be the struct
phy so that the DisplayPort controller can easily work with it.

The driver does not have any devicetree bindings because the datasheet
does not list the controller and the phy as distinct units.

The interaction with the controller can be covered by the configure
callback of the phy framework and its displayport parameters.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
---
 MAINTAINERS                       |   1 +
 drivers/phy/mediatek/Kconfig      |   8 ++
 drivers/phy/mediatek/Makefile     |   1 +
 drivers/phy/mediatek/phy-mtk-dp.c | 219 ++++++++++++++++++++++++++++++
 4 files changed, 229 insertions(+)
 create mode 100644 drivers/phy/mediatek/phy-mtk-dp.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 170bbbeefc3f4..f9a71b6d2a4a9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6367,6 +6367,7 @@ L:	linux-mediatek@lists.infradead.org (moderated for non-subscribers)
 S:	Supported
 F:	Documentation/devicetree/bindings/display/mediatek/
 F:	drivers/gpu/drm/mediatek/
+F:	drivers/phy/mediatek/phy-mtk-dp.c
 F:	drivers/phy/mediatek/phy-mtk-hdmi*
 F:	drivers/phy/mediatek/phy-mtk-mipi*
 
diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig
index 55f8e6c048ab3..f7ec860590492 100644
--- a/drivers/phy/mediatek/Kconfig
+++ b/drivers/phy/mediatek/Kconfig
@@ -55,3 +55,11 @@ config PHY_MTK_MIPI_DSI
 	select GENERIC_PHY
 	help
 	  Support MIPI DSI for Mediatek SoCs.
+
+config PHY_MTK_DP
+	tristate "MediaTek DP-PHY Driver"
+	depends on ARCH_MEDIATEK || COMPILE_TEST
+	depends on OF
+	select GENERIC_PHY
+	help
+	  Support DisplayPort PHY for Mediatek SoCs.
diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile
index ace660fbed3a1..4ba1e06504346 100644
--- a/drivers/phy/mediatek/Makefile
+++ b/drivers/phy/mediatek/Makefile
@@ -3,6 +3,7 @@
 # Makefile for the phy drivers.
 #
 
+obj-$(CONFIG_PHY_MTK_DP)		+= phy-mtk-dp.o
 obj-$(CONFIG_PHY_MTK_TPHY)		+= phy-mtk-tphy.o
 obj-$(CONFIG_PHY_MTK_UFS)		+= phy-mtk-ufs.o
 obj-$(CONFIG_PHY_MTK_XSPHY)		+= phy-mtk-xsphy.o
diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
new file mode 100644
index 0000000000000..4f8d26ec0346b
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-dp.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 BayLibre
+ * Author: Markus Schneider-Pargmann <msp@baylibre.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define PHY_OFFSET 0x1000
+
+#define MTK_DP_PHY_DIG_PLL_CTL_1		(PHY_OFFSET + 0x014)
+# define TPLL_SSC_EN				BIT(3)
+
+#define MTK_DP_PHY_DIG_BIT_RATE			(PHY_OFFSET + 0x03C)
+# define BIT_RATE_RBR				0
+# define BIT_RATE_HBR				1
+# define BIT_RATE_HBR2				2
+# define BIT_RATE_HBR3				3
+
+#define MTK_DP_PHY_DIG_SW_RST			(PHY_OFFSET + 0x038)
+# define DP_GLB_SW_RST_PHYD			BIT(0)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_3		(PHY_OFFSET + 0x138)
+#define MTK_DP_LANE1_DRIVING_PARAM_3		(PHY_OFFSET + 0x238)
+#define MTK_DP_LANE2_DRIVING_PARAM_3		(PHY_OFFSET + 0x338)
+#define MTK_DP_LANE3_DRIVING_PARAM_3		(PHY_OFFSET + 0x438)
+# define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT	0x10
+# define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT	(0x14 << 8)
+# define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT	(0x18 << 16)
+# define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT	(0x20 << 24)
+# define DRIVING_PARAM_3_DEFAULT		(XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_4		(PHY_OFFSET + 0x13C)
+#define MTK_DP_LANE1_DRIVING_PARAM_4		(PHY_OFFSET + 0x23C)
+#define MTK_DP_LANE2_DRIVING_PARAM_4		(PHY_OFFSET + 0x33C)
+#define MTK_DP_LANE3_DRIVING_PARAM_4		(PHY_OFFSET + 0x43C)
+# define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT	0x18
+# define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT	(0x1e << 8)
+# define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT	(0x24 << 16)
+# define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT	(0x20 << 24)
+# define DRIVING_PARAM_4_DEFAULT		(XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_5		(PHY_OFFSET + 0x140)
+#define MTK_DP_LANE1_DRIVING_PARAM_5		(PHY_OFFSET + 0x240)
+#define MTK_DP_LANE2_DRIVING_PARAM_5		(PHY_OFFSET + 0x340)
+#define MTK_DP_LANE3_DRIVING_PARAM_5		(PHY_OFFSET + 0x440)
+# define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT	0x28
+# define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT	(0x30 << 8)
+# define DRIVING_PARAM_5_DEFAULT		(XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_6		(PHY_OFFSET + 0x144)
+#define MTK_DP_LANE1_DRIVING_PARAM_6		(PHY_OFFSET + 0x244)
+#define MTK_DP_LANE2_DRIVING_PARAM_6		(PHY_OFFSET + 0x344)
+#define MTK_DP_LANE3_DRIVING_PARAM_6		(PHY_OFFSET + 0x444)
+# define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT	0x00
+# define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT	(0x04 << 8)
+# define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT	(0x08 << 16)
+# define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT	(0x10 << 24)
+# define DRIVING_PARAM_6_DEFAULT		(XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_7		(PHY_OFFSET + 0x148)
+#define MTK_DP_LANE1_DRIVING_PARAM_7		(PHY_OFFSET + 0x248)
+#define MTK_DP_LANE2_DRIVING_PARAM_7		(PHY_OFFSET + 0x348)
+#define MTK_DP_LANE3_DRIVING_PARAM_7		(PHY_OFFSET + 0x448)
+# define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT	0x00
+# define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT	(0x06 << 8)
+# define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT	(0x0c << 16)
+# define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT	(0x00 << 24)
+# define DRIVING_PARAM_7_DEFAULT		(XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_8		(PHY_OFFSET + 0x14C)
+#define MTK_DP_LANE1_DRIVING_PARAM_8		(PHY_OFFSET + 0x24C)
+#define MTK_DP_LANE2_DRIVING_PARAM_8		(PHY_OFFSET + 0x34C)
+#define MTK_DP_LANE3_DRIVING_PARAM_8		(PHY_OFFSET + 0x44C)
+# define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT	0x08
+# define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT	(0x00 << 8)
+# define DRIVING_PARAM_8_DEFAULT		(XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT)
+
+struct mtk_dp_phy {
+	struct regmap *regs;
+};
+
+static int mtk_dp_phy_init(struct phy *phy)
+{
+	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+	u32 driving_params[] = {
+		DRIVING_PARAM_3_DEFAULT,
+		DRIVING_PARAM_4_DEFAULT,
+		DRIVING_PARAM_5_DEFAULT,
+		DRIVING_PARAM_6_DEFAULT,
+		DRIVING_PARAM_7_DEFAULT,
+		DRIVING_PARAM_8_DEFAULT
+	};
+
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+
+	return 0;
+}
+
+static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+	u32 val;
+
+	if (opts->dp.set_rate) {
+		switch (opts->dp.link_rate) {
+		default:
+			dev_err(&phy->dev,
+				"Implementation error, unknown linkrate %x\n",
+				opts->dp.link_rate);
+			return -EINVAL;
+		case 1620:
+			val = BIT_RATE_RBR;
+			break;
+		case 2700:
+			val = BIT_RATE_HBR;
+			break;
+		case 5400:
+			val = BIT_RATE_HBR2;
+			break;
+		case 8100:
+			val = BIT_RATE_HBR3;
+			break;
+		}
+		regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE, val);
+	}
+
+	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
+			   TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN : 0);
+
+	return 0;
+}
+
+static int mtk_dp_phy_reset(struct phy *phy)
+{
+	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+
+	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
+			   DP_GLB_SW_RST_PHYD, 0);
+	usleep_range(50, 200);
+	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
+			   DP_GLB_SW_RST_PHYD, 1);
+
+	return 0;
+}
+
+static const struct phy_ops mtk_dp_phy_dev_ops = {
+	.init = mtk_dp_phy_init,
+	.configure = mtk_dp_phy_configure,
+	.reset = mtk_dp_phy_reset,
+	.owner = THIS_MODULE,
+};
+
+static int mtk_dp_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_dp_phy *dp_phy;
+	struct phy *phy;
+
+	dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
+	if (!dp_phy)
+		return -ENOMEM;
+
+	dp_phy->regs = *(struct regmap **)dev->platform_data;
+	if (!dp_phy->regs) {
+		dev_err(dev, "No data passed, requires struct regmap**\n");
+		return -EINVAL;
+	}
+
+	phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
+	if (IS_ERR(phy)) {
+		dev_err(dev, "Failed to create DP PHY: %ld\n", PTR_ERR(phy));
+		return PTR_ERR(phy);
+	}
+	phy_set_drvdata(phy, dp_phy);
+
+	// Set device data to the phy so that mtk-dp can get it easily
+	dev_set_drvdata(dev, phy);
+
+	return 0;
+}
+
+struct platform_driver mtk_dp_phy_driver = {
+	.probe = mtk_dp_phy_probe,
+	.driver = {
+		.name = "mediatek-dp-phy",
+	},
+};
+module_platform_driver(mtk_dp_phy_driver);
+
+MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
+MODULE_DESCRIPTION("MediaTek DP PHY Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.32.0


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* [PATCH v6 6/7] phy: phy-mtk-dp: Add driver for DP phy
@ 2021-11-10 13:06   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Chunfeng Yun, Kishon Vijay Abraham I, Vinod Koul, Chun-Kuang Hu,
	Philipp Zabel, Matthias Brugger
  Cc: Markus Schneider-Pargmann, linux-kernel, linux-arm-kernel,
	linux-mediatek, linux-phy, dri-devel

From: Markus Schneider-Pargmann <msp@baylibre.com>

This is a new driver that supports the integrated DisplayPort phy for
mediatek SoCs, especially the mt8195. The phy is integrated into the
DisplayPort controller and will be created by the mtk-dp driver. This
driver expects a struct regmap to be able to work on the same registers
as the DisplayPort controller. It sets the device data to be the struct
phy so that the DisplayPort controller can easily work with it.

The driver does not have any devicetree bindings because the datasheet
does not list the controller and the phy as distinct units.

The interaction with the controller can be covered by the configure
callback of the phy framework and its displayport parameters.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
---
 MAINTAINERS                       |   1 +
 drivers/phy/mediatek/Kconfig      |   8 ++
 drivers/phy/mediatek/Makefile     |   1 +
 drivers/phy/mediatek/phy-mtk-dp.c | 219 ++++++++++++++++++++++++++++++
 4 files changed, 229 insertions(+)
 create mode 100644 drivers/phy/mediatek/phy-mtk-dp.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 170bbbeefc3f4..f9a71b6d2a4a9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6367,6 +6367,7 @@ L:	linux-mediatek@lists.infradead.org (moderated for non-subscribers)
 S:	Supported
 F:	Documentation/devicetree/bindings/display/mediatek/
 F:	drivers/gpu/drm/mediatek/
+F:	drivers/phy/mediatek/phy-mtk-dp.c
 F:	drivers/phy/mediatek/phy-mtk-hdmi*
 F:	drivers/phy/mediatek/phy-mtk-mipi*
 
diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig
index 55f8e6c048ab3..f7ec860590492 100644
--- a/drivers/phy/mediatek/Kconfig
+++ b/drivers/phy/mediatek/Kconfig
@@ -55,3 +55,11 @@ config PHY_MTK_MIPI_DSI
 	select GENERIC_PHY
 	help
 	  Support MIPI DSI for Mediatek SoCs.
+
+config PHY_MTK_DP
+	tristate "MediaTek DP-PHY Driver"
+	depends on ARCH_MEDIATEK || COMPILE_TEST
+	depends on OF
+	select GENERIC_PHY
+	help
+	  Support DisplayPort PHY for Mediatek SoCs.
diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile
index ace660fbed3a1..4ba1e06504346 100644
--- a/drivers/phy/mediatek/Makefile
+++ b/drivers/phy/mediatek/Makefile
@@ -3,6 +3,7 @@
 # Makefile for the phy drivers.
 #
 
+obj-$(CONFIG_PHY_MTK_DP)		+= phy-mtk-dp.o
 obj-$(CONFIG_PHY_MTK_TPHY)		+= phy-mtk-tphy.o
 obj-$(CONFIG_PHY_MTK_UFS)		+= phy-mtk-ufs.o
 obj-$(CONFIG_PHY_MTK_XSPHY)		+= phy-mtk-xsphy.o
diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
new file mode 100644
index 0000000000000..4f8d26ec0346b
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-dp.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 BayLibre
+ * Author: Markus Schneider-Pargmann <msp@baylibre.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define PHY_OFFSET 0x1000
+
+#define MTK_DP_PHY_DIG_PLL_CTL_1		(PHY_OFFSET + 0x014)
+# define TPLL_SSC_EN				BIT(3)
+
+#define MTK_DP_PHY_DIG_BIT_RATE			(PHY_OFFSET + 0x03C)
+# define BIT_RATE_RBR				0
+# define BIT_RATE_HBR				1
+# define BIT_RATE_HBR2				2
+# define BIT_RATE_HBR3				3
+
+#define MTK_DP_PHY_DIG_SW_RST			(PHY_OFFSET + 0x038)
+# define DP_GLB_SW_RST_PHYD			BIT(0)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_3		(PHY_OFFSET + 0x138)
+#define MTK_DP_LANE1_DRIVING_PARAM_3		(PHY_OFFSET + 0x238)
+#define MTK_DP_LANE2_DRIVING_PARAM_3		(PHY_OFFSET + 0x338)
+#define MTK_DP_LANE3_DRIVING_PARAM_3		(PHY_OFFSET + 0x438)
+# define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT	0x10
+# define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT	(0x14 << 8)
+# define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT	(0x18 << 16)
+# define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT	(0x20 << 24)
+# define DRIVING_PARAM_3_DEFAULT		(XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_4		(PHY_OFFSET + 0x13C)
+#define MTK_DP_LANE1_DRIVING_PARAM_4		(PHY_OFFSET + 0x23C)
+#define MTK_DP_LANE2_DRIVING_PARAM_4		(PHY_OFFSET + 0x33C)
+#define MTK_DP_LANE3_DRIVING_PARAM_4		(PHY_OFFSET + 0x43C)
+# define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT	0x18
+# define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT	(0x1e << 8)
+# define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT	(0x24 << 16)
+# define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT	(0x20 << 24)
+# define DRIVING_PARAM_4_DEFAULT		(XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_5		(PHY_OFFSET + 0x140)
+#define MTK_DP_LANE1_DRIVING_PARAM_5		(PHY_OFFSET + 0x240)
+#define MTK_DP_LANE2_DRIVING_PARAM_5		(PHY_OFFSET + 0x340)
+#define MTK_DP_LANE3_DRIVING_PARAM_5		(PHY_OFFSET + 0x440)
+# define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT	0x28
+# define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT	(0x30 << 8)
+# define DRIVING_PARAM_5_DEFAULT		(XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_6		(PHY_OFFSET + 0x144)
+#define MTK_DP_LANE1_DRIVING_PARAM_6		(PHY_OFFSET + 0x244)
+#define MTK_DP_LANE2_DRIVING_PARAM_6		(PHY_OFFSET + 0x344)
+#define MTK_DP_LANE3_DRIVING_PARAM_6		(PHY_OFFSET + 0x444)
+# define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT	0x00
+# define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT	(0x04 << 8)
+# define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT	(0x08 << 16)
+# define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT	(0x10 << 24)
+# define DRIVING_PARAM_6_DEFAULT		(XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_7		(PHY_OFFSET + 0x148)
+#define MTK_DP_LANE1_DRIVING_PARAM_7		(PHY_OFFSET + 0x248)
+#define MTK_DP_LANE2_DRIVING_PARAM_7		(PHY_OFFSET + 0x348)
+#define MTK_DP_LANE3_DRIVING_PARAM_7		(PHY_OFFSET + 0x448)
+# define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT	0x00
+# define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT	(0x06 << 8)
+# define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT	(0x0c << 16)
+# define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT	(0x00 << 24)
+# define DRIVING_PARAM_7_DEFAULT		(XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_8		(PHY_OFFSET + 0x14C)
+#define MTK_DP_LANE1_DRIVING_PARAM_8		(PHY_OFFSET + 0x24C)
+#define MTK_DP_LANE2_DRIVING_PARAM_8		(PHY_OFFSET + 0x34C)
+#define MTK_DP_LANE3_DRIVING_PARAM_8		(PHY_OFFSET + 0x44C)
+# define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT	0x08
+# define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT	(0x00 << 8)
+# define DRIVING_PARAM_8_DEFAULT		(XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT)
+
+struct mtk_dp_phy {
+	struct regmap *regs;
+};
+
+static int mtk_dp_phy_init(struct phy *phy)
+{
+	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+	u32 driving_params[] = {
+		DRIVING_PARAM_3_DEFAULT,
+		DRIVING_PARAM_4_DEFAULT,
+		DRIVING_PARAM_5_DEFAULT,
+		DRIVING_PARAM_6_DEFAULT,
+		DRIVING_PARAM_7_DEFAULT,
+		DRIVING_PARAM_8_DEFAULT
+	};
+
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+
+	return 0;
+}
+
+static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+	u32 val;
+
+	if (opts->dp.set_rate) {
+		switch (opts->dp.link_rate) {
+		default:
+			dev_err(&phy->dev,
+				"Implementation error, unknown linkrate %x\n",
+				opts->dp.link_rate);
+			return -EINVAL;
+		case 1620:
+			val = BIT_RATE_RBR;
+			break;
+		case 2700:
+			val = BIT_RATE_HBR;
+			break;
+		case 5400:
+			val = BIT_RATE_HBR2;
+			break;
+		case 8100:
+			val = BIT_RATE_HBR3;
+			break;
+		}
+		regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE, val);
+	}
+
+	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
+			   TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN : 0);
+
+	return 0;
+}
+
+static int mtk_dp_phy_reset(struct phy *phy)
+{
+	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+
+	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
+			   DP_GLB_SW_RST_PHYD, 0);
+	usleep_range(50, 200);
+	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
+			   DP_GLB_SW_RST_PHYD, 1);
+
+	return 0;
+}
+
+static const struct phy_ops mtk_dp_phy_dev_ops = {
+	.init = mtk_dp_phy_init,
+	.configure = mtk_dp_phy_configure,
+	.reset = mtk_dp_phy_reset,
+	.owner = THIS_MODULE,
+};
+
+static int mtk_dp_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_dp_phy *dp_phy;
+	struct phy *phy;
+
+	dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
+	if (!dp_phy)
+		return -ENOMEM;
+
+	dp_phy->regs = *(struct regmap **)dev->platform_data;
+	if (!dp_phy->regs) {
+		dev_err(dev, "No data passed, requires struct regmap**\n");
+		return -EINVAL;
+	}
+
+	phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
+	if (IS_ERR(phy)) {
+		dev_err(dev, "Failed to create DP PHY: %ld\n", PTR_ERR(phy));
+		return PTR_ERR(phy);
+	}
+	phy_set_drvdata(phy, dp_phy);
+
+	// Set device data to the phy so that mtk-dp can get it easily
+	dev_set_drvdata(dev, phy);
+
+	return 0;
+}
+
+struct platform_driver mtk_dp_phy_driver = {
+	.probe = mtk_dp_phy_probe,
+	.driver = {
+		.name = "mediatek-dp-phy",
+	},
+};
+module_platform_driver(mtk_dp_phy_driver);
+
+MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
+MODULE_DESCRIPTION("MediaTek DP PHY Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH v6 6/7] phy: phy-mtk-dp: Add driver for DP phy
@ 2021-11-10 13:06   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Chunfeng Yun, Kishon Vijay Abraham I, Vinod Koul, Chun-Kuang Hu,
	Philipp Zabel, Matthias Brugger
  Cc: Markus Schneider-Pargmann, linux-kernel, linux-arm-kernel,
	linux-mediatek, linux-phy, dri-devel

From: Markus Schneider-Pargmann <msp@baylibre.com>

This is a new driver that supports the integrated DisplayPort phy for
mediatek SoCs, especially the mt8195. The phy is integrated into the
DisplayPort controller and will be created by the mtk-dp driver. This
driver expects a struct regmap to be able to work on the same registers
as the DisplayPort controller. It sets the device data to be the struct
phy so that the DisplayPort controller can easily work with it.

The driver does not have any devicetree bindings because the datasheet
does not list the controller and the phy as distinct units.

The interaction with the controller can be covered by the configure
callback of the phy framework and its displayport parameters.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
---
 MAINTAINERS                       |   1 +
 drivers/phy/mediatek/Kconfig      |   8 ++
 drivers/phy/mediatek/Makefile     |   1 +
 drivers/phy/mediatek/phy-mtk-dp.c | 219 ++++++++++++++++++++++++++++++
 4 files changed, 229 insertions(+)
 create mode 100644 drivers/phy/mediatek/phy-mtk-dp.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 170bbbeefc3f4..f9a71b6d2a4a9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6367,6 +6367,7 @@ L:	linux-mediatek@lists.infradead.org (moderated for non-subscribers)
 S:	Supported
 F:	Documentation/devicetree/bindings/display/mediatek/
 F:	drivers/gpu/drm/mediatek/
+F:	drivers/phy/mediatek/phy-mtk-dp.c
 F:	drivers/phy/mediatek/phy-mtk-hdmi*
 F:	drivers/phy/mediatek/phy-mtk-mipi*
 
diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig
index 55f8e6c048ab3..f7ec860590492 100644
--- a/drivers/phy/mediatek/Kconfig
+++ b/drivers/phy/mediatek/Kconfig
@@ -55,3 +55,11 @@ config PHY_MTK_MIPI_DSI
 	select GENERIC_PHY
 	help
 	  Support MIPI DSI for Mediatek SoCs.
+
+config PHY_MTK_DP
+	tristate "MediaTek DP-PHY Driver"
+	depends on ARCH_MEDIATEK || COMPILE_TEST
+	depends on OF
+	select GENERIC_PHY
+	help
+	  Support DisplayPort PHY for Mediatek SoCs.
diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile
index ace660fbed3a1..4ba1e06504346 100644
--- a/drivers/phy/mediatek/Makefile
+++ b/drivers/phy/mediatek/Makefile
@@ -3,6 +3,7 @@
 # Makefile for the phy drivers.
 #
 
+obj-$(CONFIG_PHY_MTK_DP)		+= phy-mtk-dp.o
 obj-$(CONFIG_PHY_MTK_TPHY)		+= phy-mtk-tphy.o
 obj-$(CONFIG_PHY_MTK_UFS)		+= phy-mtk-ufs.o
 obj-$(CONFIG_PHY_MTK_XSPHY)		+= phy-mtk-xsphy.o
diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
new file mode 100644
index 0000000000000..4f8d26ec0346b
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-dp.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 BayLibre
+ * Author: Markus Schneider-Pargmann <msp@baylibre.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define PHY_OFFSET 0x1000
+
+#define MTK_DP_PHY_DIG_PLL_CTL_1		(PHY_OFFSET + 0x014)
+# define TPLL_SSC_EN				BIT(3)
+
+#define MTK_DP_PHY_DIG_BIT_RATE			(PHY_OFFSET + 0x03C)
+# define BIT_RATE_RBR				0
+# define BIT_RATE_HBR				1
+# define BIT_RATE_HBR2				2
+# define BIT_RATE_HBR3				3
+
+#define MTK_DP_PHY_DIG_SW_RST			(PHY_OFFSET + 0x038)
+# define DP_GLB_SW_RST_PHYD			BIT(0)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_3		(PHY_OFFSET + 0x138)
+#define MTK_DP_LANE1_DRIVING_PARAM_3		(PHY_OFFSET + 0x238)
+#define MTK_DP_LANE2_DRIVING_PARAM_3		(PHY_OFFSET + 0x338)
+#define MTK_DP_LANE3_DRIVING_PARAM_3		(PHY_OFFSET + 0x438)
+# define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT	0x10
+# define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT	(0x14 << 8)
+# define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT	(0x18 << 16)
+# define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT	(0x20 << 24)
+# define DRIVING_PARAM_3_DEFAULT		(XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_4		(PHY_OFFSET + 0x13C)
+#define MTK_DP_LANE1_DRIVING_PARAM_4		(PHY_OFFSET + 0x23C)
+#define MTK_DP_LANE2_DRIVING_PARAM_4		(PHY_OFFSET + 0x33C)
+#define MTK_DP_LANE3_DRIVING_PARAM_4		(PHY_OFFSET + 0x43C)
+# define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT	0x18
+# define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT	(0x1e << 8)
+# define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT	(0x24 << 16)
+# define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT	(0x20 << 24)
+# define DRIVING_PARAM_4_DEFAULT		(XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_5		(PHY_OFFSET + 0x140)
+#define MTK_DP_LANE1_DRIVING_PARAM_5		(PHY_OFFSET + 0x240)
+#define MTK_DP_LANE2_DRIVING_PARAM_5		(PHY_OFFSET + 0x340)
+#define MTK_DP_LANE3_DRIVING_PARAM_5		(PHY_OFFSET + 0x440)
+# define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT	0x28
+# define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT	(0x30 << 8)
+# define DRIVING_PARAM_5_DEFAULT		(XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_6		(PHY_OFFSET + 0x144)
+#define MTK_DP_LANE1_DRIVING_PARAM_6		(PHY_OFFSET + 0x244)
+#define MTK_DP_LANE2_DRIVING_PARAM_6		(PHY_OFFSET + 0x344)
+#define MTK_DP_LANE3_DRIVING_PARAM_6		(PHY_OFFSET + 0x444)
+# define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT	0x00
+# define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT	(0x04 << 8)
+# define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT	(0x08 << 16)
+# define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT	(0x10 << 24)
+# define DRIVING_PARAM_6_DEFAULT		(XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_7		(PHY_OFFSET + 0x148)
+#define MTK_DP_LANE1_DRIVING_PARAM_7		(PHY_OFFSET + 0x248)
+#define MTK_DP_LANE2_DRIVING_PARAM_7		(PHY_OFFSET + 0x348)
+#define MTK_DP_LANE3_DRIVING_PARAM_7		(PHY_OFFSET + 0x448)
+# define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT	0x00
+# define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT	(0x06 << 8)
+# define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT	(0x0c << 16)
+# define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT	(0x00 << 24)
+# define DRIVING_PARAM_7_DEFAULT		(XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_8		(PHY_OFFSET + 0x14C)
+#define MTK_DP_LANE1_DRIVING_PARAM_8		(PHY_OFFSET + 0x24C)
+#define MTK_DP_LANE2_DRIVING_PARAM_8		(PHY_OFFSET + 0x34C)
+#define MTK_DP_LANE3_DRIVING_PARAM_8		(PHY_OFFSET + 0x44C)
+# define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT	0x08
+# define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT	(0x00 << 8)
+# define DRIVING_PARAM_8_DEFAULT		(XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT | \
+						 XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT)
+
+struct mtk_dp_phy {
+	struct regmap *regs;
+};
+
+static int mtk_dp_phy_init(struct phy *phy)
+{
+	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+	u32 driving_params[] = {
+		DRIVING_PARAM_3_DEFAULT,
+		DRIVING_PARAM_4_DEFAULT,
+		DRIVING_PARAM_5_DEFAULT,
+		DRIVING_PARAM_6_DEFAULT,
+		DRIVING_PARAM_7_DEFAULT,
+		DRIVING_PARAM_8_DEFAULT
+	};
+
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
+			  driving_params, ARRAY_SIZE(driving_params));
+
+	return 0;
+}
+
+static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+	u32 val;
+
+	if (opts->dp.set_rate) {
+		switch (opts->dp.link_rate) {
+		default:
+			dev_err(&phy->dev,
+				"Implementation error, unknown linkrate %x\n",
+				opts->dp.link_rate);
+			return -EINVAL;
+		case 1620:
+			val = BIT_RATE_RBR;
+			break;
+		case 2700:
+			val = BIT_RATE_HBR;
+			break;
+		case 5400:
+			val = BIT_RATE_HBR2;
+			break;
+		case 8100:
+			val = BIT_RATE_HBR3;
+			break;
+		}
+		regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE, val);
+	}
+
+	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
+			   TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN : 0);
+
+	return 0;
+}
+
+static int mtk_dp_phy_reset(struct phy *phy)
+{
+	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+
+	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
+			   DP_GLB_SW_RST_PHYD, 0);
+	usleep_range(50, 200);
+	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
+			   DP_GLB_SW_RST_PHYD, 1);
+
+	return 0;
+}
+
+static const struct phy_ops mtk_dp_phy_dev_ops = {
+	.init = mtk_dp_phy_init,
+	.configure = mtk_dp_phy_configure,
+	.reset = mtk_dp_phy_reset,
+	.owner = THIS_MODULE,
+};
+
+static int mtk_dp_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_dp_phy *dp_phy;
+	struct phy *phy;
+
+	dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
+	if (!dp_phy)
+		return -ENOMEM;
+
+	dp_phy->regs = *(struct regmap **)dev->platform_data;
+	if (!dp_phy->regs) {
+		dev_err(dev, "No data passed, requires struct regmap**\n");
+		return -EINVAL;
+	}
+
+	phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
+	if (IS_ERR(phy)) {
+		dev_err(dev, "Failed to create DP PHY: %ld\n", PTR_ERR(phy));
+		return PTR_ERR(phy);
+	}
+	phy_set_drvdata(phy, dp_phy);
+
+	// Set device data to the phy so that mtk-dp can get it easily
+	dev_set_drvdata(dev, phy);
+
+	return 0;
+}
+
+struct platform_driver mtk_dp_phy_driver = {
+	.probe = mtk_dp_phy_probe,
+	.driver = {
+		.name = "mediatek-dp-phy",
+	},
+};
+module_platform_driver(mtk_dp_phy_driver);
+
+MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
+MODULE_DESCRIPTION("MediaTek DP PHY Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.32.0


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

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

* [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-11-10 13:06 ` Guillaume Ranquet
  (?)
@ 2021-11-10 13:06   ` Guillaume Ranquet
  -1 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Chun-Kuang Hu, Philipp Zabel,
	Matthias Brugger
  Cc: Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

From: Markus Schneider-Pargmann <msp@baylibre.com>

This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
according phy driver mediatek-dp-phy.

It supports both functional units on the mt8195, the embedded
DisplayPort as well as the external DisplayPort units. It offers
hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
to 4 lanes.

The driver creates a child device for the phy. The child device will
never exist without the parent being active. As they are sharing a
register range, the parent passes a regmap pointer to the child so that
both can work with the same register range. The phy driver sets device
data that is read by the parent to get the phy device that can be used
to control the phy properties.

This driver is based on an initial version by
Jason-JH.Lin <jason-jh.lin@mediatek.com>.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Reported-by: kernel test robot <lkp@intel.com>
---
 drivers/gpu/drm/drm_edid.c              |    2 +-
 drivers/gpu/drm/mediatek/Kconfig        |    7 +
 drivers/gpu/drm/mediatek/Makefile       |    2 +
 drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
 drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
 drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
 9 files changed, 3799 insertions(+), 13 deletions(-)
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 500279a82167a..bfd98b50ceb5b 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
 	 * modes and forbids YCRCB422 support for all video modes per
 	 * HDMI 1.3 spec.
 	 */
-	info->color_formats = DRM_COLOR_FORMAT_RGB444;
+	info->color_formats |= DRM_COLOR_FORMAT_RGB444;
 
 	/* YCRCB444 is optional according to spec. */
 	if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
index 2976d21e9a34a..029b94c716131 100644
--- a/drivers/gpu/drm/mediatek/Kconfig
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
 	select PHY_MTK_HDMI
 	help
 	  DRM/KMS HDMI driver for Mediatek SoCs
+
+config MTK_DPTX_SUPPORT
+	tristate "DRM DPTX Support for Mediatek SoCs"
+	depends on DRM_MEDIATEK
+	select PHY_MTK_DP
+	help
+	  DRM/KMS Display Port driver for Mediatek SoCs.
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index 29098d7c8307c..d86a6406055e6 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
 			  mtk_hdmi_ddc.o
 
 obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
+
+obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
new file mode 100644
index 0000000000000..83087219d5a5e
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -0,0 +1,3094 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Copyright (c) 2021 BayLibre
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/hdmi-codec.h>
+#include <video/videomode.h>
+
+#include "mtk_dp_reg.h"
+
+#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
+#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
+
+#define MTK_DP_MAX_LANES 4
+#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
+
+#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
+
+#define MTK_DP_TRAIN_RETRY_LIMIT 8
+#define MTK_DP_TRAIN_MAX_ITERATIONS 5
+
+#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
+
+#define MTK_DP_DP_VERSION_11 0x11
+
+enum mtk_dp_state {
+	MTK_DP_STATE_INITIAL,
+	MTK_DP_STATE_IDLE,
+	MTK_DP_STATE_PREPARE,
+	MTK_DP_STATE_NORMAL,
+};
+
+enum mtk_dp_train_state {
+	MTK_DP_TRAIN_STATE_STARTUP = 0,
+	MTK_DP_TRAIN_STATE_CHECKCAP,
+	MTK_DP_TRAIN_STATE_CHECKEDID,
+	MTK_DP_TRAIN_STATE_TRAINING_PRE,
+	MTK_DP_TRAIN_STATE_TRAINING,
+	MTK_DP_TRAIN_STATE_CHECKTIMING,
+	MTK_DP_TRAIN_STATE_NORMAL,
+	MTK_DP_TRAIN_STATE_POWERSAVE,
+	MTK_DP_TRAIN_STATE_DPIDLE,
+};
+
+struct mtk_dp_timings {
+	struct videomode vm;
+
+	u16 htotal;
+	u16 vtotal;
+	u8 frame_rate;
+	u32 pix_rate_khz;
+};
+
+struct mtk_dp_train_info {
+	bool tps3;
+	bool tps4;
+	bool sink_ssc;
+	bool cable_plugged_in;
+	bool cable_state_change;
+	bool cr_done;
+	bool eq_done;
+
+	// link_rate is in multiple of 0.27Gbps
+	int link_rate;
+	int lane_count;
+
+	int irq_status;
+	int check_cap_count;
+};
+
+// Same values as used by the DP Spec for MISC0 bits 1 and 2
+enum mtk_dp_color_format {
+	MTK_DP_COLOR_FORMAT_RGB_444 = 0,
+	MTK_DP_COLOR_FORMAT_YUV_422 = 1,
+	MTK_DP_COLOR_FORMAT_YUV_444 = 2,
+	MTK_DP_COLOR_FORMAT_YUV_420 = 3,
+	MTK_DP_COLOR_FORMAT_YONLY = 4,
+	MTK_DP_COLOR_FORMAT_RAW = 5,
+	MTK_DP_COLOR_FORMAT_RESERVED = 6,
+	MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
+	MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
+};
+
+// Multiple of 0.27Gbps
+enum mtk_dp_linkrate {
+	MTK_DP_LINKRATE_RBR = 0x6,
+	MTK_DP_LINKRATE_HBR = 0xA,
+	MTK_DP_LINKRATE_HBR2 = 0x14,
+	MTK_DP_LINKRATE_HBR25 = 0x19,
+	MTK_DP_LINKRATE_HBR3 = 0x1E,
+};
+
+// Same values as used for DP Spec MISC0 bits 5,6,7
+enum mtk_dp_color_depth {
+	MTK_DP_COLOR_DEPTH_6BIT = 0,
+	MTK_DP_COLOR_DEPTH_8BIT = 1,
+	MTK_DP_COLOR_DEPTH_10BIT = 2,
+	MTK_DP_COLOR_DEPTH_12BIT = 3,
+	MTK_DP_COLOR_DEPTH_16BIT = 4,
+	MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
+};
+
+struct mtk_dp_audio_cfg {
+	int sample_rate;
+	int word_length_bits;
+	int channels;
+};
+
+struct mtk_dp_info {
+	enum mtk_dp_color_depth depth;
+	enum mtk_dp_color_format format;
+	struct mtk_dp_audio_cfg audio_caps;
+	struct mtk_dp_timings timings;
+};
+
+struct dp_cal_data {
+	unsigned int glb_bias_trim;
+	unsigned int clktx_impse;
+
+	unsigned int ln0_tx_impsel_pmos;
+	unsigned int ln0_tx_impsel_nmos;
+	unsigned int ln1_tx_impsel_pmos;
+	unsigned int ln1_tx_impsel_nmos;
+	unsigned int ln2_tx_impsel_pmos;
+	unsigned int ln2_tx_impsel_nmos;
+	unsigned int ln3_tx_impsel_pmos;
+	unsigned int ln3_tx_impsel_nmos;
+};
+
+struct mtk_dp {
+	struct device *dev;
+	struct platform_device *phy_dev;
+	struct phy *phy;
+	struct dp_cal_data cal_data;
+
+	struct drm_device *drm_dev;
+	struct drm_bridge bridge;
+	struct drm_bridge *next_bridge;
+	struct drm_dp_aux aux;
+
+	struct mutex edid_lock;
+	struct edid *edid;
+
+	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
+
+	struct mtk_dp_info info;
+	enum mtk_dp_state state;
+
+	struct mtk_dp_train_info train_info;
+	enum mtk_dp_train_state train_state;
+	unsigned int input_fmt;
+
+	struct regmap *regs;
+	struct clk *dp_tx_clk;
+
+	bool enabled;
+	bool audio_enable;
+
+	bool has_fec;
+	struct mutex dp_lock;
+
+	struct mutex update_plugged_status_lock;
+
+	hdmi_codec_plugged_cb plugged_cb;
+	struct device *codec_dev;
+	u8 connector_eld[MAX_ELD_BYTES];
+};
+
+enum mtk_dp_sdp_type {
+	MTK_DP_SDP_NONE = 0x00,
+	MTK_DP_SDP_ACM = 0x01,
+	MTK_DP_SDP_ISRC = 0x02,
+	MTK_DP_SDP_AVI = 0x03,
+	MTK_DP_SDP_AUI = 0x04,
+	MTK_DP_SDP_SPD = 0x05,
+	MTK_DP_SDP_MPEG = 0x06,
+	MTK_DP_SDP_NTSC = 0x07,
+	MTK_DP_SDP_VSP = 0x08,
+	MTK_DP_SDP_VSC = 0x09,
+	MTK_DP_SDP_EXT = 0x0A,
+	MTK_DP_SDP_PPS0 = 0x0B,
+	MTK_DP_SDP_PPS1 = 0x0C,
+	MTK_DP_SDP_PPS2 = 0x0D,
+	MTK_DP_SDP_PPS3 = 0x0E,
+	MTK_DP_SDP_DRM = 0x10,
+	MTK_DP_SDP_MAX_NUM
+};
+
+struct mtk_dp_sdp_packet {
+	enum mtk_dp_sdp_type type;
+	struct dp_sdp sdp;
+};
+
+#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
+union mtk_dp_audio_channel_status {
+	struct {
+		u8 rev : 1;
+		u8 is_lpcm : 1;
+		u8 copy_right : 1;
+		u8 additional_format_info : 3;
+		u8 channel_status_mode : 2;
+		u8 category_code;
+		u8 src_num : 4;
+		u8 channel_num : 4;
+		u8 sampling_freq : 4;
+		u8 clk_accuracy : 2;
+		u8 rev2 : 2;
+		u8 word_len : 4;
+		u8 original_sampling_freq : 4;
+	} iec;
+
+	u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
+};
+
+static struct regmap_config mtk_dp_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = SEC_OFFSET + 0x90,
+	.name = "mtk-dp-registers",
+};
+
+static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
+{
+	return mtk_dp->next_bridge != NULL;
+}
+
+static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
+{
+	return container_of(b, struct mtk_dp, bridge);
+}
+
+static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
+{
+	u32 read_val;
+	int ret;
+
+	ret = regmap_read(mtk_dp->regs, offset, &read_val);
+	if (ret) {
+		dev_err(mtk_dp->dev, "Failed to read register 0x%x: %d\n",
+			offset, ret);
+		return 0;
+	}
+
+	return read_val;
+}
+
+static void mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
+{
+	int ret;
+
+	ret = regmap_write(mtk_dp->regs, offset, val);
+	if (ret)
+		dev_err(mtk_dp->dev,
+			"Failed to write register 0x%x with value 0x%x: %d\n",
+			offset, val, ret);
+}
+
+static void mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, u32 val,
+			       u32 mask)
+{
+	int ret;
+
+	ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
+	if (ret)
+		dev_err(mtk_dp->dev,
+			"Failed to update register 0x%x with value 0x%x, mask 0x%x: %d\n",
+			offset, val, mask, ret);
+}
+
+static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32 offset, u8 *buf,
+				    size_t length)
+{
+	int i;
+	int num_regs = (length + 1) / 2;
+
+	// 2 bytes per register
+	for (i = 0; i < num_regs; i++) {
+		u32 val = buf[i * 2] |
+			  (i * 2 + 1 < length ? buf[i * 2 + 1] << 8 : 0);
+
+		mtk_dp_write(mtk_dp, offset + i * 4, val);
+	}
+}
+
+static unsigned long mtk_dp_sip_atf_call(unsigned int cmd, unsigned int para)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32, cmd, para, 0, 0, 0, 0, 0,
+		      &res);
+
+	pr_debug("[DPTX]%s cmd 0x%x, p1 0x%x, ret 0x%lx-0x%lx", __func__, cmd,
+		 para, res.a0, res.a1);
+	return res.a1;
+}
+
+static void mtk_dp_msa_bypass_disable(struct mtk_dp *mtk_dp)
+{
+	const u16 bits_to_set =
+		BIT(HTOTAL_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(VTOTAL_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(HSTART_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(VSTART_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(HWIDTH_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(VHEIGHT_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(HSP_SEL_DP_ENC0_P0_SHIFT) | BIT(HSW_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(VSP_SEL_DP_ENC0_P0_SHIFT) | BIT(VSW_SEL_DP_ENC0_P0_SHIFT);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, bits_to_set,
+			   bits_to_set);
+}
+
+static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
+{
+	struct mtk_dp_timings *timings = &mtk_dp->info.timings;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010, timings->htotal,
+			   HTOTAL_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
+			   timings->vm.hsync_len + timings->vm.hback_porch,
+			   HSTART_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
+			   timings->vm.hsync_len << HSW_SW_DP_ENC0_P0_SHIFT,
+			   HSW_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028, 0,
+			   HSP_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020, timings->vm.hactive,
+			   HWIDTH_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014, timings->vtotal,
+			   VTOTAL_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
+			   timings->vm.vsync_len + timings->vm.vback_porch,
+			   VSTART_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
+			   timings->vm.vsync_len << VSW_SW_DP_ENC0_P0_SHIFT,
+			   VSW_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C, 0,
+			   VSP_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024, timings->vm.vactive,
+			   VHEIGHT_SW_DP_ENC0_P0_MASK);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064, timings->vm.hactive,
+			   HDE_NUM_LAST_DP_ENC0_P0_MASK);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154, timings->htotal,
+			   PGEN_HTOTAL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
+			   timings->vm.hfront_porch,
+			   PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C, timings->vm.hsync_len,
+			   PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
+			   timings->vm.hback_porch + timings->vm.hsync_len,
+			   PGEN_HFDE_START_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164, timings->vm.hactive,
+			   PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168, timings->vtotal,
+			   PGEN_VTOTAL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
+			   timings->vm.vfront_porch,
+			   PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170, timings->vm.vsync_len,
+			   PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
+			   timings->vm.vback_porch + timings->vm.vsync_len,
+			   PGEN_VFDE_START_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178, timings->vm.vactive,
+			   PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
+				    enum mtk_dp_color_format color_format)
+{
+	u32 val;
+
+	mtk_dp->info.format = color_format;
+
+	// Update MISC0
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
+			   color_format << DP_TEST_COLOR_FORMAT_SHIFT,
+			   DP_TEST_COLOR_FORMAT_MASK);
+
+	switch (color_format) {
+	case MTK_DP_COLOR_FORMAT_YUV_422:
+		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
+		break;
+	case MTK_DP_COLOR_FORMAT_YUV_420:
+		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420;
+		break;
+	case MTK_DP_COLOR_FORMAT_YONLY:
+	case MTK_DP_COLOR_FORMAT_RAW:
+	case MTK_DP_COLOR_FORMAT_RESERVED:
+	case MTK_DP_COLOR_FORMAT_UNKNOWN:
+		drm_warn(mtk_dp->drm_dev, "Unsupported color format: %d\n",
+			 color_format);
+		fallthrough;
+	case MTK_DP_COLOR_FORMAT_RGB_444:
+	case MTK_DP_COLOR_FORMAT_YUV_444:
+		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
+		break;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
+			   PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp,
+				   enum mtk_dp_color_depth color_depth)
+{
+	u32 val;
+
+	mtk_dp->info.depth = color_depth;
+
+	// Update MISC0
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
+			   color_depth << DP_TEST_BIT_DEPTH_SHIFT,
+			   DP_TEST_BIT_DEPTH_MASK);
+
+	switch (color_depth) {
+	case MTK_DP_COLOR_DEPTH_6BIT:
+		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT;
+		break;
+	case MTK_DP_COLOR_DEPTH_8BIT:
+		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT;
+		break;
+	case MTK_DP_COLOR_DEPTH_10BIT:
+		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT;
+		break;
+	case MTK_DP_COLOR_DEPTH_12BIT:
+		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT;
+		break;
+	case MTK_DP_COLOR_DEPTH_16BIT:
+		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT;
+		break;
+	case MTK_DP_COLOR_DEPTH_UNKNOWN:
+		drm_warn(mtk_dp->drm_dev, "Unsupported color depth %d\n",
+			 color_depth);
+		return;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
+			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_mn_overwrite_disable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
+			   VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32 val)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   val << SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT,
+			   SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   BIT(VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT),
+			   VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
+			   0x20 << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
+			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
+			   0x20 << SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT,
+			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
+			   2 << VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT,
+			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
+			   4 << FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT,
+			   FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
+	mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368,
+		     1 << VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT |
+			     1 << VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT |
+			     BIT(SDP_DP13_EN_DP_ENC1_P0_SHIFT) |
+			     1 << BS2BS_MODE_DP_ENC1_P0_SHIFT);
+}
+
+static void mtk_dp_pg_disable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038, 0,
+			   VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
+			   4 << PGEN_PATTERN_SEL_SHIFT, PGEN_PATTERN_SEL_MASK);
+}
+
+static void mtk_dp_audio_setup_channels(struct mtk_dp *mtk_dp,
+					struct mtk_dp_audio_cfg *cfg)
+{
+	u32 channel_enable_bits;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3324,
+			   AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX,
+			   AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK);
+
+	//audio channel count change reset
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(9), BIT(9));
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
+			   AU_PRTY_REGEN_DP_ENC1_P0_MASK,
+			   AU_PRTY_REGEN_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
+			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK,
+			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
+			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK,
+			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK);
+
+	switch (cfg->channels) {
+	case 2:
+		channel_enable_bits = AUDIO_2CH_SEL_DP_ENC0_P0_MASK |
+				      AUDIO_2CH_EN_DP_ENC0_P0_MASK;
+		break;
+	case 8:
+	default:
+		channel_enable_bits = AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
+				      AUDIO_8CH_EN_DP_ENC0_P0_MASK;
+		break;
+	}
+	mtk_dp_update_bits(
+		mtk_dp, MTK_DP_ENC0_P0_3088,
+		channel_enable_bits | AU_EN_DP_ENC0_P0_MASK,
+		AUDIO_2CH_SEL_DP_ENC0_P0_MASK | AUDIO_2CH_EN_DP_ENC0_P0_MASK |
+			AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
+			AUDIO_8CH_EN_DP_ENC0_P0_MASK | AU_EN_DP_ENC0_P0_MASK);
+
+	//audio channel count change reset
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, 0, BIT(9));
+
+	//enable audio reset
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(0), BIT(0));
+}
+
+static void mtk_dp_audio_channel_status_set(struct mtk_dp *mtk_dp,
+					    struct mtk_dp_audio_cfg *cfg)
+{
+	union mtk_dp_audio_channel_status channel_status;
+
+	memset(&channel_status, 0, sizeof(channel_status));
+
+	switch (cfg->sample_rate) {
+	case 32000:
+		channel_status.iec.sampling_freq = 3;
+		break;
+	case 44100:
+		channel_status.iec.sampling_freq = 0;
+		break;
+	case 48000:
+		channel_status.iec.sampling_freq = 2;
+		break;
+	case 88200:
+		channel_status.iec.sampling_freq = 8;
+		break;
+	case 96000:
+		channel_status.iec.sampling_freq = 0xA;
+		break;
+	case 192000:
+		channel_status.iec.sampling_freq = 0xE;
+		break;
+	default:
+		channel_status.iec.sampling_freq = 0x1;
+		break;
+	}
+
+	switch (cfg->word_length_bits) {
+	case 16:
+		channel_status.iec.word_len = 0x02;
+		break;
+	case 20:
+		channel_status.iec.word_len = 0x03;
+		break;
+	case 24:
+		channel_status.iec.word_len = 0x0B;
+		break;
+	}
+
+	// IEC 60958 consumer channel status bits
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_308C,
+			   channel_status.buf[1] << 8 | channel_status.buf[0],
+			   CH_STATUS_0_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3090,
+			   channel_status.buf[3] << 8 | channel_status.buf[2],
+			   CH_STATUS_1_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3094, channel_status.buf[4],
+			   CH_STATUS_2_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_audio_sdp_asp_set_channels(struct mtk_dp *mtk_dp,
+					      int channels)
+{
+	if (channels != 2 && channels != 8)
+		channels = 8;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_312C,
+			   (channels - 1) << ASP_HB3_DP_ENC0_P0_SHIFT,
+			   ASP_HB2_DP_ENC0_P0_MASK | ASP_HB3_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_audio_set_divider(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
+			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2,
+			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK);
+}
+
+static bool mtk_dp_plug_state(struct mtk_dp *mtk_dp)
+{
+	return !!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
+		  HPD_DB_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_sdp_trigger_packet(struct mtk_dp *mtk_dp,
+				      enum mtk_dp_sdp_type type)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, type,
+			   SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, SDP_PACKET_W_DP_ENC1_P0,
+			   SDP_PACKET_W_DP_ENC1_P0);
+}
+
+static void mtk_dp_sdp_set_data(struct mtk_dp *mtk_dp, u8 *data_bytes)
+{
+	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_ENC1_P0_3200, data_bytes, 0x10);
+}
+
+static void mtk_dp_sdp_set_header(struct mtk_dp *mtk_dp,
+				  enum mtk_dp_sdp_type type,
+				  struct dp_sdp_header *header)
+{
+	u32 db_addr;
+
+	switch (type) {
+	case MTK_DP_SDP_DRM:
+		db_addr = MTK_DP_ENC0_P0_3138;
+		break;
+	case MTK_DP_SDP_PPS0:
+	case MTK_DP_SDP_PPS1:
+	case MTK_DP_SDP_PPS2:
+	case MTK_DP_SDP_PPS3:
+		db_addr = MTK_DP_ENC0_P0_3130;
+		break;
+	default:
+		db_addr = MTK_DP_ENC0_P0_30D8 + (type - MTK_DP_SDP_ACM) * 8;
+	}
+
+	mtk_dp_bulk_16bit_write(mtk_dp, db_addr, (u8 *)header, 4);
+}
+
+static const u32 mtk_dp_sdp_type_to_reg[MTK_DP_SDP_MAX_NUM] = {
+	/* MTK_DP_SDP_NONE => */ 0x0,
+	/* MTK_DP_SDP_ACM  => */ MTK_DP_ENC0_P0_30B4,
+	/* MTK_DP_SDP_ISRC => */ MTK_DP_ENC0_P0_30B4 + 1,
+	/* MTK_DP_SDP_AVI  => */ MTK_DP_ENC0_P0_30A4 + 1,
+	/* MTK_DP_SDP_AUI  => */ MTK_DP_ENC0_P0_30A8,
+	/* MTK_DP_SDP_SPD  => */ MTK_DP_ENC0_P0_30A8 + 1,
+	/* MTK_DP_SDP_MPEG => */ MTK_DP_ENC0_P0_30AC,
+	/* MTK_DP_SDP_NTSC => */ MTK_DP_ENC0_P0_30AC + 1,
+	/* MTK_DP_SDP_VSP  => */ MTK_DP_ENC0_P0_30B0,
+	/* MTK_DP_SDP_VSC  => */ MTK_DP_ENC0_P0_30B8,
+	/* MTK_DP_SDP_EXT  => */ MTK_DP_ENC0_P0_30B0 + 1,
+	/* MTK_DP_SDP_PPS0 => */ MTK_DP_ENC0_P0_31E8,
+	/* MTK_DP_SDP_PPS1 => */ MTK_DP_ENC0_P0_31E8,
+	/* MTK_DP_SDP_PPS2 => */ MTK_DP_ENC0_P0_31E8,
+	/* MTK_DP_SDP_PPS3 => */ MTK_DP_ENC0_P0_31E8,
+	/* MTK_DP_SDP_DRM  => */ MTK_DP_ENC0_P0_31DC,
+};
+
+static void mtk_dp_disable_sdp(struct mtk_dp *mtk_dp, enum mtk_dp_sdp_type type)
+{
+	if (type == MTK_DP_SDP_NONE)
+		return;
+
+	// Disable periodic send
+	mtk_dp_update_bits(mtk_dp, mtk_dp_sdp_type_to_reg[type] & 0xfffc, 0,
+			   0xFF << ((mtk_dp_sdp_type_to_reg[type] & 3) * 8));
+}
+
+static void mtk_dp_setup_sdp(struct mtk_dp *mtk_dp,
+			     struct mtk_dp_sdp_packet *packet)
+{
+	mtk_dp_sdp_set_data(mtk_dp, packet->sdp.db);
+	mtk_dp_sdp_set_header(mtk_dp, packet->type, &packet->sdp.sdp_header);
+
+	mtk_dp_disable_sdp(mtk_dp, packet->type);
+
+	switch (packet->type) {
+	case MTK_DP_SDP_NONE:
+		break;
+	case MTK_DP_SDP_ISRC:
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
+				   0x1C << ISRC1_HB3_DP_ENC0_P0_SHIFT,
+				   ISRC1_HB3_DP_ENC0_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, MTK_DP_SDP_ISRC,
+				   SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
+
+		if (packet->sdp.sdp_header.HB3 & BIT(2))
+			mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
+					   BIT(ISRC_CONT_DP_ENC0_P0_SHIFT),
+					   ISRC_CONT_DP_ENC0_P0_MASK);
+		else
+			mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC, 0,
+					   ISRC_CONT_DP_ENC0_P0_MASK);
+
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280,
+				   SDP_PACKET_W_DP_ENC1_P0,
+				   SDP_PACKET_W_DP_ENC1_P0);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30B4,
+				   5 << ISRC_CFG_DP_ENC0_P0_SHIFT,
+				   ISRC_CFG_DP_ENC0_P0_MASK);
+		break;
+	case MTK_DP_SDP_DRM:
+		mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31DC,
+				   5 << HDR0_CFG_DP_ENC0_P0_SHIFT,
+				   HDR0_CFG_DP_ENC0_P0_MASK);
+		break;
+	case MTK_DP_SDP_ACM:
+	case MTK_DP_SDP_AVI:
+	case MTK_DP_SDP_AUI:
+	case MTK_DP_SDP_SPD:
+	case MTK_DP_SDP_MPEG:
+	case MTK_DP_SDP_NTSC:
+	case MTK_DP_SDP_VSP:
+	case MTK_DP_SDP_VSC:
+	case MTK_DP_SDP_EXT:
+	case MTK_DP_SDP_PPS0:
+	case MTK_DP_SDP_PPS1:
+	case MTK_DP_SDP_PPS2:
+	case MTK_DP_SDP_PPS3:
+		mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
+		// Enable periodic sending
+		mtk_dp_update_bits(
+			mtk_dp, mtk_dp_sdp_type_to_reg[packet->type] & 0xfffc,
+			0x05 << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
+				 8),
+			0xff << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
+				 8));
+		break;
+	default:
+		break;
+	}
+}
+
+static void mtk_dp_sdp_vsc_ext_disable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A0, 0,
+			   BIT(7) | BIT(8) | BIT(12));
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_328C, 0, BIT(7));
+}
+
+static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_write(
+		mtk_dp, MTK_DP_AUX_P0_3640,
+		BIT(AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT));
+}
+
+static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32 addr)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644, cmd,
+			   MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648, addr,
+			   MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C, addr >> 16,
+			   MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_aux_cmd_complete(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
+			   BIT(MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT),
+			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK |
+				   PHY_FIFO_RST_AUX_TX_P0_MASK |
+				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
+			   BIT(AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT),
+			   AUX_TX_REQUEST_READY_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8 *buf,
+				       size_t length)
+{
+	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf, length);
+}
+
+static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
+				    size_t length, int read_delay)
+{
+	int read_pos;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620, 0,
+			   AUX_RD_MODE_AUX_TX_P0_MASK);
+
+	for (read_pos = 0; read_pos < length; read_pos++) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
+				   BIT(AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT),
+				   AUX_RX_FIFO_READ_PULSE_TX_P0_MASK);
+		usleep_range(read_delay, read_delay * 2);
+		buf[read_pos] =
+			(u8)(mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3620) &
+			     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK >>
+				     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT);
+	}
+}
+
+static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t length)
+{
+	if (length > 0) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
+				   (length - 1)
+					   << MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT,
+				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
+				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
+					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	} else {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
+				   BIT(AUX_NO_LENGTH_AUX_TX_P0_SHIFT),
+				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
+					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	}
+}
+
+static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp, bool is_read)
+{
+	int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
+
+	while (--wait_reply) {
+		u32 aux_irq_status;
+
+		if (is_read) {
+			u32 fifo_status =
+				mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3618);
+
+			if (fifo_status &
+			    (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
+			     AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
+				return 0;
+			}
+		}
+
+		aux_irq_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3640);
+		if (aux_irq_status & AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK)
+			return 0;
+
+		if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK)
+			return -ETIMEDOUT;
+
+		usleep_range(100, 500);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool is_read, u8 cmd,
+				  u32 addr, u8 *buf, size_t length)
+{
+	int ret;
+	u32 reply_cmd;
+
+	if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
+			(cmd == DP_AUX_NATIVE_READ && !length)))
+		return -EINVAL;
+
+	if (!is_read)
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
+				   BIT(AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT),
+				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK);
+
+	mtk_dp_aux_cmd_complete(mtk_dp);
+	mtk_dp_aux_irq_clear(mtk_dp);
+	usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
+		     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
+
+	mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
+	mtk_dp_aux_set_length(mtk_dp, length);
+
+	if (!is_read) {
+		if (length)
+			mtk_dp_aux_fill_write_fifo(mtk_dp, buf, length);
+
+		mtk_dp_update_bits(
+			mtk_dp, MTK_DP_AUX_P0_3704,
+			AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK,
+			AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK);
+	}
+
+	mtk_dp_aux_request_ready(mtk_dp);
+
+	ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
+
+	reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
+		    AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
+
+	if (ret || reply_cmd) {
+		u32 phy_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3628) &
+				 AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
+		if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
+			drm_err(mtk_dp->drm_dev,
+				"AUX Rx Aux hang, need SW reset\n");
+			ret = -EIO;
+		}
+
+		mtk_dp_aux_cmd_complete(mtk_dp);
+		mtk_dp_aux_irq_clear(mtk_dp);
+
+		usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
+			     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
+		return -ETIMEDOUT;
+	}
+
+	if (!length) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
+				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
+					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	} else if (is_read) {
+		int read_delay;
+
+		if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
+		    cmd == DP_AUX_I2C_READ)
+			read_delay = 500;
+		else
+			read_delay = 100;
+
+		mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length, read_delay);
+	}
+
+	mtk_dp_aux_cmd_complete(mtk_dp);
+	mtk_dp_aux_irq_clear(mtk_dp);
+	usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
+		     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
+
+	return 0;
+}
+
+static bool mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int lane_num,
+					  int swing_val, int preemphasis)
+{
+	u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
+
+	if (lane_num < 0 || lane_num > 3)
+		return false;
+
+	dev_dbg(mtk_dp->dev,
+		"link training swing_val= 0x%x, preemphasis = 0x%x\n",
+		swing_val, preemphasis);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+			   swing_val << (DP_TX0_VOLT_SWING_SHIFT + lane_shift),
+			   DP_TX0_VOLT_SWING_MASK << lane_shift);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+			   preemphasis << (DP_TX0_PRE_EMPH_SHIFT + lane_shift),
+			   DP_TX0_PRE_EMPH_MASK << lane_shift);
+
+	return true;
+}
+
+static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP, 0,
+			   DP_TX0_VOLT_SWING_MASK | DP_TX1_VOLT_SWING_MASK |
+				   DP_TX2_VOLT_SWING_MASK |
+				   DP_TX3_VOLT_SWING_MASK |
+				   DP_TX0_PRE_EMPH_MASK | DP_TX1_PRE_EMPH_MASK |
+				   DP_TX2_PRE_EMPH_MASK | DP_TX3_PRE_EMPH_MASK);
+}
+
+static void mtk_dp_fec_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
+			   enable ? BIT(FEC_EN_DP_TRANS_P0_SHIFT) : 0,
+			   FEC_EN_DP_TRANS_P0_MASK);
+}
+
+static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
+{
+	u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
+			 SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
+
+	if (irq_status) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, irq_status,
+				   SW_IRQ_CLR_DP_TRANS_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, 0,
+				   SW_IRQ_CLR_DP_TRANS_P0_MASK);
+	}
+
+	return irq_status;
+}
+
+static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
+{
+	u8 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
+			 IRQ_STATUS_DP_TRANS_P0_MASK) >>
+			IRQ_STATUS_DP_TRANS_P0_SHIFT;
+
+	if (irq_status) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, irq_status,
+				   IRQ_CLR_DP_TRANS_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, 0,
+				   IRQ_CLR_DP_TRANS_P0_MASK);
+	}
+
+	return irq_status;
+}
+
+static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	u32 val = 0;
+
+	if (!enable)
+		val = IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
+		      IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
+		      IRQ_MASK_DP_TRANS_P0_INT_IRQ;
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, val,
+			   IRQ_MASK_DP_TRANS_P0_MASK);
+}
+
+void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
+			   XTAL_FREQ_DP_TRANS_P0_DEFAULT,
+			   XTAL_FREQ_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
+			   BIT(FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT),
+			   FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
+			   BIT(AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT),
+			   AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
+			   SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK, IRQ_MASK_AUX_TOP_IRQ,
+			   IRQ_MASK_AUX_TOP_IRQ);
+}
+
+static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp *mtk_dp)
+{
+	// Debounce threshold
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   8 << HPD_DEB_THD_DP_TRANS_P0_SHIFT,
+			   HPD_DEB_THD_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
+			    HPD_INT_THD_DP_TRANS_P0_UPPER_1100US)
+				   << HPD_INT_THD_DP_TRANS_P0_SHIFT,
+			   HPD_INT_THD_DP_TRANS_P0_MASK);
+
+	// Connect threshold 1.5ms + 5 x 0.1ms = 2ms
+	// Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   (5 << HPD_DISC_THD_DP_TRANS_P0_SHIFT) |
+				   (5 << HPD_CONN_THD_DP_TRANS_P0_SHIFT),
+			   HPD_DISC_THD_DP_TRANS_P0_MASK |
+				   HPD_CONN_THD_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
+			   HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
+			   HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
+{
+	// modify timeout threshold = 1595 [12 : 8]
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C, 0x1595,
+			   AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658, 0,
+			   AUX_TX_OV_EN_AUX_TX_P0_MASK);
+	// 25 for 26M
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
+			   25 << AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT,
+			   AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
+	// 13 for 26M
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
+			   13 << AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT,
+			   AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
+			   BIT(MTK_ATOP_EN_AUX_TX_P0_SHIFT),
+			   MTK_ATOP_EN_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_initialize_digital_settings(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
+			   VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
+	mtk_dp_set_color_format(mtk_dp, MTK_DP_COLOR_FORMAT_RGB_444);
+	mtk_dp_set_color_depth(mtk_dp, MTK_DP_COLOR_DEPTH_8BIT);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
+			   1 << BS2BS_MODE_DP_ENC1_P0_SHIFT,
+			   BS2BS_MODE_DP_ENC1_P0_MASK);
+
+	// dp tx encoder reset all sw
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
+			   BIT(DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT),
+			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
+	usleep_range(1000, 5000);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
+			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
+			   BIT(DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT),
+			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
+	usleep_range(1000, 5000);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C, 0,
+			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
+			   lanes == 0 ? 0 : BIT(3), BIT(3) | BIT(2));
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, lanes,
+			   LANE_NUM_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
+			   lanes << LANE_NUM_DP_TRANS_P0_SHIFT,
+			   LANE_NUM_DP_TRANS_P0_MASK);
+}
+
+static int link_rate_to_mb_per_s(struct mtk_dp *mtk_dp,
+				 enum mtk_dp_linkrate linkrate)
+{
+	switch (linkrate) {
+	default:
+		drm_err(mtk_dp->drm_dev,
+			"Implementation error, unknown linkrate %d\n",
+			linkrate);
+		fallthrough;
+	case MTK_DP_LINKRATE_RBR:
+		return 1620;
+	case MTK_DP_LINKRATE_HBR:
+		return 2700;
+	case MTK_DP_LINKRATE_HBR2:
+		return 5400;
+	case MTK_DP_LINKRATE_HBR3:
+		return 8100;
+	}
+}
+
+static u32 check_cal_data_valid(u32 min, u32 max, u32 val, u32 default_val)
+{
+	if (val < min || val > max)
+		return default_val;
+
+	return val;
+}
+
+static int mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
+{
+	struct dp_cal_data *cal_data = &mtk_dp->cal_data;
+	struct device *dev = mtk_dp->dev;
+	struct nvmem_cell *cell;
+	u32 *buf;
+	size_t len;
+
+	cell = nvmem_cell_get(dev, "dp_calibration_data");
+	if (IS_ERR(cell)) {
+		dev_err(dev,
+			"Error: Failed to get nvmem cell dp_calibration_data\n");
+		return PTR_ERR(cell);
+	}
+
+	buf = (u32 *)nvmem_cell_read(cell, &len);
+	nvmem_cell_put(cell);
+
+	if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
+		dev_err(dev,
+			"Error: Failed to read nvmem_cell_read fail len %ld\n",
+			(len / sizeof(u32)));
+		return PTR_ERR(buf);
+	}
+
+	if (mtk_dp_is_edp(mtk_dp)) {
+		cal_data->glb_bias_trim = check_cal_data_valid(
+			1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf);
+		cal_data->clktx_impse =
+			check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8);
+		cal_data->ln0_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8);
+		cal_data->ln0_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8);
+		cal_data->ln1_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8);
+		cal_data->ln1_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8);
+		cal_data->ln2_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8);
+		cal_data->ln2_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8);
+		cal_data->ln3_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8);
+		cal_data->ln3_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8);
+	} else {
+		cal_data->glb_bias_trim = check_cal_data_valid(
+			1, 0x1e, (buf[0] >> 27) & 0x1f, 0xf);
+		cal_data->clktx_impse =
+			check_cal_data_valid(1, 0xe, (buf[0] >> 13) & 0xf, 0x8);
+		cal_data->ln0_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 28) & 0xf, 0x8);
+		cal_data->ln0_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 24) & 0xf, 0x8);
+		cal_data->ln1_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 20) & 0xf, 0x8);
+		cal_data->ln1_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 16) & 0xf, 0x8);
+		cal_data->ln2_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 12) & 0xf, 0x8);
+		cal_data->ln2_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 8) & 0xf, 0x8);
+		cal_data->ln3_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 4) & 0xf, 0x8);
+		cal_data->ln3_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, buf[1] & 0xf, 0x8);
+	}
+
+	kfree(buf);
+
+	return 0;
+}
+
+void mtk_dp_set_cal_data(struct mtk_dp *mtk_dp)
+{
+	struct dp_cal_data *cal_data = &mtk_dp->cal_data;
+
+	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
+			   cal_data->clktx_impse << 20, RG_CKM_PT0_CKTX_IMPSEL);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
+			   cal_data->glb_bias_trim << 16,
+			   RG_XTP_GLB_BIAS_INTR_CTRL);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
+			   cal_data->ln0_tx_impsel_pmos << 12,
+			   RG_XTP_LN0_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
+			   cal_data->ln0_tx_impsel_nmos << 16,
+			   RG_XTP_LN0_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
+			   cal_data->ln1_tx_impsel_pmos << 12,
+			   RG_XTP_LN1_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
+			   cal_data->ln1_tx_impsel_nmos << 16,
+			   RG_XTP_LN1_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
+			   cal_data->ln2_tx_impsel_pmos << 12,
+			   RG_XTP_LN2_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
+			   cal_data->ln2_tx_impsel_nmos << 16,
+			   RG_XTP_LN2_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
+			   cal_data->ln3_tx_impsel_pmos << 12,
+			   RG_XTP_LN3_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
+			   cal_data->ln3_tx_impsel_nmos << 16,
+			   RG_XTP_LN3_TX_IMPSEL_NMOS);
+}
+
+static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
+				enum mtk_dp_linkrate link_rate, int lane_count)
+{
+	int ret;
+	union phy_configure_opts
+		phy_opts = { .dp = {
+				     .link_rate = link_rate_to_mb_per_s(
+					     mtk_dp, link_rate),
+				     .set_rate = 1,
+				     .lanes = lane_count,
+				     .set_lanes = 1,
+				     .ssc = mtk_dp->train_info.sink_ssc,
+			     } };
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP,
+			   DP_PWR_STATE_MASK);
+
+	ret = phy_configure(mtk_dp->phy, &phy_opts);
+
+	mtk_dp_set_cal_data(mtk_dp);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL_LANE, DP_PWR_STATE_MASK);
+	return ret;
+}
+
+static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool enable)
+{
+	const u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
+			POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
+			POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
+			POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580, enable ? val : 0, val);
+}
+
+static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int pattern)
+{
+	if (pattern < 0 || pattern > 4) {
+		drm_err(mtk_dp->drm_dev,
+			"Implementation error, no such pattern %d\n", pattern);
+		return;
+	}
+
+	if (pattern == 1) // TPS1
+		mtk_dp_set_idle_pattern(mtk_dp, false);
+
+	mtk_dp_update_bits(
+		mtk_dp, MTK_DP_TRANS_P0_3400,
+		pattern ? BIT(pattern - 1) << PATTERN1_EN_DP_TRANS_P0_SHIFT : 0,
+		PATTERN1_EN_DP_TRANS_P0_MASK | PATTERN2_EN_DP_TRANS_P0_MASK |
+			PATTERN3_EN_DP_TRANS_P0_MASK |
+			PATTERN4_EN_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
+			   enable ? BIT(ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT) : 0,
+			   ENHANCED_FRAME_EN_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
+			   enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
+			   DP_SCR_EN_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
+{
+	u32 val = BIT(VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT);
+
+	if (enable)
+		val |= BIT(VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, val,
+			   VIDEO_MUTE_SEL_DP_ENC0_P0_MASK |
+				   VIDEO_MUTE_SW_DP_ENC0_P0_MASK);
+
+	if (mtk_dp_is_edp(mtk_dp))
+		mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable);
+	else
+		mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable);
+}
+
+static void mtk_dp_audio_mute(struct mtk_dp *mtk_dp, bool mute)
+{
+	if (mute) {
+		mtk_dp_update_bits(
+			mtk_dp, MTK_DP_ENC0_P0_3030,
+			BIT(VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT) |
+				BIT(VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT),
+			VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
+				VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
+
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088, 0,
+				   AU_EN_DP_ENC0_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0,
+				   AU_TS_CFG_DP_ENC0_P0_MASK);
+
+	} else {
+		mtk_dp_update_bits(
+			mtk_dp, MTK_DP_ENC0_P0_3030, 0,
+			VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
+				VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
+
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088,
+				   BIT(AU_EN_DP_ENC0_P0_SHIFT),
+				   AU_EN_DP_ENC0_P0_MASK);
+		// Send one every two frames
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0x0F,
+				   AU_TS_CFG_DP_ENC0_P0_MASK);
+	}
+}
+
+static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, 0,
+			   SW_RST_B_PHYD);
+	usleep_range(10, 200);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, SW_RST_B_PHYD,
+			   SW_RST_B_PHYD);
+}
+
+static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
+	usleep_range(10, 200);
+	mtk_dp_write(mtk_dp, MTK_DP_0034,
+		     DA_CKM_CKTX0_EN_FORCE_EN | DA_CKM_BIAS_LPF_EN_FORCE_VAL |
+			     DA_CKM_BIAS_EN_FORCE_VAL |
+			     DA_XTP_GLB_LDO_EN_FORCE_VAL |
+			     DA_XTP_GLB_AVD10_ON_FORCE_VAL);
+	// Disable RX
+	mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
+	mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
+		     0x550 | BIT(FUSE_SEL_SHIFT) | BIT(MEM_ISO_EN_SHIFT));
+}
+
+static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
+{
+	mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR2;
+	mtk_dp->train_info.lane_count = MTK_DP_MAX_LANES;
+	mtk_dp->train_info.irq_status = 0;
+	mtk_dp->train_info.cable_plugged_in = false;
+	mtk_dp->train_info.cable_state_change = false;
+	mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
+	mtk_dp->state = MTK_DP_STATE_INITIAL;
+
+	mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
+	mtk_dp->info.depth = MTK_DP_COLOR_DEPTH_8BIT;
+	memset(&mtk_dp->info.timings, 0, sizeof(struct mtk_dp_timings));
+	mtk_dp->info.timings.frame_rate = 60;
+
+	mtk_dp->has_fec = false;
+	mtk_dp->audio_enable = false;
+}
+
+static void mtk_dp_sdp_set_down_cnt_init(struct mtk_dp *mtk_dp,
+					 u32 sram_read_start)
+{
+	u32 sdp_down_cnt_init = 0;
+	u32 dc_offset;
+
+	if (mtk_dp->info.timings.pix_rate_khz > 0)
+		sdp_down_cnt_init = sram_read_start *
+				    mtk_dp->train_info.link_rate * 2700 * 8 /
+				    (mtk_dp->info.timings.pix_rate_khz * 4);
+
+	switch (mtk_dp->train_info.lane_count) {
+	case 1:
+		sdp_down_cnt_init = sdp_down_cnt_init > 0x1A ?
+						  sdp_down_cnt_init :
+						  0x1A; //26
+		break;
+	case 2:
+		// case for LowResolution && High Audio Sample Rate
+		dc_offset = mtk_dp->info.timings.vtotal <= 525 ? 0x04 : 0x00;
+		sdp_down_cnt_init = sdp_down_cnt_init > 0x10 ?
+						  sdp_down_cnt_init :
+						  0x10 + dc_offset; //20 or 16
+		break;
+	case 4:
+	default:
+		sdp_down_cnt_init =
+			sdp_down_cnt_init > 0x06 ? sdp_down_cnt_init : 0x06; //6
+		break;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
+			   sdp_down_cnt_init
+				   << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
+			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_sdp_set_down_cnt_init_in_hblank(struct mtk_dp *mtk_dp)
+{
+	int pix_clk_mhz;
+	u32 dc_offset;
+	u32 spd_down_cnt_init = 0;
+
+	pix_clk_mhz = mtk_dp->info.format == MTK_DP_COLOR_FORMAT_YUV_420 ?
+				    mtk_dp->info.timings.pix_rate_khz / 2000 :
+				    mtk_dp->info.timings.pix_rate_khz / 1000;
+
+	switch (mtk_dp->train_info.lane_count) {
+	case 1:
+		spd_down_cnt_init = 0x20;
+		break;
+	case 2:
+		dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x14 : 0x00;
+		spd_down_cnt_init = 0x18 + dc_offset;
+		break;
+	case 4:
+	default:
+		dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x08 : 0x00;
+		if (pix_clk_mhz > mtk_dp->train_info.link_rate * 27)
+			spd_down_cnt_init = 0x8;
+		else
+			spd_down_cnt_init = 0x10 + dc_offset;
+		break;
+	}
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364, spd_down_cnt_init,
+			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
+}
+
+static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
+{
+	u32 sram_read_start = MTK_DP_TBC_BUF_READ_START_ADDR;
+
+	if (mtk_dp->train_info.lane_count > 0) {
+		sram_read_start = min_t(
+			u32, MTK_DP_TBC_BUF_READ_START_ADDR,
+			mtk_dp->info.timings.vm.hactive /
+				(mtk_dp->train_info.lane_count * 4 * 2 * 2));
+		mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
+	}
+
+	mtk_dp_setup_encoder(mtk_dp);
+	mtk_dp_sdp_set_down_cnt_init_in_hblank(mtk_dp);
+	mtk_dp_sdp_set_down_cnt_init(mtk_dp, sram_read_start);
+}
+
+static void mtk_dp_calculate_pixrate(struct mtk_dp *mtk_dp)
+{
+	int target_frame_rate = 60;
+	int target_pixel_clk;
+
+	if (mtk_dp->info.timings.frame_rate > 0) {
+		target_frame_rate = mtk_dp->info.timings.frame_rate;
+		target_pixel_clk = (int)mtk_dp->info.timings.htotal *
+				   (int)mtk_dp->info.timings.vtotal *
+				   target_frame_rate;
+	} else if (mtk_dp->info.timings.pix_rate_khz > 0) {
+		target_pixel_clk = mtk_dp->info.timings.pix_rate_khz * 1000;
+	} else {
+		target_pixel_clk = (int)mtk_dp->info.timings.htotal *
+				   (int)mtk_dp->info.timings.vtotal *
+				   target_frame_rate;
+	}
+
+	if (target_pixel_clk > 0)
+		mtk_dp->info.timings.pix_rate_khz = target_pixel_clk / 1000;
+}
+
+static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_msa_bypass_disable(mtk_dp);
+	mtk_dp_calculate_pixrate(mtk_dp);
+	mtk_dp_pg_disable(mtk_dp);
+	mtk_dp_setup_tu(mtk_dp);
+}
+
+static void mtk_dp_edid_free(struct mtk_dp *mtk_dp)
+{
+	mutex_lock(&mtk_dp->edid_lock);
+	kfree(mtk_dp->edid);
+	mtk_dp->edid = NULL;
+	mutex_unlock(&mtk_dp->edid_lock);
+}
+
+static void mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp)
+{
+	ssize_t ret;
+	u8 sink_count;
+	u8 link_status[DP_LINK_STATUS_SIZE] = {};
+	u32 sink_count_reg;
+	u32 link_status_reg;
+	bool locked;
+
+	sink_count_reg = DP_SINK_COUNT_ESI;
+	link_status_reg = DP_LANE0_1_STATUS;
+
+	ret = drm_dp_dpcd_readb(&mtk_dp->aux, sink_count_reg, &sink_count);
+	if (ret < 0) {
+		drm_info(mtk_dp->drm_dev, "Read sink count failed: %ld\n", ret);
+		return;
+	}
+
+	ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
+	if (ret < 0) {
+		drm_info(mtk_dp->drm_dev,
+			 "Read DP_SINK_COUNT_ESI failed: %ld\n", ret);
+		return;
+	}
+
+	ret = drm_dp_dpcd_read(&mtk_dp->aux, link_status_reg, link_status,
+			       sizeof(link_status));
+	if (!ret) {
+		drm_info(mtk_dp->drm_dev, "Read link status failed: %ld\n",
+			 ret);
+		return;
+	}
+
+	locked = drm_dp_channel_eq_ok(link_status,
+				      mtk_dp->train_info.lane_count);
+	if (!locked && mtk_dp->train_state > MTK_DP_TRAIN_STATE_TRAINING_PRE)
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
+
+	if (link_status[1] & DP_REMOTE_CONTROL_COMMAND_PENDING)
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR,
+				   DP_REMOTE_CONTROL_COMMAND_PENDING);
+
+	if (DP_GET_SINK_COUNT(sink_count) &&
+	    (link_status[2] & DP_DOWNSTREAM_PORT_STATUS_CHANGED)) {
+		mtk_dp_edid_free(mtk_dp);
+		mtk_dp->train_info.check_cap_count = 0;
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
+		msleep(20);
+	}
+}
+
+static void mtk_dp_sdp_stop_sending(struct mtk_dp *mtk_dp)
+{
+	u8 packet_type;
+
+	for (packet_type = MTK_DP_SDP_ACM; packet_type < MTK_DP_SDP_MAX_NUM;
+	     packet_type++)
+		mtk_dp_disable_sdp(mtk_dp, packet_type);
+
+	mtk_dp_sdp_vsc_ext_disable(mtk_dp);
+}
+
+static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes,
+					  u8 dpcd_adjust_req[2])
+{
+	int lane;
+
+	for (lane = 0; lane < lanes; ++lane) {
+		u8 val;
+		u8 swing;
+		u8 preemphasis;
+		int index = lane / 2;
+		int shift = lane % 2 ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
+
+		swing = (dpcd_adjust_req[index] >> shift) &
+			DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
+		preemphasis = ((dpcd_adjust_req[index] >> shift) &
+			       DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
+			      DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
+		val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
+		      preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+		if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
+			val |= DP_TRAIN_MAX_SWING_REACHED;
+		if (preemphasis == 3)
+			val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+		mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing, preemphasis);
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET + lane,
+				   val);
+	}
+
+	// Wait for the signal to be stable enough
+	usleep_range(2000, 5000);
+}
+
+static void mtk_dp_read_link_status(struct mtk_dp *mtk_dp,
+				    u8 link_status[DP_LINK_STATUS_SIZE])
+{
+	drm_dp_dpcd_read(&mtk_dp->aux, DP_LANE0_1_STATUS, link_status,
+			 DP_LINK_STATUS_SIZE);
+}
+
+static int mtk_dp_train_flow(struct mtk_dp *mtk_dp, int target_link_rate,
+			     int target_lane_count)
+{
+	u8 link_status[DP_LINK_STATUS_SIZE] = {};
+	u8 lane_adjust[2] = {};
+	bool pass_tps1 = false;
+	bool pass_tps2_3 = false;
+	int train_retries;
+	int status_control;
+	int iteration_count;
+	u8 prev_lane_adjust;
+	u8 val;
+
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET, target_link_rate);
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
+			   target_lane_count | DP_LANE_COUNT_ENHANCED_FRAME_EN);
+
+	if (mtk_dp->train_info.sink_ssc)
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
+				   DP_SPREAD_AMP_0_5);
+
+	train_retries = 0;
+	status_control = 0;
+	iteration_count = 1;
+	prev_lane_adjust = 0xFF;
+
+	mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
+	mtk_dp_phy_configure(mtk_dp, target_link_rate, target_lane_count);
+
+	dev_dbg(mtk_dp->dev,
+		"Link train target_link_rate = 0x%x, target_lane_count = 0x%x\n",
+		target_link_rate, target_lane_count);
+
+	do {
+		train_retries++;
+		if (!mtk_dp->train_info.cable_plugged_in ||
+		    ((mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) !=
+		     0x0)) {
+			return -ENODEV;
+		}
+
+		if (mtk_dp->train_state < MTK_DP_TRAIN_STATE_TRAINING)
+			return -EAGAIN;
+
+		if (!pass_tps1) {
+			mtk_dp_training_set_scramble(mtk_dp, false);
+
+			if (status_control == 0) {
+				status_control = 1;
+				mtk_dp_train_set_pattern(mtk_dp, 1);
+				val = DP_LINK_SCRAMBLING_DISABLE |
+				      DP_TRAINING_PATTERN_1;
+				drm_dp_dpcd_writeb(
+					&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+					DP_LINK_SCRAMBLING_DISABLE |
+						DP_TRAINING_PATTERN_1);
+				drm_dp_dpcd_read(&mtk_dp->aux,
+						 DP_ADJUST_REQUEST_LANE0_1,
+						 lane_adjust,
+						 sizeof(lane_adjust));
+				iteration_count++;
+
+				mtk_dp_train_update_swing_pre(
+					mtk_dp, target_lane_count, lane_adjust);
+			}
+
+			drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
+							       mtk_dp->rx_cap);
+			mtk_dp_read_link_status(mtk_dp, link_status);
+
+			if (drm_dp_clock_recovery_ok(link_status,
+						     target_lane_count)) {
+				mtk_dp->train_info.cr_done = true;
+				pass_tps1 = true;
+				train_retries = 0;
+				iteration_count = 1;
+				dev_dbg(mtk_dp->dev, "Link train CR pass\n");
+			} else if (prev_lane_adjust == link_status[4]) {
+				iteration_count++;
+				if (prev_lane_adjust &
+				    DP_ADJUST_VOLTAGE_SWING_LANE0_MASK)
+					break;
+			} else {
+				prev_lane_adjust = link_status[4];
+			}
+			dev_dbg(mtk_dp->dev, "Link train CQ fail\n");
+		} else if (pass_tps1 && !pass_tps2_3) {
+			if (status_control == 1) {
+				status_control = 2;
+				if (mtk_dp->train_info.tps4) {
+					mtk_dp_train_set_pattern(mtk_dp, 4);
+					val = DP_TRAINING_PATTERN_4;
+				} else if (mtk_dp->train_info.tps3) {
+					mtk_dp_train_set_pattern(mtk_dp, 3);
+					val = DP_LINK_SCRAMBLING_DISABLE |
+					      DP_TRAINING_PATTERN_3;
+				} else {
+					mtk_dp_train_set_pattern(mtk_dp, 2);
+					val = DP_LINK_SCRAMBLING_DISABLE |
+					      DP_TRAINING_PATTERN_2;
+				}
+				drm_dp_dpcd_writeb(&mtk_dp->aux,
+						   DP_TRAINING_PATTERN_SET,
+						   val);
+
+				drm_dp_dpcd_read(&mtk_dp->aux,
+						 DP_ADJUST_REQUEST_LANE0_1,
+						 lane_adjust,
+						 sizeof(lane_adjust));
+
+				iteration_count++;
+				mtk_dp_train_update_swing_pre(
+					mtk_dp, target_lane_count, lane_adjust);
+			}
+
+			drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
+							   mtk_dp->rx_cap);
+
+			mtk_dp_read_link_status(mtk_dp, link_status);
+
+			if (!drm_dp_clock_recovery_ok(link_status,
+						      target_lane_count)) {
+				mtk_dp->train_info.cr_done = false;
+				mtk_dp->train_info.eq_done = false;
+				dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
+				break;
+			}
+
+			if (drm_dp_channel_eq_ok(link_status,
+						 target_lane_count)) {
+				mtk_dp->train_info.eq_done = true;
+				pass_tps2_3 = true;
+				dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
+				break;
+			}
+
+			if (prev_lane_adjust == link_status[4])
+				iteration_count++;
+			else
+				prev_lane_adjust = link_status[4];
+		}
+
+		drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
+				 lane_adjust, sizeof(lane_adjust));
+		mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
+					      lane_adjust);
+	} while (train_retries < MTK_DP_TRAIN_RETRY_LIMIT &&
+		 iteration_count < MTK_DP_TRAIN_MAX_ITERATIONS);
+
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+			   DP_TRAINING_PATTERN_DISABLE);
+	mtk_dp_train_set_pattern(mtk_dp, 0);
+
+	if (pass_tps2_3) {
+		mtk_dp->train_info.link_rate = target_link_rate;
+		mtk_dp->train_info.lane_count = target_lane_count;
+
+		mtk_dp_training_set_scramble(mtk_dp, true);
+
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
+				   target_lane_count |
+					   DP_LANE_COUNT_ENHANCED_FRAME_EN);
+		mtk_dp_set_enhanced_frame_mode(mtk_dp, true);
+
+		return 0;
+	}
+
+	return -ETIMEDOUT;
+}
+
+static bool mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
+{
+	u8 buf[DP_RECEIVER_CAP_SIZE] = {};
+	u8 val;
+	struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
+
+	if (!mtk_dp_plug_state(mtk_dp))
+		return false;
+
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+	usleep_range(2000, 5000);
+
+	drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
+
+	memcpy(mtk_dp->rx_cap, buf, min(sizeof(mtk_dp->rx_cap), sizeof(buf)));
+	mtk_dp->rx_cap[DP_TRAINING_AUX_RD_INTERVAL] &= DP_TRAINING_AUX_RD_MASK;
+
+	train_info->link_rate =
+		min_t(int, MTK_DP_MAX_LINK_RATE, buf[DP_MAX_LINK_RATE]);
+	train_info->lane_count =
+		min_t(int, MTK_DP_MAX_LANES, drm_dp_max_lane_count(buf));
+
+	train_info->tps3 = drm_dp_tps3_supported(buf);
+	train_info->tps4 = drm_dp_tps4_supported(buf);
+
+	train_info->sink_ssc =
+		!!(buf[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5);
+
+	train_info->sink_ssc = false;
+
+	drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
+	if (val & DP_MST_CAP) {
+		// Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0
+		drm_dp_dpcd_readb(&mtk_dp->aux,
+				  DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0, &val);
+		if (val)
+			drm_dp_dpcd_writeb(&mtk_dp->aux,
+					   DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
+					   val);
+	}
+
+	return true;
+}
+
+static int mtk_dp_edid_parse_audio_capabilities(struct mtk_dp *mtk_dp,
+						struct mtk_dp_audio_cfg *cfg)
+{
+	struct cea_sad *sads;
+	int sad_count;
+	int i;
+	int ret = 0;
+
+	mutex_lock(&mtk_dp->edid_lock);
+	if (!mtk_dp->edid) {
+		mutex_unlock(&mtk_dp->edid_lock);
+		dev_err(mtk_dp->dev, "EDID not found!\n");
+		return -EINVAL;
+	}
+
+	sad_count = drm_edid_to_sad(mtk_dp->edid, &sads);
+	mutex_unlock(&mtk_dp->edid_lock);
+	if (sad_count <= 0) {
+		drm_info(mtk_dp->drm_dev, "The SADs is NULL\n");
+		return 0;
+	}
+
+	for (i = 0; i < sad_count; i++) {
+		int sample_rate;
+		int word_length;
+		// Only PCM supported at the moment
+		if (sads[i].format != HDMI_AUDIO_CODING_TYPE_PCM)
+			continue;
+
+		sample_rate = drm_cea_sad_get_sample_rate(&sads[i]);
+		word_length =
+			drm_cea_sad_get_uncompressed_word_length(&sads[i]);
+		if (sample_rate <= 0 || word_length <= 0)
+			continue;
+
+		cfg->channels = sads[i].channels;
+		cfg->word_length_bits = word_length;
+		cfg->sample_rate = sample_rate;
+		ret = 1;
+		break;
+	}
+	kfree(sads);
+
+	return ret;
+}
+
+static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
+{
+	phy_reset(mtk_dp->phy);
+	mtk_dp_reset_swing_pre_emphasis(mtk_dp);
+
+	usleep_range(2000, 5000);
+}
+
+static int mtk_dp_train_start(struct mtk_dp *mtk_dp)
+{
+	int ret = 0;
+	int lane_count;
+	int link_rate;
+	int train_limit;
+	int max_link_rate;
+	int plug_wait;
+
+	for (plug_wait = 7; !mtk_dp_plug_state(mtk_dp) && plug_wait > 0;
+	     --plug_wait)
+		usleep_range(1000, 5000);
+	if (plug_wait == 0) {
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
+		return -ENODEV;
+	}
+
+	link_rate = mtk_dp->rx_cap[1];
+	lane_count = mtk_dp->rx_cap[2] & 0x1F;
+
+	mtk_dp->train_info.link_rate = min(MTK_DP_MAX_LINK_RATE, link_rate);
+	mtk_dp->train_info.lane_count = min(MTK_DP_MAX_LANES, lane_count);
+	link_rate = mtk_dp->train_info.link_rate;
+	lane_count = mtk_dp->train_info.lane_count;
+
+	switch (link_rate) {
+	case MTK_DP_LINKRATE_RBR:
+	case MTK_DP_LINKRATE_HBR:
+	case MTK_DP_LINKRATE_HBR2:
+	case MTK_DP_LINKRATE_HBR25:
+	case MTK_DP_LINKRATE_HBR3:
+		break;
+	default:
+		mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR3;
+		break;
+	};
+
+	max_link_rate = link_rate;
+	for (train_limit = 0; train_limit <= 6; ++train_limit) {
+		mtk_dp->train_info.cr_done = false;
+		mtk_dp->train_info.eq_done = false;
+
+		mtk_dp_train_change_mode(mtk_dp);
+		ret = mtk_dp_train_flow(mtk_dp, link_rate, lane_count);
+		if (ret == -ENODEV || ret == -EAGAIN)
+			return ret;
+
+		if (!mtk_dp->train_info.cr_done) {
+			switch (link_rate) {
+			case MTK_DP_LINKRATE_RBR:
+				lane_count = lane_count / 2;
+				link_rate = max_link_rate;
+				if (lane_count == 0x0) {
+					mtk_dp->train_state =
+						MTK_DP_TRAIN_STATE_DPIDLE;
+					return -EIO;
+				}
+				break;
+			case MTK_DP_LINKRATE_HBR:
+				link_rate = MTK_DP_LINKRATE_RBR;
+				break;
+			case MTK_DP_LINKRATE_HBR2:
+				link_rate = MTK_DP_LINKRATE_HBR;
+				break;
+			case MTK_DP_LINKRATE_HBR3:
+				link_rate = MTK_DP_LINKRATE_HBR2;
+				break;
+			default:
+				return -EINVAL;
+			};
+		} else if (!mtk_dp->train_info.eq_done) {
+			lane_count /= 2;
+			if (lane_count == 0)
+				return -EIO;
+		} else {
+			return 0;
+		}
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int mtk_dp_train_handler(struct mtk_dp *mtk_dp)
+{
+	int ret = 0;
+
+	if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
+		return ret;
+
+	switch (mtk_dp->train_state) {
+	case MTK_DP_TRAIN_STATE_STARTUP:
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
+		break;
+
+	case MTK_DP_TRAIN_STATE_CHECKCAP:
+		if (mtk_dp_parse_capabilities(mtk_dp)) {
+			mtk_dp->train_info.check_cap_count = 0;
+			mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
+		} else {
+			mtk_dp->train_info.check_cap_count++;
+
+			if (mtk_dp->train_info.check_cap_count >
+			    MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT) {
+				mtk_dp->train_info.check_cap_count = 0;
+				mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
+				ret = -ETIMEDOUT;
+			}
+		}
+		break;
+
+	case MTK_DP_TRAIN_STATE_CHECKEDID: {
+		int caps_found = mtk_dp_edid_parse_audio_capabilities(
+			mtk_dp, &mtk_dp->info.audio_caps);
+		mtk_dp->audio_enable = caps_found > 0;
+		if (!mtk_dp->audio_enable)
+			memset(&mtk_dp->info.audio_caps, 0,
+			       sizeof(mtk_dp->info.audio_caps));
+	}
+
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
+		break;
+
+	case MTK_DP_TRAIN_STATE_TRAINING_PRE:
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING;
+		break;
+
+	case MTK_DP_TRAIN_STATE_TRAINING:
+		ret = mtk_dp_train_start(mtk_dp);
+		if (!ret) {
+			mtk_dp_video_mute(mtk_dp, true);
+			mtk_dp_audio_mute(mtk_dp, true);
+			mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKTIMING;
+			mtk_dp_fec_enable(mtk_dp, mtk_dp->has_fec);
+		} else if (ret != -EAGAIN) {
+			mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
+		}
+
+		ret = 0;
+		break;
+
+	case MTK_DP_TRAIN_STATE_CHECKTIMING:
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_NORMAL;
+		break;
+	case MTK_DP_TRAIN_STATE_NORMAL:
+		break;
+	case MTK_DP_TRAIN_STATE_POWERSAVE:
+		break;
+	case MTK_DP_TRAIN_STATE_DPIDLE:
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	if (enable) {
+		mtk_dp_set_tx_out(mtk_dp);
+		mtk_dp_video_mute(mtk_dp, false);
+	} else {
+		mtk_dp_video_mute(mtk_dp, true);
+	}
+}
+
+static void mtk_dp_audio_sdp_setup(struct mtk_dp *mtk_dp,
+				   struct mtk_dp_audio_cfg *cfg)
+{
+	struct mtk_dp_sdp_packet packet;
+	struct hdmi_audio_infoframe frame;
+
+	hdmi_audio_infoframe_init(&frame);
+	frame.coding_type = HDMI_AUDIO_CODING_TYPE_PCM;
+	frame.channels = cfg->channels;
+	frame.sample_frequency = cfg->sample_rate;
+
+	switch (cfg->word_length_bits) {
+	case 16:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
+		break;
+	case 20:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_20;
+		break;
+	case 24:
+	default:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_24;
+		break;
+	}
+
+	packet.type = MTK_DP_SDP_AUI;
+	hdmi_audio_infoframe_pack_for_dp(&frame, &packet.sdp,
+					 MTK_DP_DP_VERSION_11);
+
+	mtk_dp_audio_sdp_asp_set_channels(mtk_dp, cfg->channels);
+	mtk_dp_setup_sdp(mtk_dp, &packet);
+}
+
+static void mtk_dp_audio_setup(struct mtk_dp *mtk_dp,
+			       struct mtk_dp_audio_cfg *cfg)
+{
+	mtk_dp_audio_sdp_setup(mtk_dp, cfg);
+	mtk_dp_audio_channel_status_set(mtk_dp, cfg);
+
+	mtk_dp_audio_setup_channels(mtk_dp, cfg);
+	mtk_dp_audio_set_divider(mtk_dp);
+}
+
+static void mtk_dp_video_config(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_mn_overwrite_disable(mtk_dp);
+
+	mtk_dp_set_msa(mtk_dp);
+
+	mtk_dp_set_color_depth(mtk_dp, mtk_dp->info.depth);
+	mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
+}
+
+static int mtk_dp_state_handler(struct mtk_dp *mtk_dp)
+{
+	int ret = 0;
+
+	switch (mtk_dp->state) {
+	case MTK_DP_STATE_INITIAL:
+		mtk_dp_video_mute(mtk_dp, true);
+		mtk_dp_audio_mute(mtk_dp, true);
+		mtk_dp->state = MTK_DP_STATE_IDLE;
+		break;
+
+	case MTK_DP_STATE_IDLE:
+		if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
+			mtk_dp->state = MTK_DP_STATE_PREPARE;
+		break;
+
+	case MTK_DP_STATE_PREPARE:
+		mtk_dp_video_config(mtk_dp);
+		mtk_dp_video_enable(mtk_dp, true);
+
+		if (mtk_dp->audio_enable) {
+			mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_caps);
+			mtk_dp_audio_mute(mtk_dp, false);
+		}
+
+		mtk_dp->state = MTK_DP_STATE_NORMAL;
+		break;
+
+	case MTK_DP_STATE_NORMAL:
+		if (mtk_dp->train_state != MTK_DP_TRAIN_STATE_NORMAL) {
+			mtk_dp_video_mute(mtk_dp, true);
+			mtk_dp_audio_mute(mtk_dp, true);
+			mtk_dp_sdp_stop_sending(mtk_dp);
+			mtk_dp->state = MTK_DP_STATE_IDLE;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_set_idle_pattern(mtk_dp, true);
+	mtk_dp_initialize_priv_data(mtk_dp);
+
+	mtk_dp_initialize_settings(mtk_dp);
+	mtk_dp_initialize_aux_settings(mtk_dp);
+	mtk_dp_initialize_digital_settings(mtk_dp);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
+			   BIT(RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT),
+			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK);
+	mtk_dp_initialize_hpd_detect_settings(mtk_dp);
+
+	mtk_dp_digital_sw_reset(mtk_dp);
+}
+
+static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
+{
+	struct mtk_dp *mtk_dp = dev;
+	int event;
+	u8 buf[DP_RECEIVER_CAP_SIZE] = {};
+
+	event = mtk_dp_plug_state(mtk_dp) ? connector_status_connected :
+						  connector_status_disconnected;
+
+	if (event < 0)
+		return IRQ_HANDLED;
+
+	if (mtk_dp->drm_dev) {
+		dev_info(mtk_dp->dev, "drm_helper_hpd_irq_event\n");
+		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
+	}
+
+	if (mtk_dp->train_info.cable_state_change) {
+		mtk_dp->train_info.cable_state_change = false;
+
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
+
+		if (!mtk_dp->train_info.cable_plugged_in ||
+		    !mtk_dp_plug_state(mtk_dp)) {
+			mtk_dp_video_mute(mtk_dp, true);
+			mtk_dp_audio_mute(mtk_dp, true);
+
+			mtk_dp_initialize_priv_data(mtk_dp);
+			mtk_dp_set_idle_pattern(mtk_dp, true);
+			if (mtk_dp->has_fec)
+				mtk_dp_fec_enable(mtk_dp, false);
+			mtk_dp_sdp_stop_sending(mtk_dp);
+
+			mtk_dp_edid_free(mtk_dp);
+			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+					   DP_PWR_STATE_BANDGAP_TPLL,
+					   DP_PWR_STATE_MASK);
+		} else {
+			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+					   DP_PWR_STATE_BANDGAP_TPLL_LANE,
+					   DP_PWR_STATE_MASK);
+			drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
+			mtk_dp->train_info.link_rate =
+				min_t(int, MTK_DP_MAX_LINK_RATE,
+				      buf[DP_MAX_LINK_RATE]);
+			mtk_dp->train_info.lane_count =
+				min_t(int, MTK_DP_MAX_LANES,
+				      drm_dp_max_lane_count(buf));
+		}
+	}
+
+	if (mtk_dp->train_info.irq_status & MTK_DP_HPD_INTERRUPT) {
+		dev_info(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n");
+		mtk_dp->train_info.irq_status &= ~MTK_DP_HPD_INTERRUPT;
+		mtk_dp_hpd_sink_event(mtk_dp);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_dp_hpd_isr_handler(struct mtk_dp *mtk_dp)
+{
+	bool connected;
+	u16 swirq_status = mtk_dp_swirq_get_clear(mtk_dp);
+	u8 hwirq_status = mtk_dp_hwirq_get_clear(mtk_dp);
+	struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
+
+	train_info->irq_status |= hwirq_status | swirq_status;
+
+	if (!train_info->irq_status)
+		return IRQ_HANDLED;
+
+	connected = mtk_dp_plug_state(mtk_dp);
+	if (connected || !train_info->cable_plugged_in)
+		train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
+	else if (!connected || train_info->cable_plugged_in)
+		train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
+
+	if (!(train_info->irq_status &
+	      (MTK_DP_HPD_CONNECT | MTK_DP_HPD_DISCONNECT)))
+		return IRQ_HANDLED;
+
+	if (train_info->irq_status & MTK_DP_HPD_CONNECT) {
+		train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
+		train_info->cable_plugged_in = true;
+	} else {
+		train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
+		train_info->cable_plugged_in = false;
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
+	}
+	train_info->cable_state_change = true;
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
+{
+	struct mtk_dp *mtk_dp = dev;
+	u32 irq_status;
+
+	irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS);
+
+	if (!irq_status)
+		return IRQ_HANDLED;
+
+	if (irq_status & RGS_IRQ_STATUS_TRANSMITTER)
+		return mtk_dp_hpd_isr_handler(mtk_dp);
+
+	return IRQ_HANDLED;
+}
+
+static int mtk_dp_dt_parse_pdata(struct mtk_dp *mtk_dp,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	int ret = 0;
+	void __iomem *base;
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	mtk_dp->regs = devm_regmap_init_mmio(dev, base, &mtk_dp_regmap_config);
+	if (IS_ERR(mtk_dp->regs))
+		return PTR_ERR(mtk_dp->regs);
+
+	mtk_dp->dp_tx_clk = devm_clk_get(dev, "faxi");
+	if (IS_ERR(mtk_dp->dp_tx_clk)) {
+		ret = PTR_ERR(mtk_dp->dp_tx_clk);
+		dev_err(dev, "Failed to get dptx clock: %d\n", ret);
+		mtk_dp->dp_tx_clk = NULL;
+	}
+
+	return 0;
+}
+
+static void mtk_dp_update_plugged_status(struct mtk_dp *mtk_dp)
+{
+	bool connected, has_audio;
+
+	mutex_lock(&mtk_dp->update_plugged_status_lock);
+	connected = mtk_dp_plug_state(mtk_dp);
+	has_audio = drm_detect_monitor_audio(mtk_dp->edid);
+	if (mtk_dp->plugged_cb && mtk_dp->codec_dev)
+		mtk_dp->plugged_cb(mtk_dp->codec_dev, connected & has_audio);
+	mutex_unlock(&mtk_dp->update_plugged_status_lock);
+}
+
+static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	enum drm_connector_status ret = connector_status_disconnected;
+	u8 sink_count = 0;
+
+	if (mtk_dp_is_edp(mtk_dp))
+		return connector_status_connected;
+
+	if (mtk_dp_plug_state(mtk_dp)) {
+		drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
+		if (DP_GET_SINK_COUNT(sink_count))
+			ret = connector_status_connected;
+	}
+
+	mtk_dp_update_plugged_status(mtk_dp);
+	return ret;
+}
+
+static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
+				    struct drm_connector *connector)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	bool enabled = mtk_dp->enabled;
+	struct edid *new_edid = NULL;
+
+	if (!enabled)
+		drm_bridge_chain_pre_enable(bridge);
+
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+	usleep_range(2000, 5000);
+
+	if (mtk_dp_plug_state(mtk_dp))
+		new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
+
+	if (!enabled)
+		drm_bridge_chain_post_disable(bridge);
+
+	mutex_lock(&mtk_dp->edid_lock);
+	kfree(mtk_dp->edid);
+	mtk_dp->edid = NULL;
+
+	if (!new_edid) {
+		mutex_unlock(&mtk_dp->edid_lock);
+		return NULL;
+	}
+
+	mtk_dp->edid = drm_edid_duplicate(new_edid);
+	mutex_unlock(&mtk_dp->edid_lock);
+
+	return new_edid;
+}
+
+static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
+				   struct drm_dp_aux_msg *msg)
+{
+	ssize_t err = -EAGAIN;
+	struct mtk_dp *mtk_dp;
+	bool is_read;
+	u8 request;
+	size_t accessed_bytes = 0;
+	int retry = 3, ret = 0;
+
+	mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
+
+	if (!mtk_dp->train_info.cable_plugged_in ||
+	    mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
+		err = -EAGAIN;
+		goto err;
+	}
+
+	switch (msg->request) {
+	case DP_AUX_I2C_MOT:
+	case DP_AUX_I2C_WRITE:
+	case DP_AUX_NATIVE_WRITE:
+	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
+	case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
+		request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
+		is_read = false;
+		break;
+	case DP_AUX_I2C_READ:
+	case DP_AUX_NATIVE_READ:
+	case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
+		request = msg->request;
+		is_read = true;
+		break;
+	default:
+		drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
+			msg->request);
+		err = -EINVAL;
+		goto err;
+	}
+
+	if (msg->size == 0) {
+		mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
+				       msg->address + accessed_bytes,
+				       msg->buffer + accessed_bytes, 0);
+	} else {
+		while (accessed_bytes < msg->size) {
+			size_t to_access =
+				min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
+				      msg->size - accessed_bytes);
+			retry = 3;
+			while (retry--) {
+				ret = mtk_dp_aux_do_transfer(
+					mtk_dp, is_read, request,
+					msg->address + accessed_bytes,
+					msg->buffer + accessed_bytes,
+					to_access);
+				if (ret == 0)
+					break;
+				udelay(50);
+			}
+			if (!retry && ret) {
+				drm_info(mtk_dp->drm_dev,
+					 "Failed to do AUX transfer: %d\n",
+					 ret);
+				break;
+			}
+			accessed_bytes += to_access;
+		}
+	}
+err:
+	if (!ret) {
+		msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
+		ret = msg->size;
+	} else {
+		msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
+		return err;
+	}
+
+	msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
+	return msg->size;
+}
+
+static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
+{
+	drm_dp_aux_init(&mtk_dp->aux);
+	mtk_dp->aux.name = "aux_mtk_dp";
+	mtk_dp->aux.transfer = mtk_dp_aux_transfer;
+}
+
+static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
+{
+	mutex_lock(&mtk_dp->dp_lock);
+
+	mtk_dp_hwirq_enable(mtk_dp, false);
+	mtk_dp_power_disable(mtk_dp);
+	phy_exit(mtk_dp->phy);
+	clk_disable_unprepare(mtk_dp->dp_tx_clk);
+
+	mutex_unlock(&mtk_dp->dp_lock);
+}
+
+static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
+{
+	int ret = 0;
+
+	mutex_lock(&mtk_dp->dp_lock);
+
+	ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
+	if (ret < 0) {
+		dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
+		goto err;
+	}
+	ret = phy_init(mtk_dp->phy);
+	if (ret) {
+		dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
+		goto err_phy_init;
+	}
+	ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
+	if (ret) {
+		dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
+		goto err_phy_config;
+	}
+
+	mtk_dp_init_port(mtk_dp);
+	mtk_dp_power_enable(mtk_dp);
+	mtk_dp_hwirq_enable(mtk_dp, true);
+
+err_phy_config:
+	phy_exit(mtk_dp->phy);
+err_phy_init:
+	clk_disable_unprepare(mtk_dp->dp_tx_clk);
+err:
+	mutex_unlock(&mtk_dp->dp_lock);
+	return ret;
+}
+
+static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
+				enum drm_bridge_attach_flags flags)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	int ret;
+
+	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+		dev_err(mtk_dp->dev, "Driver does not provide a connector!");
+		return -EINVAL;
+	}
+
+	ret = mtk_dp_poweron(mtk_dp);
+	if (ret)
+		return ret;
+
+	if (mtk_dp->next_bridge) {
+		ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
+					&mtk_dp->bridge, flags);
+		if (ret) {
+			drm_warn(mtk_dp->drm_dev,
+				 "Failed to attach external bridge: %d\n", ret);
+			goto err_bridge_attach;
+		}
+	}
+
+	mtk_dp->drm_dev = bridge->dev;
+
+	return 0;
+
+err_bridge_attach:
+	mtk_dp_poweroff(mtk_dp);
+	return ret;
+}
+
+static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+
+	mtk_dp->drm_dev = NULL;
+
+	mtk_dp_poweroff(mtk_dp);
+}
+
+static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
+					 struct drm_bridge_state *old_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+
+	mtk_dp_video_mute(mtk_dp, true);
+	mtk_dp_audio_mute(mtk_dp, true);
+	mtk_dp->state = MTK_DP_STATE_IDLE;
+	mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
+
+	mtk_dp->enabled = false;
+	msleep(100);
+}
+
+static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
+					  struct drm_display_mode *mode)
+{
+	struct mtk_dp_timings *timings = &mtk_dp->info.timings;
+
+	drm_display_mode_to_videomode(mode, &timings->vm);
+	timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
+	timings->htotal = mode->htotal;
+	timings->vtotal = mode->vtotal;
+}
+
+static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
+					struct drm_bridge_state *old_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	struct drm_connector *conn;
+	struct drm_connector_state *conn_state;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *crtc_state;
+	int ret = 0;
+	int i;
+
+	conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
+							bridge->encoder);
+	if (!conn) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as connector is missing\n");
+		return;
+	}
+
+	memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
+
+	conn_state =
+		drm_atomic_get_new_connector_state(old_state->base.state, conn);
+	if (!conn_state) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as connector state is missing\n");
+		return;
+	}
+
+	crtc = conn_state->crtc;
+	if (!crtc) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as connector state doesn't have a crtc\n");
+		return;
+	}
+
+	crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
+	if (!crtc_state) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as crtc state is missing\n");
+		return;
+	}
+
+	mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);
+	if (!mtk_dp_parse_capabilities(mtk_dp)) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as nothing is plugged in\n");
+		return;
+	}
+
+	//training
+	for (i = 0; i < 50; i++) {
+		ret = mtk_dp_train_handler(mtk_dp);
+		if (ret) {
+			drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
+				ret);
+			return;
+		}
+
+		ret = mtk_dp_state_handler(mtk_dp);
+		if (ret) {
+			drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
+				ret);
+			return;
+		}
+	}
+
+	mtk_dp->enabled = true;
+	mtk_dp_update_plugged_status(mtk_dp);
+}
+
+static enum drm_mode_status
+mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
+			 const struct drm_display_info *info,
+			 const struct drm_display_mode *mode)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	u32 rx_linkrate;
+	u32 bpp;
+
+	bpp = info->color_formats & DRM_COLOR_FORMAT_YCRCB422 ? 16 : 24;
+	rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
+	if (rx_linkrate * mtk_dp->train_info.lane_count < mode->clock * bpp / 8)
+		return MODE_CLOCK_HIGH;
+
+	if (mode->clock > 600000)
+		return MODE_CLOCK_HIGH;
+
+	if ((mode->clock * 1000) / (mode->htotal * mode->vtotal) > 62)
+		return MODE_CLOCK_HIGH;
+
+	return MODE_OK;
+}
+
+static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(
+	struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
+	struct drm_crtc_state *crtc_state,
+	struct drm_connector_state *conn_state, unsigned int *num_output_fmts)
+{
+	u32 *output_fmts;
+
+	*num_output_fmts = 0;
+	output_fmts = kcalloc(1, sizeof(*output_fmts), GFP_KERNEL);
+	if (!output_fmts)
+		return NULL;
+	*num_output_fmts = 1;
+	output_fmts[0] = MEDIA_BUS_FMT_FIXED;
+	return output_fmts;
+}
+
+static const u32 mt8195_input_fmts[] = {
+	MEDIA_BUS_FMT_RGB888_1X24,
+	MEDIA_BUS_FMT_YUV8_1X24,
+	MEDIA_BUS_FMT_YUYV8_1X16,
+};
+
+static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(
+	struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
+	struct drm_crtc_state *crtc_state,
+	struct drm_connector_state *conn_state, u32 output_fmt,
+	unsigned int *num_input_fmts)
+{
+	u32 *input_fmts;
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+	struct drm_display_info *display_info =
+		&conn_state->connector->display_info;
+	u32 rx_linkrate;
+	u32 bpp;
+
+	bpp = (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422) ? 16 :
+										24;
+	rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
+	*num_input_fmts = 0;
+	input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts), sizeof(*input_fmts),
+			     GFP_KERNEL);
+	if (!input_fmts)
+		return NULL;
+
+	*num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
+
+	memcpy(input_fmts, mt8195_input_fmts,
+	       sizeof(*input_fmts) * ARRAY_SIZE(mt8195_input_fmts));
+
+	if (((rx_linkrate * mtk_dp->train_info.lane_count) <
+	     (mode->clock * 24 / 8)) &&
+	    ((rx_linkrate * mtk_dp->train_info.lane_count) >
+	     (mode->clock * 16 / 8)) &&
+	    (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422)) {
+		kfree(input_fmts);
+		input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL);
+		*num_input_fmts = 1;
+		input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
+	}
+
+	return input_fmts;
+}
+
+static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
+				      struct drm_bridge_state *bridge_state,
+				      struct drm_crtc_state *crtc_state,
+				      struct drm_connector_state *conn_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	unsigned int input_bus_format;
+
+	input_bus_format = bridge_state->input_bus_cfg.format;
+
+	dev_info(mtk_dp->dev, "input format 0x%04x, output format 0x%04x\n",
+		 bridge_state->input_bus_cfg.format,
+		 bridge_state->output_bus_cfg.format);
+
+	mtk_dp->input_fmt = input_bus_format;
+	if (mtk_dp->input_fmt == MEDIA_BUS_FMT_YUYV8_1X16)
+		mtk_dp->info.format = MTK_DP_COLOR_FORMAT_YUV_422;
+	else
+		mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
+
+	return 0;
+}
+
+static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
+	.atomic_check = mtk_dp_bridge_atomic_check,
+	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+	.atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts,
+	.atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts,
+	.atomic_reset = drm_atomic_helper_bridge_reset,
+	.attach = mtk_dp_bridge_attach,
+	.detach = mtk_dp_bridge_detach,
+	.atomic_enable = mtk_dp_bridge_atomic_enable,
+	.atomic_disable = mtk_dp_bridge_atomic_disable,
+	.mode_valid = mtk_dp_bridge_mode_valid,
+	.get_edid = mtk_dp_get_edid,
+	.detect = mtk_dp_bdg_detect,
+};
+
+/*
+ * HDMI audio codec callbacks
+ */
+static int mtk_dp_audio_hw_params(struct device *dev, void *data,
+				  struct hdmi_codec_daifmt *daifmt,
+				  struct hdmi_codec_params *params)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+	struct mtk_dp_audio_cfg cfg;
+
+	if (!mtk_dp->enabled) {
+		pr_err("%s, DP is not ready!\n", __func__);
+		return -ENODEV;
+	}
+
+	cfg.channels = params->cea.channels;
+	cfg.sample_rate = params->sample_rate;
+	cfg.word_length_bits = 24;
+
+	mtk_dp_audio_setup(mtk_dp, &cfg);
+
+	return 0;
+}
+
+static int mtk_dp_audio_startup(struct device *dev, void *data)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_audio_mute(mtk_dp, false);
+
+	return 0;
+}
+
+static void mtk_dp_audio_shutdown(struct device *dev, void *data)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_audio_mute(mtk_dp, true);
+}
+
+static int mtk_dp_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
+				size_t len)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	if (mtk_dp->enabled)
+		memcpy(buf, mtk_dp->connector_eld, len);
+	else
+		memset(buf, 0, len);
+
+	return 0;
+}
+
+static int mtk_dp_audio_hook_plugged_cb(struct device *dev, void *data,
+					hdmi_codec_plugged_cb fn,
+					struct device *codec_dev)
+{
+	struct mtk_dp *mtk_dp = data;
+
+	mutex_lock(&mtk_dp->update_plugged_status_lock);
+	mtk_dp->plugged_cb = fn;
+	mtk_dp->codec_dev = codec_dev;
+	mutex_unlock(&mtk_dp->update_plugged_status_lock);
+
+	mtk_dp_update_plugged_status(mtk_dp);
+
+	return 0;
+}
+
+static const struct hdmi_codec_ops mtk_dp_audio_codec_ops = {
+	.hw_params = mtk_dp_audio_hw_params,
+	.audio_startup = mtk_dp_audio_startup,
+	.audio_shutdown = mtk_dp_audio_shutdown,
+	.get_eld = mtk_dp_audio_get_eld,
+	.hook_plugged_cb = mtk_dp_audio_hook_plugged_cb,
+	.no_capture_mute = 1,
+};
+
+static int mtk_dp_register_audio_driver(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+	struct hdmi_codec_pdata codec_data = {
+		.ops = &mtk_dp_audio_codec_ops,
+		.max_i2s_channels = 8,
+		.i2s = 1,
+		.data = mtk_dp,
+	};
+	struct platform_device *pdev;
+
+	pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
+					     PLATFORM_DEVID_AUTO, &codec_data,
+					     sizeof(codec_data));
+	if (IS_ERR(pdev))
+		return PTR_ERR(pdev);
+
+	return 0;
+}
+
+static int mtk_dp_probe(struct platform_device *pdev)
+{
+	struct mtk_dp *mtk_dp;
+	struct device *dev = &pdev->dev;
+	int ret;
+	int irq_num = 0;
+	struct drm_panel *panel = NULL;
+
+	mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
+	if (!mtk_dp)
+		return -ENOMEM;
+
+	mtk_dp->dev = dev;
+
+	irq_num = platform_get_irq(pdev, 0);
+	if (irq_num < 0) {
+		dev_err(dev, "failed to request dp irq resource\n");
+		return -EPROBE_DEFER;
+	}
+
+	ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel,
+					  &mtk_dp->next_bridge);
+	if (ret == -ENODEV) {
+		dev_info(
+			dev,
+			"No panel connected in devicetree, continuing as external DP\n");
+		mtk_dp->next_bridge = NULL;
+	} else if (ret) {
+		dev_err(dev, "Failed to find panel or bridge: %d\n", ret);
+		return ret;
+	}
+
+	if (panel) {
+		mtk_dp->next_bridge = devm_drm_panel_bridge_add(dev, panel);
+		if (IS_ERR(mtk_dp->next_bridge)) {
+			ret = PTR_ERR(mtk_dp->next_bridge);
+			dev_err(dev, "Failed to create bridge: %d\n", ret);
+			return -EPROBE_DEFER;
+		}
+	}
+
+	ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
+	if (ret)
+		return ret;
+
+	mtk_dp_aux_init(mtk_dp);
+
+	ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
+					mtk_dp_hpd_event_thread,
+					IRQ_TYPE_LEVEL_HIGH, dev_name(dev),
+					mtk_dp);
+	if (ret) {
+		dev_err(dev, "failed to request mediatek dptx irq\n");
+		return -EPROBE_DEFER;
+	}
+
+	mutex_init(&mtk_dp->dp_lock);
+	mutex_init(&mtk_dp->edid_lock);
+
+	platform_set_drvdata(pdev, mtk_dp);
+
+	if (!mtk_dp_is_edp(mtk_dp)) {
+		mutex_init(&mtk_dp->update_plugged_status_lock);
+		ret = mtk_dp_register_audio_driver(dev);
+		if (ret) {
+			dev_err(dev, "Failed to register audio driver: %d\n",
+				ret);
+			return ret;
+		}
+	}
+
+	mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
+							PLATFORM_DEVID_AUTO,
+							&mtk_dp->regs,
+							sizeof(&mtk_dp->regs));
+	if (IS_ERR(mtk_dp->phy_dev)) {
+		dev_err(dev, "Failed to create device mediatek-dp-phy: %ld\n",
+			PTR_ERR(mtk_dp->phy_dev));
+		return PTR_ERR(mtk_dp->phy_dev);
+	}
+
+	mtk_dp_get_calibration_data(mtk_dp);
+
+	mtk_dp->phy = dev_get_drvdata(&mtk_dp->phy_dev->dev);
+	if (IS_ERR(mtk_dp->phy)) {
+		dev_err(dev, "Failed to get phy: %ld\n", PTR_ERR(mtk_dp->phy));
+		platform_device_unregister(mtk_dp->phy_dev);
+		return PTR_ERR(mtk_dp->phy);
+	}
+
+	mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
+	mtk_dp->bridge.of_node = dev->of_node;
+	if (mtk_dp_is_edp(mtk_dp))
+		mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
+	else
+		mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
+
+	mtk_dp->bridge.ops =
+		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
+	drm_bridge_add(&mtk_dp->bridge);
+
+	pm_runtime_enable(dev);
+	pm_runtime_get_sync(dev);
+
+	return 0;
+}
+
+static int mtk_dp_remove(struct platform_device *pdev)
+{
+	struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
+
+	platform_device_unregister(mtk_dp->phy_dev);
+
+	mtk_dp_video_mute(mtk_dp, true);
+	mtk_dp_audio_mute(mtk_dp, true);
+
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_dp_suspend(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_power_disable(mtk_dp);
+	mtk_dp_hwirq_enable(mtk_dp, false);
+
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static int mtk_dp_resume(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	pm_runtime_get_sync(dev);
+
+	mtk_dp_init_port(mtk_dp);
+	mtk_dp_power_enable(mtk_dp);
+	mtk_dp_hwirq_enable(mtk_dp, true);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
+
+static const struct of_device_id mtk_dp_of_match[] = {
+	{
+		.compatible = "mediatek,mt8195-dp-tx",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
+
+struct platform_driver mtk_dp_driver = {
+	.probe = mtk_dp_probe,
+	.remove = mtk_dp_remove,
+	.driver = {
+		.name = "mediatek-drm-dp",
+		.of_match_table = mtk_dp_of_match,
+		.pm = &mtk_dp_pm_ops,
+	},
+};
+
+MODULE_AUTHOR("Jason-JH.Lin <jason-jh.lin@mediatek.com>");
+MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
+MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
new file mode 100644
index 0000000000000..79952ac30e9e6
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
@@ -0,0 +1,568 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Copyright (c) 2021 BayLibre
+ */
+#ifndef _MTK_DP_REG_H_
+#define _MTK_DP_REG_H_
+
+#define MTK_DP_SIP_CONTROL_AARCH32 0x82000523
+#define MTK_DP_SIP_ATF_VIDEO_UNMUTE 0x20
+#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE 0x21
+
+#define DP_PHY_GLB_BIAS_GEN_00 0x0000
+#define RG_XTP_GLB_BIAS_INTR_CTRL (0x1f << 16)
+
+#define DP_PHY_GLB_DPAUX_TX 0x0008
+#define RG_CKM_PT0_CKTX_IMPSEL (0xf << 20)
+
+#define DP_PHY_LANE_TX_0 0x104
+#define RG_XTP_LN0_TX_IMPSEL_PMOS (0xf << 12)
+#define RG_XTP_LN0_TX_IMPSEL_NMOS (0xf << 16)
+
+#define DP_PHY_LANE_TX_1 0x204
+#define RG_XTP_LN1_TX_IMPSEL_PMOS (0xf << 12)
+#define RG_XTP_LN1_TX_IMPSEL_NMOS (0xf << 16)
+
+#define DP_PHY_LANE_TX_2 0x304
+#define RG_XTP_LN2_TX_IMPSEL_PMOS (0xf << 12)
+#define RG_XTP_LN2_TX_IMPSEL_NMOS (0xf << 16)
+
+#define DP_PHY_LANE_TX_3 0x404
+#define RG_XTP_LN3_TX_IMPSEL_PMOS (0xf << 12)
+#define RG_XTP_LN3_TX_IMPSEL_NMOS (0xf << 16)
+
+#define TOP_OFFSET 0x2000
+#define ENC0_OFFSET 0x3000
+#define ENC1_OFFSET 0x3200
+#define TRANS_OFFSET 0x3400
+#define AUX_OFFSET 0x3600
+#define SEC_OFFSET 0x4000
+
+#define MTK_DP_HPD_DISCONNECT BIT(1)
+#define MTK_DP_HPD_CONNECT BIT(2)
+#define MTK_DP_HPD_INTERRUPT BIT(3)
+
+#define MTK_DP_0034 0x034
+#define DA_XTP_GLB_CKDET_EN_FORCE_VAL BIT(15)
+#define DA_XTP_GLB_CKDET_EN_FORCE_EN BIT(14)
+#define DA_CKM_INTCKTX_EN_FORCE_VAL BIT(13)
+#define DA_CKM_INTCKTX_EN_FORCE_EN BIT(12)
+#define DA_CKM_CKTX0_EN_FORCE_VAL BIT(11)
+#define DA_CKM_CKTX0_EN_FORCE_EN BIT(10)
+#define DA_CKM_XTAL_CK_FORCE_VAL BIT(9)
+#define DA_CKM_XTAL_CK_FORCE_EN BIT(8)
+#define DA_CKM_BIAS_LPF_EN_FORCE_VAL BIT(7)
+#define DA_CKM_BIAS_LPF_EN_FORCE_EN BIT(6)
+#define DA_CKM_BIAS_EN_FORCE_VAL BIT(5)
+#define DA_CKM_BIAS_EN_FORCE_EN BIT(4)
+#define DA_XTP_GLB_AVD10_ON_FORCE_VAL BIT(3)
+#define DA_XTP_GLB_AVD10_ON_FORCE BIT(2)
+#define DA_XTP_GLB_LDO_EN_FORCE_VAL BIT(1)
+#define DA_XTP_GLB_LDO_EN_FORCE_EN BIT(0)
+
+#define MTK_DP_1040 0x1040
+#define RG_DPAUX_RX_VALID_DEGLITCH_EN BIT(2)
+#define RG_XTP_GLB_CKDET_EN BIT(1)
+#define RG_DPAUX_RX_EN BIT(0)
+
+#define MTK_DP_ENC0_P0_3000 (ENC0_OFFSET + 0x000)
+#define LANE_NUM_DP_ENC0_P0_MASK 0x3
+#define VIDEO_MUTE_SW_DP_ENC0_P0_MASK 0x4
+#define VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT 2
+#define VIDEO_MUTE_SEL_DP_ENC0_P0_MASK 0x8
+#define VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT 3
+#define ENHANCED_FRAME_EN_DP_ENC0_P0_MASK 0x10
+#define ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT 4
+
+#define MTK_DP_ENC0_P0_3004 (ENC0_OFFSET + 0x004)
+#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK 0x100
+#define VIDEO_M_CODE_SEL_DP_ENC0_P0_SHIFT 8
+#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK 0x200
+#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT 9
+
+#define MTK_DP_ENC0_P0_3008 (ENC0_OFFSET + 0x008)
+#define VIDEO_M_CODE_SW_0_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_300C (ENC0_OFFSET + 0x00C)
+#define VIDEO_M_CODE_SW_1_DP_ENC0_P0_MASK 0xff
+
+#define MTK_DP_ENC0_P0_3010 (ENC0_OFFSET + 0x010)
+#define HTOTAL_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3014 (ENC0_OFFSET + 0x014)
+#define VTOTAL_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3018 (ENC0_OFFSET + 0x018)
+#define HSTART_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_301C (ENC0_OFFSET + 0x01C)
+#define VSTART_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3020 (ENC0_OFFSET + 0x020)
+#define HWIDTH_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3024 (ENC0_OFFSET + 0x024)
+#define VHEIGHT_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3028 (ENC0_OFFSET + 0x028)
+#define HSW_SW_DP_ENC0_P0_MASK 0x7fff
+#define HSW_SW_DP_ENC0_P0_SHIFT 0
+#define HSP_SW_DP_ENC0_P0_MASK 0x8000
+
+#define MTK_DP_ENC0_P0_302C (ENC0_OFFSET + 0x02C)
+#define VSW_SW_DP_ENC0_P0_MASK 0x7fff
+#define VSW_SW_DP_ENC0_P0_SHIFT 0
+#define VSP_SW_DP_ENC0_P0_MASK 0x8000
+
+#define MTK_DP_ENC0_P0_3030 (ENC0_OFFSET + 0x030)
+#define HTOTAL_SEL_DP_ENC0_P0_SHIFT 0
+#define VTOTAL_SEL_DP_ENC0_P0_SHIFT 1
+#define HSTART_SEL_DP_ENC0_P0_SHIFT 2
+#define VSTART_SEL_DP_ENC0_P0_SHIFT 3
+#define HWIDTH_SEL_DP_ENC0_P0_SHIFT 4
+#define VHEIGHT_SEL_DP_ENC0_P0_SHIFT 5
+#define HSP_SEL_DP_ENC0_P0_SHIFT 6
+#define HSW_SEL_DP_ENC0_P0_SHIFT 7
+#define VSP_SEL_DP_ENC0_P0_SHIFT 8
+#define VSW_SEL_DP_ENC0_P0_SHIFT 9
+#define VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK 0x800
+#define VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT 11
+#define VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK 0x1000
+#define VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT 12
+
+#define MTK_DP_ENC0_P0_3034 (ENC0_OFFSET + 0x034)
+
+#define MTK_DP_ENC0_P0_3038 (ENC0_OFFSET + 0x038)
+#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK 0x800
+#define VIDEO_SOURCE_SEL_DP_ENC0_P0_SHIFT 11
+
+#define MTK_DP_ENC0_P0_303C (ENC0_OFFSET + 0x03C)
+#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK 0x3f
+#define SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT 0
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK 0x700
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT 8
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT                                     \
+	(0 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT                                     \
+	(1 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT                                     \
+	(2 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT                                      \
+	(3 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT                                      \
+	(4 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK 0x7000
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT 12
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB                                     \
+	(0 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422                                \
+	(1 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420                                \
+	(2 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
+#define VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK 0x8000
+#define VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT 15
+
+#define MTK_DP_ENC0_P0_3040 (ENC0_OFFSET + 0x040)
+#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK 0xfff
+#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT 0
+
+#define MTK_DP_ENC0_P0_3044 (ENC0_OFFSET + 0x044)
+#define VIDEO_N_CODE_0_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3048 (ENC0_OFFSET + 0x048)
+#define VIDEO_N_CODE_1_DP_ENC0_P0_MASK 0xff
+
+#define MTK_DP_ENC0_P0_304C (ENC0_OFFSET + 0x04C)
+#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK 0x4
+#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK 0x100
+
+#define MTK_DP_ENC0_P0_3050 (ENC0_OFFSET + 0x050)
+#define VIDEO_N_CODE_MN_GEN_0_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3054 (ENC0_OFFSET + 0x054)
+#define VIDEO_N_CODE_MN_GEN_1_DP_ENC0_P0_MASK 0xff
+
+#define MTK_DP_ENC0_P0_3064 (ENC0_OFFSET + 0x064)
+#define HDE_NUM_LAST_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3088 (ENC0_OFFSET + 0x088)
+#define AU_EN_DP_ENC0_P0_MASK 0x40
+#define AU_EN_DP_ENC0_P0_SHIFT 6
+#define AUDIO_8CH_EN_DP_ENC0_P0_MASK 0x80
+#define AUDIO_8CH_SEL_DP_ENC0_P0_MASK 0x100
+#define AUDIO_2CH_EN_DP_ENC0_P0_MASK 0x4000
+#define AUDIO_2CH_SEL_DP_ENC0_P0_MASK 0x8000
+
+#define MTK_DP_ENC0_P0_308C (ENC0_OFFSET + 0x08C)
+#define CH_STATUS_0_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3090 (ENC0_OFFSET + 0x090)
+#define CH_STATUS_1_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3094 (ENC0_OFFSET + 0x094)
+#define CH_STATUS_2_DP_ENC0_P0_MASK 0xff
+
+#define MTK_DP_ENC0_P0_30A0 (ENC0_OFFSET + 0x0A0)
+
+#define MTK_DP_ENC0_P0_30A4 (ENC0_OFFSET + 0x0A4)
+#define AU_TS_CFG_DP_ENC0_P0_MASK 0xff
+
+#define MTK_DP_ENC0_P0_30A8 (ENC0_OFFSET + 0x0A8)
+
+#define MTK_DP_ENC0_P0_30AC (ENC0_OFFSET + 0x0AC)
+
+#define MTK_DP_ENC0_P0_30B0 (ENC0_OFFSET + 0x0B0)
+
+#define MTK_DP_ENC0_P0_30B4 (ENC0_OFFSET + 0x0B4)
+#define ISRC_CFG_DP_ENC0_P0_MASK 0xff00
+#define ISRC_CFG_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC0_P0_30B8 (ENC0_OFFSET + 0x0B8)
+
+#define MTK_DP_ENC0_P0_30BC (ENC0_OFFSET + 0x0BC)
+#define ISRC_CONT_DP_ENC0_P0_MASK 0x1
+#define ISRC_CONT_DP_ENC0_P0_SHIFT 0
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK 0x700
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT 8
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_2                             \
+	(1 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_4                             \
+	(2 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_8                             \
+	(3 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2                             \
+	(5 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_4                             \
+	(6 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_8                             \
+	(7 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+
+#define MTK_DP_ENC0_P0_30D8 (ENC0_OFFSET + 0x0D8)
+
+#define MTK_DP_ENC0_P0_312C (ENC0_OFFSET + 0x12C)
+#define ASP_HB2_DP_ENC0_P0_MASK 0xff
+#define ASP_HB3_DP_ENC0_P0_MASK 0xff00
+#define ASP_HB3_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC0_P0_3130 (ENC0_OFFSET + 0x130)
+
+#define MTK_DP_ENC0_P0_3138 (ENC0_OFFSET + 0x138)
+
+#define MTK_DP_ENC0_P0_3154 (ENC0_OFFSET + 0x154)
+#define PGEN_HTOTAL_DP_ENC0_P0_MASK 0x3fff
+
+#define MTK_DP_ENC0_P0_3158 (ENC0_OFFSET + 0x158)
+#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK 0x3fff
+
+#define MTK_DP_ENC0_P0_315C (ENC0_OFFSET + 0x15C)
+#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x3fff
+
+#define MTK_DP_ENC0_P0_3160 (ENC0_OFFSET + 0x160)
+#define PGEN_HFDE_START_DP_ENC0_P0_MASK 0x3fff
+
+#define MTK_DP_ENC0_P0_3164 (ENC0_OFFSET + 0x164)
+#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x3fff
+
+#define MTK_DP_ENC0_P0_3168 (ENC0_OFFSET + 0x168)
+#define PGEN_VTOTAL_DP_ENC0_P0_MASK 0x1fff
+
+#define MTK_DP_ENC0_P0_316C (ENC0_OFFSET + 0x16C)
+#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK 0x1fff
+
+#define MTK_DP_ENC0_P0_3170 (ENC0_OFFSET + 0x170)
+#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x1fff
+
+#define MTK_DP_ENC0_P0_3174 (ENC0_OFFSET + 0x174)
+#define PGEN_VFDE_START_DP_ENC0_P0_MASK 0x1fff
+
+#define MTK_DP_ENC0_P0_3178 (ENC0_OFFSET + 0x178)
+#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x1fff
+
+#define MTK_DP_ENC0_P0_31B0 (ENC0_OFFSET + 0x1B0)
+#define PGEN_PATTERN_SEL_SHIFT 4
+#define PGEN_PATTERN_SEL_MASK 0x0070
+
+#define MTK_DP_ENC0_P0_31C8 (ENC0_OFFSET + 0x1C8)
+#define VSC_EXT_VESA_HB0_DP_ENC0_P0_MASK 0xff
+#define VSC_EXT_VESA_HB1_DP_ENC0_P0_MASK 0xff00
+#define VSC_EXT_VESA_HB1_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC0_P0_31CC (ENC0_OFFSET + 0x1CC)
+#define VSC_EXT_VESA_HB2_DP_ENC0_P0_MASK 0xff
+#define VSC_EXT_VESA_HB2_DP_ENC0_P0_SHIFT 0
+#define VSC_EXT_VESA_HB3_DP_ENC0_P0_MASK 0xff00
+
+#define MTK_DP_ENC0_P0_31D0 (ENC0_OFFSET + 0x1D0)
+#define VSC_EXT_CEA_HB0_DP_ENC0_P0_MASK 0xff
+#define VSC_EXT_CEA_HB1_DP_ENC0_P0_MASK 0xff00
+#define VSC_EXT_CEA_HB1_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC0_P0_31D4 (ENC0_OFFSET + 0x1D4)
+#define VSC_EXT_CEA_HB2_DP_ENC0_P0_MASK 0xff
+#define VSC_EXT_CEA_HB2_DP_ENC0_P0_SHIFT 0
+#define VSC_EXT_CEA_HB3_DP_ENC0_P0_MASK 0xff00
+
+#define MTK_DP_ENC0_P0_31D8 (ENC0_OFFSET + 0x1D8)
+#define VSC_EXT_VESA_NUM_DP_ENC0_P0_MASK 0x3f
+#define VSC_EXT_VESA_NUM_DP_ENC0_P0_SHIFT 0
+#define VSC_EXT_CEA_NUM_DP_ENC0_P0_MASK 0x3f00
+#define VSC_EXT_CEA_NUM_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC0_P0_31DC (ENC0_OFFSET + 0x1DC)
+#define HDR0_CFG_DP_ENC0_P0_MASK 0xff
+#define HDR0_CFG_DP_ENC0_P0_SHIFT 0
+
+#define MTK_DP_ENC0_P0_31E8 (ENC0_OFFSET + 0x1E8)
+
+#define MTK_DP_ENC0_P0_31EC (ENC0_OFFSET + 0x1EC)
+#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK 0x10
+#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT 4
+#define ISRC1_HB3_DP_ENC0_P0_MASK 0xff00
+#define ISRC1_HB3_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC1_P0_3200 (ENC1_OFFSET + 0x000)
+
+#define MTK_DP_ENC1_P0_3280 (ENC1_OFFSET + 0x080)
+#define SDP_PACKET_TYPE_DP_ENC1_P0_MASK 0x1f
+#define SDP_PACKET_W_DP_ENC1_P0 0x20
+#define SDP_PACKET_W_DP_ENC1_P0_MASK 0x20
+#define SDP_PACKET_W_DP_ENC1_P0_SHIFT 5
+
+#define MTK_DP_ENC1_P0_328C (ENC1_OFFSET + 0x08C)
+
+#define MTK_DP_ENC1_P0_3290 (ENC1_OFFSET + 0x090)
+
+#define MTK_DP_ENC1_P0_32A0 (ENC1_OFFSET + 0x0A0)
+
+#define MTK_DP_ENC1_P0_32A4 (ENC1_OFFSET + 0x0A4)
+
+#define MTK_DP_ENC1_P0_3300 (ENC1_OFFSET + 0x100)
+#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK 0x300
+#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT 8
+
+#define MTK_DP_ENC1_P0_3304 (ENC1_OFFSET + 0x104)
+#define AU_PRTY_REGEN_DP_ENC1_P0_MASK 0x100
+#define AU_CH_STS_REGEN_DP_ENC1_P0_MASK 0x200
+#define AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK 0x1000
+
+#define MTK_DP_ENC1_P0_3324 (ENC1_OFFSET + 0x124)
+#define AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK 0x300
+#define AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT 8
+#define AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX                                       \
+	(0 << AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT)
+
+#define MTK_DP_ENC1_P0_3364 (ENC1_OFFSET + 0x164)
+#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK 0xfff
+#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT 0
+#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK 0xf000
+#define FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT 12
+
+#define MTK_DP_ENC1_P0_3368 (ENC1_OFFSET + 0x168)
+#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT 0
+#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT 4
+#define SDP_DP13_EN_DP_ENC1_P0_SHIFT 8
+#define BS2BS_MODE_DP_ENC1_P0_MASK 0x3000
+#define BS2BS_MODE_DP_ENC1_P0_SHIFT 12
+
+#define MTK_DP_ENC1_P0_33F4 (ENC1_OFFSET + 0x1F4)
+
+#define MTK_DP_TRANS_P0_3400 (TRANS_OFFSET + 0x000)
+#define PATTERN1_EN_DP_TRANS_P0_MASK 0x1000
+#define PATTERN1_EN_DP_TRANS_P0_SHIFT 12
+#define PATTERN2_EN_DP_TRANS_P0_MASK 0x2000
+#define PATTERN3_EN_DP_TRANS_P0_MASK 0x4000
+#define PATTERN4_EN_DP_TRANS_P0_MASK 0x8000
+
+#define MTK_DP_TRANS_P0_3404 (TRANS_OFFSET + 0x004)
+#define DP_SCR_EN_DP_TRANS_P0_MASK 0x1
+
+#define MTK_DP_TRANS_P0_340C (TRANS_OFFSET + 0x00C)
+#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK 0x2000
+#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT 13
+
+#define MTK_DP_TRANS_P0_3410 (TRANS_OFFSET + 0x010)
+#define HPD_DEB_THD_DP_TRANS_P0_MASK 0xf
+#define HPD_DEB_THD_DP_TRANS_P0_SHIFT 0
+#define HPD_INT_THD_DP_TRANS_P0_MASK 0xf0
+#define HPD_INT_THD_DP_TRANS_P0_SHIFT 4
+#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US (2 << HPD_INT_THD_DP_TRANS_P0_SHIFT)
+#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US                                   \
+	(2 << (HPD_INT_THD_DP_TRANS_P0_SHIFT + 2))
+#define HPD_DISC_THD_DP_TRANS_P0_MASK 0xf00
+#define HPD_DISC_THD_DP_TRANS_P0_SHIFT 8
+#define HPD_CONN_THD_DP_TRANS_P0_MASK 0xf000
+#define HPD_CONN_THD_DP_TRANS_P0_SHIFT 12
+
+#define MTK_DP_TRANS_P0_3414 (TRANS_OFFSET + 0x014)
+#define HPD_DB_DP_TRANS_P0_MASK 0x4
+
+#define MTK_DP_TRANS_P0_3418 (TRANS_OFFSET + 0x018)
+#define IRQ_CLR_DP_TRANS_P0_MASK 0xf
+#define IRQ_MASK_DP_TRANS_P0_MASK 0xf0
+#define IRQ_MASK_DP_TRANS_P0_SHIFT 4
+#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ (BIT(1) << IRQ_MASK_DP_TRANS_P0_SHIFT)
+#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ (BIT(2) << IRQ_MASK_DP_TRANS_P0_SHIFT)
+#define IRQ_MASK_DP_TRANS_P0_INT_IRQ (BIT(3) << IRQ_MASK_DP_TRANS_P0_SHIFT)
+#define IRQ_STATUS_DP_TRANS_P0_MASK 0xf000
+#define IRQ_STATUS_DP_TRANS_P0_SHIFT 12
+
+#define MTK_DP_TRANS_P0_342C (TRANS_OFFSET + 0x02C)
+#define XTAL_FREQ_DP_TRANS_P0_DEFAULT 0x69
+#define XTAL_FREQ_DP_TRANS_P0_MASK 0xff
+
+#define MTK_DP_TRANS_P0_3430 (TRANS_OFFSET + 0x030)
+#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK 0x3
+#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT BIT(1)
+
+#define MTK_DP_TRANS_P0_34A4 (TRANS_OFFSET + 0x0A4)
+#define LANE_NUM_DP_TRANS_P0_MASK 0xc
+#define LANE_NUM_DP_TRANS_P0_SHIFT 2
+
+#define MTK_DP_TRANS_P0_3540 (TRANS_OFFSET + 0x140)
+#define FEC_EN_DP_TRANS_P0_MASK 0x1
+#define FEC_EN_DP_TRANS_P0_SHIFT 0
+#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK 0x8
+#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT 3
+
+#define MTK_DP_TRANS_P0_3580 (TRANS_OFFSET + 0x180)
+#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK 0x100
+#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK 0x200
+#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK 0x400
+#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK 0x800
+
+#define MTK_DP_TRANS_P0_35C4 (TRANS_OFFSET + 0x1C4)
+#define SW_IRQ_MASK_DP_TRANS_P0_MASK 0xffff
+
+#define MTK_DP_TRANS_P0_35C8 (TRANS_OFFSET + 0x1C8)
+#define SW_IRQ_CLR_DP_TRANS_P0_MASK 0xffff
+
+#define SW_IRQ_STATUS_DP_TRANS_P0_MASK 0xffff
+#define SW_IRQ_STATUS_DP_TRANS_P0_SHIFT 0
+
+#define MTK_DP_TRANS_P0_35D0 (TRANS_OFFSET + 0x1D0)
+#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK 0xffff
+
+#define MTK_DP_TRANS_P0_35F0 (TRANS_OFFSET + 0x1F0)
+
+#define MTK_DP_AUX_P0_360C (AUX_OFFSET + 0x00C)
+#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK 0x1fff
+
+#define MTK_DP_AUX_P0_3614 (AUX_OFFSET + 0x014)
+#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK 0x7f
+#define AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT 0
+
+#define MTK_DP_AUX_P0_3618 (AUX_OFFSET + 0x018)
+#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK 0x200
+#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK 0xf
+
+#define MTK_DP_AUX_P0_3620 (AUX_OFFSET + 0x020)
+#define AUX_RD_MODE_AUX_TX_P0_MASK 0x200
+#define AUX_RX_FIFO_READ_PULSE_TX_P0_MASK 0x100
+#define AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT 8
+#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK 0xff
+#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT 0
+
+#define MTK_DP_AUX_P0_3624 (AUX_OFFSET + 0x024)
+#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK 0xf
+
+#define MTK_DP_AUX_P0_3628 (AUX_OFFSET + 0x028)
+#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK 0x3ff
+#define AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT 0
+#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE                                     \
+	(BIT(0) << AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT)
+
+#define MTK_DP_AUX_P0_362C (AUX_OFFSET + 0x02C)
+#define AUX_NO_LENGTH_AUX_TX_P0_MASK 0x1
+#define AUX_NO_LENGTH_AUX_TX_P0_SHIFT 0
+#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK 0x2
+#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK 0xfffc
+
+#define MTK_DP_AUX_P0_3630 (AUX_OFFSET + 0x030)
+#define AUX_TX_REQUEST_READY_AUX_TX_P0_MASK 0x8
+#define AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT 3
+
+#define MTK_DP_AUX_P0_3634 (AUX_OFFSET + 0x034)
+#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK 0xff00
+#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT 8
+
+#define MTK_DP_AUX_P0_3640 (AUX_OFFSET + 0x040)
+#define AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK 0x40
+#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 6
+#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 5
+#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 4
+#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT 3
+#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT 2
+#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT 1
+#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK 0x1
+#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT 0
+
+#define MTK_DP_AUX_P0_3644 (AUX_OFFSET + 0x044)
+#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK 0xf
+
+#define MTK_DP_AUX_P0_3648 (AUX_OFFSET + 0x048)
+#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK 0xffff
+
+#define MTK_DP_AUX_P0_364C (AUX_OFFSET + 0x04C)
+#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK 0xf
+
+#define MTK_DP_AUX_P0_3650 (AUX_OFFSET + 0x050)
+#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK 0xf000
+#define MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT 12
+#define PHY_FIFO_RST_AUX_TX_P0_MASK 0x200
+#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK 0x100
+#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT 8
+
+#define MTK_DP_AUX_P0_3658 (AUX_OFFSET + 0x058)
+#define AUX_TX_OV_EN_AUX_TX_P0_MASK 0x1
+
+#define MTK_DP_AUX_P0_3690 (AUX_OFFSET + 0x090)
+#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK 0x100
+#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT 8
+
+#define MTK_DP_AUX_P0_3704 (AUX_OFFSET + 0x104)
+#define AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK 0x2
+#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK 0x4
+#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT 2
+
+#define MTK_DP_AUX_P0_3708 (AUX_OFFSET + 0x108)
+
+#define MTK_DP_AUX_P0_37C8 (AUX_OFFSET + 0x1C8)
+#define MTK_ATOP_EN_AUX_TX_P0_MASK 0x1
+#define MTK_ATOP_EN_AUX_TX_P0_SHIFT 0
+
+#define MTK_DP_TOP_PWR_STATE (TOP_OFFSET + 0x00)
+#define DP_PWR_STATE_MASK 0x3
+#define DP_PWR_STATE_BANDGAP 1
+#define DP_PWR_STATE_BANDGAP_TPLL 2
+#define DP_PWR_STATE_BANDGAP_TPLL_LANE 3
+
+#define MTK_DP_TOP_SWING_EMP (TOP_OFFSET + 0x04)
+#define DP_TX0_VOLT_SWING_MASK 0x3
+#define DP_TX0_VOLT_SWING_SHIFT 0
+#define DP_TX0_PRE_EMPH_MASK 0xc
+#define DP_TX0_PRE_EMPH_SHIFT 2
+#define DP_TX1_VOLT_SWING_MASK 0x300
+#define DP_TX1_VOLT_SWING_SHIFT 8
+#define DP_TX1_PRE_EMPH_MASK 0xc00
+#define DP_TX2_VOLT_SWING_MASK 0x30000
+#define DP_TX2_PRE_EMPH_MASK 0xc0000
+#define DP_TX3_VOLT_SWING_MASK 0x3000000
+#define DP_TX3_PRE_EMPH_MASK 0xc000000
+
+#define MTK_DP_TOP_RESET_AND_PROBE (TOP_OFFSET + 0x20)
+#define SW_RST_B_SHIFT 0
+#define SW_RST_B_PHYD (BIT(4) << SW_RST_B_SHIFT)
+
+#define MTK_DP_TOP_IRQ_STATUS (TOP_OFFSET + 0x28)
+#define RGS_IRQ_STATUS_SHIFT 0
+#define RGS_IRQ_STATUS_TRANSMITTER (BIT(1) << RGS_IRQ_STATUS_SHIFT)
+
+#define MTK_DP_TOP_IRQ_MASK (TOP_OFFSET + 0x2C)
+#define IRQ_MASK_AUX_TOP_IRQ BIT(2)
+
+#define MTK_DP_TOP_MEM_PD (TOP_OFFSET + 0x38)
+#define MEM_ISO_EN_SHIFT 0
+#define FUSE_SEL_SHIFT 2
+
+#endif /*_MTK_DP_REG_H_*/
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
index 384074f69111b..e6e88e3cd811d 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
@@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
 	MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
 };
 
+enum TVDPLL_CLK {
+	TVDPLL_PLL = 0,
+	TVDPLL_D2 = 2,
+	TVDPLL_D4 = 4,
+	TVDPLL_D8 = 8,
+	TVDPLL_D16 = 16,
+};
+
 struct mtk_dpi {
 	struct drm_encoder encoder;
 	struct drm_bridge bridge;
@@ -71,8 +79,10 @@ struct mtk_dpi {
 	void __iomem *regs;
 	struct device *dev;
 	struct clk *engine_clk;
+	struct clk *dpi_ck_cg;
 	struct clk *pixel_clk;
 	struct clk *tvd_clk;
+	struct clk *pclk_src[5];
 	int irq;
 	struct drm_display_mode mode;
 	const struct mtk_dpi_conf *conf;
@@ -135,6 +145,7 @@ struct mtk_dpi_conf {
 	u32 hvsize_mask;
 	u32 channel_swap_shift;
 	u32 yuv422_en_bit;
+	u32 csc_enable_bit;
 	const struct mtk_dpi_yc_limit *limit;
 };
 
@@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
 
 static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
 {
-	mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
+	mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
 }
 
 static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
@@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
 		mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
 }
 
+static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
+{
+	u32 matrix_sel = 0;
+
+	switch (format) {
+	case MTK_DPI_COLOR_FORMAT_YCBCR_422:
+	case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
+	case MTK_DPI_COLOR_FORMAT_YCBCR_444:
+	case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
+	case MTK_DPI_COLOR_FORMAT_XV_YCC:
+		if (dpi->mode.hdisplay <= 720)
+			matrix_sel = 0x2;
+		break;
+	default:
+		break;
+	}
+	mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
+}
+
 static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
 					enum mtk_dpi_out_color_format format)
 {
 	if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
 	    (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
 		mtk_dpi_config_yuv422_enable(dpi, false);
-		if (dpi->conf->csc_support)
+		if (dpi->conf->csc_support) {
 			mtk_dpi_config_csc_enable(dpi, true);
+			mtk_dpi_matrix_sel(dpi, format);
+		}
 		if (dpi->conf->swap_input_support)
 			mtk_dpi_config_swap_input(dpi, false);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
 	} else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
 		   (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
 		mtk_dpi_config_yuv422_enable(dpi, true);
-		if (dpi->conf->csc_support)
+		if (dpi->conf->csc_support) {
 			mtk_dpi_config_csc_enable(dpi, true);
+			mtk_dpi_matrix_sel(dpi, format);
+		}
 		if (dpi->conf->swap_input_support)
 			mtk_dpi_config_swap_input(dpi, true);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
@@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
 	mtk_dpi_disable(dpi);
 	clk_disable_unprepare(dpi->pixel_clk);
 	clk_disable_unprepare(dpi->engine_clk);
+	clk_disable_unprepare(dpi->dpi_ck_cg);
+	clk_disable_unprepare(dpi->tvd_clk);
 }
 
 static int mtk_dpi_power_on(struct mtk_dpi *dpi)
@@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
 	if (++dpi->refcount != 1)
 		return 0;
 
+	ret = clk_prepare_enable(dpi->tvd_clk);
+	if (ret) {
+		dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
+		goto err_pixel;
+	}
+
 	ret = clk_prepare_enable(dpi->engine_clk);
 	if (ret) {
 		dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
 		goto err_refcount;
 	}
 
+	ret = clk_prepare_enable(dpi->dpi_ck_cg);
+	if (ret) {
+		dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
+		goto err_ck_cg;
+	}
+
 	ret = clk_prepare_enable(dpi->pixel_clk);
 	if (ret) {
 		dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
@@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
 	if (dpi->pinctrl && dpi->pins_dpi)
 		pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
 
-	mtk_dpi_enable(dpi);
 	return 0;
 
 err_pixel:
+	clk_disable_unprepare(dpi->dpi_ck_cg);
+err_ck_cg:
 	clk_disable_unprepare(dpi->engine_clk);
 err_refcount:
 	dpi->refcount--;
@@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 	pll_rate = clk_get_rate(dpi->tvd_clk);
 
 	vm.pixelclock = pll_rate / factor;
-	if (dpi->conf->is_dpintf)
-		clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
-	else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
+	if (dpi->conf->is_dpintf) {
+		if (factor == 1)
+			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
+		else if (factor == 2)
+			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
+		else if (factor == 4)
+			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
+		else
+			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
+	} else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
 		 (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
 		clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
 	else
@@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 	}
 	mtk_dpi_sw_reset(dpi, false);
 
+	mtk_dpi_enable(dpi);
+
 	return 0;
 }
 
@@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
 	u32 *input_fmts;
 
 	*num_input_fmts = 0;
-
 	input_fmts = kcalloc(1, sizeof(*input_fmts),
 			     GFP_KERNEL);
 	if (!input_fmts)
@@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
 		if (dpi->conf->num_output_fmts)
 			out_bus_format = dpi->conf->output_fmts[0];
 
-	dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
+	dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
 		bridge_state->input_bus_cfg.format,
 		bridge_state->output_bus_cfg.format);
 
@@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
 	dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
 	dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
 	dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
-	dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
+	if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
+		dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
+	else
+		dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
 
 	return 0;
 }
@@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
 	MEDIA_BUS_FMT_RGB888_2X12_BE,
 };
 
+static const u32 mt8195_output_fmts[] = {
+	MEDIA_BUS_FMT_RGB888_1X24,
+	MEDIA_BUS_FMT_YUV8_1X24,
+	MEDIA_BUS_FMT_YUYV8_1X16,
+};
+
 static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
 	.c_bottom = 0x0010,
 	.c_top = 0x0FE0,
@@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
 	.hvsize_mask = HSIZE_MASK,
 	.channel_swap_shift = CH_SWAP,
 	.yuv422_en_bit = YUV422_EN,
+	.csc_enable_bit = CSC_ENABLE,
 	.limit = &mtk_dpi_limit,
 };
 
@@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
 	.hvsize_mask = HSIZE_MASK,
 	.channel_swap_shift = CH_SWAP,
 	.yuv422_en_bit = YUV422_EN,
+	.csc_enable_bit = CSC_ENABLE,
 	.limit = &mtk_dpi_limit,
 };
 
@@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
 	.hvsize_mask = HSIZE_MASK,
 	.channel_swap_shift = CH_SWAP,
 	.yuv422_en_bit = YUV422_EN,
+	.csc_enable_bit = CSC_ENABLE,
 	.limit = &mtk_dpi_limit,
 };
 
@@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
 	.hvsize_mask = HSIZE_MASK,
 	.channel_swap_shift = CH_SWAP,
 	.yuv422_en_bit = YUV422_EN,
+	.csc_enable_bit = CSC_ENABLE,
 	.limit = &mtk_dpi_limit,
 };
 
 static const struct mtk_dpi_conf mt8195_dpintf_conf = {
 	.cal_factor = mt8195_dpintf_calculate_factor,
-	.output_fmts = mt8173_output_fmts,
-	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.output_fmts = mt8195_output_fmts,
+	.num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
 	.is_dpintf = true,
+	.csc_support = true,
 	.dimension_mask = DPINTF_HPW_MASK,
 	.hvsize_mask = DPINTF_HSIZE_MASK,
 	.channel_swap_shift = DPINTF_CH_SWAP,
 	.yuv422_en_bit = DPINTF_YUV422_EN,
+	.csc_enable_bit = DPINTF_CSC_ENABLE,
 	.limit = &mtk_dpintf_limit,
 };
 
@@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
+	if (IS_ERR(dpi->dpi_ck_cg)) {
+		ret = PTR_ERR(dpi->dpi_ck_cg);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
+				ret);
+
+		return ret;
+	}
+
 	dpi->pixel_clk = devm_clk_get(dev, "pixel");
 	if (IS_ERR(dpi->pixel_clk)) {
 		ret = PTR_ERR(dpi->pixel_clk);
@@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
+	dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
+	dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
+	dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
+
 	dpi->irq = platform_get_irq(pdev, 0);
 	if (dpi->irq <= 0)
 		return -EINVAL;
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi_regs.h b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
index 72efe6ee25842..91b32dfffced2 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
+++ b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
@@ -41,6 +41,7 @@
 #define FAKE_DE_RODD			BIT(22)
 #define FAKE_DE_REVEN			BIT(23)
 #define DPINTF_YUV422_EN		BIT(24)
+#define DPINTF_CSC_ENABLE		BIT(26)
 #define DPINTF_INPUT_2P_EN		BIT(29)
 
 #define DPI_OUTPUT_SETTING	0x14
@@ -229,4 +230,29 @@
 
 #define EDGE_SEL_EN			BIT(5)
 #define H_FRE_2N			BIT(25)
+
+#define DPI_MATRIX_SET	0xB4
+#define INT_MATRIX_SEL			BIT(0)
+#define INT_MATRIX_SEL_MASK		(0x1F << 0)
+#define RGB_TO_JPEG			0x00
+#define RGB_TO_FULL709			0x01
+#define RGB_TO_BT601			0x02
+#define RGB_TO_BT709			0x03
+#define JPEG_TO_RGB			0x04
+#define FULL709_TO_RGB			0x05
+#define BT601_TO_RGB			0x06
+#define BT709_TO_RGB			0x07
+#define JPEG_TO_BT601			0x08
+#define JPEG_TO_BT709			0x09
+#define BT601_TO_JPEG			0xA
+#define BT709_TO_JPEG			0xB
+#define BT709_TO_BT601			0xC
+#define BT601_TO_BT709			0xD
+#define JPEG_TO_CERGB			0x14
+#define FULL709_TO_CERGB		0x15
+#define BT601_TO_CERGB			0x16
+#define BT709_TO_CERGB			0x17
+#define RGB_TO_CERGB			0x1C
+#define MATRIX_BIT			BIT(8)
+#define EXT_MATRIX_EN			BIT(12)
 #endif /* __MTK_DPI_REGS_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index 1ff4e31c86345..65c7ccaaf1969 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -672,6 +672,7 @@ static struct platform_driver * const mtk_drm_drivers[] = {
 	&mtk_disp_ovl_driver,
 	&mtk_disp_rdma_driver,
 	&mtk_dpi_driver,
+	&mtk_dp_driver,
 	&mtk_drm_platform_driver,
 	&mtk_dsi_driver,
 };
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
index 3e7d1e6fbe010..8926416f4419d 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
@@ -53,6 +53,7 @@ extern struct platform_driver mtk_disp_gamma_driver;
 extern struct platform_driver mtk_disp_ovl_driver;
 extern struct platform_driver mtk_disp_rdma_driver;
 extern struct platform_driver mtk_dpi_driver;
+extern struct platform_driver mtk_dp_driver;
 extern struct platform_driver mtk_dsi_driver;
 
 #endif /* MTK_DRM_DRV_H */
-- 
2.32.0


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

* [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-10 13:06   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Chun-Kuang Hu, Philipp Zabel,
	Matthias Brugger
  Cc: kernel test robot, linux-kernel, dri-devel,
	Markus Schneider-Pargmann, linux-mediatek, linux-arm-kernel

From: Markus Schneider-Pargmann <msp@baylibre.com>

This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
according phy driver mediatek-dp-phy.

It supports both functional units on the mt8195, the embedded
DisplayPort as well as the external DisplayPort units. It offers
hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
to 4 lanes.

The driver creates a child device for the phy. The child device will
never exist without the parent being active. As they are sharing a
register range, the parent passes a regmap pointer to the child so that
both can work with the same register range. The phy driver sets device
data that is read by the parent to get the phy device that can be used
to control the phy properties.

This driver is based on an initial version by
Jason-JH.Lin <jason-jh.lin@mediatek.com>.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Reported-by: kernel test robot <lkp@intel.com>
---
 drivers/gpu/drm/drm_edid.c              |    2 +-
 drivers/gpu/drm/mediatek/Kconfig        |    7 +
 drivers/gpu/drm/mediatek/Makefile       |    2 +
 drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
 drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
 drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
 9 files changed, 3799 insertions(+), 13 deletions(-)
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 500279a82167a..bfd98b50ceb5b 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
 	 * modes and forbids YCRCB422 support for all video modes per
 	 * HDMI 1.3 spec.
 	 */
-	info->color_formats = DRM_COLOR_FORMAT_RGB444;
+	info->color_formats |= DRM_COLOR_FORMAT_RGB444;
 
 	/* YCRCB444 is optional according to spec. */
 	if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
index 2976d21e9a34a..029b94c716131 100644
--- a/drivers/gpu/drm/mediatek/Kconfig
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
 	select PHY_MTK_HDMI
 	help
 	  DRM/KMS HDMI driver for Mediatek SoCs
+
+config MTK_DPTX_SUPPORT
+	tristate "DRM DPTX Support for Mediatek SoCs"
+	depends on DRM_MEDIATEK
+	select PHY_MTK_DP
+	help
+	  DRM/KMS Display Port driver for Mediatek SoCs.
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index 29098d7c8307c..d86a6406055e6 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
 			  mtk_hdmi_ddc.o
 
 obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
+
+obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
new file mode 100644
index 0000000000000..83087219d5a5e
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -0,0 +1,3094 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Copyright (c) 2021 BayLibre
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/hdmi-codec.h>
+#include <video/videomode.h>
+
+#include "mtk_dp_reg.h"
+
+#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
+#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
+
+#define MTK_DP_MAX_LANES 4
+#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
+
+#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
+
+#define MTK_DP_TRAIN_RETRY_LIMIT 8
+#define MTK_DP_TRAIN_MAX_ITERATIONS 5
+
+#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
+
+#define MTK_DP_DP_VERSION_11 0x11
+
+enum mtk_dp_state {
+	MTK_DP_STATE_INITIAL,
+	MTK_DP_STATE_IDLE,
+	MTK_DP_STATE_PREPARE,
+	MTK_DP_STATE_NORMAL,
+};
+
+enum mtk_dp_train_state {
+	MTK_DP_TRAIN_STATE_STARTUP = 0,
+	MTK_DP_TRAIN_STATE_CHECKCAP,
+	MTK_DP_TRAIN_STATE_CHECKEDID,
+	MTK_DP_TRAIN_STATE_TRAINING_PRE,
+	MTK_DP_TRAIN_STATE_TRAINING,
+	MTK_DP_TRAIN_STATE_CHECKTIMING,
+	MTK_DP_TRAIN_STATE_NORMAL,
+	MTK_DP_TRAIN_STATE_POWERSAVE,
+	MTK_DP_TRAIN_STATE_DPIDLE,
+};
+
+struct mtk_dp_timings {
+	struct videomode vm;
+
+	u16 htotal;
+	u16 vtotal;
+	u8 frame_rate;
+	u32 pix_rate_khz;
+};
+
+struct mtk_dp_train_info {
+	bool tps3;
+	bool tps4;
+	bool sink_ssc;
+	bool cable_plugged_in;
+	bool cable_state_change;
+	bool cr_done;
+	bool eq_done;
+
+	// link_rate is in multiple of 0.27Gbps
+	int link_rate;
+	int lane_count;
+
+	int irq_status;
+	int check_cap_count;
+};
+
+// Same values as used by the DP Spec for MISC0 bits 1 and 2
+enum mtk_dp_color_format {
+	MTK_DP_COLOR_FORMAT_RGB_444 = 0,
+	MTK_DP_COLOR_FORMAT_YUV_422 = 1,
+	MTK_DP_COLOR_FORMAT_YUV_444 = 2,
+	MTK_DP_COLOR_FORMAT_YUV_420 = 3,
+	MTK_DP_COLOR_FORMAT_YONLY = 4,
+	MTK_DP_COLOR_FORMAT_RAW = 5,
+	MTK_DP_COLOR_FORMAT_RESERVED = 6,
+	MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
+	MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
+};
+
+// Multiple of 0.27Gbps
+enum mtk_dp_linkrate {
+	MTK_DP_LINKRATE_RBR = 0x6,
+	MTK_DP_LINKRATE_HBR = 0xA,
+	MTK_DP_LINKRATE_HBR2 = 0x14,
+	MTK_DP_LINKRATE_HBR25 = 0x19,
+	MTK_DP_LINKRATE_HBR3 = 0x1E,
+};
+
+// Same values as used for DP Spec MISC0 bits 5,6,7
+enum mtk_dp_color_depth {
+	MTK_DP_COLOR_DEPTH_6BIT = 0,
+	MTK_DP_COLOR_DEPTH_8BIT = 1,
+	MTK_DP_COLOR_DEPTH_10BIT = 2,
+	MTK_DP_COLOR_DEPTH_12BIT = 3,
+	MTK_DP_COLOR_DEPTH_16BIT = 4,
+	MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
+};
+
+struct mtk_dp_audio_cfg {
+	int sample_rate;
+	int word_length_bits;
+	int channels;
+};
+
+struct mtk_dp_info {
+	enum mtk_dp_color_depth depth;
+	enum mtk_dp_color_format format;
+	struct mtk_dp_audio_cfg audio_caps;
+	struct mtk_dp_timings timings;
+};
+
+struct dp_cal_data {
+	unsigned int glb_bias_trim;
+	unsigned int clktx_impse;
+
+	unsigned int ln0_tx_impsel_pmos;
+	unsigned int ln0_tx_impsel_nmos;
+	unsigned int ln1_tx_impsel_pmos;
+	unsigned int ln1_tx_impsel_nmos;
+	unsigned int ln2_tx_impsel_pmos;
+	unsigned int ln2_tx_impsel_nmos;
+	unsigned int ln3_tx_impsel_pmos;
+	unsigned int ln3_tx_impsel_nmos;
+};
+
+struct mtk_dp {
+	struct device *dev;
+	struct platform_device *phy_dev;
+	struct phy *phy;
+	struct dp_cal_data cal_data;
+
+	struct drm_device *drm_dev;
+	struct drm_bridge bridge;
+	struct drm_bridge *next_bridge;
+	struct drm_dp_aux aux;
+
+	struct mutex edid_lock;
+	struct edid *edid;
+
+	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
+
+	struct mtk_dp_info info;
+	enum mtk_dp_state state;
+
+	struct mtk_dp_train_info train_info;
+	enum mtk_dp_train_state train_state;
+	unsigned int input_fmt;
+
+	struct regmap *regs;
+	struct clk *dp_tx_clk;
+
+	bool enabled;
+	bool audio_enable;
+
+	bool has_fec;
+	struct mutex dp_lock;
+
+	struct mutex update_plugged_status_lock;
+
+	hdmi_codec_plugged_cb plugged_cb;
+	struct device *codec_dev;
+	u8 connector_eld[MAX_ELD_BYTES];
+};
+
+enum mtk_dp_sdp_type {
+	MTK_DP_SDP_NONE = 0x00,
+	MTK_DP_SDP_ACM = 0x01,
+	MTK_DP_SDP_ISRC = 0x02,
+	MTK_DP_SDP_AVI = 0x03,
+	MTK_DP_SDP_AUI = 0x04,
+	MTK_DP_SDP_SPD = 0x05,
+	MTK_DP_SDP_MPEG = 0x06,
+	MTK_DP_SDP_NTSC = 0x07,
+	MTK_DP_SDP_VSP = 0x08,
+	MTK_DP_SDP_VSC = 0x09,
+	MTK_DP_SDP_EXT = 0x0A,
+	MTK_DP_SDP_PPS0 = 0x0B,
+	MTK_DP_SDP_PPS1 = 0x0C,
+	MTK_DP_SDP_PPS2 = 0x0D,
+	MTK_DP_SDP_PPS3 = 0x0E,
+	MTK_DP_SDP_DRM = 0x10,
+	MTK_DP_SDP_MAX_NUM
+};
+
+struct mtk_dp_sdp_packet {
+	enum mtk_dp_sdp_type type;
+	struct dp_sdp sdp;
+};
+
+#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
+union mtk_dp_audio_channel_status {
+	struct {
+		u8 rev : 1;
+		u8 is_lpcm : 1;
+		u8 copy_right : 1;
+		u8 additional_format_info : 3;
+		u8 channel_status_mode : 2;
+		u8 category_code;
+		u8 src_num : 4;
+		u8 channel_num : 4;
+		u8 sampling_freq : 4;
+		u8 clk_accuracy : 2;
+		u8 rev2 : 2;
+		u8 word_len : 4;
+		u8 original_sampling_freq : 4;
+	} iec;
+
+	u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
+};
+
+static struct regmap_config mtk_dp_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = SEC_OFFSET + 0x90,
+	.name = "mtk-dp-registers",
+};
+
+static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
+{
+	return mtk_dp->next_bridge != NULL;
+}
+
+static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
+{
+	return container_of(b, struct mtk_dp, bridge);
+}
+
+static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
+{
+	u32 read_val;
+	int ret;
+
+	ret = regmap_read(mtk_dp->regs, offset, &read_val);
+	if (ret) {
+		dev_err(mtk_dp->dev, "Failed to read register 0x%x: %d\n",
+			offset, ret);
+		return 0;
+	}
+
+	return read_val;
+}
+
+static void mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
+{
+	int ret;
+
+	ret = regmap_write(mtk_dp->regs, offset, val);
+	if (ret)
+		dev_err(mtk_dp->dev,
+			"Failed to write register 0x%x with value 0x%x: %d\n",
+			offset, val, ret);
+}
+
+static void mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, u32 val,
+			       u32 mask)
+{
+	int ret;
+
+	ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
+	if (ret)
+		dev_err(mtk_dp->dev,
+			"Failed to update register 0x%x with value 0x%x, mask 0x%x: %d\n",
+			offset, val, mask, ret);
+}
+
+static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32 offset, u8 *buf,
+				    size_t length)
+{
+	int i;
+	int num_regs = (length + 1) / 2;
+
+	// 2 bytes per register
+	for (i = 0; i < num_regs; i++) {
+		u32 val = buf[i * 2] |
+			  (i * 2 + 1 < length ? buf[i * 2 + 1] << 8 : 0);
+
+		mtk_dp_write(mtk_dp, offset + i * 4, val);
+	}
+}
+
+static unsigned long mtk_dp_sip_atf_call(unsigned int cmd, unsigned int para)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32, cmd, para, 0, 0, 0, 0, 0,
+		      &res);
+
+	pr_debug("[DPTX]%s cmd 0x%x, p1 0x%x, ret 0x%lx-0x%lx", __func__, cmd,
+		 para, res.a0, res.a1);
+	return res.a1;
+}
+
+static void mtk_dp_msa_bypass_disable(struct mtk_dp *mtk_dp)
+{
+	const u16 bits_to_set =
+		BIT(HTOTAL_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(VTOTAL_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(HSTART_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(VSTART_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(HWIDTH_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(VHEIGHT_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(HSP_SEL_DP_ENC0_P0_SHIFT) | BIT(HSW_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(VSP_SEL_DP_ENC0_P0_SHIFT) | BIT(VSW_SEL_DP_ENC0_P0_SHIFT);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, bits_to_set,
+			   bits_to_set);
+}
+
+static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
+{
+	struct mtk_dp_timings *timings = &mtk_dp->info.timings;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010, timings->htotal,
+			   HTOTAL_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
+			   timings->vm.hsync_len + timings->vm.hback_porch,
+			   HSTART_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
+			   timings->vm.hsync_len << HSW_SW_DP_ENC0_P0_SHIFT,
+			   HSW_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028, 0,
+			   HSP_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020, timings->vm.hactive,
+			   HWIDTH_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014, timings->vtotal,
+			   VTOTAL_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
+			   timings->vm.vsync_len + timings->vm.vback_porch,
+			   VSTART_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
+			   timings->vm.vsync_len << VSW_SW_DP_ENC0_P0_SHIFT,
+			   VSW_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C, 0,
+			   VSP_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024, timings->vm.vactive,
+			   VHEIGHT_SW_DP_ENC0_P0_MASK);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064, timings->vm.hactive,
+			   HDE_NUM_LAST_DP_ENC0_P0_MASK);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154, timings->htotal,
+			   PGEN_HTOTAL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
+			   timings->vm.hfront_porch,
+			   PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C, timings->vm.hsync_len,
+			   PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
+			   timings->vm.hback_porch + timings->vm.hsync_len,
+			   PGEN_HFDE_START_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164, timings->vm.hactive,
+			   PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168, timings->vtotal,
+			   PGEN_VTOTAL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
+			   timings->vm.vfront_porch,
+			   PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170, timings->vm.vsync_len,
+			   PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
+			   timings->vm.vback_porch + timings->vm.vsync_len,
+			   PGEN_VFDE_START_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178, timings->vm.vactive,
+			   PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
+				    enum mtk_dp_color_format color_format)
+{
+	u32 val;
+
+	mtk_dp->info.format = color_format;
+
+	// Update MISC0
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
+			   color_format << DP_TEST_COLOR_FORMAT_SHIFT,
+			   DP_TEST_COLOR_FORMAT_MASK);
+
+	switch (color_format) {
+	case MTK_DP_COLOR_FORMAT_YUV_422:
+		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
+		break;
+	case MTK_DP_COLOR_FORMAT_YUV_420:
+		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420;
+		break;
+	case MTK_DP_COLOR_FORMAT_YONLY:
+	case MTK_DP_COLOR_FORMAT_RAW:
+	case MTK_DP_COLOR_FORMAT_RESERVED:
+	case MTK_DP_COLOR_FORMAT_UNKNOWN:
+		drm_warn(mtk_dp->drm_dev, "Unsupported color format: %d\n",
+			 color_format);
+		fallthrough;
+	case MTK_DP_COLOR_FORMAT_RGB_444:
+	case MTK_DP_COLOR_FORMAT_YUV_444:
+		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
+		break;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
+			   PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp,
+				   enum mtk_dp_color_depth color_depth)
+{
+	u32 val;
+
+	mtk_dp->info.depth = color_depth;
+
+	// Update MISC0
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
+			   color_depth << DP_TEST_BIT_DEPTH_SHIFT,
+			   DP_TEST_BIT_DEPTH_MASK);
+
+	switch (color_depth) {
+	case MTK_DP_COLOR_DEPTH_6BIT:
+		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT;
+		break;
+	case MTK_DP_COLOR_DEPTH_8BIT:
+		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT;
+		break;
+	case MTK_DP_COLOR_DEPTH_10BIT:
+		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT;
+		break;
+	case MTK_DP_COLOR_DEPTH_12BIT:
+		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT;
+		break;
+	case MTK_DP_COLOR_DEPTH_16BIT:
+		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT;
+		break;
+	case MTK_DP_COLOR_DEPTH_UNKNOWN:
+		drm_warn(mtk_dp->drm_dev, "Unsupported color depth %d\n",
+			 color_depth);
+		return;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
+			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_mn_overwrite_disable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
+			   VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32 val)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   val << SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT,
+			   SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   BIT(VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT),
+			   VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
+			   0x20 << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
+			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
+			   0x20 << SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT,
+			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
+			   2 << VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT,
+			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
+			   4 << FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT,
+			   FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
+	mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368,
+		     1 << VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT |
+			     1 << VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT |
+			     BIT(SDP_DP13_EN_DP_ENC1_P0_SHIFT) |
+			     1 << BS2BS_MODE_DP_ENC1_P0_SHIFT);
+}
+
+static void mtk_dp_pg_disable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038, 0,
+			   VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
+			   4 << PGEN_PATTERN_SEL_SHIFT, PGEN_PATTERN_SEL_MASK);
+}
+
+static void mtk_dp_audio_setup_channels(struct mtk_dp *mtk_dp,
+					struct mtk_dp_audio_cfg *cfg)
+{
+	u32 channel_enable_bits;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3324,
+			   AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX,
+			   AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK);
+
+	//audio channel count change reset
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(9), BIT(9));
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
+			   AU_PRTY_REGEN_DP_ENC1_P0_MASK,
+			   AU_PRTY_REGEN_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
+			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK,
+			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
+			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK,
+			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK);
+
+	switch (cfg->channels) {
+	case 2:
+		channel_enable_bits = AUDIO_2CH_SEL_DP_ENC0_P0_MASK |
+				      AUDIO_2CH_EN_DP_ENC0_P0_MASK;
+		break;
+	case 8:
+	default:
+		channel_enable_bits = AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
+				      AUDIO_8CH_EN_DP_ENC0_P0_MASK;
+		break;
+	}
+	mtk_dp_update_bits(
+		mtk_dp, MTK_DP_ENC0_P0_3088,
+		channel_enable_bits | AU_EN_DP_ENC0_P0_MASK,
+		AUDIO_2CH_SEL_DP_ENC0_P0_MASK | AUDIO_2CH_EN_DP_ENC0_P0_MASK |
+			AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
+			AUDIO_8CH_EN_DP_ENC0_P0_MASK | AU_EN_DP_ENC0_P0_MASK);
+
+	//audio channel count change reset
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, 0, BIT(9));
+
+	//enable audio reset
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(0), BIT(0));
+}
+
+static void mtk_dp_audio_channel_status_set(struct mtk_dp *mtk_dp,
+					    struct mtk_dp_audio_cfg *cfg)
+{
+	union mtk_dp_audio_channel_status channel_status;
+
+	memset(&channel_status, 0, sizeof(channel_status));
+
+	switch (cfg->sample_rate) {
+	case 32000:
+		channel_status.iec.sampling_freq = 3;
+		break;
+	case 44100:
+		channel_status.iec.sampling_freq = 0;
+		break;
+	case 48000:
+		channel_status.iec.sampling_freq = 2;
+		break;
+	case 88200:
+		channel_status.iec.sampling_freq = 8;
+		break;
+	case 96000:
+		channel_status.iec.sampling_freq = 0xA;
+		break;
+	case 192000:
+		channel_status.iec.sampling_freq = 0xE;
+		break;
+	default:
+		channel_status.iec.sampling_freq = 0x1;
+		break;
+	}
+
+	switch (cfg->word_length_bits) {
+	case 16:
+		channel_status.iec.word_len = 0x02;
+		break;
+	case 20:
+		channel_status.iec.word_len = 0x03;
+		break;
+	case 24:
+		channel_status.iec.word_len = 0x0B;
+		break;
+	}
+
+	// IEC 60958 consumer channel status bits
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_308C,
+			   channel_status.buf[1] << 8 | channel_status.buf[0],
+			   CH_STATUS_0_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3090,
+			   channel_status.buf[3] << 8 | channel_status.buf[2],
+			   CH_STATUS_1_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3094, channel_status.buf[4],
+			   CH_STATUS_2_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_audio_sdp_asp_set_channels(struct mtk_dp *mtk_dp,
+					      int channels)
+{
+	if (channels != 2 && channels != 8)
+		channels = 8;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_312C,
+			   (channels - 1) << ASP_HB3_DP_ENC0_P0_SHIFT,
+			   ASP_HB2_DP_ENC0_P0_MASK | ASP_HB3_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_audio_set_divider(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
+			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2,
+			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK);
+}
+
+static bool mtk_dp_plug_state(struct mtk_dp *mtk_dp)
+{
+	return !!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
+		  HPD_DB_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_sdp_trigger_packet(struct mtk_dp *mtk_dp,
+				      enum mtk_dp_sdp_type type)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, type,
+			   SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, SDP_PACKET_W_DP_ENC1_P0,
+			   SDP_PACKET_W_DP_ENC1_P0);
+}
+
+static void mtk_dp_sdp_set_data(struct mtk_dp *mtk_dp, u8 *data_bytes)
+{
+	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_ENC1_P0_3200, data_bytes, 0x10);
+}
+
+static void mtk_dp_sdp_set_header(struct mtk_dp *mtk_dp,
+				  enum mtk_dp_sdp_type type,
+				  struct dp_sdp_header *header)
+{
+	u32 db_addr;
+
+	switch (type) {
+	case MTK_DP_SDP_DRM:
+		db_addr = MTK_DP_ENC0_P0_3138;
+		break;
+	case MTK_DP_SDP_PPS0:
+	case MTK_DP_SDP_PPS1:
+	case MTK_DP_SDP_PPS2:
+	case MTK_DP_SDP_PPS3:
+		db_addr = MTK_DP_ENC0_P0_3130;
+		break;
+	default:
+		db_addr = MTK_DP_ENC0_P0_30D8 + (type - MTK_DP_SDP_ACM) * 8;
+	}
+
+	mtk_dp_bulk_16bit_write(mtk_dp, db_addr, (u8 *)header, 4);
+}
+
+static const u32 mtk_dp_sdp_type_to_reg[MTK_DP_SDP_MAX_NUM] = {
+	/* MTK_DP_SDP_NONE => */ 0x0,
+	/* MTK_DP_SDP_ACM  => */ MTK_DP_ENC0_P0_30B4,
+	/* MTK_DP_SDP_ISRC => */ MTK_DP_ENC0_P0_30B4 + 1,
+	/* MTK_DP_SDP_AVI  => */ MTK_DP_ENC0_P0_30A4 + 1,
+	/* MTK_DP_SDP_AUI  => */ MTK_DP_ENC0_P0_30A8,
+	/* MTK_DP_SDP_SPD  => */ MTK_DP_ENC0_P0_30A8 + 1,
+	/* MTK_DP_SDP_MPEG => */ MTK_DP_ENC0_P0_30AC,
+	/* MTK_DP_SDP_NTSC => */ MTK_DP_ENC0_P0_30AC + 1,
+	/* MTK_DP_SDP_VSP  => */ MTK_DP_ENC0_P0_30B0,
+	/* MTK_DP_SDP_VSC  => */ MTK_DP_ENC0_P0_30B8,
+	/* MTK_DP_SDP_EXT  => */ MTK_DP_ENC0_P0_30B0 + 1,
+	/* MTK_DP_SDP_PPS0 => */ MTK_DP_ENC0_P0_31E8,
+	/* MTK_DP_SDP_PPS1 => */ MTK_DP_ENC0_P0_31E8,
+	/* MTK_DP_SDP_PPS2 => */ MTK_DP_ENC0_P0_31E8,
+	/* MTK_DP_SDP_PPS3 => */ MTK_DP_ENC0_P0_31E8,
+	/* MTK_DP_SDP_DRM  => */ MTK_DP_ENC0_P0_31DC,
+};
+
+static void mtk_dp_disable_sdp(struct mtk_dp *mtk_dp, enum mtk_dp_sdp_type type)
+{
+	if (type == MTK_DP_SDP_NONE)
+		return;
+
+	// Disable periodic send
+	mtk_dp_update_bits(mtk_dp, mtk_dp_sdp_type_to_reg[type] & 0xfffc, 0,
+			   0xFF << ((mtk_dp_sdp_type_to_reg[type] & 3) * 8));
+}
+
+static void mtk_dp_setup_sdp(struct mtk_dp *mtk_dp,
+			     struct mtk_dp_sdp_packet *packet)
+{
+	mtk_dp_sdp_set_data(mtk_dp, packet->sdp.db);
+	mtk_dp_sdp_set_header(mtk_dp, packet->type, &packet->sdp.sdp_header);
+
+	mtk_dp_disable_sdp(mtk_dp, packet->type);
+
+	switch (packet->type) {
+	case MTK_DP_SDP_NONE:
+		break;
+	case MTK_DP_SDP_ISRC:
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
+				   0x1C << ISRC1_HB3_DP_ENC0_P0_SHIFT,
+				   ISRC1_HB3_DP_ENC0_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, MTK_DP_SDP_ISRC,
+				   SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
+
+		if (packet->sdp.sdp_header.HB3 & BIT(2))
+			mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
+					   BIT(ISRC_CONT_DP_ENC0_P0_SHIFT),
+					   ISRC_CONT_DP_ENC0_P0_MASK);
+		else
+			mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC, 0,
+					   ISRC_CONT_DP_ENC0_P0_MASK);
+
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280,
+				   SDP_PACKET_W_DP_ENC1_P0,
+				   SDP_PACKET_W_DP_ENC1_P0);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30B4,
+				   5 << ISRC_CFG_DP_ENC0_P0_SHIFT,
+				   ISRC_CFG_DP_ENC0_P0_MASK);
+		break;
+	case MTK_DP_SDP_DRM:
+		mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31DC,
+				   5 << HDR0_CFG_DP_ENC0_P0_SHIFT,
+				   HDR0_CFG_DP_ENC0_P0_MASK);
+		break;
+	case MTK_DP_SDP_ACM:
+	case MTK_DP_SDP_AVI:
+	case MTK_DP_SDP_AUI:
+	case MTK_DP_SDP_SPD:
+	case MTK_DP_SDP_MPEG:
+	case MTK_DP_SDP_NTSC:
+	case MTK_DP_SDP_VSP:
+	case MTK_DP_SDP_VSC:
+	case MTK_DP_SDP_EXT:
+	case MTK_DP_SDP_PPS0:
+	case MTK_DP_SDP_PPS1:
+	case MTK_DP_SDP_PPS2:
+	case MTK_DP_SDP_PPS3:
+		mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
+		// Enable periodic sending
+		mtk_dp_update_bits(
+			mtk_dp, mtk_dp_sdp_type_to_reg[packet->type] & 0xfffc,
+			0x05 << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
+				 8),
+			0xff << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
+				 8));
+		break;
+	default:
+		break;
+	}
+}
+
+static void mtk_dp_sdp_vsc_ext_disable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A0, 0,
+			   BIT(7) | BIT(8) | BIT(12));
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_328C, 0, BIT(7));
+}
+
+static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_write(
+		mtk_dp, MTK_DP_AUX_P0_3640,
+		BIT(AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT));
+}
+
+static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32 addr)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644, cmd,
+			   MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648, addr,
+			   MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C, addr >> 16,
+			   MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_aux_cmd_complete(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
+			   BIT(MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT),
+			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK |
+				   PHY_FIFO_RST_AUX_TX_P0_MASK |
+				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
+			   BIT(AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT),
+			   AUX_TX_REQUEST_READY_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8 *buf,
+				       size_t length)
+{
+	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf, length);
+}
+
+static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
+				    size_t length, int read_delay)
+{
+	int read_pos;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620, 0,
+			   AUX_RD_MODE_AUX_TX_P0_MASK);
+
+	for (read_pos = 0; read_pos < length; read_pos++) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
+				   BIT(AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT),
+				   AUX_RX_FIFO_READ_PULSE_TX_P0_MASK);
+		usleep_range(read_delay, read_delay * 2);
+		buf[read_pos] =
+			(u8)(mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3620) &
+			     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK >>
+				     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT);
+	}
+}
+
+static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t length)
+{
+	if (length > 0) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
+				   (length - 1)
+					   << MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT,
+				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
+				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
+					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	} else {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
+				   BIT(AUX_NO_LENGTH_AUX_TX_P0_SHIFT),
+				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
+					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	}
+}
+
+static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp, bool is_read)
+{
+	int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
+
+	while (--wait_reply) {
+		u32 aux_irq_status;
+
+		if (is_read) {
+			u32 fifo_status =
+				mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3618);
+
+			if (fifo_status &
+			    (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
+			     AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
+				return 0;
+			}
+		}
+
+		aux_irq_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3640);
+		if (aux_irq_status & AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK)
+			return 0;
+
+		if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK)
+			return -ETIMEDOUT;
+
+		usleep_range(100, 500);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool is_read, u8 cmd,
+				  u32 addr, u8 *buf, size_t length)
+{
+	int ret;
+	u32 reply_cmd;
+
+	if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
+			(cmd == DP_AUX_NATIVE_READ && !length)))
+		return -EINVAL;
+
+	if (!is_read)
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
+				   BIT(AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT),
+				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK);
+
+	mtk_dp_aux_cmd_complete(mtk_dp);
+	mtk_dp_aux_irq_clear(mtk_dp);
+	usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
+		     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
+
+	mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
+	mtk_dp_aux_set_length(mtk_dp, length);
+
+	if (!is_read) {
+		if (length)
+			mtk_dp_aux_fill_write_fifo(mtk_dp, buf, length);
+
+		mtk_dp_update_bits(
+			mtk_dp, MTK_DP_AUX_P0_3704,
+			AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK,
+			AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK);
+	}
+
+	mtk_dp_aux_request_ready(mtk_dp);
+
+	ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
+
+	reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
+		    AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
+
+	if (ret || reply_cmd) {
+		u32 phy_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3628) &
+				 AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
+		if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
+			drm_err(mtk_dp->drm_dev,
+				"AUX Rx Aux hang, need SW reset\n");
+			ret = -EIO;
+		}
+
+		mtk_dp_aux_cmd_complete(mtk_dp);
+		mtk_dp_aux_irq_clear(mtk_dp);
+
+		usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
+			     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
+		return -ETIMEDOUT;
+	}
+
+	if (!length) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
+				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
+					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	} else if (is_read) {
+		int read_delay;
+
+		if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
+		    cmd == DP_AUX_I2C_READ)
+			read_delay = 500;
+		else
+			read_delay = 100;
+
+		mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length, read_delay);
+	}
+
+	mtk_dp_aux_cmd_complete(mtk_dp);
+	mtk_dp_aux_irq_clear(mtk_dp);
+	usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
+		     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
+
+	return 0;
+}
+
+static bool mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int lane_num,
+					  int swing_val, int preemphasis)
+{
+	u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
+
+	if (lane_num < 0 || lane_num > 3)
+		return false;
+
+	dev_dbg(mtk_dp->dev,
+		"link training swing_val= 0x%x, preemphasis = 0x%x\n",
+		swing_val, preemphasis);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+			   swing_val << (DP_TX0_VOLT_SWING_SHIFT + lane_shift),
+			   DP_TX0_VOLT_SWING_MASK << lane_shift);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+			   preemphasis << (DP_TX0_PRE_EMPH_SHIFT + lane_shift),
+			   DP_TX0_PRE_EMPH_MASK << lane_shift);
+
+	return true;
+}
+
+static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP, 0,
+			   DP_TX0_VOLT_SWING_MASK | DP_TX1_VOLT_SWING_MASK |
+				   DP_TX2_VOLT_SWING_MASK |
+				   DP_TX3_VOLT_SWING_MASK |
+				   DP_TX0_PRE_EMPH_MASK | DP_TX1_PRE_EMPH_MASK |
+				   DP_TX2_PRE_EMPH_MASK | DP_TX3_PRE_EMPH_MASK);
+}
+
+static void mtk_dp_fec_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
+			   enable ? BIT(FEC_EN_DP_TRANS_P0_SHIFT) : 0,
+			   FEC_EN_DP_TRANS_P0_MASK);
+}
+
+static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
+{
+	u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
+			 SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
+
+	if (irq_status) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, irq_status,
+				   SW_IRQ_CLR_DP_TRANS_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, 0,
+				   SW_IRQ_CLR_DP_TRANS_P0_MASK);
+	}
+
+	return irq_status;
+}
+
+static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
+{
+	u8 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
+			 IRQ_STATUS_DP_TRANS_P0_MASK) >>
+			IRQ_STATUS_DP_TRANS_P0_SHIFT;
+
+	if (irq_status) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, irq_status,
+				   IRQ_CLR_DP_TRANS_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, 0,
+				   IRQ_CLR_DP_TRANS_P0_MASK);
+	}
+
+	return irq_status;
+}
+
+static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	u32 val = 0;
+
+	if (!enable)
+		val = IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
+		      IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
+		      IRQ_MASK_DP_TRANS_P0_INT_IRQ;
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, val,
+			   IRQ_MASK_DP_TRANS_P0_MASK);
+}
+
+void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
+			   XTAL_FREQ_DP_TRANS_P0_DEFAULT,
+			   XTAL_FREQ_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
+			   BIT(FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT),
+			   FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
+			   BIT(AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT),
+			   AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
+			   SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK, IRQ_MASK_AUX_TOP_IRQ,
+			   IRQ_MASK_AUX_TOP_IRQ);
+}
+
+static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp *mtk_dp)
+{
+	// Debounce threshold
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   8 << HPD_DEB_THD_DP_TRANS_P0_SHIFT,
+			   HPD_DEB_THD_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
+			    HPD_INT_THD_DP_TRANS_P0_UPPER_1100US)
+				   << HPD_INT_THD_DP_TRANS_P0_SHIFT,
+			   HPD_INT_THD_DP_TRANS_P0_MASK);
+
+	// Connect threshold 1.5ms + 5 x 0.1ms = 2ms
+	// Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   (5 << HPD_DISC_THD_DP_TRANS_P0_SHIFT) |
+				   (5 << HPD_CONN_THD_DP_TRANS_P0_SHIFT),
+			   HPD_DISC_THD_DP_TRANS_P0_MASK |
+				   HPD_CONN_THD_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
+			   HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
+			   HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
+{
+	// modify timeout threshold = 1595 [12 : 8]
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C, 0x1595,
+			   AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658, 0,
+			   AUX_TX_OV_EN_AUX_TX_P0_MASK);
+	// 25 for 26M
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
+			   25 << AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT,
+			   AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
+	// 13 for 26M
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
+			   13 << AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT,
+			   AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
+			   BIT(MTK_ATOP_EN_AUX_TX_P0_SHIFT),
+			   MTK_ATOP_EN_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_initialize_digital_settings(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
+			   VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
+	mtk_dp_set_color_format(mtk_dp, MTK_DP_COLOR_FORMAT_RGB_444);
+	mtk_dp_set_color_depth(mtk_dp, MTK_DP_COLOR_DEPTH_8BIT);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
+			   1 << BS2BS_MODE_DP_ENC1_P0_SHIFT,
+			   BS2BS_MODE_DP_ENC1_P0_MASK);
+
+	// dp tx encoder reset all sw
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
+			   BIT(DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT),
+			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
+	usleep_range(1000, 5000);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
+			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
+			   BIT(DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT),
+			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
+	usleep_range(1000, 5000);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C, 0,
+			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
+			   lanes == 0 ? 0 : BIT(3), BIT(3) | BIT(2));
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, lanes,
+			   LANE_NUM_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
+			   lanes << LANE_NUM_DP_TRANS_P0_SHIFT,
+			   LANE_NUM_DP_TRANS_P0_MASK);
+}
+
+static int link_rate_to_mb_per_s(struct mtk_dp *mtk_dp,
+				 enum mtk_dp_linkrate linkrate)
+{
+	switch (linkrate) {
+	default:
+		drm_err(mtk_dp->drm_dev,
+			"Implementation error, unknown linkrate %d\n",
+			linkrate);
+		fallthrough;
+	case MTK_DP_LINKRATE_RBR:
+		return 1620;
+	case MTK_DP_LINKRATE_HBR:
+		return 2700;
+	case MTK_DP_LINKRATE_HBR2:
+		return 5400;
+	case MTK_DP_LINKRATE_HBR3:
+		return 8100;
+	}
+}
+
+static u32 check_cal_data_valid(u32 min, u32 max, u32 val, u32 default_val)
+{
+	if (val < min || val > max)
+		return default_val;
+
+	return val;
+}
+
+static int mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
+{
+	struct dp_cal_data *cal_data = &mtk_dp->cal_data;
+	struct device *dev = mtk_dp->dev;
+	struct nvmem_cell *cell;
+	u32 *buf;
+	size_t len;
+
+	cell = nvmem_cell_get(dev, "dp_calibration_data");
+	if (IS_ERR(cell)) {
+		dev_err(dev,
+			"Error: Failed to get nvmem cell dp_calibration_data\n");
+		return PTR_ERR(cell);
+	}
+
+	buf = (u32 *)nvmem_cell_read(cell, &len);
+	nvmem_cell_put(cell);
+
+	if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
+		dev_err(dev,
+			"Error: Failed to read nvmem_cell_read fail len %ld\n",
+			(len / sizeof(u32)));
+		return PTR_ERR(buf);
+	}
+
+	if (mtk_dp_is_edp(mtk_dp)) {
+		cal_data->glb_bias_trim = check_cal_data_valid(
+			1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf);
+		cal_data->clktx_impse =
+			check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8);
+		cal_data->ln0_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8);
+		cal_data->ln0_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8);
+		cal_data->ln1_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8);
+		cal_data->ln1_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8);
+		cal_data->ln2_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8);
+		cal_data->ln2_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8);
+		cal_data->ln3_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8);
+		cal_data->ln3_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8);
+	} else {
+		cal_data->glb_bias_trim = check_cal_data_valid(
+			1, 0x1e, (buf[0] >> 27) & 0x1f, 0xf);
+		cal_data->clktx_impse =
+			check_cal_data_valid(1, 0xe, (buf[0] >> 13) & 0xf, 0x8);
+		cal_data->ln0_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 28) & 0xf, 0x8);
+		cal_data->ln0_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 24) & 0xf, 0x8);
+		cal_data->ln1_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 20) & 0xf, 0x8);
+		cal_data->ln1_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 16) & 0xf, 0x8);
+		cal_data->ln2_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 12) & 0xf, 0x8);
+		cal_data->ln2_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 8) & 0xf, 0x8);
+		cal_data->ln3_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 4) & 0xf, 0x8);
+		cal_data->ln3_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, buf[1] & 0xf, 0x8);
+	}
+
+	kfree(buf);
+
+	return 0;
+}
+
+void mtk_dp_set_cal_data(struct mtk_dp *mtk_dp)
+{
+	struct dp_cal_data *cal_data = &mtk_dp->cal_data;
+
+	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
+			   cal_data->clktx_impse << 20, RG_CKM_PT0_CKTX_IMPSEL);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
+			   cal_data->glb_bias_trim << 16,
+			   RG_XTP_GLB_BIAS_INTR_CTRL);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
+			   cal_data->ln0_tx_impsel_pmos << 12,
+			   RG_XTP_LN0_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
+			   cal_data->ln0_tx_impsel_nmos << 16,
+			   RG_XTP_LN0_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
+			   cal_data->ln1_tx_impsel_pmos << 12,
+			   RG_XTP_LN1_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
+			   cal_data->ln1_tx_impsel_nmos << 16,
+			   RG_XTP_LN1_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
+			   cal_data->ln2_tx_impsel_pmos << 12,
+			   RG_XTP_LN2_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
+			   cal_data->ln2_tx_impsel_nmos << 16,
+			   RG_XTP_LN2_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
+			   cal_data->ln3_tx_impsel_pmos << 12,
+			   RG_XTP_LN3_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
+			   cal_data->ln3_tx_impsel_nmos << 16,
+			   RG_XTP_LN3_TX_IMPSEL_NMOS);
+}
+
+static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
+				enum mtk_dp_linkrate link_rate, int lane_count)
+{
+	int ret;
+	union phy_configure_opts
+		phy_opts = { .dp = {
+				     .link_rate = link_rate_to_mb_per_s(
+					     mtk_dp, link_rate),
+				     .set_rate = 1,
+				     .lanes = lane_count,
+				     .set_lanes = 1,
+				     .ssc = mtk_dp->train_info.sink_ssc,
+			     } };
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP,
+			   DP_PWR_STATE_MASK);
+
+	ret = phy_configure(mtk_dp->phy, &phy_opts);
+
+	mtk_dp_set_cal_data(mtk_dp);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL_LANE, DP_PWR_STATE_MASK);
+	return ret;
+}
+
+static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool enable)
+{
+	const u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
+			POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
+			POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
+			POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580, enable ? val : 0, val);
+}
+
+static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int pattern)
+{
+	if (pattern < 0 || pattern > 4) {
+		drm_err(mtk_dp->drm_dev,
+			"Implementation error, no such pattern %d\n", pattern);
+		return;
+	}
+
+	if (pattern == 1) // TPS1
+		mtk_dp_set_idle_pattern(mtk_dp, false);
+
+	mtk_dp_update_bits(
+		mtk_dp, MTK_DP_TRANS_P0_3400,
+		pattern ? BIT(pattern - 1) << PATTERN1_EN_DP_TRANS_P0_SHIFT : 0,
+		PATTERN1_EN_DP_TRANS_P0_MASK | PATTERN2_EN_DP_TRANS_P0_MASK |
+			PATTERN3_EN_DP_TRANS_P0_MASK |
+			PATTERN4_EN_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
+			   enable ? BIT(ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT) : 0,
+			   ENHANCED_FRAME_EN_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
+			   enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
+			   DP_SCR_EN_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
+{
+	u32 val = BIT(VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT);
+
+	if (enable)
+		val |= BIT(VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, val,
+			   VIDEO_MUTE_SEL_DP_ENC0_P0_MASK |
+				   VIDEO_MUTE_SW_DP_ENC0_P0_MASK);
+
+	if (mtk_dp_is_edp(mtk_dp))
+		mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable);
+	else
+		mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable);
+}
+
+static void mtk_dp_audio_mute(struct mtk_dp *mtk_dp, bool mute)
+{
+	if (mute) {
+		mtk_dp_update_bits(
+			mtk_dp, MTK_DP_ENC0_P0_3030,
+			BIT(VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT) |
+				BIT(VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT),
+			VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
+				VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
+
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088, 0,
+				   AU_EN_DP_ENC0_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0,
+				   AU_TS_CFG_DP_ENC0_P0_MASK);
+
+	} else {
+		mtk_dp_update_bits(
+			mtk_dp, MTK_DP_ENC0_P0_3030, 0,
+			VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
+				VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
+
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088,
+				   BIT(AU_EN_DP_ENC0_P0_SHIFT),
+				   AU_EN_DP_ENC0_P0_MASK);
+		// Send one every two frames
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0x0F,
+				   AU_TS_CFG_DP_ENC0_P0_MASK);
+	}
+}
+
+static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, 0,
+			   SW_RST_B_PHYD);
+	usleep_range(10, 200);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, SW_RST_B_PHYD,
+			   SW_RST_B_PHYD);
+}
+
+static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
+	usleep_range(10, 200);
+	mtk_dp_write(mtk_dp, MTK_DP_0034,
+		     DA_CKM_CKTX0_EN_FORCE_EN | DA_CKM_BIAS_LPF_EN_FORCE_VAL |
+			     DA_CKM_BIAS_EN_FORCE_VAL |
+			     DA_XTP_GLB_LDO_EN_FORCE_VAL |
+			     DA_XTP_GLB_AVD10_ON_FORCE_VAL);
+	// Disable RX
+	mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
+	mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
+		     0x550 | BIT(FUSE_SEL_SHIFT) | BIT(MEM_ISO_EN_SHIFT));
+}
+
+static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
+{
+	mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR2;
+	mtk_dp->train_info.lane_count = MTK_DP_MAX_LANES;
+	mtk_dp->train_info.irq_status = 0;
+	mtk_dp->train_info.cable_plugged_in = false;
+	mtk_dp->train_info.cable_state_change = false;
+	mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
+	mtk_dp->state = MTK_DP_STATE_INITIAL;
+
+	mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
+	mtk_dp->info.depth = MTK_DP_COLOR_DEPTH_8BIT;
+	memset(&mtk_dp->info.timings, 0, sizeof(struct mtk_dp_timings));
+	mtk_dp->info.timings.frame_rate = 60;
+
+	mtk_dp->has_fec = false;
+	mtk_dp->audio_enable = false;
+}
+
+static void mtk_dp_sdp_set_down_cnt_init(struct mtk_dp *mtk_dp,
+					 u32 sram_read_start)
+{
+	u32 sdp_down_cnt_init = 0;
+	u32 dc_offset;
+
+	if (mtk_dp->info.timings.pix_rate_khz > 0)
+		sdp_down_cnt_init = sram_read_start *
+				    mtk_dp->train_info.link_rate * 2700 * 8 /
+				    (mtk_dp->info.timings.pix_rate_khz * 4);
+
+	switch (mtk_dp->train_info.lane_count) {
+	case 1:
+		sdp_down_cnt_init = sdp_down_cnt_init > 0x1A ?
+						  sdp_down_cnt_init :
+						  0x1A; //26
+		break;
+	case 2:
+		// case for LowResolution && High Audio Sample Rate
+		dc_offset = mtk_dp->info.timings.vtotal <= 525 ? 0x04 : 0x00;
+		sdp_down_cnt_init = sdp_down_cnt_init > 0x10 ?
+						  sdp_down_cnt_init :
+						  0x10 + dc_offset; //20 or 16
+		break;
+	case 4:
+	default:
+		sdp_down_cnt_init =
+			sdp_down_cnt_init > 0x06 ? sdp_down_cnt_init : 0x06; //6
+		break;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
+			   sdp_down_cnt_init
+				   << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
+			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_sdp_set_down_cnt_init_in_hblank(struct mtk_dp *mtk_dp)
+{
+	int pix_clk_mhz;
+	u32 dc_offset;
+	u32 spd_down_cnt_init = 0;
+
+	pix_clk_mhz = mtk_dp->info.format == MTK_DP_COLOR_FORMAT_YUV_420 ?
+				    mtk_dp->info.timings.pix_rate_khz / 2000 :
+				    mtk_dp->info.timings.pix_rate_khz / 1000;
+
+	switch (mtk_dp->train_info.lane_count) {
+	case 1:
+		spd_down_cnt_init = 0x20;
+		break;
+	case 2:
+		dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x14 : 0x00;
+		spd_down_cnt_init = 0x18 + dc_offset;
+		break;
+	case 4:
+	default:
+		dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x08 : 0x00;
+		if (pix_clk_mhz > mtk_dp->train_info.link_rate * 27)
+			spd_down_cnt_init = 0x8;
+		else
+			spd_down_cnt_init = 0x10 + dc_offset;
+		break;
+	}
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364, spd_down_cnt_init,
+			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
+}
+
+static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
+{
+	u32 sram_read_start = MTK_DP_TBC_BUF_READ_START_ADDR;
+
+	if (mtk_dp->train_info.lane_count > 0) {
+		sram_read_start = min_t(
+			u32, MTK_DP_TBC_BUF_READ_START_ADDR,
+			mtk_dp->info.timings.vm.hactive /
+				(mtk_dp->train_info.lane_count * 4 * 2 * 2));
+		mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
+	}
+
+	mtk_dp_setup_encoder(mtk_dp);
+	mtk_dp_sdp_set_down_cnt_init_in_hblank(mtk_dp);
+	mtk_dp_sdp_set_down_cnt_init(mtk_dp, sram_read_start);
+}
+
+static void mtk_dp_calculate_pixrate(struct mtk_dp *mtk_dp)
+{
+	int target_frame_rate = 60;
+	int target_pixel_clk;
+
+	if (mtk_dp->info.timings.frame_rate > 0) {
+		target_frame_rate = mtk_dp->info.timings.frame_rate;
+		target_pixel_clk = (int)mtk_dp->info.timings.htotal *
+				   (int)mtk_dp->info.timings.vtotal *
+				   target_frame_rate;
+	} else if (mtk_dp->info.timings.pix_rate_khz > 0) {
+		target_pixel_clk = mtk_dp->info.timings.pix_rate_khz * 1000;
+	} else {
+		target_pixel_clk = (int)mtk_dp->info.timings.htotal *
+				   (int)mtk_dp->info.timings.vtotal *
+				   target_frame_rate;
+	}
+
+	if (target_pixel_clk > 0)
+		mtk_dp->info.timings.pix_rate_khz = target_pixel_clk / 1000;
+}
+
+static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_msa_bypass_disable(mtk_dp);
+	mtk_dp_calculate_pixrate(mtk_dp);
+	mtk_dp_pg_disable(mtk_dp);
+	mtk_dp_setup_tu(mtk_dp);
+}
+
+static void mtk_dp_edid_free(struct mtk_dp *mtk_dp)
+{
+	mutex_lock(&mtk_dp->edid_lock);
+	kfree(mtk_dp->edid);
+	mtk_dp->edid = NULL;
+	mutex_unlock(&mtk_dp->edid_lock);
+}
+
+static void mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp)
+{
+	ssize_t ret;
+	u8 sink_count;
+	u8 link_status[DP_LINK_STATUS_SIZE] = {};
+	u32 sink_count_reg;
+	u32 link_status_reg;
+	bool locked;
+
+	sink_count_reg = DP_SINK_COUNT_ESI;
+	link_status_reg = DP_LANE0_1_STATUS;
+
+	ret = drm_dp_dpcd_readb(&mtk_dp->aux, sink_count_reg, &sink_count);
+	if (ret < 0) {
+		drm_info(mtk_dp->drm_dev, "Read sink count failed: %ld\n", ret);
+		return;
+	}
+
+	ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
+	if (ret < 0) {
+		drm_info(mtk_dp->drm_dev,
+			 "Read DP_SINK_COUNT_ESI failed: %ld\n", ret);
+		return;
+	}
+
+	ret = drm_dp_dpcd_read(&mtk_dp->aux, link_status_reg, link_status,
+			       sizeof(link_status));
+	if (!ret) {
+		drm_info(mtk_dp->drm_dev, "Read link status failed: %ld\n",
+			 ret);
+		return;
+	}
+
+	locked = drm_dp_channel_eq_ok(link_status,
+				      mtk_dp->train_info.lane_count);
+	if (!locked && mtk_dp->train_state > MTK_DP_TRAIN_STATE_TRAINING_PRE)
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
+
+	if (link_status[1] & DP_REMOTE_CONTROL_COMMAND_PENDING)
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR,
+				   DP_REMOTE_CONTROL_COMMAND_PENDING);
+
+	if (DP_GET_SINK_COUNT(sink_count) &&
+	    (link_status[2] & DP_DOWNSTREAM_PORT_STATUS_CHANGED)) {
+		mtk_dp_edid_free(mtk_dp);
+		mtk_dp->train_info.check_cap_count = 0;
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
+		msleep(20);
+	}
+}
+
+static void mtk_dp_sdp_stop_sending(struct mtk_dp *mtk_dp)
+{
+	u8 packet_type;
+
+	for (packet_type = MTK_DP_SDP_ACM; packet_type < MTK_DP_SDP_MAX_NUM;
+	     packet_type++)
+		mtk_dp_disable_sdp(mtk_dp, packet_type);
+
+	mtk_dp_sdp_vsc_ext_disable(mtk_dp);
+}
+
+static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes,
+					  u8 dpcd_adjust_req[2])
+{
+	int lane;
+
+	for (lane = 0; lane < lanes; ++lane) {
+		u8 val;
+		u8 swing;
+		u8 preemphasis;
+		int index = lane / 2;
+		int shift = lane % 2 ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
+
+		swing = (dpcd_adjust_req[index] >> shift) &
+			DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
+		preemphasis = ((dpcd_adjust_req[index] >> shift) &
+			       DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
+			      DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
+		val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
+		      preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+		if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
+			val |= DP_TRAIN_MAX_SWING_REACHED;
+		if (preemphasis == 3)
+			val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+		mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing, preemphasis);
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET + lane,
+				   val);
+	}
+
+	// Wait for the signal to be stable enough
+	usleep_range(2000, 5000);
+}
+
+static void mtk_dp_read_link_status(struct mtk_dp *mtk_dp,
+				    u8 link_status[DP_LINK_STATUS_SIZE])
+{
+	drm_dp_dpcd_read(&mtk_dp->aux, DP_LANE0_1_STATUS, link_status,
+			 DP_LINK_STATUS_SIZE);
+}
+
+static int mtk_dp_train_flow(struct mtk_dp *mtk_dp, int target_link_rate,
+			     int target_lane_count)
+{
+	u8 link_status[DP_LINK_STATUS_SIZE] = {};
+	u8 lane_adjust[2] = {};
+	bool pass_tps1 = false;
+	bool pass_tps2_3 = false;
+	int train_retries;
+	int status_control;
+	int iteration_count;
+	u8 prev_lane_adjust;
+	u8 val;
+
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET, target_link_rate);
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
+			   target_lane_count | DP_LANE_COUNT_ENHANCED_FRAME_EN);
+
+	if (mtk_dp->train_info.sink_ssc)
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
+				   DP_SPREAD_AMP_0_5);
+
+	train_retries = 0;
+	status_control = 0;
+	iteration_count = 1;
+	prev_lane_adjust = 0xFF;
+
+	mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
+	mtk_dp_phy_configure(mtk_dp, target_link_rate, target_lane_count);
+
+	dev_dbg(mtk_dp->dev,
+		"Link train target_link_rate = 0x%x, target_lane_count = 0x%x\n",
+		target_link_rate, target_lane_count);
+
+	do {
+		train_retries++;
+		if (!mtk_dp->train_info.cable_plugged_in ||
+		    ((mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) !=
+		     0x0)) {
+			return -ENODEV;
+		}
+
+		if (mtk_dp->train_state < MTK_DP_TRAIN_STATE_TRAINING)
+			return -EAGAIN;
+
+		if (!pass_tps1) {
+			mtk_dp_training_set_scramble(mtk_dp, false);
+
+			if (status_control == 0) {
+				status_control = 1;
+				mtk_dp_train_set_pattern(mtk_dp, 1);
+				val = DP_LINK_SCRAMBLING_DISABLE |
+				      DP_TRAINING_PATTERN_1;
+				drm_dp_dpcd_writeb(
+					&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+					DP_LINK_SCRAMBLING_DISABLE |
+						DP_TRAINING_PATTERN_1);
+				drm_dp_dpcd_read(&mtk_dp->aux,
+						 DP_ADJUST_REQUEST_LANE0_1,
+						 lane_adjust,
+						 sizeof(lane_adjust));
+				iteration_count++;
+
+				mtk_dp_train_update_swing_pre(
+					mtk_dp, target_lane_count, lane_adjust);
+			}
+
+			drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
+							       mtk_dp->rx_cap);
+			mtk_dp_read_link_status(mtk_dp, link_status);
+
+			if (drm_dp_clock_recovery_ok(link_status,
+						     target_lane_count)) {
+				mtk_dp->train_info.cr_done = true;
+				pass_tps1 = true;
+				train_retries = 0;
+				iteration_count = 1;
+				dev_dbg(mtk_dp->dev, "Link train CR pass\n");
+			} else if (prev_lane_adjust == link_status[4]) {
+				iteration_count++;
+				if (prev_lane_adjust &
+				    DP_ADJUST_VOLTAGE_SWING_LANE0_MASK)
+					break;
+			} else {
+				prev_lane_adjust = link_status[4];
+			}
+			dev_dbg(mtk_dp->dev, "Link train CQ fail\n");
+		} else if (pass_tps1 && !pass_tps2_3) {
+			if (status_control == 1) {
+				status_control = 2;
+				if (mtk_dp->train_info.tps4) {
+					mtk_dp_train_set_pattern(mtk_dp, 4);
+					val = DP_TRAINING_PATTERN_4;
+				} else if (mtk_dp->train_info.tps3) {
+					mtk_dp_train_set_pattern(mtk_dp, 3);
+					val = DP_LINK_SCRAMBLING_DISABLE |
+					      DP_TRAINING_PATTERN_3;
+				} else {
+					mtk_dp_train_set_pattern(mtk_dp, 2);
+					val = DP_LINK_SCRAMBLING_DISABLE |
+					      DP_TRAINING_PATTERN_2;
+				}
+				drm_dp_dpcd_writeb(&mtk_dp->aux,
+						   DP_TRAINING_PATTERN_SET,
+						   val);
+
+				drm_dp_dpcd_read(&mtk_dp->aux,
+						 DP_ADJUST_REQUEST_LANE0_1,
+						 lane_adjust,
+						 sizeof(lane_adjust));
+
+				iteration_count++;
+				mtk_dp_train_update_swing_pre(
+					mtk_dp, target_lane_count, lane_adjust);
+			}
+
+			drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
+							   mtk_dp->rx_cap);
+
+			mtk_dp_read_link_status(mtk_dp, link_status);
+
+			if (!drm_dp_clock_recovery_ok(link_status,
+						      target_lane_count)) {
+				mtk_dp->train_info.cr_done = false;
+				mtk_dp->train_info.eq_done = false;
+				dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
+				break;
+			}
+
+			if (drm_dp_channel_eq_ok(link_status,
+						 target_lane_count)) {
+				mtk_dp->train_info.eq_done = true;
+				pass_tps2_3 = true;
+				dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
+				break;
+			}
+
+			if (prev_lane_adjust == link_status[4])
+				iteration_count++;
+			else
+				prev_lane_adjust = link_status[4];
+		}
+
+		drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
+				 lane_adjust, sizeof(lane_adjust));
+		mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
+					      lane_adjust);
+	} while (train_retries < MTK_DP_TRAIN_RETRY_LIMIT &&
+		 iteration_count < MTK_DP_TRAIN_MAX_ITERATIONS);
+
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+			   DP_TRAINING_PATTERN_DISABLE);
+	mtk_dp_train_set_pattern(mtk_dp, 0);
+
+	if (pass_tps2_3) {
+		mtk_dp->train_info.link_rate = target_link_rate;
+		mtk_dp->train_info.lane_count = target_lane_count;
+
+		mtk_dp_training_set_scramble(mtk_dp, true);
+
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
+				   target_lane_count |
+					   DP_LANE_COUNT_ENHANCED_FRAME_EN);
+		mtk_dp_set_enhanced_frame_mode(mtk_dp, true);
+
+		return 0;
+	}
+
+	return -ETIMEDOUT;
+}
+
+static bool mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
+{
+	u8 buf[DP_RECEIVER_CAP_SIZE] = {};
+	u8 val;
+	struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
+
+	if (!mtk_dp_plug_state(mtk_dp))
+		return false;
+
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+	usleep_range(2000, 5000);
+
+	drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
+
+	memcpy(mtk_dp->rx_cap, buf, min(sizeof(mtk_dp->rx_cap), sizeof(buf)));
+	mtk_dp->rx_cap[DP_TRAINING_AUX_RD_INTERVAL] &= DP_TRAINING_AUX_RD_MASK;
+
+	train_info->link_rate =
+		min_t(int, MTK_DP_MAX_LINK_RATE, buf[DP_MAX_LINK_RATE]);
+	train_info->lane_count =
+		min_t(int, MTK_DP_MAX_LANES, drm_dp_max_lane_count(buf));
+
+	train_info->tps3 = drm_dp_tps3_supported(buf);
+	train_info->tps4 = drm_dp_tps4_supported(buf);
+
+	train_info->sink_ssc =
+		!!(buf[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5);
+
+	train_info->sink_ssc = false;
+
+	drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
+	if (val & DP_MST_CAP) {
+		// Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0
+		drm_dp_dpcd_readb(&mtk_dp->aux,
+				  DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0, &val);
+		if (val)
+			drm_dp_dpcd_writeb(&mtk_dp->aux,
+					   DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
+					   val);
+	}
+
+	return true;
+}
+
+static int mtk_dp_edid_parse_audio_capabilities(struct mtk_dp *mtk_dp,
+						struct mtk_dp_audio_cfg *cfg)
+{
+	struct cea_sad *sads;
+	int sad_count;
+	int i;
+	int ret = 0;
+
+	mutex_lock(&mtk_dp->edid_lock);
+	if (!mtk_dp->edid) {
+		mutex_unlock(&mtk_dp->edid_lock);
+		dev_err(mtk_dp->dev, "EDID not found!\n");
+		return -EINVAL;
+	}
+
+	sad_count = drm_edid_to_sad(mtk_dp->edid, &sads);
+	mutex_unlock(&mtk_dp->edid_lock);
+	if (sad_count <= 0) {
+		drm_info(mtk_dp->drm_dev, "The SADs is NULL\n");
+		return 0;
+	}
+
+	for (i = 0; i < sad_count; i++) {
+		int sample_rate;
+		int word_length;
+		// Only PCM supported at the moment
+		if (sads[i].format != HDMI_AUDIO_CODING_TYPE_PCM)
+			continue;
+
+		sample_rate = drm_cea_sad_get_sample_rate(&sads[i]);
+		word_length =
+			drm_cea_sad_get_uncompressed_word_length(&sads[i]);
+		if (sample_rate <= 0 || word_length <= 0)
+			continue;
+
+		cfg->channels = sads[i].channels;
+		cfg->word_length_bits = word_length;
+		cfg->sample_rate = sample_rate;
+		ret = 1;
+		break;
+	}
+	kfree(sads);
+
+	return ret;
+}
+
+static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
+{
+	phy_reset(mtk_dp->phy);
+	mtk_dp_reset_swing_pre_emphasis(mtk_dp);
+
+	usleep_range(2000, 5000);
+}
+
+static int mtk_dp_train_start(struct mtk_dp *mtk_dp)
+{
+	int ret = 0;
+	int lane_count;
+	int link_rate;
+	int train_limit;
+	int max_link_rate;
+	int plug_wait;
+
+	for (plug_wait = 7; !mtk_dp_plug_state(mtk_dp) && plug_wait > 0;
+	     --plug_wait)
+		usleep_range(1000, 5000);
+	if (plug_wait == 0) {
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
+		return -ENODEV;
+	}
+
+	link_rate = mtk_dp->rx_cap[1];
+	lane_count = mtk_dp->rx_cap[2] & 0x1F;
+
+	mtk_dp->train_info.link_rate = min(MTK_DP_MAX_LINK_RATE, link_rate);
+	mtk_dp->train_info.lane_count = min(MTK_DP_MAX_LANES, lane_count);
+	link_rate = mtk_dp->train_info.link_rate;
+	lane_count = mtk_dp->train_info.lane_count;
+
+	switch (link_rate) {
+	case MTK_DP_LINKRATE_RBR:
+	case MTK_DP_LINKRATE_HBR:
+	case MTK_DP_LINKRATE_HBR2:
+	case MTK_DP_LINKRATE_HBR25:
+	case MTK_DP_LINKRATE_HBR3:
+		break;
+	default:
+		mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR3;
+		break;
+	};
+
+	max_link_rate = link_rate;
+	for (train_limit = 0; train_limit <= 6; ++train_limit) {
+		mtk_dp->train_info.cr_done = false;
+		mtk_dp->train_info.eq_done = false;
+
+		mtk_dp_train_change_mode(mtk_dp);
+		ret = mtk_dp_train_flow(mtk_dp, link_rate, lane_count);
+		if (ret == -ENODEV || ret == -EAGAIN)
+			return ret;
+
+		if (!mtk_dp->train_info.cr_done) {
+			switch (link_rate) {
+			case MTK_DP_LINKRATE_RBR:
+				lane_count = lane_count / 2;
+				link_rate = max_link_rate;
+				if (lane_count == 0x0) {
+					mtk_dp->train_state =
+						MTK_DP_TRAIN_STATE_DPIDLE;
+					return -EIO;
+				}
+				break;
+			case MTK_DP_LINKRATE_HBR:
+				link_rate = MTK_DP_LINKRATE_RBR;
+				break;
+			case MTK_DP_LINKRATE_HBR2:
+				link_rate = MTK_DP_LINKRATE_HBR;
+				break;
+			case MTK_DP_LINKRATE_HBR3:
+				link_rate = MTK_DP_LINKRATE_HBR2;
+				break;
+			default:
+				return -EINVAL;
+			};
+		} else if (!mtk_dp->train_info.eq_done) {
+			lane_count /= 2;
+			if (lane_count == 0)
+				return -EIO;
+		} else {
+			return 0;
+		}
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int mtk_dp_train_handler(struct mtk_dp *mtk_dp)
+{
+	int ret = 0;
+
+	if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
+		return ret;
+
+	switch (mtk_dp->train_state) {
+	case MTK_DP_TRAIN_STATE_STARTUP:
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
+		break;
+
+	case MTK_DP_TRAIN_STATE_CHECKCAP:
+		if (mtk_dp_parse_capabilities(mtk_dp)) {
+			mtk_dp->train_info.check_cap_count = 0;
+			mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
+		} else {
+			mtk_dp->train_info.check_cap_count++;
+
+			if (mtk_dp->train_info.check_cap_count >
+			    MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT) {
+				mtk_dp->train_info.check_cap_count = 0;
+				mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
+				ret = -ETIMEDOUT;
+			}
+		}
+		break;
+
+	case MTK_DP_TRAIN_STATE_CHECKEDID: {
+		int caps_found = mtk_dp_edid_parse_audio_capabilities(
+			mtk_dp, &mtk_dp->info.audio_caps);
+		mtk_dp->audio_enable = caps_found > 0;
+		if (!mtk_dp->audio_enable)
+			memset(&mtk_dp->info.audio_caps, 0,
+			       sizeof(mtk_dp->info.audio_caps));
+	}
+
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
+		break;
+
+	case MTK_DP_TRAIN_STATE_TRAINING_PRE:
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING;
+		break;
+
+	case MTK_DP_TRAIN_STATE_TRAINING:
+		ret = mtk_dp_train_start(mtk_dp);
+		if (!ret) {
+			mtk_dp_video_mute(mtk_dp, true);
+			mtk_dp_audio_mute(mtk_dp, true);
+			mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKTIMING;
+			mtk_dp_fec_enable(mtk_dp, mtk_dp->has_fec);
+		} else if (ret != -EAGAIN) {
+			mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
+		}
+
+		ret = 0;
+		break;
+
+	case MTK_DP_TRAIN_STATE_CHECKTIMING:
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_NORMAL;
+		break;
+	case MTK_DP_TRAIN_STATE_NORMAL:
+		break;
+	case MTK_DP_TRAIN_STATE_POWERSAVE:
+		break;
+	case MTK_DP_TRAIN_STATE_DPIDLE:
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	if (enable) {
+		mtk_dp_set_tx_out(mtk_dp);
+		mtk_dp_video_mute(mtk_dp, false);
+	} else {
+		mtk_dp_video_mute(mtk_dp, true);
+	}
+}
+
+static void mtk_dp_audio_sdp_setup(struct mtk_dp *mtk_dp,
+				   struct mtk_dp_audio_cfg *cfg)
+{
+	struct mtk_dp_sdp_packet packet;
+	struct hdmi_audio_infoframe frame;
+
+	hdmi_audio_infoframe_init(&frame);
+	frame.coding_type = HDMI_AUDIO_CODING_TYPE_PCM;
+	frame.channels = cfg->channels;
+	frame.sample_frequency = cfg->sample_rate;
+
+	switch (cfg->word_length_bits) {
+	case 16:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
+		break;
+	case 20:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_20;
+		break;
+	case 24:
+	default:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_24;
+		break;
+	}
+
+	packet.type = MTK_DP_SDP_AUI;
+	hdmi_audio_infoframe_pack_for_dp(&frame, &packet.sdp,
+					 MTK_DP_DP_VERSION_11);
+
+	mtk_dp_audio_sdp_asp_set_channels(mtk_dp, cfg->channels);
+	mtk_dp_setup_sdp(mtk_dp, &packet);
+}
+
+static void mtk_dp_audio_setup(struct mtk_dp *mtk_dp,
+			       struct mtk_dp_audio_cfg *cfg)
+{
+	mtk_dp_audio_sdp_setup(mtk_dp, cfg);
+	mtk_dp_audio_channel_status_set(mtk_dp, cfg);
+
+	mtk_dp_audio_setup_channels(mtk_dp, cfg);
+	mtk_dp_audio_set_divider(mtk_dp);
+}
+
+static void mtk_dp_video_config(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_mn_overwrite_disable(mtk_dp);
+
+	mtk_dp_set_msa(mtk_dp);
+
+	mtk_dp_set_color_depth(mtk_dp, mtk_dp->info.depth);
+	mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
+}
+
+static int mtk_dp_state_handler(struct mtk_dp *mtk_dp)
+{
+	int ret = 0;
+
+	switch (mtk_dp->state) {
+	case MTK_DP_STATE_INITIAL:
+		mtk_dp_video_mute(mtk_dp, true);
+		mtk_dp_audio_mute(mtk_dp, true);
+		mtk_dp->state = MTK_DP_STATE_IDLE;
+		break;
+
+	case MTK_DP_STATE_IDLE:
+		if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
+			mtk_dp->state = MTK_DP_STATE_PREPARE;
+		break;
+
+	case MTK_DP_STATE_PREPARE:
+		mtk_dp_video_config(mtk_dp);
+		mtk_dp_video_enable(mtk_dp, true);
+
+		if (mtk_dp->audio_enable) {
+			mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_caps);
+			mtk_dp_audio_mute(mtk_dp, false);
+		}
+
+		mtk_dp->state = MTK_DP_STATE_NORMAL;
+		break;
+
+	case MTK_DP_STATE_NORMAL:
+		if (mtk_dp->train_state != MTK_DP_TRAIN_STATE_NORMAL) {
+			mtk_dp_video_mute(mtk_dp, true);
+			mtk_dp_audio_mute(mtk_dp, true);
+			mtk_dp_sdp_stop_sending(mtk_dp);
+			mtk_dp->state = MTK_DP_STATE_IDLE;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_set_idle_pattern(mtk_dp, true);
+	mtk_dp_initialize_priv_data(mtk_dp);
+
+	mtk_dp_initialize_settings(mtk_dp);
+	mtk_dp_initialize_aux_settings(mtk_dp);
+	mtk_dp_initialize_digital_settings(mtk_dp);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
+			   BIT(RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT),
+			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK);
+	mtk_dp_initialize_hpd_detect_settings(mtk_dp);
+
+	mtk_dp_digital_sw_reset(mtk_dp);
+}
+
+static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
+{
+	struct mtk_dp *mtk_dp = dev;
+	int event;
+	u8 buf[DP_RECEIVER_CAP_SIZE] = {};
+
+	event = mtk_dp_plug_state(mtk_dp) ? connector_status_connected :
+						  connector_status_disconnected;
+
+	if (event < 0)
+		return IRQ_HANDLED;
+
+	if (mtk_dp->drm_dev) {
+		dev_info(mtk_dp->dev, "drm_helper_hpd_irq_event\n");
+		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
+	}
+
+	if (mtk_dp->train_info.cable_state_change) {
+		mtk_dp->train_info.cable_state_change = false;
+
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
+
+		if (!mtk_dp->train_info.cable_plugged_in ||
+		    !mtk_dp_plug_state(mtk_dp)) {
+			mtk_dp_video_mute(mtk_dp, true);
+			mtk_dp_audio_mute(mtk_dp, true);
+
+			mtk_dp_initialize_priv_data(mtk_dp);
+			mtk_dp_set_idle_pattern(mtk_dp, true);
+			if (mtk_dp->has_fec)
+				mtk_dp_fec_enable(mtk_dp, false);
+			mtk_dp_sdp_stop_sending(mtk_dp);
+
+			mtk_dp_edid_free(mtk_dp);
+			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+					   DP_PWR_STATE_BANDGAP_TPLL,
+					   DP_PWR_STATE_MASK);
+		} else {
+			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+					   DP_PWR_STATE_BANDGAP_TPLL_LANE,
+					   DP_PWR_STATE_MASK);
+			drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
+			mtk_dp->train_info.link_rate =
+				min_t(int, MTK_DP_MAX_LINK_RATE,
+				      buf[DP_MAX_LINK_RATE]);
+			mtk_dp->train_info.lane_count =
+				min_t(int, MTK_DP_MAX_LANES,
+				      drm_dp_max_lane_count(buf));
+		}
+	}
+
+	if (mtk_dp->train_info.irq_status & MTK_DP_HPD_INTERRUPT) {
+		dev_info(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n");
+		mtk_dp->train_info.irq_status &= ~MTK_DP_HPD_INTERRUPT;
+		mtk_dp_hpd_sink_event(mtk_dp);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_dp_hpd_isr_handler(struct mtk_dp *mtk_dp)
+{
+	bool connected;
+	u16 swirq_status = mtk_dp_swirq_get_clear(mtk_dp);
+	u8 hwirq_status = mtk_dp_hwirq_get_clear(mtk_dp);
+	struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
+
+	train_info->irq_status |= hwirq_status | swirq_status;
+
+	if (!train_info->irq_status)
+		return IRQ_HANDLED;
+
+	connected = mtk_dp_plug_state(mtk_dp);
+	if (connected || !train_info->cable_plugged_in)
+		train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
+	else if (!connected || train_info->cable_plugged_in)
+		train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
+
+	if (!(train_info->irq_status &
+	      (MTK_DP_HPD_CONNECT | MTK_DP_HPD_DISCONNECT)))
+		return IRQ_HANDLED;
+
+	if (train_info->irq_status & MTK_DP_HPD_CONNECT) {
+		train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
+		train_info->cable_plugged_in = true;
+	} else {
+		train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
+		train_info->cable_plugged_in = false;
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
+	}
+	train_info->cable_state_change = true;
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
+{
+	struct mtk_dp *mtk_dp = dev;
+	u32 irq_status;
+
+	irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS);
+
+	if (!irq_status)
+		return IRQ_HANDLED;
+
+	if (irq_status & RGS_IRQ_STATUS_TRANSMITTER)
+		return mtk_dp_hpd_isr_handler(mtk_dp);
+
+	return IRQ_HANDLED;
+}
+
+static int mtk_dp_dt_parse_pdata(struct mtk_dp *mtk_dp,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	int ret = 0;
+	void __iomem *base;
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	mtk_dp->regs = devm_regmap_init_mmio(dev, base, &mtk_dp_regmap_config);
+	if (IS_ERR(mtk_dp->regs))
+		return PTR_ERR(mtk_dp->regs);
+
+	mtk_dp->dp_tx_clk = devm_clk_get(dev, "faxi");
+	if (IS_ERR(mtk_dp->dp_tx_clk)) {
+		ret = PTR_ERR(mtk_dp->dp_tx_clk);
+		dev_err(dev, "Failed to get dptx clock: %d\n", ret);
+		mtk_dp->dp_tx_clk = NULL;
+	}
+
+	return 0;
+}
+
+static void mtk_dp_update_plugged_status(struct mtk_dp *mtk_dp)
+{
+	bool connected, has_audio;
+
+	mutex_lock(&mtk_dp->update_plugged_status_lock);
+	connected = mtk_dp_plug_state(mtk_dp);
+	has_audio = drm_detect_monitor_audio(mtk_dp->edid);
+	if (mtk_dp->plugged_cb && mtk_dp->codec_dev)
+		mtk_dp->plugged_cb(mtk_dp->codec_dev, connected & has_audio);
+	mutex_unlock(&mtk_dp->update_plugged_status_lock);
+}
+
+static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	enum drm_connector_status ret = connector_status_disconnected;
+	u8 sink_count = 0;
+
+	if (mtk_dp_is_edp(mtk_dp))
+		return connector_status_connected;
+
+	if (mtk_dp_plug_state(mtk_dp)) {
+		drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
+		if (DP_GET_SINK_COUNT(sink_count))
+			ret = connector_status_connected;
+	}
+
+	mtk_dp_update_plugged_status(mtk_dp);
+	return ret;
+}
+
+static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
+				    struct drm_connector *connector)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	bool enabled = mtk_dp->enabled;
+	struct edid *new_edid = NULL;
+
+	if (!enabled)
+		drm_bridge_chain_pre_enable(bridge);
+
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+	usleep_range(2000, 5000);
+
+	if (mtk_dp_plug_state(mtk_dp))
+		new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
+
+	if (!enabled)
+		drm_bridge_chain_post_disable(bridge);
+
+	mutex_lock(&mtk_dp->edid_lock);
+	kfree(mtk_dp->edid);
+	mtk_dp->edid = NULL;
+
+	if (!new_edid) {
+		mutex_unlock(&mtk_dp->edid_lock);
+		return NULL;
+	}
+
+	mtk_dp->edid = drm_edid_duplicate(new_edid);
+	mutex_unlock(&mtk_dp->edid_lock);
+
+	return new_edid;
+}
+
+static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
+				   struct drm_dp_aux_msg *msg)
+{
+	ssize_t err = -EAGAIN;
+	struct mtk_dp *mtk_dp;
+	bool is_read;
+	u8 request;
+	size_t accessed_bytes = 0;
+	int retry = 3, ret = 0;
+
+	mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
+
+	if (!mtk_dp->train_info.cable_plugged_in ||
+	    mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
+		err = -EAGAIN;
+		goto err;
+	}
+
+	switch (msg->request) {
+	case DP_AUX_I2C_MOT:
+	case DP_AUX_I2C_WRITE:
+	case DP_AUX_NATIVE_WRITE:
+	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
+	case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
+		request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
+		is_read = false;
+		break;
+	case DP_AUX_I2C_READ:
+	case DP_AUX_NATIVE_READ:
+	case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
+		request = msg->request;
+		is_read = true;
+		break;
+	default:
+		drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
+			msg->request);
+		err = -EINVAL;
+		goto err;
+	}
+
+	if (msg->size == 0) {
+		mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
+				       msg->address + accessed_bytes,
+				       msg->buffer + accessed_bytes, 0);
+	} else {
+		while (accessed_bytes < msg->size) {
+			size_t to_access =
+				min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
+				      msg->size - accessed_bytes);
+			retry = 3;
+			while (retry--) {
+				ret = mtk_dp_aux_do_transfer(
+					mtk_dp, is_read, request,
+					msg->address + accessed_bytes,
+					msg->buffer + accessed_bytes,
+					to_access);
+				if (ret == 0)
+					break;
+				udelay(50);
+			}
+			if (!retry && ret) {
+				drm_info(mtk_dp->drm_dev,
+					 "Failed to do AUX transfer: %d\n",
+					 ret);
+				break;
+			}
+			accessed_bytes += to_access;
+		}
+	}
+err:
+	if (!ret) {
+		msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
+		ret = msg->size;
+	} else {
+		msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
+		return err;
+	}
+
+	msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
+	return msg->size;
+}
+
+static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
+{
+	drm_dp_aux_init(&mtk_dp->aux);
+	mtk_dp->aux.name = "aux_mtk_dp";
+	mtk_dp->aux.transfer = mtk_dp_aux_transfer;
+}
+
+static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
+{
+	mutex_lock(&mtk_dp->dp_lock);
+
+	mtk_dp_hwirq_enable(mtk_dp, false);
+	mtk_dp_power_disable(mtk_dp);
+	phy_exit(mtk_dp->phy);
+	clk_disable_unprepare(mtk_dp->dp_tx_clk);
+
+	mutex_unlock(&mtk_dp->dp_lock);
+}
+
+static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
+{
+	int ret = 0;
+
+	mutex_lock(&mtk_dp->dp_lock);
+
+	ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
+	if (ret < 0) {
+		dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
+		goto err;
+	}
+	ret = phy_init(mtk_dp->phy);
+	if (ret) {
+		dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
+		goto err_phy_init;
+	}
+	ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
+	if (ret) {
+		dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
+		goto err_phy_config;
+	}
+
+	mtk_dp_init_port(mtk_dp);
+	mtk_dp_power_enable(mtk_dp);
+	mtk_dp_hwirq_enable(mtk_dp, true);
+
+err_phy_config:
+	phy_exit(mtk_dp->phy);
+err_phy_init:
+	clk_disable_unprepare(mtk_dp->dp_tx_clk);
+err:
+	mutex_unlock(&mtk_dp->dp_lock);
+	return ret;
+}
+
+static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
+				enum drm_bridge_attach_flags flags)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	int ret;
+
+	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+		dev_err(mtk_dp->dev, "Driver does not provide a connector!");
+		return -EINVAL;
+	}
+
+	ret = mtk_dp_poweron(mtk_dp);
+	if (ret)
+		return ret;
+
+	if (mtk_dp->next_bridge) {
+		ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
+					&mtk_dp->bridge, flags);
+		if (ret) {
+			drm_warn(mtk_dp->drm_dev,
+				 "Failed to attach external bridge: %d\n", ret);
+			goto err_bridge_attach;
+		}
+	}
+
+	mtk_dp->drm_dev = bridge->dev;
+
+	return 0;
+
+err_bridge_attach:
+	mtk_dp_poweroff(mtk_dp);
+	return ret;
+}
+
+static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+
+	mtk_dp->drm_dev = NULL;
+
+	mtk_dp_poweroff(mtk_dp);
+}
+
+static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
+					 struct drm_bridge_state *old_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+
+	mtk_dp_video_mute(mtk_dp, true);
+	mtk_dp_audio_mute(mtk_dp, true);
+	mtk_dp->state = MTK_DP_STATE_IDLE;
+	mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
+
+	mtk_dp->enabled = false;
+	msleep(100);
+}
+
+static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
+					  struct drm_display_mode *mode)
+{
+	struct mtk_dp_timings *timings = &mtk_dp->info.timings;
+
+	drm_display_mode_to_videomode(mode, &timings->vm);
+	timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
+	timings->htotal = mode->htotal;
+	timings->vtotal = mode->vtotal;
+}
+
+static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
+					struct drm_bridge_state *old_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	struct drm_connector *conn;
+	struct drm_connector_state *conn_state;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *crtc_state;
+	int ret = 0;
+	int i;
+
+	conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
+							bridge->encoder);
+	if (!conn) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as connector is missing\n");
+		return;
+	}
+
+	memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
+
+	conn_state =
+		drm_atomic_get_new_connector_state(old_state->base.state, conn);
+	if (!conn_state) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as connector state is missing\n");
+		return;
+	}
+
+	crtc = conn_state->crtc;
+	if (!crtc) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as connector state doesn't have a crtc\n");
+		return;
+	}
+
+	crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
+	if (!crtc_state) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as crtc state is missing\n");
+		return;
+	}
+
+	mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);
+	if (!mtk_dp_parse_capabilities(mtk_dp)) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as nothing is plugged in\n");
+		return;
+	}
+
+	//training
+	for (i = 0; i < 50; i++) {
+		ret = mtk_dp_train_handler(mtk_dp);
+		if (ret) {
+			drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
+				ret);
+			return;
+		}
+
+		ret = mtk_dp_state_handler(mtk_dp);
+		if (ret) {
+			drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
+				ret);
+			return;
+		}
+	}
+
+	mtk_dp->enabled = true;
+	mtk_dp_update_plugged_status(mtk_dp);
+}
+
+static enum drm_mode_status
+mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
+			 const struct drm_display_info *info,
+			 const struct drm_display_mode *mode)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	u32 rx_linkrate;
+	u32 bpp;
+
+	bpp = info->color_formats & DRM_COLOR_FORMAT_YCRCB422 ? 16 : 24;
+	rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
+	if (rx_linkrate * mtk_dp->train_info.lane_count < mode->clock * bpp / 8)
+		return MODE_CLOCK_HIGH;
+
+	if (mode->clock > 600000)
+		return MODE_CLOCK_HIGH;
+
+	if ((mode->clock * 1000) / (mode->htotal * mode->vtotal) > 62)
+		return MODE_CLOCK_HIGH;
+
+	return MODE_OK;
+}
+
+static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(
+	struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
+	struct drm_crtc_state *crtc_state,
+	struct drm_connector_state *conn_state, unsigned int *num_output_fmts)
+{
+	u32 *output_fmts;
+
+	*num_output_fmts = 0;
+	output_fmts = kcalloc(1, sizeof(*output_fmts), GFP_KERNEL);
+	if (!output_fmts)
+		return NULL;
+	*num_output_fmts = 1;
+	output_fmts[0] = MEDIA_BUS_FMT_FIXED;
+	return output_fmts;
+}
+
+static const u32 mt8195_input_fmts[] = {
+	MEDIA_BUS_FMT_RGB888_1X24,
+	MEDIA_BUS_FMT_YUV8_1X24,
+	MEDIA_BUS_FMT_YUYV8_1X16,
+};
+
+static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(
+	struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
+	struct drm_crtc_state *crtc_state,
+	struct drm_connector_state *conn_state, u32 output_fmt,
+	unsigned int *num_input_fmts)
+{
+	u32 *input_fmts;
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+	struct drm_display_info *display_info =
+		&conn_state->connector->display_info;
+	u32 rx_linkrate;
+	u32 bpp;
+
+	bpp = (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422) ? 16 :
+										24;
+	rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
+	*num_input_fmts = 0;
+	input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts), sizeof(*input_fmts),
+			     GFP_KERNEL);
+	if (!input_fmts)
+		return NULL;
+
+	*num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
+
+	memcpy(input_fmts, mt8195_input_fmts,
+	       sizeof(*input_fmts) * ARRAY_SIZE(mt8195_input_fmts));
+
+	if (((rx_linkrate * mtk_dp->train_info.lane_count) <
+	     (mode->clock * 24 / 8)) &&
+	    ((rx_linkrate * mtk_dp->train_info.lane_count) >
+	     (mode->clock * 16 / 8)) &&
+	    (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422)) {
+		kfree(input_fmts);
+		input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL);
+		*num_input_fmts = 1;
+		input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
+	}
+
+	return input_fmts;
+}
+
+static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
+				      struct drm_bridge_state *bridge_state,
+				      struct drm_crtc_state *crtc_state,
+				      struct drm_connector_state *conn_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	unsigned int input_bus_format;
+
+	input_bus_format = bridge_state->input_bus_cfg.format;
+
+	dev_info(mtk_dp->dev, "input format 0x%04x, output format 0x%04x\n",
+		 bridge_state->input_bus_cfg.format,
+		 bridge_state->output_bus_cfg.format);
+
+	mtk_dp->input_fmt = input_bus_format;
+	if (mtk_dp->input_fmt == MEDIA_BUS_FMT_YUYV8_1X16)
+		mtk_dp->info.format = MTK_DP_COLOR_FORMAT_YUV_422;
+	else
+		mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
+
+	return 0;
+}
+
+static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
+	.atomic_check = mtk_dp_bridge_atomic_check,
+	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+	.atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts,
+	.atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts,
+	.atomic_reset = drm_atomic_helper_bridge_reset,
+	.attach = mtk_dp_bridge_attach,
+	.detach = mtk_dp_bridge_detach,
+	.atomic_enable = mtk_dp_bridge_atomic_enable,
+	.atomic_disable = mtk_dp_bridge_atomic_disable,
+	.mode_valid = mtk_dp_bridge_mode_valid,
+	.get_edid = mtk_dp_get_edid,
+	.detect = mtk_dp_bdg_detect,
+};
+
+/*
+ * HDMI audio codec callbacks
+ */
+static int mtk_dp_audio_hw_params(struct device *dev, void *data,
+				  struct hdmi_codec_daifmt *daifmt,
+				  struct hdmi_codec_params *params)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+	struct mtk_dp_audio_cfg cfg;
+
+	if (!mtk_dp->enabled) {
+		pr_err("%s, DP is not ready!\n", __func__);
+		return -ENODEV;
+	}
+
+	cfg.channels = params->cea.channels;
+	cfg.sample_rate = params->sample_rate;
+	cfg.word_length_bits = 24;
+
+	mtk_dp_audio_setup(mtk_dp, &cfg);
+
+	return 0;
+}
+
+static int mtk_dp_audio_startup(struct device *dev, void *data)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_audio_mute(mtk_dp, false);
+
+	return 0;
+}
+
+static void mtk_dp_audio_shutdown(struct device *dev, void *data)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_audio_mute(mtk_dp, true);
+}
+
+static int mtk_dp_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
+				size_t len)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	if (mtk_dp->enabled)
+		memcpy(buf, mtk_dp->connector_eld, len);
+	else
+		memset(buf, 0, len);
+
+	return 0;
+}
+
+static int mtk_dp_audio_hook_plugged_cb(struct device *dev, void *data,
+					hdmi_codec_plugged_cb fn,
+					struct device *codec_dev)
+{
+	struct mtk_dp *mtk_dp = data;
+
+	mutex_lock(&mtk_dp->update_plugged_status_lock);
+	mtk_dp->plugged_cb = fn;
+	mtk_dp->codec_dev = codec_dev;
+	mutex_unlock(&mtk_dp->update_plugged_status_lock);
+
+	mtk_dp_update_plugged_status(mtk_dp);
+
+	return 0;
+}
+
+static const struct hdmi_codec_ops mtk_dp_audio_codec_ops = {
+	.hw_params = mtk_dp_audio_hw_params,
+	.audio_startup = mtk_dp_audio_startup,
+	.audio_shutdown = mtk_dp_audio_shutdown,
+	.get_eld = mtk_dp_audio_get_eld,
+	.hook_plugged_cb = mtk_dp_audio_hook_plugged_cb,
+	.no_capture_mute = 1,
+};
+
+static int mtk_dp_register_audio_driver(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+	struct hdmi_codec_pdata codec_data = {
+		.ops = &mtk_dp_audio_codec_ops,
+		.max_i2s_channels = 8,
+		.i2s = 1,
+		.data = mtk_dp,
+	};
+	struct platform_device *pdev;
+
+	pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
+					     PLATFORM_DEVID_AUTO, &codec_data,
+					     sizeof(codec_data));
+	if (IS_ERR(pdev))
+		return PTR_ERR(pdev);
+
+	return 0;
+}
+
+static int mtk_dp_probe(struct platform_device *pdev)
+{
+	struct mtk_dp *mtk_dp;
+	struct device *dev = &pdev->dev;
+	int ret;
+	int irq_num = 0;
+	struct drm_panel *panel = NULL;
+
+	mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
+	if (!mtk_dp)
+		return -ENOMEM;
+
+	mtk_dp->dev = dev;
+
+	irq_num = platform_get_irq(pdev, 0);
+	if (irq_num < 0) {
+		dev_err(dev, "failed to request dp irq resource\n");
+		return -EPROBE_DEFER;
+	}
+
+	ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel,
+					  &mtk_dp->next_bridge);
+	if (ret == -ENODEV) {
+		dev_info(
+			dev,
+			"No panel connected in devicetree, continuing as external DP\n");
+		mtk_dp->next_bridge = NULL;
+	} else if (ret) {
+		dev_err(dev, "Failed to find panel or bridge: %d\n", ret);
+		return ret;
+	}
+
+	if (panel) {
+		mtk_dp->next_bridge = devm_drm_panel_bridge_add(dev, panel);
+		if (IS_ERR(mtk_dp->next_bridge)) {
+			ret = PTR_ERR(mtk_dp->next_bridge);
+			dev_err(dev, "Failed to create bridge: %d\n", ret);
+			return -EPROBE_DEFER;
+		}
+	}
+
+	ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
+	if (ret)
+		return ret;
+
+	mtk_dp_aux_init(mtk_dp);
+
+	ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
+					mtk_dp_hpd_event_thread,
+					IRQ_TYPE_LEVEL_HIGH, dev_name(dev),
+					mtk_dp);
+	if (ret) {
+		dev_err(dev, "failed to request mediatek dptx irq\n");
+		return -EPROBE_DEFER;
+	}
+
+	mutex_init(&mtk_dp->dp_lock);
+	mutex_init(&mtk_dp->edid_lock);
+
+	platform_set_drvdata(pdev, mtk_dp);
+
+	if (!mtk_dp_is_edp(mtk_dp)) {
+		mutex_init(&mtk_dp->update_plugged_status_lock);
+		ret = mtk_dp_register_audio_driver(dev);
+		if (ret) {
+			dev_err(dev, "Failed to register audio driver: %d\n",
+				ret);
+			return ret;
+		}
+	}
+
+	mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
+							PLATFORM_DEVID_AUTO,
+							&mtk_dp->regs,
+							sizeof(&mtk_dp->regs));
+	if (IS_ERR(mtk_dp->phy_dev)) {
+		dev_err(dev, "Failed to create device mediatek-dp-phy: %ld\n",
+			PTR_ERR(mtk_dp->phy_dev));
+		return PTR_ERR(mtk_dp->phy_dev);
+	}
+
+	mtk_dp_get_calibration_data(mtk_dp);
+
+	mtk_dp->phy = dev_get_drvdata(&mtk_dp->phy_dev->dev);
+	if (IS_ERR(mtk_dp->phy)) {
+		dev_err(dev, "Failed to get phy: %ld\n", PTR_ERR(mtk_dp->phy));
+		platform_device_unregister(mtk_dp->phy_dev);
+		return PTR_ERR(mtk_dp->phy);
+	}
+
+	mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
+	mtk_dp->bridge.of_node = dev->of_node;
+	if (mtk_dp_is_edp(mtk_dp))
+		mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
+	else
+		mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
+
+	mtk_dp->bridge.ops =
+		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
+	drm_bridge_add(&mtk_dp->bridge);
+
+	pm_runtime_enable(dev);
+	pm_runtime_get_sync(dev);
+
+	return 0;
+}
+
+static int mtk_dp_remove(struct platform_device *pdev)
+{
+	struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
+
+	platform_device_unregister(mtk_dp->phy_dev);
+
+	mtk_dp_video_mute(mtk_dp, true);
+	mtk_dp_audio_mute(mtk_dp, true);
+
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_dp_suspend(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_power_disable(mtk_dp);
+	mtk_dp_hwirq_enable(mtk_dp, false);
+
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static int mtk_dp_resume(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	pm_runtime_get_sync(dev);
+
+	mtk_dp_init_port(mtk_dp);
+	mtk_dp_power_enable(mtk_dp);
+	mtk_dp_hwirq_enable(mtk_dp, true);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
+
+static const struct of_device_id mtk_dp_of_match[] = {
+	{
+		.compatible = "mediatek,mt8195-dp-tx",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
+
+struct platform_driver mtk_dp_driver = {
+	.probe = mtk_dp_probe,
+	.remove = mtk_dp_remove,
+	.driver = {
+		.name = "mediatek-drm-dp",
+		.of_match_table = mtk_dp_of_match,
+		.pm = &mtk_dp_pm_ops,
+	},
+};
+
+MODULE_AUTHOR("Jason-JH.Lin <jason-jh.lin@mediatek.com>");
+MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
+MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
new file mode 100644
index 0000000000000..79952ac30e9e6
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
@@ -0,0 +1,568 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Copyright (c) 2021 BayLibre
+ */
+#ifndef _MTK_DP_REG_H_
+#define _MTK_DP_REG_H_
+
+#define MTK_DP_SIP_CONTROL_AARCH32 0x82000523
+#define MTK_DP_SIP_ATF_VIDEO_UNMUTE 0x20
+#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE 0x21
+
+#define DP_PHY_GLB_BIAS_GEN_00 0x0000
+#define RG_XTP_GLB_BIAS_INTR_CTRL (0x1f << 16)
+
+#define DP_PHY_GLB_DPAUX_TX 0x0008
+#define RG_CKM_PT0_CKTX_IMPSEL (0xf << 20)
+
+#define DP_PHY_LANE_TX_0 0x104
+#define RG_XTP_LN0_TX_IMPSEL_PMOS (0xf << 12)
+#define RG_XTP_LN0_TX_IMPSEL_NMOS (0xf << 16)
+
+#define DP_PHY_LANE_TX_1 0x204
+#define RG_XTP_LN1_TX_IMPSEL_PMOS (0xf << 12)
+#define RG_XTP_LN1_TX_IMPSEL_NMOS (0xf << 16)
+
+#define DP_PHY_LANE_TX_2 0x304
+#define RG_XTP_LN2_TX_IMPSEL_PMOS (0xf << 12)
+#define RG_XTP_LN2_TX_IMPSEL_NMOS (0xf << 16)
+
+#define DP_PHY_LANE_TX_3 0x404
+#define RG_XTP_LN3_TX_IMPSEL_PMOS (0xf << 12)
+#define RG_XTP_LN3_TX_IMPSEL_NMOS (0xf << 16)
+
+#define TOP_OFFSET 0x2000
+#define ENC0_OFFSET 0x3000
+#define ENC1_OFFSET 0x3200
+#define TRANS_OFFSET 0x3400
+#define AUX_OFFSET 0x3600
+#define SEC_OFFSET 0x4000
+
+#define MTK_DP_HPD_DISCONNECT BIT(1)
+#define MTK_DP_HPD_CONNECT BIT(2)
+#define MTK_DP_HPD_INTERRUPT BIT(3)
+
+#define MTK_DP_0034 0x034
+#define DA_XTP_GLB_CKDET_EN_FORCE_VAL BIT(15)
+#define DA_XTP_GLB_CKDET_EN_FORCE_EN BIT(14)
+#define DA_CKM_INTCKTX_EN_FORCE_VAL BIT(13)
+#define DA_CKM_INTCKTX_EN_FORCE_EN BIT(12)
+#define DA_CKM_CKTX0_EN_FORCE_VAL BIT(11)
+#define DA_CKM_CKTX0_EN_FORCE_EN BIT(10)
+#define DA_CKM_XTAL_CK_FORCE_VAL BIT(9)
+#define DA_CKM_XTAL_CK_FORCE_EN BIT(8)
+#define DA_CKM_BIAS_LPF_EN_FORCE_VAL BIT(7)
+#define DA_CKM_BIAS_LPF_EN_FORCE_EN BIT(6)
+#define DA_CKM_BIAS_EN_FORCE_VAL BIT(5)
+#define DA_CKM_BIAS_EN_FORCE_EN BIT(4)
+#define DA_XTP_GLB_AVD10_ON_FORCE_VAL BIT(3)
+#define DA_XTP_GLB_AVD10_ON_FORCE BIT(2)
+#define DA_XTP_GLB_LDO_EN_FORCE_VAL BIT(1)
+#define DA_XTP_GLB_LDO_EN_FORCE_EN BIT(0)
+
+#define MTK_DP_1040 0x1040
+#define RG_DPAUX_RX_VALID_DEGLITCH_EN BIT(2)
+#define RG_XTP_GLB_CKDET_EN BIT(1)
+#define RG_DPAUX_RX_EN BIT(0)
+
+#define MTK_DP_ENC0_P0_3000 (ENC0_OFFSET + 0x000)
+#define LANE_NUM_DP_ENC0_P0_MASK 0x3
+#define VIDEO_MUTE_SW_DP_ENC0_P0_MASK 0x4
+#define VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT 2
+#define VIDEO_MUTE_SEL_DP_ENC0_P0_MASK 0x8
+#define VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT 3
+#define ENHANCED_FRAME_EN_DP_ENC0_P0_MASK 0x10
+#define ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT 4
+
+#define MTK_DP_ENC0_P0_3004 (ENC0_OFFSET + 0x004)
+#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK 0x100
+#define VIDEO_M_CODE_SEL_DP_ENC0_P0_SHIFT 8
+#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK 0x200
+#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT 9
+
+#define MTK_DP_ENC0_P0_3008 (ENC0_OFFSET + 0x008)
+#define VIDEO_M_CODE_SW_0_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_300C (ENC0_OFFSET + 0x00C)
+#define VIDEO_M_CODE_SW_1_DP_ENC0_P0_MASK 0xff
+
+#define MTK_DP_ENC0_P0_3010 (ENC0_OFFSET + 0x010)
+#define HTOTAL_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3014 (ENC0_OFFSET + 0x014)
+#define VTOTAL_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3018 (ENC0_OFFSET + 0x018)
+#define HSTART_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_301C (ENC0_OFFSET + 0x01C)
+#define VSTART_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3020 (ENC0_OFFSET + 0x020)
+#define HWIDTH_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3024 (ENC0_OFFSET + 0x024)
+#define VHEIGHT_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3028 (ENC0_OFFSET + 0x028)
+#define HSW_SW_DP_ENC0_P0_MASK 0x7fff
+#define HSW_SW_DP_ENC0_P0_SHIFT 0
+#define HSP_SW_DP_ENC0_P0_MASK 0x8000
+
+#define MTK_DP_ENC0_P0_302C (ENC0_OFFSET + 0x02C)
+#define VSW_SW_DP_ENC0_P0_MASK 0x7fff
+#define VSW_SW_DP_ENC0_P0_SHIFT 0
+#define VSP_SW_DP_ENC0_P0_MASK 0x8000
+
+#define MTK_DP_ENC0_P0_3030 (ENC0_OFFSET + 0x030)
+#define HTOTAL_SEL_DP_ENC0_P0_SHIFT 0
+#define VTOTAL_SEL_DP_ENC0_P0_SHIFT 1
+#define HSTART_SEL_DP_ENC0_P0_SHIFT 2
+#define VSTART_SEL_DP_ENC0_P0_SHIFT 3
+#define HWIDTH_SEL_DP_ENC0_P0_SHIFT 4
+#define VHEIGHT_SEL_DP_ENC0_P0_SHIFT 5
+#define HSP_SEL_DP_ENC0_P0_SHIFT 6
+#define HSW_SEL_DP_ENC0_P0_SHIFT 7
+#define VSP_SEL_DP_ENC0_P0_SHIFT 8
+#define VSW_SEL_DP_ENC0_P0_SHIFT 9
+#define VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK 0x800
+#define VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT 11
+#define VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK 0x1000
+#define VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT 12
+
+#define MTK_DP_ENC0_P0_3034 (ENC0_OFFSET + 0x034)
+
+#define MTK_DP_ENC0_P0_3038 (ENC0_OFFSET + 0x038)
+#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK 0x800
+#define VIDEO_SOURCE_SEL_DP_ENC0_P0_SHIFT 11
+
+#define MTK_DP_ENC0_P0_303C (ENC0_OFFSET + 0x03C)
+#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK 0x3f
+#define SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT 0
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK 0x700
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT 8
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT                                     \
+	(0 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT                                     \
+	(1 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT                                     \
+	(2 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT                                      \
+	(3 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT                                      \
+	(4 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK 0x7000
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT 12
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB                                     \
+	(0 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422                                \
+	(1 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420                                \
+	(2 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
+#define VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK 0x8000
+#define VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT 15
+
+#define MTK_DP_ENC0_P0_3040 (ENC0_OFFSET + 0x040)
+#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK 0xfff
+#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT 0
+
+#define MTK_DP_ENC0_P0_3044 (ENC0_OFFSET + 0x044)
+#define VIDEO_N_CODE_0_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3048 (ENC0_OFFSET + 0x048)
+#define VIDEO_N_CODE_1_DP_ENC0_P0_MASK 0xff
+
+#define MTK_DP_ENC0_P0_304C (ENC0_OFFSET + 0x04C)
+#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK 0x4
+#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK 0x100
+
+#define MTK_DP_ENC0_P0_3050 (ENC0_OFFSET + 0x050)
+#define VIDEO_N_CODE_MN_GEN_0_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3054 (ENC0_OFFSET + 0x054)
+#define VIDEO_N_CODE_MN_GEN_1_DP_ENC0_P0_MASK 0xff
+
+#define MTK_DP_ENC0_P0_3064 (ENC0_OFFSET + 0x064)
+#define HDE_NUM_LAST_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3088 (ENC0_OFFSET + 0x088)
+#define AU_EN_DP_ENC0_P0_MASK 0x40
+#define AU_EN_DP_ENC0_P0_SHIFT 6
+#define AUDIO_8CH_EN_DP_ENC0_P0_MASK 0x80
+#define AUDIO_8CH_SEL_DP_ENC0_P0_MASK 0x100
+#define AUDIO_2CH_EN_DP_ENC0_P0_MASK 0x4000
+#define AUDIO_2CH_SEL_DP_ENC0_P0_MASK 0x8000
+
+#define MTK_DP_ENC0_P0_308C (ENC0_OFFSET + 0x08C)
+#define CH_STATUS_0_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3090 (ENC0_OFFSET + 0x090)
+#define CH_STATUS_1_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3094 (ENC0_OFFSET + 0x094)
+#define CH_STATUS_2_DP_ENC0_P0_MASK 0xff
+
+#define MTK_DP_ENC0_P0_30A0 (ENC0_OFFSET + 0x0A0)
+
+#define MTK_DP_ENC0_P0_30A4 (ENC0_OFFSET + 0x0A4)
+#define AU_TS_CFG_DP_ENC0_P0_MASK 0xff
+
+#define MTK_DP_ENC0_P0_30A8 (ENC0_OFFSET + 0x0A8)
+
+#define MTK_DP_ENC0_P0_30AC (ENC0_OFFSET + 0x0AC)
+
+#define MTK_DP_ENC0_P0_30B0 (ENC0_OFFSET + 0x0B0)
+
+#define MTK_DP_ENC0_P0_30B4 (ENC0_OFFSET + 0x0B4)
+#define ISRC_CFG_DP_ENC0_P0_MASK 0xff00
+#define ISRC_CFG_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC0_P0_30B8 (ENC0_OFFSET + 0x0B8)
+
+#define MTK_DP_ENC0_P0_30BC (ENC0_OFFSET + 0x0BC)
+#define ISRC_CONT_DP_ENC0_P0_MASK 0x1
+#define ISRC_CONT_DP_ENC0_P0_SHIFT 0
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK 0x700
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT 8
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_2                             \
+	(1 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_4                             \
+	(2 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_8                             \
+	(3 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2                             \
+	(5 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_4                             \
+	(6 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_8                             \
+	(7 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+
+#define MTK_DP_ENC0_P0_30D8 (ENC0_OFFSET + 0x0D8)
+
+#define MTK_DP_ENC0_P0_312C (ENC0_OFFSET + 0x12C)
+#define ASP_HB2_DP_ENC0_P0_MASK 0xff
+#define ASP_HB3_DP_ENC0_P0_MASK 0xff00
+#define ASP_HB3_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC0_P0_3130 (ENC0_OFFSET + 0x130)
+
+#define MTK_DP_ENC0_P0_3138 (ENC0_OFFSET + 0x138)
+
+#define MTK_DP_ENC0_P0_3154 (ENC0_OFFSET + 0x154)
+#define PGEN_HTOTAL_DP_ENC0_P0_MASK 0x3fff
+
+#define MTK_DP_ENC0_P0_3158 (ENC0_OFFSET + 0x158)
+#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK 0x3fff
+
+#define MTK_DP_ENC0_P0_315C (ENC0_OFFSET + 0x15C)
+#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x3fff
+
+#define MTK_DP_ENC0_P0_3160 (ENC0_OFFSET + 0x160)
+#define PGEN_HFDE_START_DP_ENC0_P0_MASK 0x3fff
+
+#define MTK_DP_ENC0_P0_3164 (ENC0_OFFSET + 0x164)
+#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x3fff
+
+#define MTK_DP_ENC0_P0_3168 (ENC0_OFFSET + 0x168)
+#define PGEN_VTOTAL_DP_ENC0_P0_MASK 0x1fff
+
+#define MTK_DP_ENC0_P0_316C (ENC0_OFFSET + 0x16C)
+#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK 0x1fff
+
+#define MTK_DP_ENC0_P0_3170 (ENC0_OFFSET + 0x170)
+#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x1fff
+
+#define MTK_DP_ENC0_P0_3174 (ENC0_OFFSET + 0x174)
+#define PGEN_VFDE_START_DP_ENC0_P0_MASK 0x1fff
+
+#define MTK_DP_ENC0_P0_3178 (ENC0_OFFSET + 0x178)
+#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x1fff
+
+#define MTK_DP_ENC0_P0_31B0 (ENC0_OFFSET + 0x1B0)
+#define PGEN_PATTERN_SEL_SHIFT 4
+#define PGEN_PATTERN_SEL_MASK 0x0070
+
+#define MTK_DP_ENC0_P0_31C8 (ENC0_OFFSET + 0x1C8)
+#define VSC_EXT_VESA_HB0_DP_ENC0_P0_MASK 0xff
+#define VSC_EXT_VESA_HB1_DP_ENC0_P0_MASK 0xff00
+#define VSC_EXT_VESA_HB1_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC0_P0_31CC (ENC0_OFFSET + 0x1CC)
+#define VSC_EXT_VESA_HB2_DP_ENC0_P0_MASK 0xff
+#define VSC_EXT_VESA_HB2_DP_ENC0_P0_SHIFT 0
+#define VSC_EXT_VESA_HB3_DP_ENC0_P0_MASK 0xff00
+
+#define MTK_DP_ENC0_P0_31D0 (ENC0_OFFSET + 0x1D0)
+#define VSC_EXT_CEA_HB0_DP_ENC0_P0_MASK 0xff
+#define VSC_EXT_CEA_HB1_DP_ENC0_P0_MASK 0xff00
+#define VSC_EXT_CEA_HB1_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC0_P0_31D4 (ENC0_OFFSET + 0x1D4)
+#define VSC_EXT_CEA_HB2_DP_ENC0_P0_MASK 0xff
+#define VSC_EXT_CEA_HB2_DP_ENC0_P0_SHIFT 0
+#define VSC_EXT_CEA_HB3_DP_ENC0_P0_MASK 0xff00
+
+#define MTK_DP_ENC0_P0_31D8 (ENC0_OFFSET + 0x1D8)
+#define VSC_EXT_VESA_NUM_DP_ENC0_P0_MASK 0x3f
+#define VSC_EXT_VESA_NUM_DP_ENC0_P0_SHIFT 0
+#define VSC_EXT_CEA_NUM_DP_ENC0_P0_MASK 0x3f00
+#define VSC_EXT_CEA_NUM_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC0_P0_31DC (ENC0_OFFSET + 0x1DC)
+#define HDR0_CFG_DP_ENC0_P0_MASK 0xff
+#define HDR0_CFG_DP_ENC0_P0_SHIFT 0
+
+#define MTK_DP_ENC0_P0_31E8 (ENC0_OFFSET + 0x1E8)
+
+#define MTK_DP_ENC0_P0_31EC (ENC0_OFFSET + 0x1EC)
+#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK 0x10
+#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT 4
+#define ISRC1_HB3_DP_ENC0_P0_MASK 0xff00
+#define ISRC1_HB3_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC1_P0_3200 (ENC1_OFFSET + 0x000)
+
+#define MTK_DP_ENC1_P0_3280 (ENC1_OFFSET + 0x080)
+#define SDP_PACKET_TYPE_DP_ENC1_P0_MASK 0x1f
+#define SDP_PACKET_W_DP_ENC1_P0 0x20
+#define SDP_PACKET_W_DP_ENC1_P0_MASK 0x20
+#define SDP_PACKET_W_DP_ENC1_P0_SHIFT 5
+
+#define MTK_DP_ENC1_P0_328C (ENC1_OFFSET + 0x08C)
+
+#define MTK_DP_ENC1_P0_3290 (ENC1_OFFSET + 0x090)
+
+#define MTK_DP_ENC1_P0_32A0 (ENC1_OFFSET + 0x0A0)
+
+#define MTK_DP_ENC1_P0_32A4 (ENC1_OFFSET + 0x0A4)
+
+#define MTK_DP_ENC1_P0_3300 (ENC1_OFFSET + 0x100)
+#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK 0x300
+#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT 8
+
+#define MTK_DP_ENC1_P0_3304 (ENC1_OFFSET + 0x104)
+#define AU_PRTY_REGEN_DP_ENC1_P0_MASK 0x100
+#define AU_CH_STS_REGEN_DP_ENC1_P0_MASK 0x200
+#define AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK 0x1000
+
+#define MTK_DP_ENC1_P0_3324 (ENC1_OFFSET + 0x124)
+#define AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK 0x300
+#define AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT 8
+#define AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX                                       \
+	(0 << AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT)
+
+#define MTK_DP_ENC1_P0_3364 (ENC1_OFFSET + 0x164)
+#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK 0xfff
+#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT 0
+#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK 0xf000
+#define FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT 12
+
+#define MTK_DP_ENC1_P0_3368 (ENC1_OFFSET + 0x168)
+#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT 0
+#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT 4
+#define SDP_DP13_EN_DP_ENC1_P0_SHIFT 8
+#define BS2BS_MODE_DP_ENC1_P0_MASK 0x3000
+#define BS2BS_MODE_DP_ENC1_P0_SHIFT 12
+
+#define MTK_DP_ENC1_P0_33F4 (ENC1_OFFSET + 0x1F4)
+
+#define MTK_DP_TRANS_P0_3400 (TRANS_OFFSET + 0x000)
+#define PATTERN1_EN_DP_TRANS_P0_MASK 0x1000
+#define PATTERN1_EN_DP_TRANS_P0_SHIFT 12
+#define PATTERN2_EN_DP_TRANS_P0_MASK 0x2000
+#define PATTERN3_EN_DP_TRANS_P0_MASK 0x4000
+#define PATTERN4_EN_DP_TRANS_P0_MASK 0x8000
+
+#define MTK_DP_TRANS_P0_3404 (TRANS_OFFSET + 0x004)
+#define DP_SCR_EN_DP_TRANS_P0_MASK 0x1
+
+#define MTK_DP_TRANS_P0_340C (TRANS_OFFSET + 0x00C)
+#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK 0x2000
+#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT 13
+
+#define MTK_DP_TRANS_P0_3410 (TRANS_OFFSET + 0x010)
+#define HPD_DEB_THD_DP_TRANS_P0_MASK 0xf
+#define HPD_DEB_THD_DP_TRANS_P0_SHIFT 0
+#define HPD_INT_THD_DP_TRANS_P0_MASK 0xf0
+#define HPD_INT_THD_DP_TRANS_P0_SHIFT 4
+#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US (2 << HPD_INT_THD_DP_TRANS_P0_SHIFT)
+#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US                                   \
+	(2 << (HPD_INT_THD_DP_TRANS_P0_SHIFT + 2))
+#define HPD_DISC_THD_DP_TRANS_P0_MASK 0xf00
+#define HPD_DISC_THD_DP_TRANS_P0_SHIFT 8
+#define HPD_CONN_THD_DP_TRANS_P0_MASK 0xf000
+#define HPD_CONN_THD_DP_TRANS_P0_SHIFT 12
+
+#define MTK_DP_TRANS_P0_3414 (TRANS_OFFSET + 0x014)
+#define HPD_DB_DP_TRANS_P0_MASK 0x4
+
+#define MTK_DP_TRANS_P0_3418 (TRANS_OFFSET + 0x018)
+#define IRQ_CLR_DP_TRANS_P0_MASK 0xf
+#define IRQ_MASK_DP_TRANS_P0_MASK 0xf0
+#define IRQ_MASK_DP_TRANS_P0_SHIFT 4
+#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ (BIT(1) << IRQ_MASK_DP_TRANS_P0_SHIFT)
+#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ (BIT(2) << IRQ_MASK_DP_TRANS_P0_SHIFT)
+#define IRQ_MASK_DP_TRANS_P0_INT_IRQ (BIT(3) << IRQ_MASK_DP_TRANS_P0_SHIFT)
+#define IRQ_STATUS_DP_TRANS_P0_MASK 0xf000
+#define IRQ_STATUS_DP_TRANS_P0_SHIFT 12
+
+#define MTK_DP_TRANS_P0_342C (TRANS_OFFSET + 0x02C)
+#define XTAL_FREQ_DP_TRANS_P0_DEFAULT 0x69
+#define XTAL_FREQ_DP_TRANS_P0_MASK 0xff
+
+#define MTK_DP_TRANS_P0_3430 (TRANS_OFFSET + 0x030)
+#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK 0x3
+#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT BIT(1)
+
+#define MTK_DP_TRANS_P0_34A4 (TRANS_OFFSET + 0x0A4)
+#define LANE_NUM_DP_TRANS_P0_MASK 0xc
+#define LANE_NUM_DP_TRANS_P0_SHIFT 2
+
+#define MTK_DP_TRANS_P0_3540 (TRANS_OFFSET + 0x140)
+#define FEC_EN_DP_TRANS_P0_MASK 0x1
+#define FEC_EN_DP_TRANS_P0_SHIFT 0
+#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK 0x8
+#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT 3
+
+#define MTK_DP_TRANS_P0_3580 (TRANS_OFFSET + 0x180)
+#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK 0x100
+#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK 0x200
+#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK 0x400
+#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK 0x800
+
+#define MTK_DP_TRANS_P0_35C4 (TRANS_OFFSET + 0x1C4)
+#define SW_IRQ_MASK_DP_TRANS_P0_MASK 0xffff
+
+#define MTK_DP_TRANS_P0_35C8 (TRANS_OFFSET + 0x1C8)
+#define SW_IRQ_CLR_DP_TRANS_P0_MASK 0xffff
+
+#define SW_IRQ_STATUS_DP_TRANS_P0_MASK 0xffff
+#define SW_IRQ_STATUS_DP_TRANS_P0_SHIFT 0
+
+#define MTK_DP_TRANS_P0_35D0 (TRANS_OFFSET + 0x1D0)
+#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK 0xffff
+
+#define MTK_DP_TRANS_P0_35F0 (TRANS_OFFSET + 0x1F0)
+
+#define MTK_DP_AUX_P0_360C (AUX_OFFSET + 0x00C)
+#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK 0x1fff
+
+#define MTK_DP_AUX_P0_3614 (AUX_OFFSET + 0x014)
+#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK 0x7f
+#define AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT 0
+
+#define MTK_DP_AUX_P0_3618 (AUX_OFFSET + 0x018)
+#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK 0x200
+#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK 0xf
+
+#define MTK_DP_AUX_P0_3620 (AUX_OFFSET + 0x020)
+#define AUX_RD_MODE_AUX_TX_P0_MASK 0x200
+#define AUX_RX_FIFO_READ_PULSE_TX_P0_MASK 0x100
+#define AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT 8
+#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK 0xff
+#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT 0
+
+#define MTK_DP_AUX_P0_3624 (AUX_OFFSET + 0x024)
+#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK 0xf
+
+#define MTK_DP_AUX_P0_3628 (AUX_OFFSET + 0x028)
+#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK 0x3ff
+#define AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT 0
+#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE                                     \
+	(BIT(0) << AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT)
+
+#define MTK_DP_AUX_P0_362C (AUX_OFFSET + 0x02C)
+#define AUX_NO_LENGTH_AUX_TX_P0_MASK 0x1
+#define AUX_NO_LENGTH_AUX_TX_P0_SHIFT 0
+#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK 0x2
+#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK 0xfffc
+
+#define MTK_DP_AUX_P0_3630 (AUX_OFFSET + 0x030)
+#define AUX_TX_REQUEST_READY_AUX_TX_P0_MASK 0x8
+#define AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT 3
+
+#define MTK_DP_AUX_P0_3634 (AUX_OFFSET + 0x034)
+#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK 0xff00
+#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT 8
+
+#define MTK_DP_AUX_P0_3640 (AUX_OFFSET + 0x040)
+#define AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK 0x40
+#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 6
+#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 5
+#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 4
+#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT 3
+#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT 2
+#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT 1
+#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK 0x1
+#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT 0
+
+#define MTK_DP_AUX_P0_3644 (AUX_OFFSET + 0x044)
+#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK 0xf
+
+#define MTK_DP_AUX_P0_3648 (AUX_OFFSET + 0x048)
+#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK 0xffff
+
+#define MTK_DP_AUX_P0_364C (AUX_OFFSET + 0x04C)
+#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK 0xf
+
+#define MTK_DP_AUX_P0_3650 (AUX_OFFSET + 0x050)
+#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK 0xf000
+#define MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT 12
+#define PHY_FIFO_RST_AUX_TX_P0_MASK 0x200
+#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK 0x100
+#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT 8
+
+#define MTK_DP_AUX_P0_3658 (AUX_OFFSET + 0x058)
+#define AUX_TX_OV_EN_AUX_TX_P0_MASK 0x1
+
+#define MTK_DP_AUX_P0_3690 (AUX_OFFSET + 0x090)
+#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK 0x100
+#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT 8
+
+#define MTK_DP_AUX_P0_3704 (AUX_OFFSET + 0x104)
+#define AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK 0x2
+#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK 0x4
+#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT 2
+
+#define MTK_DP_AUX_P0_3708 (AUX_OFFSET + 0x108)
+
+#define MTK_DP_AUX_P0_37C8 (AUX_OFFSET + 0x1C8)
+#define MTK_ATOP_EN_AUX_TX_P0_MASK 0x1
+#define MTK_ATOP_EN_AUX_TX_P0_SHIFT 0
+
+#define MTK_DP_TOP_PWR_STATE (TOP_OFFSET + 0x00)
+#define DP_PWR_STATE_MASK 0x3
+#define DP_PWR_STATE_BANDGAP 1
+#define DP_PWR_STATE_BANDGAP_TPLL 2
+#define DP_PWR_STATE_BANDGAP_TPLL_LANE 3
+
+#define MTK_DP_TOP_SWING_EMP (TOP_OFFSET + 0x04)
+#define DP_TX0_VOLT_SWING_MASK 0x3
+#define DP_TX0_VOLT_SWING_SHIFT 0
+#define DP_TX0_PRE_EMPH_MASK 0xc
+#define DP_TX0_PRE_EMPH_SHIFT 2
+#define DP_TX1_VOLT_SWING_MASK 0x300
+#define DP_TX1_VOLT_SWING_SHIFT 8
+#define DP_TX1_PRE_EMPH_MASK 0xc00
+#define DP_TX2_VOLT_SWING_MASK 0x30000
+#define DP_TX2_PRE_EMPH_MASK 0xc0000
+#define DP_TX3_VOLT_SWING_MASK 0x3000000
+#define DP_TX3_PRE_EMPH_MASK 0xc000000
+
+#define MTK_DP_TOP_RESET_AND_PROBE (TOP_OFFSET + 0x20)
+#define SW_RST_B_SHIFT 0
+#define SW_RST_B_PHYD (BIT(4) << SW_RST_B_SHIFT)
+
+#define MTK_DP_TOP_IRQ_STATUS (TOP_OFFSET + 0x28)
+#define RGS_IRQ_STATUS_SHIFT 0
+#define RGS_IRQ_STATUS_TRANSMITTER (BIT(1) << RGS_IRQ_STATUS_SHIFT)
+
+#define MTK_DP_TOP_IRQ_MASK (TOP_OFFSET + 0x2C)
+#define IRQ_MASK_AUX_TOP_IRQ BIT(2)
+
+#define MTK_DP_TOP_MEM_PD (TOP_OFFSET + 0x38)
+#define MEM_ISO_EN_SHIFT 0
+#define FUSE_SEL_SHIFT 2
+
+#endif /*_MTK_DP_REG_H_*/
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
index 384074f69111b..e6e88e3cd811d 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
@@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
 	MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
 };
 
+enum TVDPLL_CLK {
+	TVDPLL_PLL = 0,
+	TVDPLL_D2 = 2,
+	TVDPLL_D4 = 4,
+	TVDPLL_D8 = 8,
+	TVDPLL_D16 = 16,
+};
+
 struct mtk_dpi {
 	struct drm_encoder encoder;
 	struct drm_bridge bridge;
@@ -71,8 +79,10 @@ struct mtk_dpi {
 	void __iomem *regs;
 	struct device *dev;
 	struct clk *engine_clk;
+	struct clk *dpi_ck_cg;
 	struct clk *pixel_clk;
 	struct clk *tvd_clk;
+	struct clk *pclk_src[5];
 	int irq;
 	struct drm_display_mode mode;
 	const struct mtk_dpi_conf *conf;
@@ -135,6 +145,7 @@ struct mtk_dpi_conf {
 	u32 hvsize_mask;
 	u32 channel_swap_shift;
 	u32 yuv422_en_bit;
+	u32 csc_enable_bit;
 	const struct mtk_dpi_yc_limit *limit;
 };
 
@@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
 
 static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
 {
-	mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
+	mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
 }
 
 static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
@@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
 		mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
 }
 
+static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
+{
+	u32 matrix_sel = 0;
+
+	switch (format) {
+	case MTK_DPI_COLOR_FORMAT_YCBCR_422:
+	case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
+	case MTK_DPI_COLOR_FORMAT_YCBCR_444:
+	case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
+	case MTK_DPI_COLOR_FORMAT_XV_YCC:
+		if (dpi->mode.hdisplay <= 720)
+			matrix_sel = 0x2;
+		break;
+	default:
+		break;
+	}
+	mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
+}
+
 static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
 					enum mtk_dpi_out_color_format format)
 {
 	if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
 	    (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
 		mtk_dpi_config_yuv422_enable(dpi, false);
-		if (dpi->conf->csc_support)
+		if (dpi->conf->csc_support) {
 			mtk_dpi_config_csc_enable(dpi, true);
+			mtk_dpi_matrix_sel(dpi, format);
+		}
 		if (dpi->conf->swap_input_support)
 			mtk_dpi_config_swap_input(dpi, false);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
 	} else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
 		   (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
 		mtk_dpi_config_yuv422_enable(dpi, true);
-		if (dpi->conf->csc_support)
+		if (dpi->conf->csc_support) {
 			mtk_dpi_config_csc_enable(dpi, true);
+			mtk_dpi_matrix_sel(dpi, format);
+		}
 		if (dpi->conf->swap_input_support)
 			mtk_dpi_config_swap_input(dpi, true);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
@@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
 	mtk_dpi_disable(dpi);
 	clk_disable_unprepare(dpi->pixel_clk);
 	clk_disable_unprepare(dpi->engine_clk);
+	clk_disable_unprepare(dpi->dpi_ck_cg);
+	clk_disable_unprepare(dpi->tvd_clk);
 }
 
 static int mtk_dpi_power_on(struct mtk_dpi *dpi)
@@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
 	if (++dpi->refcount != 1)
 		return 0;
 
+	ret = clk_prepare_enable(dpi->tvd_clk);
+	if (ret) {
+		dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
+		goto err_pixel;
+	}
+
 	ret = clk_prepare_enable(dpi->engine_clk);
 	if (ret) {
 		dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
 		goto err_refcount;
 	}
 
+	ret = clk_prepare_enable(dpi->dpi_ck_cg);
+	if (ret) {
+		dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
+		goto err_ck_cg;
+	}
+
 	ret = clk_prepare_enable(dpi->pixel_clk);
 	if (ret) {
 		dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
@@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
 	if (dpi->pinctrl && dpi->pins_dpi)
 		pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
 
-	mtk_dpi_enable(dpi);
 	return 0;
 
 err_pixel:
+	clk_disable_unprepare(dpi->dpi_ck_cg);
+err_ck_cg:
 	clk_disable_unprepare(dpi->engine_clk);
 err_refcount:
 	dpi->refcount--;
@@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 	pll_rate = clk_get_rate(dpi->tvd_clk);
 
 	vm.pixelclock = pll_rate / factor;
-	if (dpi->conf->is_dpintf)
-		clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
-	else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
+	if (dpi->conf->is_dpintf) {
+		if (factor == 1)
+			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
+		else if (factor == 2)
+			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
+		else if (factor == 4)
+			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
+		else
+			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
+	} else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
 		 (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
 		clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
 	else
@@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 	}
 	mtk_dpi_sw_reset(dpi, false);
 
+	mtk_dpi_enable(dpi);
+
 	return 0;
 }
 
@@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
 	u32 *input_fmts;
 
 	*num_input_fmts = 0;
-
 	input_fmts = kcalloc(1, sizeof(*input_fmts),
 			     GFP_KERNEL);
 	if (!input_fmts)
@@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
 		if (dpi->conf->num_output_fmts)
 			out_bus_format = dpi->conf->output_fmts[0];
 
-	dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
+	dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
 		bridge_state->input_bus_cfg.format,
 		bridge_state->output_bus_cfg.format);
 
@@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
 	dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
 	dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
 	dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
-	dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
+	if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
+		dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
+	else
+		dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
 
 	return 0;
 }
@@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
 	MEDIA_BUS_FMT_RGB888_2X12_BE,
 };
 
+static const u32 mt8195_output_fmts[] = {
+	MEDIA_BUS_FMT_RGB888_1X24,
+	MEDIA_BUS_FMT_YUV8_1X24,
+	MEDIA_BUS_FMT_YUYV8_1X16,
+};
+
 static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
 	.c_bottom = 0x0010,
 	.c_top = 0x0FE0,
@@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
 	.hvsize_mask = HSIZE_MASK,
 	.channel_swap_shift = CH_SWAP,
 	.yuv422_en_bit = YUV422_EN,
+	.csc_enable_bit = CSC_ENABLE,
 	.limit = &mtk_dpi_limit,
 };
 
@@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
 	.hvsize_mask = HSIZE_MASK,
 	.channel_swap_shift = CH_SWAP,
 	.yuv422_en_bit = YUV422_EN,
+	.csc_enable_bit = CSC_ENABLE,
 	.limit = &mtk_dpi_limit,
 };
 
@@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
 	.hvsize_mask = HSIZE_MASK,
 	.channel_swap_shift = CH_SWAP,
 	.yuv422_en_bit = YUV422_EN,
+	.csc_enable_bit = CSC_ENABLE,
 	.limit = &mtk_dpi_limit,
 };
 
@@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
 	.hvsize_mask = HSIZE_MASK,
 	.channel_swap_shift = CH_SWAP,
 	.yuv422_en_bit = YUV422_EN,
+	.csc_enable_bit = CSC_ENABLE,
 	.limit = &mtk_dpi_limit,
 };
 
 static const struct mtk_dpi_conf mt8195_dpintf_conf = {
 	.cal_factor = mt8195_dpintf_calculate_factor,
-	.output_fmts = mt8173_output_fmts,
-	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.output_fmts = mt8195_output_fmts,
+	.num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
 	.is_dpintf = true,
+	.csc_support = true,
 	.dimension_mask = DPINTF_HPW_MASK,
 	.hvsize_mask = DPINTF_HSIZE_MASK,
 	.channel_swap_shift = DPINTF_CH_SWAP,
 	.yuv422_en_bit = DPINTF_YUV422_EN,
+	.csc_enable_bit = DPINTF_CSC_ENABLE,
 	.limit = &mtk_dpintf_limit,
 };
 
@@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
+	if (IS_ERR(dpi->dpi_ck_cg)) {
+		ret = PTR_ERR(dpi->dpi_ck_cg);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
+				ret);
+
+		return ret;
+	}
+
 	dpi->pixel_clk = devm_clk_get(dev, "pixel");
 	if (IS_ERR(dpi->pixel_clk)) {
 		ret = PTR_ERR(dpi->pixel_clk);
@@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
+	dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
+	dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
+	dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
+
 	dpi->irq = platform_get_irq(pdev, 0);
 	if (dpi->irq <= 0)
 		return -EINVAL;
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi_regs.h b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
index 72efe6ee25842..91b32dfffced2 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
+++ b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
@@ -41,6 +41,7 @@
 #define FAKE_DE_RODD			BIT(22)
 #define FAKE_DE_REVEN			BIT(23)
 #define DPINTF_YUV422_EN		BIT(24)
+#define DPINTF_CSC_ENABLE		BIT(26)
 #define DPINTF_INPUT_2P_EN		BIT(29)
 
 #define DPI_OUTPUT_SETTING	0x14
@@ -229,4 +230,29 @@
 
 #define EDGE_SEL_EN			BIT(5)
 #define H_FRE_2N			BIT(25)
+
+#define DPI_MATRIX_SET	0xB4
+#define INT_MATRIX_SEL			BIT(0)
+#define INT_MATRIX_SEL_MASK		(0x1F << 0)
+#define RGB_TO_JPEG			0x00
+#define RGB_TO_FULL709			0x01
+#define RGB_TO_BT601			0x02
+#define RGB_TO_BT709			0x03
+#define JPEG_TO_RGB			0x04
+#define FULL709_TO_RGB			0x05
+#define BT601_TO_RGB			0x06
+#define BT709_TO_RGB			0x07
+#define JPEG_TO_BT601			0x08
+#define JPEG_TO_BT709			0x09
+#define BT601_TO_JPEG			0xA
+#define BT709_TO_JPEG			0xB
+#define BT709_TO_BT601			0xC
+#define BT601_TO_BT709			0xD
+#define JPEG_TO_CERGB			0x14
+#define FULL709_TO_CERGB		0x15
+#define BT601_TO_CERGB			0x16
+#define BT709_TO_CERGB			0x17
+#define RGB_TO_CERGB			0x1C
+#define MATRIX_BIT			BIT(8)
+#define EXT_MATRIX_EN			BIT(12)
 #endif /* __MTK_DPI_REGS_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index 1ff4e31c86345..65c7ccaaf1969 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -672,6 +672,7 @@ static struct platform_driver * const mtk_drm_drivers[] = {
 	&mtk_disp_ovl_driver,
 	&mtk_disp_rdma_driver,
 	&mtk_dpi_driver,
+	&mtk_dp_driver,
 	&mtk_drm_platform_driver,
 	&mtk_dsi_driver,
 };
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
index 3e7d1e6fbe010..8926416f4419d 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
@@ -53,6 +53,7 @@ extern struct platform_driver mtk_disp_gamma_driver;
 extern struct platform_driver mtk_disp_ovl_driver;
 extern struct platform_driver mtk_disp_rdma_driver;
 extern struct platform_driver mtk_dpi_driver;
+extern struct platform_driver mtk_dp_driver;
 extern struct platform_driver mtk_dsi_driver;
 
 #endif /* MTK_DRM_DRV_H */
-- 
2.32.0


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

* [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-10 13:06   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-10 13:06 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Chun-Kuang Hu, Philipp Zabel,
	Matthias Brugger
  Cc: Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

From: Markus Schneider-Pargmann <msp@baylibre.com>

This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
according phy driver mediatek-dp-phy.

It supports both functional units on the mt8195, the embedded
DisplayPort as well as the external DisplayPort units. It offers
hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
to 4 lanes.

The driver creates a child device for the phy. The child device will
never exist without the parent being active. As they are sharing a
register range, the parent passes a regmap pointer to the child so that
both can work with the same register range. The phy driver sets device
data that is read by the parent to get the phy device that can be used
to control the phy properties.

This driver is based on an initial version by
Jason-JH.Lin <jason-jh.lin@mediatek.com>.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Reported-by: kernel test robot <lkp@intel.com>
---
 drivers/gpu/drm/drm_edid.c              |    2 +-
 drivers/gpu/drm/mediatek/Kconfig        |    7 +
 drivers/gpu/drm/mediatek/Makefile       |    2 +
 drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
 drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
 drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
 9 files changed, 3799 insertions(+), 13 deletions(-)
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 500279a82167a..bfd98b50ceb5b 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
 	 * modes and forbids YCRCB422 support for all video modes per
 	 * HDMI 1.3 spec.
 	 */
-	info->color_formats = DRM_COLOR_FORMAT_RGB444;
+	info->color_formats |= DRM_COLOR_FORMAT_RGB444;
 
 	/* YCRCB444 is optional according to spec. */
 	if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
index 2976d21e9a34a..029b94c716131 100644
--- a/drivers/gpu/drm/mediatek/Kconfig
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
 	select PHY_MTK_HDMI
 	help
 	  DRM/KMS HDMI driver for Mediatek SoCs
+
+config MTK_DPTX_SUPPORT
+	tristate "DRM DPTX Support for Mediatek SoCs"
+	depends on DRM_MEDIATEK
+	select PHY_MTK_DP
+	help
+	  DRM/KMS Display Port driver for Mediatek SoCs.
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index 29098d7c8307c..d86a6406055e6 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
 			  mtk_hdmi_ddc.o
 
 obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
+
+obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
new file mode 100644
index 0000000000000..83087219d5a5e
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -0,0 +1,3094 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Copyright (c) 2021 BayLibre
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/hdmi-codec.h>
+#include <video/videomode.h>
+
+#include "mtk_dp_reg.h"
+
+#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
+#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
+
+#define MTK_DP_MAX_LANES 4
+#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
+
+#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
+
+#define MTK_DP_TRAIN_RETRY_LIMIT 8
+#define MTK_DP_TRAIN_MAX_ITERATIONS 5
+
+#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
+
+#define MTK_DP_DP_VERSION_11 0x11
+
+enum mtk_dp_state {
+	MTK_DP_STATE_INITIAL,
+	MTK_DP_STATE_IDLE,
+	MTK_DP_STATE_PREPARE,
+	MTK_DP_STATE_NORMAL,
+};
+
+enum mtk_dp_train_state {
+	MTK_DP_TRAIN_STATE_STARTUP = 0,
+	MTK_DP_TRAIN_STATE_CHECKCAP,
+	MTK_DP_TRAIN_STATE_CHECKEDID,
+	MTK_DP_TRAIN_STATE_TRAINING_PRE,
+	MTK_DP_TRAIN_STATE_TRAINING,
+	MTK_DP_TRAIN_STATE_CHECKTIMING,
+	MTK_DP_TRAIN_STATE_NORMAL,
+	MTK_DP_TRAIN_STATE_POWERSAVE,
+	MTK_DP_TRAIN_STATE_DPIDLE,
+};
+
+struct mtk_dp_timings {
+	struct videomode vm;
+
+	u16 htotal;
+	u16 vtotal;
+	u8 frame_rate;
+	u32 pix_rate_khz;
+};
+
+struct mtk_dp_train_info {
+	bool tps3;
+	bool tps4;
+	bool sink_ssc;
+	bool cable_plugged_in;
+	bool cable_state_change;
+	bool cr_done;
+	bool eq_done;
+
+	// link_rate is in multiple of 0.27Gbps
+	int link_rate;
+	int lane_count;
+
+	int irq_status;
+	int check_cap_count;
+};
+
+// Same values as used by the DP Spec for MISC0 bits 1 and 2
+enum mtk_dp_color_format {
+	MTK_DP_COLOR_FORMAT_RGB_444 = 0,
+	MTK_DP_COLOR_FORMAT_YUV_422 = 1,
+	MTK_DP_COLOR_FORMAT_YUV_444 = 2,
+	MTK_DP_COLOR_FORMAT_YUV_420 = 3,
+	MTK_DP_COLOR_FORMAT_YONLY = 4,
+	MTK_DP_COLOR_FORMAT_RAW = 5,
+	MTK_DP_COLOR_FORMAT_RESERVED = 6,
+	MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
+	MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
+};
+
+// Multiple of 0.27Gbps
+enum mtk_dp_linkrate {
+	MTK_DP_LINKRATE_RBR = 0x6,
+	MTK_DP_LINKRATE_HBR = 0xA,
+	MTK_DP_LINKRATE_HBR2 = 0x14,
+	MTK_DP_LINKRATE_HBR25 = 0x19,
+	MTK_DP_LINKRATE_HBR3 = 0x1E,
+};
+
+// Same values as used for DP Spec MISC0 bits 5,6,7
+enum mtk_dp_color_depth {
+	MTK_DP_COLOR_DEPTH_6BIT = 0,
+	MTK_DP_COLOR_DEPTH_8BIT = 1,
+	MTK_DP_COLOR_DEPTH_10BIT = 2,
+	MTK_DP_COLOR_DEPTH_12BIT = 3,
+	MTK_DP_COLOR_DEPTH_16BIT = 4,
+	MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
+};
+
+struct mtk_dp_audio_cfg {
+	int sample_rate;
+	int word_length_bits;
+	int channels;
+};
+
+struct mtk_dp_info {
+	enum mtk_dp_color_depth depth;
+	enum mtk_dp_color_format format;
+	struct mtk_dp_audio_cfg audio_caps;
+	struct mtk_dp_timings timings;
+};
+
+struct dp_cal_data {
+	unsigned int glb_bias_trim;
+	unsigned int clktx_impse;
+
+	unsigned int ln0_tx_impsel_pmos;
+	unsigned int ln0_tx_impsel_nmos;
+	unsigned int ln1_tx_impsel_pmos;
+	unsigned int ln1_tx_impsel_nmos;
+	unsigned int ln2_tx_impsel_pmos;
+	unsigned int ln2_tx_impsel_nmos;
+	unsigned int ln3_tx_impsel_pmos;
+	unsigned int ln3_tx_impsel_nmos;
+};
+
+struct mtk_dp {
+	struct device *dev;
+	struct platform_device *phy_dev;
+	struct phy *phy;
+	struct dp_cal_data cal_data;
+
+	struct drm_device *drm_dev;
+	struct drm_bridge bridge;
+	struct drm_bridge *next_bridge;
+	struct drm_dp_aux aux;
+
+	struct mutex edid_lock;
+	struct edid *edid;
+
+	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
+
+	struct mtk_dp_info info;
+	enum mtk_dp_state state;
+
+	struct mtk_dp_train_info train_info;
+	enum mtk_dp_train_state train_state;
+	unsigned int input_fmt;
+
+	struct regmap *regs;
+	struct clk *dp_tx_clk;
+
+	bool enabled;
+	bool audio_enable;
+
+	bool has_fec;
+	struct mutex dp_lock;
+
+	struct mutex update_plugged_status_lock;
+
+	hdmi_codec_plugged_cb plugged_cb;
+	struct device *codec_dev;
+	u8 connector_eld[MAX_ELD_BYTES];
+};
+
+enum mtk_dp_sdp_type {
+	MTK_DP_SDP_NONE = 0x00,
+	MTK_DP_SDP_ACM = 0x01,
+	MTK_DP_SDP_ISRC = 0x02,
+	MTK_DP_SDP_AVI = 0x03,
+	MTK_DP_SDP_AUI = 0x04,
+	MTK_DP_SDP_SPD = 0x05,
+	MTK_DP_SDP_MPEG = 0x06,
+	MTK_DP_SDP_NTSC = 0x07,
+	MTK_DP_SDP_VSP = 0x08,
+	MTK_DP_SDP_VSC = 0x09,
+	MTK_DP_SDP_EXT = 0x0A,
+	MTK_DP_SDP_PPS0 = 0x0B,
+	MTK_DP_SDP_PPS1 = 0x0C,
+	MTK_DP_SDP_PPS2 = 0x0D,
+	MTK_DP_SDP_PPS3 = 0x0E,
+	MTK_DP_SDP_DRM = 0x10,
+	MTK_DP_SDP_MAX_NUM
+};
+
+struct mtk_dp_sdp_packet {
+	enum mtk_dp_sdp_type type;
+	struct dp_sdp sdp;
+};
+
+#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
+union mtk_dp_audio_channel_status {
+	struct {
+		u8 rev : 1;
+		u8 is_lpcm : 1;
+		u8 copy_right : 1;
+		u8 additional_format_info : 3;
+		u8 channel_status_mode : 2;
+		u8 category_code;
+		u8 src_num : 4;
+		u8 channel_num : 4;
+		u8 sampling_freq : 4;
+		u8 clk_accuracy : 2;
+		u8 rev2 : 2;
+		u8 word_len : 4;
+		u8 original_sampling_freq : 4;
+	} iec;
+
+	u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
+};
+
+static struct regmap_config mtk_dp_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = SEC_OFFSET + 0x90,
+	.name = "mtk-dp-registers",
+};
+
+static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
+{
+	return mtk_dp->next_bridge != NULL;
+}
+
+static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
+{
+	return container_of(b, struct mtk_dp, bridge);
+}
+
+static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
+{
+	u32 read_val;
+	int ret;
+
+	ret = regmap_read(mtk_dp->regs, offset, &read_val);
+	if (ret) {
+		dev_err(mtk_dp->dev, "Failed to read register 0x%x: %d\n",
+			offset, ret);
+		return 0;
+	}
+
+	return read_val;
+}
+
+static void mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
+{
+	int ret;
+
+	ret = regmap_write(mtk_dp->regs, offset, val);
+	if (ret)
+		dev_err(mtk_dp->dev,
+			"Failed to write register 0x%x with value 0x%x: %d\n",
+			offset, val, ret);
+}
+
+static void mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, u32 val,
+			       u32 mask)
+{
+	int ret;
+
+	ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
+	if (ret)
+		dev_err(mtk_dp->dev,
+			"Failed to update register 0x%x with value 0x%x, mask 0x%x: %d\n",
+			offset, val, mask, ret);
+}
+
+static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32 offset, u8 *buf,
+				    size_t length)
+{
+	int i;
+	int num_regs = (length + 1) / 2;
+
+	// 2 bytes per register
+	for (i = 0; i < num_regs; i++) {
+		u32 val = buf[i * 2] |
+			  (i * 2 + 1 < length ? buf[i * 2 + 1] << 8 : 0);
+
+		mtk_dp_write(mtk_dp, offset + i * 4, val);
+	}
+}
+
+static unsigned long mtk_dp_sip_atf_call(unsigned int cmd, unsigned int para)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32, cmd, para, 0, 0, 0, 0, 0,
+		      &res);
+
+	pr_debug("[DPTX]%s cmd 0x%x, p1 0x%x, ret 0x%lx-0x%lx", __func__, cmd,
+		 para, res.a0, res.a1);
+	return res.a1;
+}
+
+static void mtk_dp_msa_bypass_disable(struct mtk_dp *mtk_dp)
+{
+	const u16 bits_to_set =
+		BIT(HTOTAL_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(VTOTAL_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(HSTART_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(VSTART_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(HWIDTH_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(VHEIGHT_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(HSP_SEL_DP_ENC0_P0_SHIFT) | BIT(HSW_SEL_DP_ENC0_P0_SHIFT) |
+		BIT(VSP_SEL_DP_ENC0_P0_SHIFT) | BIT(VSW_SEL_DP_ENC0_P0_SHIFT);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, bits_to_set,
+			   bits_to_set);
+}
+
+static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
+{
+	struct mtk_dp_timings *timings = &mtk_dp->info.timings;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010, timings->htotal,
+			   HTOTAL_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
+			   timings->vm.hsync_len + timings->vm.hback_porch,
+			   HSTART_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
+			   timings->vm.hsync_len << HSW_SW_DP_ENC0_P0_SHIFT,
+			   HSW_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028, 0,
+			   HSP_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020, timings->vm.hactive,
+			   HWIDTH_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014, timings->vtotal,
+			   VTOTAL_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
+			   timings->vm.vsync_len + timings->vm.vback_porch,
+			   VSTART_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
+			   timings->vm.vsync_len << VSW_SW_DP_ENC0_P0_SHIFT,
+			   VSW_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C, 0,
+			   VSP_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024, timings->vm.vactive,
+			   VHEIGHT_SW_DP_ENC0_P0_MASK);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064, timings->vm.hactive,
+			   HDE_NUM_LAST_DP_ENC0_P0_MASK);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154, timings->htotal,
+			   PGEN_HTOTAL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
+			   timings->vm.hfront_porch,
+			   PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C, timings->vm.hsync_len,
+			   PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
+			   timings->vm.hback_porch + timings->vm.hsync_len,
+			   PGEN_HFDE_START_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164, timings->vm.hactive,
+			   PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168, timings->vtotal,
+			   PGEN_VTOTAL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
+			   timings->vm.vfront_porch,
+			   PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170, timings->vm.vsync_len,
+			   PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
+			   timings->vm.vback_porch + timings->vm.vsync_len,
+			   PGEN_VFDE_START_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178, timings->vm.vactive,
+			   PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
+				    enum mtk_dp_color_format color_format)
+{
+	u32 val;
+
+	mtk_dp->info.format = color_format;
+
+	// Update MISC0
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
+			   color_format << DP_TEST_COLOR_FORMAT_SHIFT,
+			   DP_TEST_COLOR_FORMAT_MASK);
+
+	switch (color_format) {
+	case MTK_DP_COLOR_FORMAT_YUV_422:
+		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
+		break;
+	case MTK_DP_COLOR_FORMAT_YUV_420:
+		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420;
+		break;
+	case MTK_DP_COLOR_FORMAT_YONLY:
+	case MTK_DP_COLOR_FORMAT_RAW:
+	case MTK_DP_COLOR_FORMAT_RESERVED:
+	case MTK_DP_COLOR_FORMAT_UNKNOWN:
+		drm_warn(mtk_dp->drm_dev, "Unsupported color format: %d\n",
+			 color_format);
+		fallthrough;
+	case MTK_DP_COLOR_FORMAT_RGB_444:
+	case MTK_DP_COLOR_FORMAT_YUV_444:
+		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
+		break;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
+			   PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp,
+				   enum mtk_dp_color_depth color_depth)
+{
+	u32 val;
+
+	mtk_dp->info.depth = color_depth;
+
+	// Update MISC0
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
+			   color_depth << DP_TEST_BIT_DEPTH_SHIFT,
+			   DP_TEST_BIT_DEPTH_MASK);
+
+	switch (color_depth) {
+	case MTK_DP_COLOR_DEPTH_6BIT:
+		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT;
+		break;
+	case MTK_DP_COLOR_DEPTH_8BIT:
+		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT;
+		break;
+	case MTK_DP_COLOR_DEPTH_10BIT:
+		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT;
+		break;
+	case MTK_DP_COLOR_DEPTH_12BIT:
+		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT;
+		break;
+	case MTK_DP_COLOR_DEPTH_16BIT:
+		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT;
+		break;
+	case MTK_DP_COLOR_DEPTH_UNKNOWN:
+		drm_warn(mtk_dp->drm_dev, "Unsupported color depth %d\n",
+			 color_depth);
+		return;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
+			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_mn_overwrite_disable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
+			   VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32 val)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   val << SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT,
+			   SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   BIT(VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT),
+			   VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
+			   0x20 << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
+			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
+			   0x20 << SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT,
+			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
+			   2 << VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT,
+			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
+			   4 << FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT,
+			   FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
+	mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368,
+		     1 << VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT |
+			     1 << VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT |
+			     BIT(SDP_DP13_EN_DP_ENC1_P0_SHIFT) |
+			     1 << BS2BS_MODE_DP_ENC1_P0_SHIFT);
+}
+
+static void mtk_dp_pg_disable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038, 0,
+			   VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
+			   4 << PGEN_PATTERN_SEL_SHIFT, PGEN_PATTERN_SEL_MASK);
+}
+
+static void mtk_dp_audio_setup_channels(struct mtk_dp *mtk_dp,
+					struct mtk_dp_audio_cfg *cfg)
+{
+	u32 channel_enable_bits;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3324,
+			   AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX,
+			   AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK);
+
+	//audio channel count change reset
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(9), BIT(9));
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
+			   AU_PRTY_REGEN_DP_ENC1_P0_MASK,
+			   AU_PRTY_REGEN_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
+			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK,
+			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
+			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK,
+			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK);
+
+	switch (cfg->channels) {
+	case 2:
+		channel_enable_bits = AUDIO_2CH_SEL_DP_ENC0_P0_MASK |
+				      AUDIO_2CH_EN_DP_ENC0_P0_MASK;
+		break;
+	case 8:
+	default:
+		channel_enable_bits = AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
+				      AUDIO_8CH_EN_DP_ENC0_P0_MASK;
+		break;
+	}
+	mtk_dp_update_bits(
+		mtk_dp, MTK_DP_ENC0_P0_3088,
+		channel_enable_bits | AU_EN_DP_ENC0_P0_MASK,
+		AUDIO_2CH_SEL_DP_ENC0_P0_MASK | AUDIO_2CH_EN_DP_ENC0_P0_MASK |
+			AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
+			AUDIO_8CH_EN_DP_ENC0_P0_MASK | AU_EN_DP_ENC0_P0_MASK);
+
+	//audio channel count change reset
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, 0, BIT(9));
+
+	//enable audio reset
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(0), BIT(0));
+}
+
+static void mtk_dp_audio_channel_status_set(struct mtk_dp *mtk_dp,
+					    struct mtk_dp_audio_cfg *cfg)
+{
+	union mtk_dp_audio_channel_status channel_status;
+
+	memset(&channel_status, 0, sizeof(channel_status));
+
+	switch (cfg->sample_rate) {
+	case 32000:
+		channel_status.iec.sampling_freq = 3;
+		break;
+	case 44100:
+		channel_status.iec.sampling_freq = 0;
+		break;
+	case 48000:
+		channel_status.iec.sampling_freq = 2;
+		break;
+	case 88200:
+		channel_status.iec.sampling_freq = 8;
+		break;
+	case 96000:
+		channel_status.iec.sampling_freq = 0xA;
+		break;
+	case 192000:
+		channel_status.iec.sampling_freq = 0xE;
+		break;
+	default:
+		channel_status.iec.sampling_freq = 0x1;
+		break;
+	}
+
+	switch (cfg->word_length_bits) {
+	case 16:
+		channel_status.iec.word_len = 0x02;
+		break;
+	case 20:
+		channel_status.iec.word_len = 0x03;
+		break;
+	case 24:
+		channel_status.iec.word_len = 0x0B;
+		break;
+	}
+
+	// IEC 60958 consumer channel status bits
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_308C,
+			   channel_status.buf[1] << 8 | channel_status.buf[0],
+			   CH_STATUS_0_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3090,
+			   channel_status.buf[3] << 8 | channel_status.buf[2],
+			   CH_STATUS_1_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3094, channel_status.buf[4],
+			   CH_STATUS_2_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_audio_sdp_asp_set_channels(struct mtk_dp *mtk_dp,
+					      int channels)
+{
+	if (channels != 2 && channels != 8)
+		channels = 8;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_312C,
+			   (channels - 1) << ASP_HB3_DP_ENC0_P0_SHIFT,
+			   ASP_HB2_DP_ENC0_P0_MASK | ASP_HB3_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_audio_set_divider(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
+			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2,
+			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK);
+}
+
+static bool mtk_dp_plug_state(struct mtk_dp *mtk_dp)
+{
+	return !!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
+		  HPD_DB_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_sdp_trigger_packet(struct mtk_dp *mtk_dp,
+				      enum mtk_dp_sdp_type type)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, type,
+			   SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, SDP_PACKET_W_DP_ENC1_P0,
+			   SDP_PACKET_W_DP_ENC1_P0);
+}
+
+static void mtk_dp_sdp_set_data(struct mtk_dp *mtk_dp, u8 *data_bytes)
+{
+	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_ENC1_P0_3200, data_bytes, 0x10);
+}
+
+static void mtk_dp_sdp_set_header(struct mtk_dp *mtk_dp,
+				  enum mtk_dp_sdp_type type,
+				  struct dp_sdp_header *header)
+{
+	u32 db_addr;
+
+	switch (type) {
+	case MTK_DP_SDP_DRM:
+		db_addr = MTK_DP_ENC0_P0_3138;
+		break;
+	case MTK_DP_SDP_PPS0:
+	case MTK_DP_SDP_PPS1:
+	case MTK_DP_SDP_PPS2:
+	case MTK_DP_SDP_PPS3:
+		db_addr = MTK_DP_ENC0_P0_3130;
+		break;
+	default:
+		db_addr = MTK_DP_ENC0_P0_30D8 + (type - MTK_DP_SDP_ACM) * 8;
+	}
+
+	mtk_dp_bulk_16bit_write(mtk_dp, db_addr, (u8 *)header, 4);
+}
+
+static const u32 mtk_dp_sdp_type_to_reg[MTK_DP_SDP_MAX_NUM] = {
+	/* MTK_DP_SDP_NONE => */ 0x0,
+	/* MTK_DP_SDP_ACM  => */ MTK_DP_ENC0_P0_30B4,
+	/* MTK_DP_SDP_ISRC => */ MTK_DP_ENC0_P0_30B4 + 1,
+	/* MTK_DP_SDP_AVI  => */ MTK_DP_ENC0_P0_30A4 + 1,
+	/* MTK_DP_SDP_AUI  => */ MTK_DP_ENC0_P0_30A8,
+	/* MTK_DP_SDP_SPD  => */ MTK_DP_ENC0_P0_30A8 + 1,
+	/* MTK_DP_SDP_MPEG => */ MTK_DP_ENC0_P0_30AC,
+	/* MTK_DP_SDP_NTSC => */ MTK_DP_ENC0_P0_30AC + 1,
+	/* MTK_DP_SDP_VSP  => */ MTK_DP_ENC0_P0_30B0,
+	/* MTK_DP_SDP_VSC  => */ MTK_DP_ENC0_P0_30B8,
+	/* MTK_DP_SDP_EXT  => */ MTK_DP_ENC0_P0_30B0 + 1,
+	/* MTK_DP_SDP_PPS0 => */ MTK_DP_ENC0_P0_31E8,
+	/* MTK_DP_SDP_PPS1 => */ MTK_DP_ENC0_P0_31E8,
+	/* MTK_DP_SDP_PPS2 => */ MTK_DP_ENC0_P0_31E8,
+	/* MTK_DP_SDP_PPS3 => */ MTK_DP_ENC0_P0_31E8,
+	/* MTK_DP_SDP_DRM  => */ MTK_DP_ENC0_P0_31DC,
+};
+
+static void mtk_dp_disable_sdp(struct mtk_dp *mtk_dp, enum mtk_dp_sdp_type type)
+{
+	if (type == MTK_DP_SDP_NONE)
+		return;
+
+	// Disable periodic send
+	mtk_dp_update_bits(mtk_dp, mtk_dp_sdp_type_to_reg[type] & 0xfffc, 0,
+			   0xFF << ((mtk_dp_sdp_type_to_reg[type] & 3) * 8));
+}
+
+static void mtk_dp_setup_sdp(struct mtk_dp *mtk_dp,
+			     struct mtk_dp_sdp_packet *packet)
+{
+	mtk_dp_sdp_set_data(mtk_dp, packet->sdp.db);
+	mtk_dp_sdp_set_header(mtk_dp, packet->type, &packet->sdp.sdp_header);
+
+	mtk_dp_disable_sdp(mtk_dp, packet->type);
+
+	switch (packet->type) {
+	case MTK_DP_SDP_NONE:
+		break;
+	case MTK_DP_SDP_ISRC:
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
+				   0x1C << ISRC1_HB3_DP_ENC0_P0_SHIFT,
+				   ISRC1_HB3_DP_ENC0_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, MTK_DP_SDP_ISRC,
+				   SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
+
+		if (packet->sdp.sdp_header.HB3 & BIT(2))
+			mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
+					   BIT(ISRC_CONT_DP_ENC0_P0_SHIFT),
+					   ISRC_CONT_DP_ENC0_P0_MASK);
+		else
+			mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC, 0,
+					   ISRC_CONT_DP_ENC0_P0_MASK);
+
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280,
+				   SDP_PACKET_W_DP_ENC1_P0,
+				   SDP_PACKET_W_DP_ENC1_P0);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30B4,
+				   5 << ISRC_CFG_DP_ENC0_P0_SHIFT,
+				   ISRC_CFG_DP_ENC0_P0_MASK);
+		break;
+	case MTK_DP_SDP_DRM:
+		mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31DC,
+				   5 << HDR0_CFG_DP_ENC0_P0_SHIFT,
+				   HDR0_CFG_DP_ENC0_P0_MASK);
+		break;
+	case MTK_DP_SDP_ACM:
+	case MTK_DP_SDP_AVI:
+	case MTK_DP_SDP_AUI:
+	case MTK_DP_SDP_SPD:
+	case MTK_DP_SDP_MPEG:
+	case MTK_DP_SDP_NTSC:
+	case MTK_DP_SDP_VSP:
+	case MTK_DP_SDP_VSC:
+	case MTK_DP_SDP_EXT:
+	case MTK_DP_SDP_PPS0:
+	case MTK_DP_SDP_PPS1:
+	case MTK_DP_SDP_PPS2:
+	case MTK_DP_SDP_PPS3:
+		mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
+		// Enable periodic sending
+		mtk_dp_update_bits(
+			mtk_dp, mtk_dp_sdp_type_to_reg[packet->type] & 0xfffc,
+			0x05 << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
+				 8),
+			0xff << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
+				 8));
+		break;
+	default:
+		break;
+	}
+}
+
+static void mtk_dp_sdp_vsc_ext_disable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A0, 0,
+			   BIT(7) | BIT(8) | BIT(12));
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_328C, 0, BIT(7));
+}
+
+static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_write(
+		mtk_dp, MTK_DP_AUX_P0_3640,
+		BIT(AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
+			BIT(AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT));
+}
+
+static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32 addr)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644, cmd,
+			   MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648, addr,
+			   MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C, addr >> 16,
+			   MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_aux_cmd_complete(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
+			   BIT(MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT),
+			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK |
+				   PHY_FIFO_RST_AUX_TX_P0_MASK |
+				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
+			   BIT(AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT),
+			   AUX_TX_REQUEST_READY_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8 *buf,
+				       size_t length)
+{
+	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf, length);
+}
+
+static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
+				    size_t length, int read_delay)
+{
+	int read_pos;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620, 0,
+			   AUX_RD_MODE_AUX_TX_P0_MASK);
+
+	for (read_pos = 0; read_pos < length; read_pos++) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
+				   BIT(AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT),
+				   AUX_RX_FIFO_READ_PULSE_TX_P0_MASK);
+		usleep_range(read_delay, read_delay * 2);
+		buf[read_pos] =
+			(u8)(mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3620) &
+			     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK >>
+				     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT);
+	}
+}
+
+static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t length)
+{
+	if (length > 0) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
+				   (length - 1)
+					   << MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT,
+				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
+				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
+					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	} else {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
+				   BIT(AUX_NO_LENGTH_AUX_TX_P0_SHIFT),
+				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
+					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	}
+}
+
+static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp, bool is_read)
+{
+	int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
+
+	while (--wait_reply) {
+		u32 aux_irq_status;
+
+		if (is_read) {
+			u32 fifo_status =
+				mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3618);
+
+			if (fifo_status &
+			    (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
+			     AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
+				return 0;
+			}
+		}
+
+		aux_irq_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3640);
+		if (aux_irq_status & AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK)
+			return 0;
+
+		if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK)
+			return -ETIMEDOUT;
+
+		usleep_range(100, 500);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool is_read, u8 cmd,
+				  u32 addr, u8 *buf, size_t length)
+{
+	int ret;
+	u32 reply_cmd;
+
+	if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
+			(cmd == DP_AUX_NATIVE_READ && !length)))
+		return -EINVAL;
+
+	if (!is_read)
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
+				   BIT(AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT),
+				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK);
+
+	mtk_dp_aux_cmd_complete(mtk_dp);
+	mtk_dp_aux_irq_clear(mtk_dp);
+	usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
+		     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
+
+	mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
+	mtk_dp_aux_set_length(mtk_dp, length);
+
+	if (!is_read) {
+		if (length)
+			mtk_dp_aux_fill_write_fifo(mtk_dp, buf, length);
+
+		mtk_dp_update_bits(
+			mtk_dp, MTK_DP_AUX_P0_3704,
+			AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK,
+			AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK);
+	}
+
+	mtk_dp_aux_request_ready(mtk_dp);
+
+	ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
+
+	reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
+		    AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
+
+	if (ret || reply_cmd) {
+		u32 phy_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3628) &
+				 AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
+		if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
+			drm_err(mtk_dp->drm_dev,
+				"AUX Rx Aux hang, need SW reset\n");
+			ret = -EIO;
+		}
+
+		mtk_dp_aux_cmd_complete(mtk_dp);
+		mtk_dp_aux_irq_clear(mtk_dp);
+
+		usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
+			     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
+		return -ETIMEDOUT;
+	}
+
+	if (!length) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
+				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
+					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	} else if (is_read) {
+		int read_delay;
+
+		if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
+		    cmd == DP_AUX_I2C_READ)
+			read_delay = 500;
+		else
+			read_delay = 100;
+
+		mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length, read_delay);
+	}
+
+	mtk_dp_aux_cmd_complete(mtk_dp);
+	mtk_dp_aux_irq_clear(mtk_dp);
+	usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
+		     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
+
+	return 0;
+}
+
+static bool mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int lane_num,
+					  int swing_val, int preemphasis)
+{
+	u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
+
+	if (lane_num < 0 || lane_num > 3)
+		return false;
+
+	dev_dbg(mtk_dp->dev,
+		"link training swing_val= 0x%x, preemphasis = 0x%x\n",
+		swing_val, preemphasis);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+			   swing_val << (DP_TX0_VOLT_SWING_SHIFT + lane_shift),
+			   DP_TX0_VOLT_SWING_MASK << lane_shift);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+			   preemphasis << (DP_TX0_PRE_EMPH_SHIFT + lane_shift),
+			   DP_TX0_PRE_EMPH_MASK << lane_shift);
+
+	return true;
+}
+
+static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP, 0,
+			   DP_TX0_VOLT_SWING_MASK | DP_TX1_VOLT_SWING_MASK |
+				   DP_TX2_VOLT_SWING_MASK |
+				   DP_TX3_VOLT_SWING_MASK |
+				   DP_TX0_PRE_EMPH_MASK | DP_TX1_PRE_EMPH_MASK |
+				   DP_TX2_PRE_EMPH_MASK | DP_TX3_PRE_EMPH_MASK);
+}
+
+static void mtk_dp_fec_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
+			   enable ? BIT(FEC_EN_DP_TRANS_P0_SHIFT) : 0,
+			   FEC_EN_DP_TRANS_P0_MASK);
+}
+
+static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
+{
+	u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
+			 SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
+
+	if (irq_status) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, irq_status,
+				   SW_IRQ_CLR_DP_TRANS_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, 0,
+				   SW_IRQ_CLR_DP_TRANS_P0_MASK);
+	}
+
+	return irq_status;
+}
+
+static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
+{
+	u8 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
+			 IRQ_STATUS_DP_TRANS_P0_MASK) >>
+			IRQ_STATUS_DP_TRANS_P0_SHIFT;
+
+	if (irq_status) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, irq_status,
+				   IRQ_CLR_DP_TRANS_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, 0,
+				   IRQ_CLR_DP_TRANS_P0_MASK);
+	}
+
+	return irq_status;
+}
+
+static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	u32 val = 0;
+
+	if (!enable)
+		val = IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
+		      IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
+		      IRQ_MASK_DP_TRANS_P0_INT_IRQ;
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, val,
+			   IRQ_MASK_DP_TRANS_P0_MASK);
+}
+
+void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
+			   XTAL_FREQ_DP_TRANS_P0_DEFAULT,
+			   XTAL_FREQ_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
+			   BIT(FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT),
+			   FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
+			   BIT(AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT),
+			   AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
+			   SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK, IRQ_MASK_AUX_TOP_IRQ,
+			   IRQ_MASK_AUX_TOP_IRQ);
+}
+
+static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp *mtk_dp)
+{
+	// Debounce threshold
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   8 << HPD_DEB_THD_DP_TRANS_P0_SHIFT,
+			   HPD_DEB_THD_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
+			    HPD_INT_THD_DP_TRANS_P0_UPPER_1100US)
+				   << HPD_INT_THD_DP_TRANS_P0_SHIFT,
+			   HPD_INT_THD_DP_TRANS_P0_MASK);
+
+	// Connect threshold 1.5ms + 5 x 0.1ms = 2ms
+	// Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   (5 << HPD_DISC_THD_DP_TRANS_P0_SHIFT) |
+				   (5 << HPD_CONN_THD_DP_TRANS_P0_SHIFT),
+			   HPD_DISC_THD_DP_TRANS_P0_MASK |
+				   HPD_CONN_THD_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
+			   HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
+			   HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
+{
+	// modify timeout threshold = 1595 [12 : 8]
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C, 0x1595,
+			   AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658, 0,
+			   AUX_TX_OV_EN_AUX_TX_P0_MASK);
+	// 25 for 26M
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
+			   25 << AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT,
+			   AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
+	// 13 for 26M
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
+			   13 << AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT,
+			   AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
+			   BIT(MTK_ATOP_EN_AUX_TX_P0_SHIFT),
+			   MTK_ATOP_EN_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_initialize_digital_settings(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
+			   VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
+	mtk_dp_set_color_format(mtk_dp, MTK_DP_COLOR_FORMAT_RGB_444);
+	mtk_dp_set_color_depth(mtk_dp, MTK_DP_COLOR_DEPTH_8BIT);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
+			   1 << BS2BS_MODE_DP_ENC1_P0_SHIFT,
+			   BS2BS_MODE_DP_ENC1_P0_MASK);
+
+	// dp tx encoder reset all sw
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
+			   BIT(DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT),
+			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
+	usleep_range(1000, 5000);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
+			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
+			   BIT(DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT),
+			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
+	usleep_range(1000, 5000);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C, 0,
+			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
+			   lanes == 0 ? 0 : BIT(3), BIT(3) | BIT(2));
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, lanes,
+			   LANE_NUM_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
+			   lanes << LANE_NUM_DP_TRANS_P0_SHIFT,
+			   LANE_NUM_DP_TRANS_P0_MASK);
+}
+
+static int link_rate_to_mb_per_s(struct mtk_dp *mtk_dp,
+				 enum mtk_dp_linkrate linkrate)
+{
+	switch (linkrate) {
+	default:
+		drm_err(mtk_dp->drm_dev,
+			"Implementation error, unknown linkrate %d\n",
+			linkrate);
+		fallthrough;
+	case MTK_DP_LINKRATE_RBR:
+		return 1620;
+	case MTK_DP_LINKRATE_HBR:
+		return 2700;
+	case MTK_DP_LINKRATE_HBR2:
+		return 5400;
+	case MTK_DP_LINKRATE_HBR3:
+		return 8100;
+	}
+}
+
+static u32 check_cal_data_valid(u32 min, u32 max, u32 val, u32 default_val)
+{
+	if (val < min || val > max)
+		return default_val;
+
+	return val;
+}
+
+static int mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
+{
+	struct dp_cal_data *cal_data = &mtk_dp->cal_data;
+	struct device *dev = mtk_dp->dev;
+	struct nvmem_cell *cell;
+	u32 *buf;
+	size_t len;
+
+	cell = nvmem_cell_get(dev, "dp_calibration_data");
+	if (IS_ERR(cell)) {
+		dev_err(dev,
+			"Error: Failed to get nvmem cell dp_calibration_data\n");
+		return PTR_ERR(cell);
+	}
+
+	buf = (u32 *)nvmem_cell_read(cell, &len);
+	nvmem_cell_put(cell);
+
+	if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
+		dev_err(dev,
+			"Error: Failed to read nvmem_cell_read fail len %ld\n",
+			(len / sizeof(u32)));
+		return PTR_ERR(buf);
+	}
+
+	if (mtk_dp_is_edp(mtk_dp)) {
+		cal_data->glb_bias_trim = check_cal_data_valid(
+			1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf);
+		cal_data->clktx_impse =
+			check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8);
+		cal_data->ln0_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8);
+		cal_data->ln0_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8);
+		cal_data->ln1_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8);
+		cal_data->ln1_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8);
+		cal_data->ln2_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8);
+		cal_data->ln2_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8);
+		cal_data->ln3_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8);
+		cal_data->ln3_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8);
+	} else {
+		cal_data->glb_bias_trim = check_cal_data_valid(
+			1, 0x1e, (buf[0] >> 27) & 0x1f, 0xf);
+		cal_data->clktx_impse =
+			check_cal_data_valid(1, 0xe, (buf[0] >> 13) & 0xf, 0x8);
+		cal_data->ln0_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 28) & 0xf, 0x8);
+		cal_data->ln0_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 24) & 0xf, 0x8);
+		cal_data->ln1_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 20) & 0xf, 0x8);
+		cal_data->ln1_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 16) & 0xf, 0x8);
+		cal_data->ln2_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 12) & 0xf, 0x8);
+		cal_data->ln2_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 8) & 0xf, 0x8);
+		cal_data->ln3_tx_impsel_pmos =
+			check_cal_data_valid(1, 0xe, (buf[1] >> 4) & 0xf, 0x8);
+		cal_data->ln3_tx_impsel_nmos =
+			check_cal_data_valid(1, 0xe, buf[1] & 0xf, 0x8);
+	}
+
+	kfree(buf);
+
+	return 0;
+}
+
+void mtk_dp_set_cal_data(struct mtk_dp *mtk_dp)
+{
+	struct dp_cal_data *cal_data = &mtk_dp->cal_data;
+
+	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
+			   cal_data->clktx_impse << 20, RG_CKM_PT0_CKTX_IMPSEL);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
+			   cal_data->glb_bias_trim << 16,
+			   RG_XTP_GLB_BIAS_INTR_CTRL);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
+			   cal_data->ln0_tx_impsel_pmos << 12,
+			   RG_XTP_LN0_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
+			   cal_data->ln0_tx_impsel_nmos << 16,
+			   RG_XTP_LN0_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
+			   cal_data->ln1_tx_impsel_pmos << 12,
+			   RG_XTP_LN1_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
+			   cal_data->ln1_tx_impsel_nmos << 16,
+			   RG_XTP_LN1_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
+			   cal_data->ln2_tx_impsel_pmos << 12,
+			   RG_XTP_LN2_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
+			   cal_data->ln2_tx_impsel_nmos << 16,
+			   RG_XTP_LN2_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
+			   cal_data->ln3_tx_impsel_pmos << 12,
+			   RG_XTP_LN3_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
+			   cal_data->ln3_tx_impsel_nmos << 16,
+			   RG_XTP_LN3_TX_IMPSEL_NMOS);
+}
+
+static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
+				enum mtk_dp_linkrate link_rate, int lane_count)
+{
+	int ret;
+	union phy_configure_opts
+		phy_opts = { .dp = {
+				     .link_rate = link_rate_to_mb_per_s(
+					     mtk_dp, link_rate),
+				     .set_rate = 1,
+				     .lanes = lane_count,
+				     .set_lanes = 1,
+				     .ssc = mtk_dp->train_info.sink_ssc,
+			     } };
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP,
+			   DP_PWR_STATE_MASK);
+
+	ret = phy_configure(mtk_dp->phy, &phy_opts);
+
+	mtk_dp_set_cal_data(mtk_dp);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL_LANE, DP_PWR_STATE_MASK);
+	return ret;
+}
+
+static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool enable)
+{
+	const u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
+			POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
+			POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
+			POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580, enable ? val : 0, val);
+}
+
+static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int pattern)
+{
+	if (pattern < 0 || pattern > 4) {
+		drm_err(mtk_dp->drm_dev,
+			"Implementation error, no such pattern %d\n", pattern);
+		return;
+	}
+
+	if (pattern == 1) // TPS1
+		mtk_dp_set_idle_pattern(mtk_dp, false);
+
+	mtk_dp_update_bits(
+		mtk_dp, MTK_DP_TRANS_P0_3400,
+		pattern ? BIT(pattern - 1) << PATTERN1_EN_DP_TRANS_P0_SHIFT : 0,
+		PATTERN1_EN_DP_TRANS_P0_MASK | PATTERN2_EN_DP_TRANS_P0_MASK |
+			PATTERN3_EN_DP_TRANS_P0_MASK |
+			PATTERN4_EN_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
+			   enable ? BIT(ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT) : 0,
+			   ENHANCED_FRAME_EN_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
+			   enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
+			   DP_SCR_EN_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
+{
+	u32 val = BIT(VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT);
+
+	if (enable)
+		val |= BIT(VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, val,
+			   VIDEO_MUTE_SEL_DP_ENC0_P0_MASK |
+				   VIDEO_MUTE_SW_DP_ENC0_P0_MASK);
+
+	if (mtk_dp_is_edp(mtk_dp))
+		mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable);
+	else
+		mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable);
+}
+
+static void mtk_dp_audio_mute(struct mtk_dp *mtk_dp, bool mute)
+{
+	if (mute) {
+		mtk_dp_update_bits(
+			mtk_dp, MTK_DP_ENC0_P0_3030,
+			BIT(VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT) |
+				BIT(VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT),
+			VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
+				VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
+
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088, 0,
+				   AU_EN_DP_ENC0_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0,
+				   AU_TS_CFG_DP_ENC0_P0_MASK);
+
+	} else {
+		mtk_dp_update_bits(
+			mtk_dp, MTK_DP_ENC0_P0_3030, 0,
+			VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
+				VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
+
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088,
+				   BIT(AU_EN_DP_ENC0_P0_SHIFT),
+				   AU_EN_DP_ENC0_P0_MASK);
+		// Send one every two frames
+		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0x0F,
+				   AU_TS_CFG_DP_ENC0_P0_MASK);
+	}
+}
+
+static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, 0,
+			   SW_RST_B_PHYD);
+	usleep_range(10, 200);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, SW_RST_B_PHYD,
+			   SW_RST_B_PHYD);
+}
+
+static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
+	usleep_range(10, 200);
+	mtk_dp_write(mtk_dp, MTK_DP_0034,
+		     DA_CKM_CKTX0_EN_FORCE_EN | DA_CKM_BIAS_LPF_EN_FORCE_VAL |
+			     DA_CKM_BIAS_EN_FORCE_VAL |
+			     DA_XTP_GLB_LDO_EN_FORCE_VAL |
+			     DA_XTP_GLB_AVD10_ON_FORCE_VAL);
+	// Disable RX
+	mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
+	mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
+		     0x550 | BIT(FUSE_SEL_SHIFT) | BIT(MEM_ISO_EN_SHIFT));
+}
+
+static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
+{
+	mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR2;
+	mtk_dp->train_info.lane_count = MTK_DP_MAX_LANES;
+	mtk_dp->train_info.irq_status = 0;
+	mtk_dp->train_info.cable_plugged_in = false;
+	mtk_dp->train_info.cable_state_change = false;
+	mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
+	mtk_dp->state = MTK_DP_STATE_INITIAL;
+
+	mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
+	mtk_dp->info.depth = MTK_DP_COLOR_DEPTH_8BIT;
+	memset(&mtk_dp->info.timings, 0, sizeof(struct mtk_dp_timings));
+	mtk_dp->info.timings.frame_rate = 60;
+
+	mtk_dp->has_fec = false;
+	mtk_dp->audio_enable = false;
+}
+
+static void mtk_dp_sdp_set_down_cnt_init(struct mtk_dp *mtk_dp,
+					 u32 sram_read_start)
+{
+	u32 sdp_down_cnt_init = 0;
+	u32 dc_offset;
+
+	if (mtk_dp->info.timings.pix_rate_khz > 0)
+		sdp_down_cnt_init = sram_read_start *
+				    mtk_dp->train_info.link_rate * 2700 * 8 /
+				    (mtk_dp->info.timings.pix_rate_khz * 4);
+
+	switch (mtk_dp->train_info.lane_count) {
+	case 1:
+		sdp_down_cnt_init = sdp_down_cnt_init > 0x1A ?
+						  sdp_down_cnt_init :
+						  0x1A; //26
+		break;
+	case 2:
+		// case for LowResolution && High Audio Sample Rate
+		dc_offset = mtk_dp->info.timings.vtotal <= 525 ? 0x04 : 0x00;
+		sdp_down_cnt_init = sdp_down_cnt_init > 0x10 ?
+						  sdp_down_cnt_init :
+						  0x10 + dc_offset; //20 or 16
+		break;
+	case 4:
+	default:
+		sdp_down_cnt_init =
+			sdp_down_cnt_init > 0x06 ? sdp_down_cnt_init : 0x06; //6
+		break;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
+			   sdp_down_cnt_init
+				   << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
+			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_sdp_set_down_cnt_init_in_hblank(struct mtk_dp *mtk_dp)
+{
+	int pix_clk_mhz;
+	u32 dc_offset;
+	u32 spd_down_cnt_init = 0;
+
+	pix_clk_mhz = mtk_dp->info.format == MTK_DP_COLOR_FORMAT_YUV_420 ?
+				    mtk_dp->info.timings.pix_rate_khz / 2000 :
+				    mtk_dp->info.timings.pix_rate_khz / 1000;
+
+	switch (mtk_dp->train_info.lane_count) {
+	case 1:
+		spd_down_cnt_init = 0x20;
+		break;
+	case 2:
+		dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x14 : 0x00;
+		spd_down_cnt_init = 0x18 + dc_offset;
+		break;
+	case 4:
+	default:
+		dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x08 : 0x00;
+		if (pix_clk_mhz > mtk_dp->train_info.link_rate * 27)
+			spd_down_cnt_init = 0x8;
+		else
+			spd_down_cnt_init = 0x10 + dc_offset;
+		break;
+	}
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364, spd_down_cnt_init,
+			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
+}
+
+static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
+{
+	u32 sram_read_start = MTK_DP_TBC_BUF_READ_START_ADDR;
+
+	if (mtk_dp->train_info.lane_count > 0) {
+		sram_read_start = min_t(
+			u32, MTK_DP_TBC_BUF_READ_START_ADDR,
+			mtk_dp->info.timings.vm.hactive /
+				(mtk_dp->train_info.lane_count * 4 * 2 * 2));
+		mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
+	}
+
+	mtk_dp_setup_encoder(mtk_dp);
+	mtk_dp_sdp_set_down_cnt_init_in_hblank(mtk_dp);
+	mtk_dp_sdp_set_down_cnt_init(mtk_dp, sram_read_start);
+}
+
+static void mtk_dp_calculate_pixrate(struct mtk_dp *mtk_dp)
+{
+	int target_frame_rate = 60;
+	int target_pixel_clk;
+
+	if (mtk_dp->info.timings.frame_rate > 0) {
+		target_frame_rate = mtk_dp->info.timings.frame_rate;
+		target_pixel_clk = (int)mtk_dp->info.timings.htotal *
+				   (int)mtk_dp->info.timings.vtotal *
+				   target_frame_rate;
+	} else if (mtk_dp->info.timings.pix_rate_khz > 0) {
+		target_pixel_clk = mtk_dp->info.timings.pix_rate_khz * 1000;
+	} else {
+		target_pixel_clk = (int)mtk_dp->info.timings.htotal *
+				   (int)mtk_dp->info.timings.vtotal *
+				   target_frame_rate;
+	}
+
+	if (target_pixel_clk > 0)
+		mtk_dp->info.timings.pix_rate_khz = target_pixel_clk / 1000;
+}
+
+static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_msa_bypass_disable(mtk_dp);
+	mtk_dp_calculate_pixrate(mtk_dp);
+	mtk_dp_pg_disable(mtk_dp);
+	mtk_dp_setup_tu(mtk_dp);
+}
+
+static void mtk_dp_edid_free(struct mtk_dp *mtk_dp)
+{
+	mutex_lock(&mtk_dp->edid_lock);
+	kfree(mtk_dp->edid);
+	mtk_dp->edid = NULL;
+	mutex_unlock(&mtk_dp->edid_lock);
+}
+
+static void mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp)
+{
+	ssize_t ret;
+	u8 sink_count;
+	u8 link_status[DP_LINK_STATUS_SIZE] = {};
+	u32 sink_count_reg;
+	u32 link_status_reg;
+	bool locked;
+
+	sink_count_reg = DP_SINK_COUNT_ESI;
+	link_status_reg = DP_LANE0_1_STATUS;
+
+	ret = drm_dp_dpcd_readb(&mtk_dp->aux, sink_count_reg, &sink_count);
+	if (ret < 0) {
+		drm_info(mtk_dp->drm_dev, "Read sink count failed: %ld\n", ret);
+		return;
+	}
+
+	ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
+	if (ret < 0) {
+		drm_info(mtk_dp->drm_dev,
+			 "Read DP_SINK_COUNT_ESI failed: %ld\n", ret);
+		return;
+	}
+
+	ret = drm_dp_dpcd_read(&mtk_dp->aux, link_status_reg, link_status,
+			       sizeof(link_status));
+	if (!ret) {
+		drm_info(mtk_dp->drm_dev, "Read link status failed: %ld\n",
+			 ret);
+		return;
+	}
+
+	locked = drm_dp_channel_eq_ok(link_status,
+				      mtk_dp->train_info.lane_count);
+	if (!locked && mtk_dp->train_state > MTK_DP_TRAIN_STATE_TRAINING_PRE)
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
+
+	if (link_status[1] & DP_REMOTE_CONTROL_COMMAND_PENDING)
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR,
+				   DP_REMOTE_CONTROL_COMMAND_PENDING);
+
+	if (DP_GET_SINK_COUNT(sink_count) &&
+	    (link_status[2] & DP_DOWNSTREAM_PORT_STATUS_CHANGED)) {
+		mtk_dp_edid_free(mtk_dp);
+		mtk_dp->train_info.check_cap_count = 0;
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
+		msleep(20);
+	}
+}
+
+static void mtk_dp_sdp_stop_sending(struct mtk_dp *mtk_dp)
+{
+	u8 packet_type;
+
+	for (packet_type = MTK_DP_SDP_ACM; packet_type < MTK_DP_SDP_MAX_NUM;
+	     packet_type++)
+		mtk_dp_disable_sdp(mtk_dp, packet_type);
+
+	mtk_dp_sdp_vsc_ext_disable(mtk_dp);
+}
+
+static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes,
+					  u8 dpcd_adjust_req[2])
+{
+	int lane;
+
+	for (lane = 0; lane < lanes; ++lane) {
+		u8 val;
+		u8 swing;
+		u8 preemphasis;
+		int index = lane / 2;
+		int shift = lane % 2 ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
+
+		swing = (dpcd_adjust_req[index] >> shift) &
+			DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
+		preemphasis = ((dpcd_adjust_req[index] >> shift) &
+			       DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
+			      DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
+		val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
+		      preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+		if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
+			val |= DP_TRAIN_MAX_SWING_REACHED;
+		if (preemphasis == 3)
+			val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+		mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing, preemphasis);
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET + lane,
+				   val);
+	}
+
+	// Wait for the signal to be stable enough
+	usleep_range(2000, 5000);
+}
+
+static void mtk_dp_read_link_status(struct mtk_dp *mtk_dp,
+				    u8 link_status[DP_LINK_STATUS_SIZE])
+{
+	drm_dp_dpcd_read(&mtk_dp->aux, DP_LANE0_1_STATUS, link_status,
+			 DP_LINK_STATUS_SIZE);
+}
+
+static int mtk_dp_train_flow(struct mtk_dp *mtk_dp, int target_link_rate,
+			     int target_lane_count)
+{
+	u8 link_status[DP_LINK_STATUS_SIZE] = {};
+	u8 lane_adjust[2] = {};
+	bool pass_tps1 = false;
+	bool pass_tps2_3 = false;
+	int train_retries;
+	int status_control;
+	int iteration_count;
+	u8 prev_lane_adjust;
+	u8 val;
+
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET, target_link_rate);
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
+			   target_lane_count | DP_LANE_COUNT_ENHANCED_FRAME_EN);
+
+	if (mtk_dp->train_info.sink_ssc)
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
+				   DP_SPREAD_AMP_0_5);
+
+	train_retries = 0;
+	status_control = 0;
+	iteration_count = 1;
+	prev_lane_adjust = 0xFF;
+
+	mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
+	mtk_dp_phy_configure(mtk_dp, target_link_rate, target_lane_count);
+
+	dev_dbg(mtk_dp->dev,
+		"Link train target_link_rate = 0x%x, target_lane_count = 0x%x\n",
+		target_link_rate, target_lane_count);
+
+	do {
+		train_retries++;
+		if (!mtk_dp->train_info.cable_plugged_in ||
+		    ((mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) !=
+		     0x0)) {
+			return -ENODEV;
+		}
+
+		if (mtk_dp->train_state < MTK_DP_TRAIN_STATE_TRAINING)
+			return -EAGAIN;
+
+		if (!pass_tps1) {
+			mtk_dp_training_set_scramble(mtk_dp, false);
+
+			if (status_control == 0) {
+				status_control = 1;
+				mtk_dp_train_set_pattern(mtk_dp, 1);
+				val = DP_LINK_SCRAMBLING_DISABLE |
+				      DP_TRAINING_PATTERN_1;
+				drm_dp_dpcd_writeb(
+					&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+					DP_LINK_SCRAMBLING_DISABLE |
+						DP_TRAINING_PATTERN_1);
+				drm_dp_dpcd_read(&mtk_dp->aux,
+						 DP_ADJUST_REQUEST_LANE0_1,
+						 lane_adjust,
+						 sizeof(lane_adjust));
+				iteration_count++;
+
+				mtk_dp_train_update_swing_pre(
+					mtk_dp, target_lane_count, lane_adjust);
+			}
+
+			drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
+							       mtk_dp->rx_cap);
+			mtk_dp_read_link_status(mtk_dp, link_status);
+
+			if (drm_dp_clock_recovery_ok(link_status,
+						     target_lane_count)) {
+				mtk_dp->train_info.cr_done = true;
+				pass_tps1 = true;
+				train_retries = 0;
+				iteration_count = 1;
+				dev_dbg(mtk_dp->dev, "Link train CR pass\n");
+			} else if (prev_lane_adjust == link_status[4]) {
+				iteration_count++;
+				if (prev_lane_adjust &
+				    DP_ADJUST_VOLTAGE_SWING_LANE0_MASK)
+					break;
+			} else {
+				prev_lane_adjust = link_status[4];
+			}
+			dev_dbg(mtk_dp->dev, "Link train CQ fail\n");
+		} else if (pass_tps1 && !pass_tps2_3) {
+			if (status_control == 1) {
+				status_control = 2;
+				if (mtk_dp->train_info.tps4) {
+					mtk_dp_train_set_pattern(mtk_dp, 4);
+					val = DP_TRAINING_PATTERN_4;
+				} else if (mtk_dp->train_info.tps3) {
+					mtk_dp_train_set_pattern(mtk_dp, 3);
+					val = DP_LINK_SCRAMBLING_DISABLE |
+					      DP_TRAINING_PATTERN_3;
+				} else {
+					mtk_dp_train_set_pattern(mtk_dp, 2);
+					val = DP_LINK_SCRAMBLING_DISABLE |
+					      DP_TRAINING_PATTERN_2;
+				}
+				drm_dp_dpcd_writeb(&mtk_dp->aux,
+						   DP_TRAINING_PATTERN_SET,
+						   val);
+
+				drm_dp_dpcd_read(&mtk_dp->aux,
+						 DP_ADJUST_REQUEST_LANE0_1,
+						 lane_adjust,
+						 sizeof(lane_adjust));
+
+				iteration_count++;
+				mtk_dp_train_update_swing_pre(
+					mtk_dp, target_lane_count, lane_adjust);
+			}
+
+			drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
+							   mtk_dp->rx_cap);
+
+			mtk_dp_read_link_status(mtk_dp, link_status);
+
+			if (!drm_dp_clock_recovery_ok(link_status,
+						      target_lane_count)) {
+				mtk_dp->train_info.cr_done = false;
+				mtk_dp->train_info.eq_done = false;
+				dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
+				break;
+			}
+
+			if (drm_dp_channel_eq_ok(link_status,
+						 target_lane_count)) {
+				mtk_dp->train_info.eq_done = true;
+				pass_tps2_3 = true;
+				dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
+				break;
+			}
+
+			if (prev_lane_adjust == link_status[4])
+				iteration_count++;
+			else
+				prev_lane_adjust = link_status[4];
+		}
+
+		drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
+				 lane_adjust, sizeof(lane_adjust));
+		mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
+					      lane_adjust);
+	} while (train_retries < MTK_DP_TRAIN_RETRY_LIMIT &&
+		 iteration_count < MTK_DP_TRAIN_MAX_ITERATIONS);
+
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+			   DP_TRAINING_PATTERN_DISABLE);
+	mtk_dp_train_set_pattern(mtk_dp, 0);
+
+	if (pass_tps2_3) {
+		mtk_dp->train_info.link_rate = target_link_rate;
+		mtk_dp->train_info.lane_count = target_lane_count;
+
+		mtk_dp_training_set_scramble(mtk_dp, true);
+
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
+				   target_lane_count |
+					   DP_LANE_COUNT_ENHANCED_FRAME_EN);
+		mtk_dp_set_enhanced_frame_mode(mtk_dp, true);
+
+		return 0;
+	}
+
+	return -ETIMEDOUT;
+}
+
+static bool mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
+{
+	u8 buf[DP_RECEIVER_CAP_SIZE] = {};
+	u8 val;
+	struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
+
+	if (!mtk_dp_plug_state(mtk_dp))
+		return false;
+
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+	usleep_range(2000, 5000);
+
+	drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
+
+	memcpy(mtk_dp->rx_cap, buf, min(sizeof(mtk_dp->rx_cap), sizeof(buf)));
+	mtk_dp->rx_cap[DP_TRAINING_AUX_RD_INTERVAL] &= DP_TRAINING_AUX_RD_MASK;
+
+	train_info->link_rate =
+		min_t(int, MTK_DP_MAX_LINK_RATE, buf[DP_MAX_LINK_RATE]);
+	train_info->lane_count =
+		min_t(int, MTK_DP_MAX_LANES, drm_dp_max_lane_count(buf));
+
+	train_info->tps3 = drm_dp_tps3_supported(buf);
+	train_info->tps4 = drm_dp_tps4_supported(buf);
+
+	train_info->sink_ssc =
+		!!(buf[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5);
+
+	train_info->sink_ssc = false;
+
+	drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
+	if (val & DP_MST_CAP) {
+		// Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0
+		drm_dp_dpcd_readb(&mtk_dp->aux,
+				  DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0, &val);
+		if (val)
+			drm_dp_dpcd_writeb(&mtk_dp->aux,
+					   DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
+					   val);
+	}
+
+	return true;
+}
+
+static int mtk_dp_edid_parse_audio_capabilities(struct mtk_dp *mtk_dp,
+						struct mtk_dp_audio_cfg *cfg)
+{
+	struct cea_sad *sads;
+	int sad_count;
+	int i;
+	int ret = 0;
+
+	mutex_lock(&mtk_dp->edid_lock);
+	if (!mtk_dp->edid) {
+		mutex_unlock(&mtk_dp->edid_lock);
+		dev_err(mtk_dp->dev, "EDID not found!\n");
+		return -EINVAL;
+	}
+
+	sad_count = drm_edid_to_sad(mtk_dp->edid, &sads);
+	mutex_unlock(&mtk_dp->edid_lock);
+	if (sad_count <= 0) {
+		drm_info(mtk_dp->drm_dev, "The SADs is NULL\n");
+		return 0;
+	}
+
+	for (i = 0; i < sad_count; i++) {
+		int sample_rate;
+		int word_length;
+		// Only PCM supported at the moment
+		if (sads[i].format != HDMI_AUDIO_CODING_TYPE_PCM)
+			continue;
+
+		sample_rate = drm_cea_sad_get_sample_rate(&sads[i]);
+		word_length =
+			drm_cea_sad_get_uncompressed_word_length(&sads[i]);
+		if (sample_rate <= 0 || word_length <= 0)
+			continue;
+
+		cfg->channels = sads[i].channels;
+		cfg->word_length_bits = word_length;
+		cfg->sample_rate = sample_rate;
+		ret = 1;
+		break;
+	}
+	kfree(sads);
+
+	return ret;
+}
+
+static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
+{
+	phy_reset(mtk_dp->phy);
+	mtk_dp_reset_swing_pre_emphasis(mtk_dp);
+
+	usleep_range(2000, 5000);
+}
+
+static int mtk_dp_train_start(struct mtk_dp *mtk_dp)
+{
+	int ret = 0;
+	int lane_count;
+	int link_rate;
+	int train_limit;
+	int max_link_rate;
+	int plug_wait;
+
+	for (plug_wait = 7; !mtk_dp_plug_state(mtk_dp) && plug_wait > 0;
+	     --plug_wait)
+		usleep_range(1000, 5000);
+	if (plug_wait == 0) {
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
+		return -ENODEV;
+	}
+
+	link_rate = mtk_dp->rx_cap[1];
+	lane_count = mtk_dp->rx_cap[2] & 0x1F;
+
+	mtk_dp->train_info.link_rate = min(MTK_DP_MAX_LINK_RATE, link_rate);
+	mtk_dp->train_info.lane_count = min(MTK_DP_MAX_LANES, lane_count);
+	link_rate = mtk_dp->train_info.link_rate;
+	lane_count = mtk_dp->train_info.lane_count;
+
+	switch (link_rate) {
+	case MTK_DP_LINKRATE_RBR:
+	case MTK_DP_LINKRATE_HBR:
+	case MTK_DP_LINKRATE_HBR2:
+	case MTK_DP_LINKRATE_HBR25:
+	case MTK_DP_LINKRATE_HBR3:
+		break;
+	default:
+		mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR3;
+		break;
+	};
+
+	max_link_rate = link_rate;
+	for (train_limit = 0; train_limit <= 6; ++train_limit) {
+		mtk_dp->train_info.cr_done = false;
+		mtk_dp->train_info.eq_done = false;
+
+		mtk_dp_train_change_mode(mtk_dp);
+		ret = mtk_dp_train_flow(mtk_dp, link_rate, lane_count);
+		if (ret == -ENODEV || ret == -EAGAIN)
+			return ret;
+
+		if (!mtk_dp->train_info.cr_done) {
+			switch (link_rate) {
+			case MTK_DP_LINKRATE_RBR:
+				lane_count = lane_count / 2;
+				link_rate = max_link_rate;
+				if (lane_count == 0x0) {
+					mtk_dp->train_state =
+						MTK_DP_TRAIN_STATE_DPIDLE;
+					return -EIO;
+				}
+				break;
+			case MTK_DP_LINKRATE_HBR:
+				link_rate = MTK_DP_LINKRATE_RBR;
+				break;
+			case MTK_DP_LINKRATE_HBR2:
+				link_rate = MTK_DP_LINKRATE_HBR;
+				break;
+			case MTK_DP_LINKRATE_HBR3:
+				link_rate = MTK_DP_LINKRATE_HBR2;
+				break;
+			default:
+				return -EINVAL;
+			};
+		} else if (!mtk_dp->train_info.eq_done) {
+			lane_count /= 2;
+			if (lane_count == 0)
+				return -EIO;
+		} else {
+			return 0;
+		}
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int mtk_dp_train_handler(struct mtk_dp *mtk_dp)
+{
+	int ret = 0;
+
+	if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
+		return ret;
+
+	switch (mtk_dp->train_state) {
+	case MTK_DP_TRAIN_STATE_STARTUP:
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
+		break;
+
+	case MTK_DP_TRAIN_STATE_CHECKCAP:
+		if (mtk_dp_parse_capabilities(mtk_dp)) {
+			mtk_dp->train_info.check_cap_count = 0;
+			mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
+		} else {
+			mtk_dp->train_info.check_cap_count++;
+
+			if (mtk_dp->train_info.check_cap_count >
+			    MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT) {
+				mtk_dp->train_info.check_cap_count = 0;
+				mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
+				ret = -ETIMEDOUT;
+			}
+		}
+		break;
+
+	case MTK_DP_TRAIN_STATE_CHECKEDID: {
+		int caps_found = mtk_dp_edid_parse_audio_capabilities(
+			mtk_dp, &mtk_dp->info.audio_caps);
+		mtk_dp->audio_enable = caps_found > 0;
+		if (!mtk_dp->audio_enable)
+			memset(&mtk_dp->info.audio_caps, 0,
+			       sizeof(mtk_dp->info.audio_caps));
+	}
+
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
+		break;
+
+	case MTK_DP_TRAIN_STATE_TRAINING_PRE:
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING;
+		break;
+
+	case MTK_DP_TRAIN_STATE_TRAINING:
+		ret = mtk_dp_train_start(mtk_dp);
+		if (!ret) {
+			mtk_dp_video_mute(mtk_dp, true);
+			mtk_dp_audio_mute(mtk_dp, true);
+			mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKTIMING;
+			mtk_dp_fec_enable(mtk_dp, mtk_dp->has_fec);
+		} else if (ret != -EAGAIN) {
+			mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
+		}
+
+		ret = 0;
+		break;
+
+	case MTK_DP_TRAIN_STATE_CHECKTIMING:
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_NORMAL;
+		break;
+	case MTK_DP_TRAIN_STATE_NORMAL:
+		break;
+	case MTK_DP_TRAIN_STATE_POWERSAVE:
+		break;
+	case MTK_DP_TRAIN_STATE_DPIDLE:
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	if (enable) {
+		mtk_dp_set_tx_out(mtk_dp);
+		mtk_dp_video_mute(mtk_dp, false);
+	} else {
+		mtk_dp_video_mute(mtk_dp, true);
+	}
+}
+
+static void mtk_dp_audio_sdp_setup(struct mtk_dp *mtk_dp,
+				   struct mtk_dp_audio_cfg *cfg)
+{
+	struct mtk_dp_sdp_packet packet;
+	struct hdmi_audio_infoframe frame;
+
+	hdmi_audio_infoframe_init(&frame);
+	frame.coding_type = HDMI_AUDIO_CODING_TYPE_PCM;
+	frame.channels = cfg->channels;
+	frame.sample_frequency = cfg->sample_rate;
+
+	switch (cfg->word_length_bits) {
+	case 16:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
+		break;
+	case 20:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_20;
+		break;
+	case 24:
+	default:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_24;
+		break;
+	}
+
+	packet.type = MTK_DP_SDP_AUI;
+	hdmi_audio_infoframe_pack_for_dp(&frame, &packet.sdp,
+					 MTK_DP_DP_VERSION_11);
+
+	mtk_dp_audio_sdp_asp_set_channels(mtk_dp, cfg->channels);
+	mtk_dp_setup_sdp(mtk_dp, &packet);
+}
+
+static void mtk_dp_audio_setup(struct mtk_dp *mtk_dp,
+			       struct mtk_dp_audio_cfg *cfg)
+{
+	mtk_dp_audio_sdp_setup(mtk_dp, cfg);
+	mtk_dp_audio_channel_status_set(mtk_dp, cfg);
+
+	mtk_dp_audio_setup_channels(mtk_dp, cfg);
+	mtk_dp_audio_set_divider(mtk_dp);
+}
+
+static void mtk_dp_video_config(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_mn_overwrite_disable(mtk_dp);
+
+	mtk_dp_set_msa(mtk_dp);
+
+	mtk_dp_set_color_depth(mtk_dp, mtk_dp->info.depth);
+	mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
+}
+
+static int mtk_dp_state_handler(struct mtk_dp *mtk_dp)
+{
+	int ret = 0;
+
+	switch (mtk_dp->state) {
+	case MTK_DP_STATE_INITIAL:
+		mtk_dp_video_mute(mtk_dp, true);
+		mtk_dp_audio_mute(mtk_dp, true);
+		mtk_dp->state = MTK_DP_STATE_IDLE;
+		break;
+
+	case MTK_DP_STATE_IDLE:
+		if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
+			mtk_dp->state = MTK_DP_STATE_PREPARE;
+		break;
+
+	case MTK_DP_STATE_PREPARE:
+		mtk_dp_video_config(mtk_dp);
+		mtk_dp_video_enable(mtk_dp, true);
+
+		if (mtk_dp->audio_enable) {
+			mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_caps);
+			mtk_dp_audio_mute(mtk_dp, false);
+		}
+
+		mtk_dp->state = MTK_DP_STATE_NORMAL;
+		break;
+
+	case MTK_DP_STATE_NORMAL:
+		if (mtk_dp->train_state != MTK_DP_TRAIN_STATE_NORMAL) {
+			mtk_dp_video_mute(mtk_dp, true);
+			mtk_dp_audio_mute(mtk_dp, true);
+			mtk_dp_sdp_stop_sending(mtk_dp);
+			mtk_dp->state = MTK_DP_STATE_IDLE;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_set_idle_pattern(mtk_dp, true);
+	mtk_dp_initialize_priv_data(mtk_dp);
+
+	mtk_dp_initialize_settings(mtk_dp);
+	mtk_dp_initialize_aux_settings(mtk_dp);
+	mtk_dp_initialize_digital_settings(mtk_dp);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
+			   BIT(RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT),
+			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK);
+	mtk_dp_initialize_hpd_detect_settings(mtk_dp);
+
+	mtk_dp_digital_sw_reset(mtk_dp);
+}
+
+static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
+{
+	struct mtk_dp *mtk_dp = dev;
+	int event;
+	u8 buf[DP_RECEIVER_CAP_SIZE] = {};
+
+	event = mtk_dp_plug_state(mtk_dp) ? connector_status_connected :
+						  connector_status_disconnected;
+
+	if (event < 0)
+		return IRQ_HANDLED;
+
+	if (mtk_dp->drm_dev) {
+		dev_info(mtk_dp->dev, "drm_helper_hpd_irq_event\n");
+		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
+	}
+
+	if (mtk_dp->train_info.cable_state_change) {
+		mtk_dp->train_info.cable_state_change = false;
+
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
+
+		if (!mtk_dp->train_info.cable_plugged_in ||
+		    !mtk_dp_plug_state(mtk_dp)) {
+			mtk_dp_video_mute(mtk_dp, true);
+			mtk_dp_audio_mute(mtk_dp, true);
+
+			mtk_dp_initialize_priv_data(mtk_dp);
+			mtk_dp_set_idle_pattern(mtk_dp, true);
+			if (mtk_dp->has_fec)
+				mtk_dp_fec_enable(mtk_dp, false);
+			mtk_dp_sdp_stop_sending(mtk_dp);
+
+			mtk_dp_edid_free(mtk_dp);
+			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+					   DP_PWR_STATE_BANDGAP_TPLL,
+					   DP_PWR_STATE_MASK);
+		} else {
+			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+					   DP_PWR_STATE_BANDGAP_TPLL_LANE,
+					   DP_PWR_STATE_MASK);
+			drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
+			mtk_dp->train_info.link_rate =
+				min_t(int, MTK_DP_MAX_LINK_RATE,
+				      buf[DP_MAX_LINK_RATE]);
+			mtk_dp->train_info.lane_count =
+				min_t(int, MTK_DP_MAX_LANES,
+				      drm_dp_max_lane_count(buf));
+		}
+	}
+
+	if (mtk_dp->train_info.irq_status & MTK_DP_HPD_INTERRUPT) {
+		dev_info(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n");
+		mtk_dp->train_info.irq_status &= ~MTK_DP_HPD_INTERRUPT;
+		mtk_dp_hpd_sink_event(mtk_dp);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_dp_hpd_isr_handler(struct mtk_dp *mtk_dp)
+{
+	bool connected;
+	u16 swirq_status = mtk_dp_swirq_get_clear(mtk_dp);
+	u8 hwirq_status = mtk_dp_hwirq_get_clear(mtk_dp);
+	struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
+
+	train_info->irq_status |= hwirq_status | swirq_status;
+
+	if (!train_info->irq_status)
+		return IRQ_HANDLED;
+
+	connected = mtk_dp_plug_state(mtk_dp);
+	if (connected || !train_info->cable_plugged_in)
+		train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
+	else if (!connected || train_info->cable_plugged_in)
+		train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
+
+	if (!(train_info->irq_status &
+	      (MTK_DP_HPD_CONNECT | MTK_DP_HPD_DISCONNECT)))
+		return IRQ_HANDLED;
+
+	if (train_info->irq_status & MTK_DP_HPD_CONNECT) {
+		train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
+		train_info->cable_plugged_in = true;
+	} else {
+		train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
+		train_info->cable_plugged_in = false;
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
+	}
+	train_info->cable_state_change = true;
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
+{
+	struct mtk_dp *mtk_dp = dev;
+	u32 irq_status;
+
+	irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS);
+
+	if (!irq_status)
+		return IRQ_HANDLED;
+
+	if (irq_status & RGS_IRQ_STATUS_TRANSMITTER)
+		return mtk_dp_hpd_isr_handler(mtk_dp);
+
+	return IRQ_HANDLED;
+}
+
+static int mtk_dp_dt_parse_pdata(struct mtk_dp *mtk_dp,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	int ret = 0;
+	void __iomem *base;
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	mtk_dp->regs = devm_regmap_init_mmio(dev, base, &mtk_dp_regmap_config);
+	if (IS_ERR(mtk_dp->regs))
+		return PTR_ERR(mtk_dp->regs);
+
+	mtk_dp->dp_tx_clk = devm_clk_get(dev, "faxi");
+	if (IS_ERR(mtk_dp->dp_tx_clk)) {
+		ret = PTR_ERR(mtk_dp->dp_tx_clk);
+		dev_err(dev, "Failed to get dptx clock: %d\n", ret);
+		mtk_dp->dp_tx_clk = NULL;
+	}
+
+	return 0;
+}
+
+static void mtk_dp_update_plugged_status(struct mtk_dp *mtk_dp)
+{
+	bool connected, has_audio;
+
+	mutex_lock(&mtk_dp->update_plugged_status_lock);
+	connected = mtk_dp_plug_state(mtk_dp);
+	has_audio = drm_detect_monitor_audio(mtk_dp->edid);
+	if (mtk_dp->plugged_cb && mtk_dp->codec_dev)
+		mtk_dp->plugged_cb(mtk_dp->codec_dev, connected & has_audio);
+	mutex_unlock(&mtk_dp->update_plugged_status_lock);
+}
+
+static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	enum drm_connector_status ret = connector_status_disconnected;
+	u8 sink_count = 0;
+
+	if (mtk_dp_is_edp(mtk_dp))
+		return connector_status_connected;
+
+	if (mtk_dp_plug_state(mtk_dp)) {
+		drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
+		if (DP_GET_SINK_COUNT(sink_count))
+			ret = connector_status_connected;
+	}
+
+	mtk_dp_update_plugged_status(mtk_dp);
+	return ret;
+}
+
+static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
+				    struct drm_connector *connector)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	bool enabled = mtk_dp->enabled;
+	struct edid *new_edid = NULL;
+
+	if (!enabled)
+		drm_bridge_chain_pre_enable(bridge);
+
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+	usleep_range(2000, 5000);
+
+	if (mtk_dp_plug_state(mtk_dp))
+		new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
+
+	if (!enabled)
+		drm_bridge_chain_post_disable(bridge);
+
+	mutex_lock(&mtk_dp->edid_lock);
+	kfree(mtk_dp->edid);
+	mtk_dp->edid = NULL;
+
+	if (!new_edid) {
+		mutex_unlock(&mtk_dp->edid_lock);
+		return NULL;
+	}
+
+	mtk_dp->edid = drm_edid_duplicate(new_edid);
+	mutex_unlock(&mtk_dp->edid_lock);
+
+	return new_edid;
+}
+
+static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
+				   struct drm_dp_aux_msg *msg)
+{
+	ssize_t err = -EAGAIN;
+	struct mtk_dp *mtk_dp;
+	bool is_read;
+	u8 request;
+	size_t accessed_bytes = 0;
+	int retry = 3, ret = 0;
+
+	mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
+
+	if (!mtk_dp->train_info.cable_plugged_in ||
+	    mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
+		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
+		err = -EAGAIN;
+		goto err;
+	}
+
+	switch (msg->request) {
+	case DP_AUX_I2C_MOT:
+	case DP_AUX_I2C_WRITE:
+	case DP_AUX_NATIVE_WRITE:
+	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
+	case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
+		request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
+		is_read = false;
+		break;
+	case DP_AUX_I2C_READ:
+	case DP_AUX_NATIVE_READ:
+	case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
+		request = msg->request;
+		is_read = true;
+		break;
+	default:
+		drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
+			msg->request);
+		err = -EINVAL;
+		goto err;
+	}
+
+	if (msg->size == 0) {
+		mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
+				       msg->address + accessed_bytes,
+				       msg->buffer + accessed_bytes, 0);
+	} else {
+		while (accessed_bytes < msg->size) {
+			size_t to_access =
+				min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
+				      msg->size - accessed_bytes);
+			retry = 3;
+			while (retry--) {
+				ret = mtk_dp_aux_do_transfer(
+					mtk_dp, is_read, request,
+					msg->address + accessed_bytes,
+					msg->buffer + accessed_bytes,
+					to_access);
+				if (ret == 0)
+					break;
+				udelay(50);
+			}
+			if (!retry && ret) {
+				drm_info(mtk_dp->drm_dev,
+					 "Failed to do AUX transfer: %d\n",
+					 ret);
+				break;
+			}
+			accessed_bytes += to_access;
+		}
+	}
+err:
+	if (!ret) {
+		msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
+		ret = msg->size;
+	} else {
+		msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
+		return err;
+	}
+
+	msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
+	return msg->size;
+}
+
+static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
+{
+	drm_dp_aux_init(&mtk_dp->aux);
+	mtk_dp->aux.name = "aux_mtk_dp";
+	mtk_dp->aux.transfer = mtk_dp_aux_transfer;
+}
+
+static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
+{
+	mutex_lock(&mtk_dp->dp_lock);
+
+	mtk_dp_hwirq_enable(mtk_dp, false);
+	mtk_dp_power_disable(mtk_dp);
+	phy_exit(mtk_dp->phy);
+	clk_disable_unprepare(mtk_dp->dp_tx_clk);
+
+	mutex_unlock(&mtk_dp->dp_lock);
+}
+
+static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
+{
+	int ret = 0;
+
+	mutex_lock(&mtk_dp->dp_lock);
+
+	ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
+	if (ret < 0) {
+		dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
+		goto err;
+	}
+	ret = phy_init(mtk_dp->phy);
+	if (ret) {
+		dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
+		goto err_phy_init;
+	}
+	ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
+	if (ret) {
+		dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
+		goto err_phy_config;
+	}
+
+	mtk_dp_init_port(mtk_dp);
+	mtk_dp_power_enable(mtk_dp);
+	mtk_dp_hwirq_enable(mtk_dp, true);
+
+err_phy_config:
+	phy_exit(mtk_dp->phy);
+err_phy_init:
+	clk_disable_unprepare(mtk_dp->dp_tx_clk);
+err:
+	mutex_unlock(&mtk_dp->dp_lock);
+	return ret;
+}
+
+static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
+				enum drm_bridge_attach_flags flags)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	int ret;
+
+	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+		dev_err(mtk_dp->dev, "Driver does not provide a connector!");
+		return -EINVAL;
+	}
+
+	ret = mtk_dp_poweron(mtk_dp);
+	if (ret)
+		return ret;
+
+	if (mtk_dp->next_bridge) {
+		ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
+					&mtk_dp->bridge, flags);
+		if (ret) {
+			drm_warn(mtk_dp->drm_dev,
+				 "Failed to attach external bridge: %d\n", ret);
+			goto err_bridge_attach;
+		}
+	}
+
+	mtk_dp->drm_dev = bridge->dev;
+
+	return 0;
+
+err_bridge_attach:
+	mtk_dp_poweroff(mtk_dp);
+	return ret;
+}
+
+static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+
+	mtk_dp->drm_dev = NULL;
+
+	mtk_dp_poweroff(mtk_dp);
+}
+
+static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
+					 struct drm_bridge_state *old_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+
+	mtk_dp_video_mute(mtk_dp, true);
+	mtk_dp_audio_mute(mtk_dp, true);
+	mtk_dp->state = MTK_DP_STATE_IDLE;
+	mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
+
+	mtk_dp->enabled = false;
+	msleep(100);
+}
+
+static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
+					  struct drm_display_mode *mode)
+{
+	struct mtk_dp_timings *timings = &mtk_dp->info.timings;
+
+	drm_display_mode_to_videomode(mode, &timings->vm);
+	timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
+	timings->htotal = mode->htotal;
+	timings->vtotal = mode->vtotal;
+}
+
+static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
+					struct drm_bridge_state *old_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	struct drm_connector *conn;
+	struct drm_connector_state *conn_state;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *crtc_state;
+	int ret = 0;
+	int i;
+
+	conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
+							bridge->encoder);
+	if (!conn) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as connector is missing\n");
+		return;
+	}
+
+	memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
+
+	conn_state =
+		drm_atomic_get_new_connector_state(old_state->base.state, conn);
+	if (!conn_state) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as connector state is missing\n");
+		return;
+	}
+
+	crtc = conn_state->crtc;
+	if (!crtc) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as connector state doesn't have a crtc\n");
+		return;
+	}
+
+	crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
+	if (!crtc_state) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as crtc state is missing\n");
+		return;
+	}
+
+	mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);
+	if (!mtk_dp_parse_capabilities(mtk_dp)) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as nothing is plugged in\n");
+		return;
+	}
+
+	//training
+	for (i = 0; i < 50; i++) {
+		ret = mtk_dp_train_handler(mtk_dp);
+		if (ret) {
+			drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
+				ret);
+			return;
+		}
+
+		ret = mtk_dp_state_handler(mtk_dp);
+		if (ret) {
+			drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
+				ret);
+			return;
+		}
+	}
+
+	mtk_dp->enabled = true;
+	mtk_dp_update_plugged_status(mtk_dp);
+}
+
+static enum drm_mode_status
+mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
+			 const struct drm_display_info *info,
+			 const struct drm_display_mode *mode)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	u32 rx_linkrate;
+	u32 bpp;
+
+	bpp = info->color_formats & DRM_COLOR_FORMAT_YCRCB422 ? 16 : 24;
+	rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
+	if (rx_linkrate * mtk_dp->train_info.lane_count < mode->clock * bpp / 8)
+		return MODE_CLOCK_HIGH;
+
+	if (mode->clock > 600000)
+		return MODE_CLOCK_HIGH;
+
+	if ((mode->clock * 1000) / (mode->htotal * mode->vtotal) > 62)
+		return MODE_CLOCK_HIGH;
+
+	return MODE_OK;
+}
+
+static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(
+	struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
+	struct drm_crtc_state *crtc_state,
+	struct drm_connector_state *conn_state, unsigned int *num_output_fmts)
+{
+	u32 *output_fmts;
+
+	*num_output_fmts = 0;
+	output_fmts = kcalloc(1, sizeof(*output_fmts), GFP_KERNEL);
+	if (!output_fmts)
+		return NULL;
+	*num_output_fmts = 1;
+	output_fmts[0] = MEDIA_BUS_FMT_FIXED;
+	return output_fmts;
+}
+
+static const u32 mt8195_input_fmts[] = {
+	MEDIA_BUS_FMT_RGB888_1X24,
+	MEDIA_BUS_FMT_YUV8_1X24,
+	MEDIA_BUS_FMT_YUYV8_1X16,
+};
+
+static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(
+	struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
+	struct drm_crtc_state *crtc_state,
+	struct drm_connector_state *conn_state, u32 output_fmt,
+	unsigned int *num_input_fmts)
+{
+	u32 *input_fmts;
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+	struct drm_display_info *display_info =
+		&conn_state->connector->display_info;
+	u32 rx_linkrate;
+	u32 bpp;
+
+	bpp = (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422) ? 16 :
+										24;
+	rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
+	*num_input_fmts = 0;
+	input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts), sizeof(*input_fmts),
+			     GFP_KERNEL);
+	if (!input_fmts)
+		return NULL;
+
+	*num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
+
+	memcpy(input_fmts, mt8195_input_fmts,
+	       sizeof(*input_fmts) * ARRAY_SIZE(mt8195_input_fmts));
+
+	if (((rx_linkrate * mtk_dp->train_info.lane_count) <
+	     (mode->clock * 24 / 8)) &&
+	    ((rx_linkrate * mtk_dp->train_info.lane_count) >
+	     (mode->clock * 16 / 8)) &&
+	    (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422)) {
+		kfree(input_fmts);
+		input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL);
+		*num_input_fmts = 1;
+		input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
+	}
+
+	return input_fmts;
+}
+
+static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
+				      struct drm_bridge_state *bridge_state,
+				      struct drm_crtc_state *crtc_state,
+				      struct drm_connector_state *conn_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	unsigned int input_bus_format;
+
+	input_bus_format = bridge_state->input_bus_cfg.format;
+
+	dev_info(mtk_dp->dev, "input format 0x%04x, output format 0x%04x\n",
+		 bridge_state->input_bus_cfg.format,
+		 bridge_state->output_bus_cfg.format);
+
+	mtk_dp->input_fmt = input_bus_format;
+	if (mtk_dp->input_fmt == MEDIA_BUS_FMT_YUYV8_1X16)
+		mtk_dp->info.format = MTK_DP_COLOR_FORMAT_YUV_422;
+	else
+		mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
+
+	return 0;
+}
+
+static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
+	.atomic_check = mtk_dp_bridge_atomic_check,
+	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+	.atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts,
+	.atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts,
+	.atomic_reset = drm_atomic_helper_bridge_reset,
+	.attach = mtk_dp_bridge_attach,
+	.detach = mtk_dp_bridge_detach,
+	.atomic_enable = mtk_dp_bridge_atomic_enable,
+	.atomic_disable = mtk_dp_bridge_atomic_disable,
+	.mode_valid = mtk_dp_bridge_mode_valid,
+	.get_edid = mtk_dp_get_edid,
+	.detect = mtk_dp_bdg_detect,
+};
+
+/*
+ * HDMI audio codec callbacks
+ */
+static int mtk_dp_audio_hw_params(struct device *dev, void *data,
+				  struct hdmi_codec_daifmt *daifmt,
+				  struct hdmi_codec_params *params)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+	struct mtk_dp_audio_cfg cfg;
+
+	if (!mtk_dp->enabled) {
+		pr_err("%s, DP is not ready!\n", __func__);
+		return -ENODEV;
+	}
+
+	cfg.channels = params->cea.channels;
+	cfg.sample_rate = params->sample_rate;
+	cfg.word_length_bits = 24;
+
+	mtk_dp_audio_setup(mtk_dp, &cfg);
+
+	return 0;
+}
+
+static int mtk_dp_audio_startup(struct device *dev, void *data)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_audio_mute(mtk_dp, false);
+
+	return 0;
+}
+
+static void mtk_dp_audio_shutdown(struct device *dev, void *data)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_audio_mute(mtk_dp, true);
+}
+
+static int mtk_dp_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
+				size_t len)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	if (mtk_dp->enabled)
+		memcpy(buf, mtk_dp->connector_eld, len);
+	else
+		memset(buf, 0, len);
+
+	return 0;
+}
+
+static int mtk_dp_audio_hook_plugged_cb(struct device *dev, void *data,
+					hdmi_codec_plugged_cb fn,
+					struct device *codec_dev)
+{
+	struct mtk_dp *mtk_dp = data;
+
+	mutex_lock(&mtk_dp->update_plugged_status_lock);
+	mtk_dp->plugged_cb = fn;
+	mtk_dp->codec_dev = codec_dev;
+	mutex_unlock(&mtk_dp->update_plugged_status_lock);
+
+	mtk_dp_update_plugged_status(mtk_dp);
+
+	return 0;
+}
+
+static const struct hdmi_codec_ops mtk_dp_audio_codec_ops = {
+	.hw_params = mtk_dp_audio_hw_params,
+	.audio_startup = mtk_dp_audio_startup,
+	.audio_shutdown = mtk_dp_audio_shutdown,
+	.get_eld = mtk_dp_audio_get_eld,
+	.hook_plugged_cb = mtk_dp_audio_hook_plugged_cb,
+	.no_capture_mute = 1,
+};
+
+static int mtk_dp_register_audio_driver(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+	struct hdmi_codec_pdata codec_data = {
+		.ops = &mtk_dp_audio_codec_ops,
+		.max_i2s_channels = 8,
+		.i2s = 1,
+		.data = mtk_dp,
+	};
+	struct platform_device *pdev;
+
+	pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
+					     PLATFORM_DEVID_AUTO, &codec_data,
+					     sizeof(codec_data));
+	if (IS_ERR(pdev))
+		return PTR_ERR(pdev);
+
+	return 0;
+}
+
+static int mtk_dp_probe(struct platform_device *pdev)
+{
+	struct mtk_dp *mtk_dp;
+	struct device *dev = &pdev->dev;
+	int ret;
+	int irq_num = 0;
+	struct drm_panel *panel = NULL;
+
+	mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
+	if (!mtk_dp)
+		return -ENOMEM;
+
+	mtk_dp->dev = dev;
+
+	irq_num = platform_get_irq(pdev, 0);
+	if (irq_num < 0) {
+		dev_err(dev, "failed to request dp irq resource\n");
+		return -EPROBE_DEFER;
+	}
+
+	ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel,
+					  &mtk_dp->next_bridge);
+	if (ret == -ENODEV) {
+		dev_info(
+			dev,
+			"No panel connected in devicetree, continuing as external DP\n");
+		mtk_dp->next_bridge = NULL;
+	} else if (ret) {
+		dev_err(dev, "Failed to find panel or bridge: %d\n", ret);
+		return ret;
+	}
+
+	if (panel) {
+		mtk_dp->next_bridge = devm_drm_panel_bridge_add(dev, panel);
+		if (IS_ERR(mtk_dp->next_bridge)) {
+			ret = PTR_ERR(mtk_dp->next_bridge);
+			dev_err(dev, "Failed to create bridge: %d\n", ret);
+			return -EPROBE_DEFER;
+		}
+	}
+
+	ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
+	if (ret)
+		return ret;
+
+	mtk_dp_aux_init(mtk_dp);
+
+	ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
+					mtk_dp_hpd_event_thread,
+					IRQ_TYPE_LEVEL_HIGH, dev_name(dev),
+					mtk_dp);
+	if (ret) {
+		dev_err(dev, "failed to request mediatek dptx irq\n");
+		return -EPROBE_DEFER;
+	}
+
+	mutex_init(&mtk_dp->dp_lock);
+	mutex_init(&mtk_dp->edid_lock);
+
+	platform_set_drvdata(pdev, mtk_dp);
+
+	if (!mtk_dp_is_edp(mtk_dp)) {
+		mutex_init(&mtk_dp->update_plugged_status_lock);
+		ret = mtk_dp_register_audio_driver(dev);
+		if (ret) {
+			dev_err(dev, "Failed to register audio driver: %d\n",
+				ret);
+			return ret;
+		}
+	}
+
+	mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
+							PLATFORM_DEVID_AUTO,
+							&mtk_dp->regs,
+							sizeof(&mtk_dp->regs));
+	if (IS_ERR(mtk_dp->phy_dev)) {
+		dev_err(dev, "Failed to create device mediatek-dp-phy: %ld\n",
+			PTR_ERR(mtk_dp->phy_dev));
+		return PTR_ERR(mtk_dp->phy_dev);
+	}
+
+	mtk_dp_get_calibration_data(mtk_dp);
+
+	mtk_dp->phy = dev_get_drvdata(&mtk_dp->phy_dev->dev);
+	if (IS_ERR(mtk_dp->phy)) {
+		dev_err(dev, "Failed to get phy: %ld\n", PTR_ERR(mtk_dp->phy));
+		platform_device_unregister(mtk_dp->phy_dev);
+		return PTR_ERR(mtk_dp->phy);
+	}
+
+	mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
+	mtk_dp->bridge.of_node = dev->of_node;
+	if (mtk_dp_is_edp(mtk_dp))
+		mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
+	else
+		mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
+
+	mtk_dp->bridge.ops =
+		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
+	drm_bridge_add(&mtk_dp->bridge);
+
+	pm_runtime_enable(dev);
+	pm_runtime_get_sync(dev);
+
+	return 0;
+}
+
+static int mtk_dp_remove(struct platform_device *pdev)
+{
+	struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
+
+	platform_device_unregister(mtk_dp->phy_dev);
+
+	mtk_dp_video_mute(mtk_dp, true);
+	mtk_dp_audio_mute(mtk_dp, true);
+
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_dp_suspend(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_power_disable(mtk_dp);
+	mtk_dp_hwirq_enable(mtk_dp, false);
+
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static int mtk_dp_resume(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	pm_runtime_get_sync(dev);
+
+	mtk_dp_init_port(mtk_dp);
+	mtk_dp_power_enable(mtk_dp);
+	mtk_dp_hwirq_enable(mtk_dp, true);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
+
+static const struct of_device_id mtk_dp_of_match[] = {
+	{
+		.compatible = "mediatek,mt8195-dp-tx",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
+
+struct platform_driver mtk_dp_driver = {
+	.probe = mtk_dp_probe,
+	.remove = mtk_dp_remove,
+	.driver = {
+		.name = "mediatek-drm-dp",
+		.of_match_table = mtk_dp_of_match,
+		.pm = &mtk_dp_pm_ops,
+	},
+};
+
+MODULE_AUTHOR("Jason-JH.Lin <jason-jh.lin@mediatek.com>");
+MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
+MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
new file mode 100644
index 0000000000000..79952ac30e9e6
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
@@ -0,0 +1,568 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Copyright (c) 2021 BayLibre
+ */
+#ifndef _MTK_DP_REG_H_
+#define _MTK_DP_REG_H_
+
+#define MTK_DP_SIP_CONTROL_AARCH32 0x82000523
+#define MTK_DP_SIP_ATF_VIDEO_UNMUTE 0x20
+#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE 0x21
+
+#define DP_PHY_GLB_BIAS_GEN_00 0x0000
+#define RG_XTP_GLB_BIAS_INTR_CTRL (0x1f << 16)
+
+#define DP_PHY_GLB_DPAUX_TX 0x0008
+#define RG_CKM_PT0_CKTX_IMPSEL (0xf << 20)
+
+#define DP_PHY_LANE_TX_0 0x104
+#define RG_XTP_LN0_TX_IMPSEL_PMOS (0xf << 12)
+#define RG_XTP_LN0_TX_IMPSEL_NMOS (0xf << 16)
+
+#define DP_PHY_LANE_TX_1 0x204
+#define RG_XTP_LN1_TX_IMPSEL_PMOS (0xf << 12)
+#define RG_XTP_LN1_TX_IMPSEL_NMOS (0xf << 16)
+
+#define DP_PHY_LANE_TX_2 0x304
+#define RG_XTP_LN2_TX_IMPSEL_PMOS (0xf << 12)
+#define RG_XTP_LN2_TX_IMPSEL_NMOS (0xf << 16)
+
+#define DP_PHY_LANE_TX_3 0x404
+#define RG_XTP_LN3_TX_IMPSEL_PMOS (0xf << 12)
+#define RG_XTP_LN3_TX_IMPSEL_NMOS (0xf << 16)
+
+#define TOP_OFFSET 0x2000
+#define ENC0_OFFSET 0x3000
+#define ENC1_OFFSET 0x3200
+#define TRANS_OFFSET 0x3400
+#define AUX_OFFSET 0x3600
+#define SEC_OFFSET 0x4000
+
+#define MTK_DP_HPD_DISCONNECT BIT(1)
+#define MTK_DP_HPD_CONNECT BIT(2)
+#define MTK_DP_HPD_INTERRUPT BIT(3)
+
+#define MTK_DP_0034 0x034
+#define DA_XTP_GLB_CKDET_EN_FORCE_VAL BIT(15)
+#define DA_XTP_GLB_CKDET_EN_FORCE_EN BIT(14)
+#define DA_CKM_INTCKTX_EN_FORCE_VAL BIT(13)
+#define DA_CKM_INTCKTX_EN_FORCE_EN BIT(12)
+#define DA_CKM_CKTX0_EN_FORCE_VAL BIT(11)
+#define DA_CKM_CKTX0_EN_FORCE_EN BIT(10)
+#define DA_CKM_XTAL_CK_FORCE_VAL BIT(9)
+#define DA_CKM_XTAL_CK_FORCE_EN BIT(8)
+#define DA_CKM_BIAS_LPF_EN_FORCE_VAL BIT(7)
+#define DA_CKM_BIAS_LPF_EN_FORCE_EN BIT(6)
+#define DA_CKM_BIAS_EN_FORCE_VAL BIT(5)
+#define DA_CKM_BIAS_EN_FORCE_EN BIT(4)
+#define DA_XTP_GLB_AVD10_ON_FORCE_VAL BIT(3)
+#define DA_XTP_GLB_AVD10_ON_FORCE BIT(2)
+#define DA_XTP_GLB_LDO_EN_FORCE_VAL BIT(1)
+#define DA_XTP_GLB_LDO_EN_FORCE_EN BIT(0)
+
+#define MTK_DP_1040 0x1040
+#define RG_DPAUX_RX_VALID_DEGLITCH_EN BIT(2)
+#define RG_XTP_GLB_CKDET_EN BIT(1)
+#define RG_DPAUX_RX_EN BIT(0)
+
+#define MTK_DP_ENC0_P0_3000 (ENC0_OFFSET + 0x000)
+#define LANE_NUM_DP_ENC0_P0_MASK 0x3
+#define VIDEO_MUTE_SW_DP_ENC0_P0_MASK 0x4
+#define VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT 2
+#define VIDEO_MUTE_SEL_DP_ENC0_P0_MASK 0x8
+#define VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT 3
+#define ENHANCED_FRAME_EN_DP_ENC0_P0_MASK 0x10
+#define ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT 4
+
+#define MTK_DP_ENC0_P0_3004 (ENC0_OFFSET + 0x004)
+#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK 0x100
+#define VIDEO_M_CODE_SEL_DP_ENC0_P0_SHIFT 8
+#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK 0x200
+#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT 9
+
+#define MTK_DP_ENC0_P0_3008 (ENC0_OFFSET + 0x008)
+#define VIDEO_M_CODE_SW_0_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_300C (ENC0_OFFSET + 0x00C)
+#define VIDEO_M_CODE_SW_1_DP_ENC0_P0_MASK 0xff
+
+#define MTK_DP_ENC0_P0_3010 (ENC0_OFFSET + 0x010)
+#define HTOTAL_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3014 (ENC0_OFFSET + 0x014)
+#define VTOTAL_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3018 (ENC0_OFFSET + 0x018)
+#define HSTART_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_301C (ENC0_OFFSET + 0x01C)
+#define VSTART_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3020 (ENC0_OFFSET + 0x020)
+#define HWIDTH_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3024 (ENC0_OFFSET + 0x024)
+#define VHEIGHT_SW_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3028 (ENC0_OFFSET + 0x028)
+#define HSW_SW_DP_ENC0_P0_MASK 0x7fff
+#define HSW_SW_DP_ENC0_P0_SHIFT 0
+#define HSP_SW_DP_ENC0_P0_MASK 0x8000
+
+#define MTK_DP_ENC0_P0_302C (ENC0_OFFSET + 0x02C)
+#define VSW_SW_DP_ENC0_P0_MASK 0x7fff
+#define VSW_SW_DP_ENC0_P0_SHIFT 0
+#define VSP_SW_DP_ENC0_P0_MASK 0x8000
+
+#define MTK_DP_ENC0_P0_3030 (ENC0_OFFSET + 0x030)
+#define HTOTAL_SEL_DP_ENC0_P0_SHIFT 0
+#define VTOTAL_SEL_DP_ENC0_P0_SHIFT 1
+#define HSTART_SEL_DP_ENC0_P0_SHIFT 2
+#define VSTART_SEL_DP_ENC0_P0_SHIFT 3
+#define HWIDTH_SEL_DP_ENC0_P0_SHIFT 4
+#define VHEIGHT_SEL_DP_ENC0_P0_SHIFT 5
+#define HSP_SEL_DP_ENC0_P0_SHIFT 6
+#define HSW_SEL_DP_ENC0_P0_SHIFT 7
+#define VSP_SEL_DP_ENC0_P0_SHIFT 8
+#define VSW_SEL_DP_ENC0_P0_SHIFT 9
+#define VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK 0x800
+#define VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT 11
+#define VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK 0x1000
+#define VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT 12
+
+#define MTK_DP_ENC0_P0_3034 (ENC0_OFFSET + 0x034)
+
+#define MTK_DP_ENC0_P0_3038 (ENC0_OFFSET + 0x038)
+#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK 0x800
+#define VIDEO_SOURCE_SEL_DP_ENC0_P0_SHIFT 11
+
+#define MTK_DP_ENC0_P0_303C (ENC0_OFFSET + 0x03C)
+#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK 0x3f
+#define SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT 0
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK 0x700
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT 8
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT                                     \
+	(0 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT                                     \
+	(1 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT                                     \
+	(2 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT                                      \
+	(3 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT                                      \
+	(4 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK 0x7000
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT 12
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB                                     \
+	(0 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422                                \
+	(1 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420                                \
+	(2 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
+#define VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK 0x8000
+#define VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT 15
+
+#define MTK_DP_ENC0_P0_3040 (ENC0_OFFSET + 0x040)
+#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK 0xfff
+#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT 0
+
+#define MTK_DP_ENC0_P0_3044 (ENC0_OFFSET + 0x044)
+#define VIDEO_N_CODE_0_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3048 (ENC0_OFFSET + 0x048)
+#define VIDEO_N_CODE_1_DP_ENC0_P0_MASK 0xff
+
+#define MTK_DP_ENC0_P0_304C (ENC0_OFFSET + 0x04C)
+#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK 0x4
+#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK 0x100
+
+#define MTK_DP_ENC0_P0_3050 (ENC0_OFFSET + 0x050)
+#define VIDEO_N_CODE_MN_GEN_0_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3054 (ENC0_OFFSET + 0x054)
+#define VIDEO_N_CODE_MN_GEN_1_DP_ENC0_P0_MASK 0xff
+
+#define MTK_DP_ENC0_P0_3064 (ENC0_OFFSET + 0x064)
+#define HDE_NUM_LAST_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3088 (ENC0_OFFSET + 0x088)
+#define AU_EN_DP_ENC0_P0_MASK 0x40
+#define AU_EN_DP_ENC0_P0_SHIFT 6
+#define AUDIO_8CH_EN_DP_ENC0_P0_MASK 0x80
+#define AUDIO_8CH_SEL_DP_ENC0_P0_MASK 0x100
+#define AUDIO_2CH_EN_DP_ENC0_P0_MASK 0x4000
+#define AUDIO_2CH_SEL_DP_ENC0_P0_MASK 0x8000
+
+#define MTK_DP_ENC0_P0_308C (ENC0_OFFSET + 0x08C)
+#define CH_STATUS_0_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3090 (ENC0_OFFSET + 0x090)
+#define CH_STATUS_1_DP_ENC0_P0_MASK 0xffff
+
+#define MTK_DP_ENC0_P0_3094 (ENC0_OFFSET + 0x094)
+#define CH_STATUS_2_DP_ENC0_P0_MASK 0xff
+
+#define MTK_DP_ENC0_P0_30A0 (ENC0_OFFSET + 0x0A0)
+
+#define MTK_DP_ENC0_P0_30A4 (ENC0_OFFSET + 0x0A4)
+#define AU_TS_CFG_DP_ENC0_P0_MASK 0xff
+
+#define MTK_DP_ENC0_P0_30A8 (ENC0_OFFSET + 0x0A8)
+
+#define MTK_DP_ENC0_P0_30AC (ENC0_OFFSET + 0x0AC)
+
+#define MTK_DP_ENC0_P0_30B0 (ENC0_OFFSET + 0x0B0)
+
+#define MTK_DP_ENC0_P0_30B4 (ENC0_OFFSET + 0x0B4)
+#define ISRC_CFG_DP_ENC0_P0_MASK 0xff00
+#define ISRC_CFG_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC0_P0_30B8 (ENC0_OFFSET + 0x0B8)
+
+#define MTK_DP_ENC0_P0_30BC (ENC0_OFFSET + 0x0BC)
+#define ISRC_CONT_DP_ENC0_P0_MASK 0x1
+#define ISRC_CONT_DP_ENC0_P0_SHIFT 0
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK 0x700
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT 8
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_2                             \
+	(1 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_4                             \
+	(2 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_8                             \
+	(3 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2                             \
+	(5 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_4                             \
+	(6 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_8                             \
+	(7 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
+
+#define MTK_DP_ENC0_P0_30D8 (ENC0_OFFSET + 0x0D8)
+
+#define MTK_DP_ENC0_P0_312C (ENC0_OFFSET + 0x12C)
+#define ASP_HB2_DP_ENC0_P0_MASK 0xff
+#define ASP_HB3_DP_ENC0_P0_MASK 0xff00
+#define ASP_HB3_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC0_P0_3130 (ENC0_OFFSET + 0x130)
+
+#define MTK_DP_ENC0_P0_3138 (ENC0_OFFSET + 0x138)
+
+#define MTK_DP_ENC0_P0_3154 (ENC0_OFFSET + 0x154)
+#define PGEN_HTOTAL_DP_ENC0_P0_MASK 0x3fff
+
+#define MTK_DP_ENC0_P0_3158 (ENC0_OFFSET + 0x158)
+#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK 0x3fff
+
+#define MTK_DP_ENC0_P0_315C (ENC0_OFFSET + 0x15C)
+#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x3fff
+
+#define MTK_DP_ENC0_P0_3160 (ENC0_OFFSET + 0x160)
+#define PGEN_HFDE_START_DP_ENC0_P0_MASK 0x3fff
+
+#define MTK_DP_ENC0_P0_3164 (ENC0_OFFSET + 0x164)
+#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x3fff
+
+#define MTK_DP_ENC0_P0_3168 (ENC0_OFFSET + 0x168)
+#define PGEN_VTOTAL_DP_ENC0_P0_MASK 0x1fff
+
+#define MTK_DP_ENC0_P0_316C (ENC0_OFFSET + 0x16C)
+#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK 0x1fff
+
+#define MTK_DP_ENC0_P0_3170 (ENC0_OFFSET + 0x170)
+#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x1fff
+
+#define MTK_DP_ENC0_P0_3174 (ENC0_OFFSET + 0x174)
+#define PGEN_VFDE_START_DP_ENC0_P0_MASK 0x1fff
+
+#define MTK_DP_ENC0_P0_3178 (ENC0_OFFSET + 0x178)
+#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x1fff
+
+#define MTK_DP_ENC0_P0_31B0 (ENC0_OFFSET + 0x1B0)
+#define PGEN_PATTERN_SEL_SHIFT 4
+#define PGEN_PATTERN_SEL_MASK 0x0070
+
+#define MTK_DP_ENC0_P0_31C8 (ENC0_OFFSET + 0x1C8)
+#define VSC_EXT_VESA_HB0_DP_ENC0_P0_MASK 0xff
+#define VSC_EXT_VESA_HB1_DP_ENC0_P0_MASK 0xff00
+#define VSC_EXT_VESA_HB1_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC0_P0_31CC (ENC0_OFFSET + 0x1CC)
+#define VSC_EXT_VESA_HB2_DP_ENC0_P0_MASK 0xff
+#define VSC_EXT_VESA_HB2_DP_ENC0_P0_SHIFT 0
+#define VSC_EXT_VESA_HB3_DP_ENC0_P0_MASK 0xff00
+
+#define MTK_DP_ENC0_P0_31D0 (ENC0_OFFSET + 0x1D0)
+#define VSC_EXT_CEA_HB0_DP_ENC0_P0_MASK 0xff
+#define VSC_EXT_CEA_HB1_DP_ENC0_P0_MASK 0xff00
+#define VSC_EXT_CEA_HB1_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC0_P0_31D4 (ENC0_OFFSET + 0x1D4)
+#define VSC_EXT_CEA_HB2_DP_ENC0_P0_MASK 0xff
+#define VSC_EXT_CEA_HB2_DP_ENC0_P0_SHIFT 0
+#define VSC_EXT_CEA_HB3_DP_ENC0_P0_MASK 0xff00
+
+#define MTK_DP_ENC0_P0_31D8 (ENC0_OFFSET + 0x1D8)
+#define VSC_EXT_VESA_NUM_DP_ENC0_P0_MASK 0x3f
+#define VSC_EXT_VESA_NUM_DP_ENC0_P0_SHIFT 0
+#define VSC_EXT_CEA_NUM_DP_ENC0_P0_MASK 0x3f00
+#define VSC_EXT_CEA_NUM_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC0_P0_31DC (ENC0_OFFSET + 0x1DC)
+#define HDR0_CFG_DP_ENC0_P0_MASK 0xff
+#define HDR0_CFG_DP_ENC0_P0_SHIFT 0
+
+#define MTK_DP_ENC0_P0_31E8 (ENC0_OFFSET + 0x1E8)
+
+#define MTK_DP_ENC0_P0_31EC (ENC0_OFFSET + 0x1EC)
+#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK 0x10
+#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT 4
+#define ISRC1_HB3_DP_ENC0_P0_MASK 0xff00
+#define ISRC1_HB3_DP_ENC0_P0_SHIFT 8
+
+#define MTK_DP_ENC1_P0_3200 (ENC1_OFFSET + 0x000)
+
+#define MTK_DP_ENC1_P0_3280 (ENC1_OFFSET + 0x080)
+#define SDP_PACKET_TYPE_DP_ENC1_P0_MASK 0x1f
+#define SDP_PACKET_W_DP_ENC1_P0 0x20
+#define SDP_PACKET_W_DP_ENC1_P0_MASK 0x20
+#define SDP_PACKET_W_DP_ENC1_P0_SHIFT 5
+
+#define MTK_DP_ENC1_P0_328C (ENC1_OFFSET + 0x08C)
+
+#define MTK_DP_ENC1_P0_3290 (ENC1_OFFSET + 0x090)
+
+#define MTK_DP_ENC1_P0_32A0 (ENC1_OFFSET + 0x0A0)
+
+#define MTK_DP_ENC1_P0_32A4 (ENC1_OFFSET + 0x0A4)
+
+#define MTK_DP_ENC1_P0_3300 (ENC1_OFFSET + 0x100)
+#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK 0x300
+#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT 8
+
+#define MTK_DP_ENC1_P0_3304 (ENC1_OFFSET + 0x104)
+#define AU_PRTY_REGEN_DP_ENC1_P0_MASK 0x100
+#define AU_CH_STS_REGEN_DP_ENC1_P0_MASK 0x200
+#define AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK 0x1000
+
+#define MTK_DP_ENC1_P0_3324 (ENC1_OFFSET + 0x124)
+#define AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK 0x300
+#define AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT 8
+#define AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX                                       \
+	(0 << AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT)
+
+#define MTK_DP_ENC1_P0_3364 (ENC1_OFFSET + 0x164)
+#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK 0xfff
+#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT 0
+#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK 0xf000
+#define FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT 12
+
+#define MTK_DP_ENC1_P0_3368 (ENC1_OFFSET + 0x168)
+#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT 0
+#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT 4
+#define SDP_DP13_EN_DP_ENC1_P0_SHIFT 8
+#define BS2BS_MODE_DP_ENC1_P0_MASK 0x3000
+#define BS2BS_MODE_DP_ENC1_P0_SHIFT 12
+
+#define MTK_DP_ENC1_P0_33F4 (ENC1_OFFSET + 0x1F4)
+
+#define MTK_DP_TRANS_P0_3400 (TRANS_OFFSET + 0x000)
+#define PATTERN1_EN_DP_TRANS_P0_MASK 0x1000
+#define PATTERN1_EN_DP_TRANS_P0_SHIFT 12
+#define PATTERN2_EN_DP_TRANS_P0_MASK 0x2000
+#define PATTERN3_EN_DP_TRANS_P0_MASK 0x4000
+#define PATTERN4_EN_DP_TRANS_P0_MASK 0x8000
+
+#define MTK_DP_TRANS_P0_3404 (TRANS_OFFSET + 0x004)
+#define DP_SCR_EN_DP_TRANS_P0_MASK 0x1
+
+#define MTK_DP_TRANS_P0_340C (TRANS_OFFSET + 0x00C)
+#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK 0x2000
+#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT 13
+
+#define MTK_DP_TRANS_P0_3410 (TRANS_OFFSET + 0x010)
+#define HPD_DEB_THD_DP_TRANS_P0_MASK 0xf
+#define HPD_DEB_THD_DP_TRANS_P0_SHIFT 0
+#define HPD_INT_THD_DP_TRANS_P0_MASK 0xf0
+#define HPD_INT_THD_DP_TRANS_P0_SHIFT 4
+#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US (2 << HPD_INT_THD_DP_TRANS_P0_SHIFT)
+#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US                                   \
+	(2 << (HPD_INT_THD_DP_TRANS_P0_SHIFT + 2))
+#define HPD_DISC_THD_DP_TRANS_P0_MASK 0xf00
+#define HPD_DISC_THD_DP_TRANS_P0_SHIFT 8
+#define HPD_CONN_THD_DP_TRANS_P0_MASK 0xf000
+#define HPD_CONN_THD_DP_TRANS_P0_SHIFT 12
+
+#define MTK_DP_TRANS_P0_3414 (TRANS_OFFSET + 0x014)
+#define HPD_DB_DP_TRANS_P0_MASK 0x4
+
+#define MTK_DP_TRANS_P0_3418 (TRANS_OFFSET + 0x018)
+#define IRQ_CLR_DP_TRANS_P0_MASK 0xf
+#define IRQ_MASK_DP_TRANS_P0_MASK 0xf0
+#define IRQ_MASK_DP_TRANS_P0_SHIFT 4
+#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ (BIT(1) << IRQ_MASK_DP_TRANS_P0_SHIFT)
+#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ (BIT(2) << IRQ_MASK_DP_TRANS_P0_SHIFT)
+#define IRQ_MASK_DP_TRANS_P0_INT_IRQ (BIT(3) << IRQ_MASK_DP_TRANS_P0_SHIFT)
+#define IRQ_STATUS_DP_TRANS_P0_MASK 0xf000
+#define IRQ_STATUS_DP_TRANS_P0_SHIFT 12
+
+#define MTK_DP_TRANS_P0_342C (TRANS_OFFSET + 0x02C)
+#define XTAL_FREQ_DP_TRANS_P0_DEFAULT 0x69
+#define XTAL_FREQ_DP_TRANS_P0_MASK 0xff
+
+#define MTK_DP_TRANS_P0_3430 (TRANS_OFFSET + 0x030)
+#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK 0x3
+#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT BIT(1)
+
+#define MTK_DP_TRANS_P0_34A4 (TRANS_OFFSET + 0x0A4)
+#define LANE_NUM_DP_TRANS_P0_MASK 0xc
+#define LANE_NUM_DP_TRANS_P0_SHIFT 2
+
+#define MTK_DP_TRANS_P0_3540 (TRANS_OFFSET + 0x140)
+#define FEC_EN_DP_TRANS_P0_MASK 0x1
+#define FEC_EN_DP_TRANS_P0_SHIFT 0
+#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK 0x8
+#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT 3
+
+#define MTK_DP_TRANS_P0_3580 (TRANS_OFFSET + 0x180)
+#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK 0x100
+#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK 0x200
+#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK 0x400
+#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK 0x800
+
+#define MTK_DP_TRANS_P0_35C4 (TRANS_OFFSET + 0x1C4)
+#define SW_IRQ_MASK_DP_TRANS_P0_MASK 0xffff
+
+#define MTK_DP_TRANS_P0_35C8 (TRANS_OFFSET + 0x1C8)
+#define SW_IRQ_CLR_DP_TRANS_P0_MASK 0xffff
+
+#define SW_IRQ_STATUS_DP_TRANS_P0_MASK 0xffff
+#define SW_IRQ_STATUS_DP_TRANS_P0_SHIFT 0
+
+#define MTK_DP_TRANS_P0_35D0 (TRANS_OFFSET + 0x1D0)
+#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK 0xffff
+
+#define MTK_DP_TRANS_P0_35F0 (TRANS_OFFSET + 0x1F0)
+
+#define MTK_DP_AUX_P0_360C (AUX_OFFSET + 0x00C)
+#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK 0x1fff
+
+#define MTK_DP_AUX_P0_3614 (AUX_OFFSET + 0x014)
+#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK 0x7f
+#define AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT 0
+
+#define MTK_DP_AUX_P0_3618 (AUX_OFFSET + 0x018)
+#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK 0x200
+#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK 0xf
+
+#define MTK_DP_AUX_P0_3620 (AUX_OFFSET + 0x020)
+#define AUX_RD_MODE_AUX_TX_P0_MASK 0x200
+#define AUX_RX_FIFO_READ_PULSE_TX_P0_MASK 0x100
+#define AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT 8
+#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK 0xff
+#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT 0
+
+#define MTK_DP_AUX_P0_3624 (AUX_OFFSET + 0x024)
+#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK 0xf
+
+#define MTK_DP_AUX_P0_3628 (AUX_OFFSET + 0x028)
+#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK 0x3ff
+#define AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT 0
+#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE                                     \
+	(BIT(0) << AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT)
+
+#define MTK_DP_AUX_P0_362C (AUX_OFFSET + 0x02C)
+#define AUX_NO_LENGTH_AUX_TX_P0_MASK 0x1
+#define AUX_NO_LENGTH_AUX_TX_P0_SHIFT 0
+#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK 0x2
+#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK 0xfffc
+
+#define MTK_DP_AUX_P0_3630 (AUX_OFFSET + 0x030)
+#define AUX_TX_REQUEST_READY_AUX_TX_P0_MASK 0x8
+#define AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT 3
+
+#define MTK_DP_AUX_P0_3634 (AUX_OFFSET + 0x034)
+#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK 0xff00
+#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT 8
+
+#define MTK_DP_AUX_P0_3640 (AUX_OFFSET + 0x040)
+#define AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK 0x40
+#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 6
+#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 5
+#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 4
+#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT 3
+#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT 2
+#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT 1
+#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK 0x1
+#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT 0
+
+#define MTK_DP_AUX_P0_3644 (AUX_OFFSET + 0x044)
+#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK 0xf
+
+#define MTK_DP_AUX_P0_3648 (AUX_OFFSET + 0x048)
+#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK 0xffff
+
+#define MTK_DP_AUX_P0_364C (AUX_OFFSET + 0x04C)
+#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK 0xf
+
+#define MTK_DP_AUX_P0_3650 (AUX_OFFSET + 0x050)
+#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK 0xf000
+#define MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT 12
+#define PHY_FIFO_RST_AUX_TX_P0_MASK 0x200
+#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK 0x100
+#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT 8
+
+#define MTK_DP_AUX_P0_3658 (AUX_OFFSET + 0x058)
+#define AUX_TX_OV_EN_AUX_TX_P0_MASK 0x1
+
+#define MTK_DP_AUX_P0_3690 (AUX_OFFSET + 0x090)
+#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK 0x100
+#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT 8
+
+#define MTK_DP_AUX_P0_3704 (AUX_OFFSET + 0x104)
+#define AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK 0x2
+#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK 0x4
+#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT 2
+
+#define MTK_DP_AUX_P0_3708 (AUX_OFFSET + 0x108)
+
+#define MTK_DP_AUX_P0_37C8 (AUX_OFFSET + 0x1C8)
+#define MTK_ATOP_EN_AUX_TX_P0_MASK 0x1
+#define MTK_ATOP_EN_AUX_TX_P0_SHIFT 0
+
+#define MTK_DP_TOP_PWR_STATE (TOP_OFFSET + 0x00)
+#define DP_PWR_STATE_MASK 0x3
+#define DP_PWR_STATE_BANDGAP 1
+#define DP_PWR_STATE_BANDGAP_TPLL 2
+#define DP_PWR_STATE_BANDGAP_TPLL_LANE 3
+
+#define MTK_DP_TOP_SWING_EMP (TOP_OFFSET + 0x04)
+#define DP_TX0_VOLT_SWING_MASK 0x3
+#define DP_TX0_VOLT_SWING_SHIFT 0
+#define DP_TX0_PRE_EMPH_MASK 0xc
+#define DP_TX0_PRE_EMPH_SHIFT 2
+#define DP_TX1_VOLT_SWING_MASK 0x300
+#define DP_TX1_VOLT_SWING_SHIFT 8
+#define DP_TX1_PRE_EMPH_MASK 0xc00
+#define DP_TX2_VOLT_SWING_MASK 0x30000
+#define DP_TX2_PRE_EMPH_MASK 0xc0000
+#define DP_TX3_VOLT_SWING_MASK 0x3000000
+#define DP_TX3_PRE_EMPH_MASK 0xc000000
+
+#define MTK_DP_TOP_RESET_AND_PROBE (TOP_OFFSET + 0x20)
+#define SW_RST_B_SHIFT 0
+#define SW_RST_B_PHYD (BIT(4) << SW_RST_B_SHIFT)
+
+#define MTK_DP_TOP_IRQ_STATUS (TOP_OFFSET + 0x28)
+#define RGS_IRQ_STATUS_SHIFT 0
+#define RGS_IRQ_STATUS_TRANSMITTER (BIT(1) << RGS_IRQ_STATUS_SHIFT)
+
+#define MTK_DP_TOP_IRQ_MASK (TOP_OFFSET + 0x2C)
+#define IRQ_MASK_AUX_TOP_IRQ BIT(2)
+
+#define MTK_DP_TOP_MEM_PD (TOP_OFFSET + 0x38)
+#define MEM_ISO_EN_SHIFT 0
+#define FUSE_SEL_SHIFT 2
+
+#endif /*_MTK_DP_REG_H_*/
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
index 384074f69111b..e6e88e3cd811d 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
@@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
 	MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
 };
 
+enum TVDPLL_CLK {
+	TVDPLL_PLL = 0,
+	TVDPLL_D2 = 2,
+	TVDPLL_D4 = 4,
+	TVDPLL_D8 = 8,
+	TVDPLL_D16 = 16,
+};
+
 struct mtk_dpi {
 	struct drm_encoder encoder;
 	struct drm_bridge bridge;
@@ -71,8 +79,10 @@ struct mtk_dpi {
 	void __iomem *regs;
 	struct device *dev;
 	struct clk *engine_clk;
+	struct clk *dpi_ck_cg;
 	struct clk *pixel_clk;
 	struct clk *tvd_clk;
+	struct clk *pclk_src[5];
 	int irq;
 	struct drm_display_mode mode;
 	const struct mtk_dpi_conf *conf;
@@ -135,6 +145,7 @@ struct mtk_dpi_conf {
 	u32 hvsize_mask;
 	u32 channel_swap_shift;
 	u32 yuv422_en_bit;
+	u32 csc_enable_bit;
 	const struct mtk_dpi_yc_limit *limit;
 };
 
@@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
 
 static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
 {
-	mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
+	mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
 }
 
 static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
@@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
 		mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
 }
 
+static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
+{
+	u32 matrix_sel = 0;
+
+	switch (format) {
+	case MTK_DPI_COLOR_FORMAT_YCBCR_422:
+	case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
+	case MTK_DPI_COLOR_FORMAT_YCBCR_444:
+	case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
+	case MTK_DPI_COLOR_FORMAT_XV_YCC:
+		if (dpi->mode.hdisplay <= 720)
+			matrix_sel = 0x2;
+		break;
+	default:
+		break;
+	}
+	mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
+}
+
 static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
 					enum mtk_dpi_out_color_format format)
 {
 	if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
 	    (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
 		mtk_dpi_config_yuv422_enable(dpi, false);
-		if (dpi->conf->csc_support)
+		if (dpi->conf->csc_support) {
 			mtk_dpi_config_csc_enable(dpi, true);
+			mtk_dpi_matrix_sel(dpi, format);
+		}
 		if (dpi->conf->swap_input_support)
 			mtk_dpi_config_swap_input(dpi, false);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
 	} else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
 		   (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
 		mtk_dpi_config_yuv422_enable(dpi, true);
-		if (dpi->conf->csc_support)
+		if (dpi->conf->csc_support) {
 			mtk_dpi_config_csc_enable(dpi, true);
+			mtk_dpi_matrix_sel(dpi, format);
+		}
 		if (dpi->conf->swap_input_support)
 			mtk_dpi_config_swap_input(dpi, true);
 		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
@@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
 	mtk_dpi_disable(dpi);
 	clk_disable_unprepare(dpi->pixel_clk);
 	clk_disable_unprepare(dpi->engine_clk);
+	clk_disable_unprepare(dpi->dpi_ck_cg);
+	clk_disable_unprepare(dpi->tvd_clk);
 }
 
 static int mtk_dpi_power_on(struct mtk_dpi *dpi)
@@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
 	if (++dpi->refcount != 1)
 		return 0;
 
+	ret = clk_prepare_enable(dpi->tvd_clk);
+	if (ret) {
+		dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
+		goto err_pixel;
+	}
+
 	ret = clk_prepare_enable(dpi->engine_clk);
 	if (ret) {
 		dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
 		goto err_refcount;
 	}
 
+	ret = clk_prepare_enable(dpi->dpi_ck_cg);
+	if (ret) {
+		dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
+		goto err_ck_cg;
+	}
+
 	ret = clk_prepare_enable(dpi->pixel_clk);
 	if (ret) {
 		dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
@@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
 	if (dpi->pinctrl && dpi->pins_dpi)
 		pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
 
-	mtk_dpi_enable(dpi);
 	return 0;
 
 err_pixel:
+	clk_disable_unprepare(dpi->dpi_ck_cg);
+err_ck_cg:
 	clk_disable_unprepare(dpi->engine_clk);
 err_refcount:
 	dpi->refcount--;
@@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 	pll_rate = clk_get_rate(dpi->tvd_clk);
 
 	vm.pixelclock = pll_rate / factor;
-	if (dpi->conf->is_dpintf)
-		clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
-	else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
+	if (dpi->conf->is_dpintf) {
+		if (factor == 1)
+			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
+		else if (factor == 2)
+			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
+		else if (factor == 4)
+			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
+		else
+			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
+	} else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
 		 (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
 		clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
 	else
@@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 	}
 	mtk_dpi_sw_reset(dpi, false);
 
+	mtk_dpi_enable(dpi);
+
 	return 0;
 }
 
@@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
 	u32 *input_fmts;
 
 	*num_input_fmts = 0;
-
 	input_fmts = kcalloc(1, sizeof(*input_fmts),
 			     GFP_KERNEL);
 	if (!input_fmts)
@@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
 		if (dpi->conf->num_output_fmts)
 			out_bus_format = dpi->conf->output_fmts[0];
 
-	dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
+	dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
 		bridge_state->input_bus_cfg.format,
 		bridge_state->output_bus_cfg.format);
 
@@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
 	dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
 	dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
 	dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
-	dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
+	if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
+		dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
+	else
+		dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
 
 	return 0;
 }
@@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
 	MEDIA_BUS_FMT_RGB888_2X12_BE,
 };
 
+static const u32 mt8195_output_fmts[] = {
+	MEDIA_BUS_FMT_RGB888_1X24,
+	MEDIA_BUS_FMT_YUV8_1X24,
+	MEDIA_BUS_FMT_YUYV8_1X16,
+};
+
 static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
 	.c_bottom = 0x0010,
 	.c_top = 0x0FE0,
@@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
 	.hvsize_mask = HSIZE_MASK,
 	.channel_swap_shift = CH_SWAP,
 	.yuv422_en_bit = YUV422_EN,
+	.csc_enable_bit = CSC_ENABLE,
 	.limit = &mtk_dpi_limit,
 };
 
@@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
 	.hvsize_mask = HSIZE_MASK,
 	.channel_swap_shift = CH_SWAP,
 	.yuv422_en_bit = YUV422_EN,
+	.csc_enable_bit = CSC_ENABLE,
 	.limit = &mtk_dpi_limit,
 };
 
@@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
 	.hvsize_mask = HSIZE_MASK,
 	.channel_swap_shift = CH_SWAP,
 	.yuv422_en_bit = YUV422_EN,
+	.csc_enable_bit = CSC_ENABLE,
 	.limit = &mtk_dpi_limit,
 };
 
@@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
 	.hvsize_mask = HSIZE_MASK,
 	.channel_swap_shift = CH_SWAP,
 	.yuv422_en_bit = YUV422_EN,
+	.csc_enable_bit = CSC_ENABLE,
 	.limit = &mtk_dpi_limit,
 };
 
 static const struct mtk_dpi_conf mt8195_dpintf_conf = {
 	.cal_factor = mt8195_dpintf_calculate_factor,
-	.output_fmts = mt8173_output_fmts,
-	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
+	.output_fmts = mt8195_output_fmts,
+	.num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
 	.is_dpintf = true,
+	.csc_support = true,
 	.dimension_mask = DPINTF_HPW_MASK,
 	.hvsize_mask = DPINTF_HSIZE_MASK,
 	.channel_swap_shift = DPINTF_CH_SWAP,
 	.yuv422_en_bit = DPINTF_YUV422_EN,
+	.csc_enable_bit = DPINTF_CSC_ENABLE,
 	.limit = &mtk_dpintf_limit,
 };
 
@@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
+	if (IS_ERR(dpi->dpi_ck_cg)) {
+		ret = PTR_ERR(dpi->dpi_ck_cg);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
+				ret);
+
+		return ret;
+	}
+
 	dpi->pixel_clk = devm_clk_get(dev, "pixel");
 	if (IS_ERR(dpi->pixel_clk)) {
 		ret = PTR_ERR(dpi->pixel_clk);
@@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
+	dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
+	dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
+	dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
+
 	dpi->irq = platform_get_irq(pdev, 0);
 	if (dpi->irq <= 0)
 		return -EINVAL;
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi_regs.h b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
index 72efe6ee25842..91b32dfffced2 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
+++ b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h
@@ -41,6 +41,7 @@
 #define FAKE_DE_RODD			BIT(22)
 #define FAKE_DE_REVEN			BIT(23)
 #define DPINTF_YUV422_EN		BIT(24)
+#define DPINTF_CSC_ENABLE		BIT(26)
 #define DPINTF_INPUT_2P_EN		BIT(29)
 
 #define DPI_OUTPUT_SETTING	0x14
@@ -229,4 +230,29 @@
 
 #define EDGE_SEL_EN			BIT(5)
 #define H_FRE_2N			BIT(25)
+
+#define DPI_MATRIX_SET	0xB4
+#define INT_MATRIX_SEL			BIT(0)
+#define INT_MATRIX_SEL_MASK		(0x1F << 0)
+#define RGB_TO_JPEG			0x00
+#define RGB_TO_FULL709			0x01
+#define RGB_TO_BT601			0x02
+#define RGB_TO_BT709			0x03
+#define JPEG_TO_RGB			0x04
+#define FULL709_TO_RGB			0x05
+#define BT601_TO_RGB			0x06
+#define BT709_TO_RGB			0x07
+#define JPEG_TO_BT601			0x08
+#define JPEG_TO_BT709			0x09
+#define BT601_TO_JPEG			0xA
+#define BT709_TO_JPEG			0xB
+#define BT709_TO_BT601			0xC
+#define BT601_TO_BT709			0xD
+#define JPEG_TO_CERGB			0x14
+#define FULL709_TO_CERGB		0x15
+#define BT601_TO_CERGB			0x16
+#define BT709_TO_CERGB			0x17
+#define RGB_TO_CERGB			0x1C
+#define MATRIX_BIT			BIT(8)
+#define EXT_MATRIX_EN			BIT(12)
 #endif /* __MTK_DPI_REGS_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index 1ff4e31c86345..65c7ccaaf1969 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -672,6 +672,7 @@ static struct platform_driver * const mtk_drm_drivers[] = {
 	&mtk_disp_ovl_driver,
 	&mtk_disp_rdma_driver,
 	&mtk_dpi_driver,
+	&mtk_dp_driver,
 	&mtk_drm_platform_driver,
 	&mtk_dsi_driver,
 };
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
index 3e7d1e6fbe010..8926416f4419d 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
@@ -53,6 +53,7 @@ extern struct platform_driver mtk_disp_gamma_driver;
 extern struct platform_driver mtk_disp_ovl_driver;
 extern struct platform_driver mtk_disp_rdma_driver;
 extern struct platform_driver mtk_dpi_driver;
+extern struct platform_driver mtk_dp_driver;
 extern struct platform_driver mtk_dsi_driver;
 
 #endif /* MTK_DRM_DRV_H */
-- 
2.32.0


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 2/7] dt-bindings: mediatek,dp: Add Display Port binding
  2021-11-10 13:06   ` Guillaume Ranquet
  (?)
  (?)
@ 2021-11-10 19:44     ` Rob Herring
  -1 siblings, 0 replies; 122+ messages in thread
From: Rob Herring @ 2021-11-10 19:44 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Chun-Kuang Hu, devicetree, Rob Herring, Matthias Brugger,
	dri-devel, Markus Schneider-Pargmann, linux-arm-kernel,
	linux-kernel, David Airlie, Jitao shi, Philipp Zabel,
	linux-mediatek, Daniel Vetter, CK Hu

On Wed, 10 Nov 2021 14:06:18 +0100, Guillaume Ranquet wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This controller is present on several mediatek hardware. Currently
> mt8195 and mt8395 have this controller without a functional difference,
> so only one compatible field is added.
> 
> The controller can have two forms, as a normal display port and as an
> embedded display port.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reviewed-by: Rob Herring <robh@kernel.org>
> ---
>  .../display/mediatek/mediatek,dp.yaml         | 87 +++++++++++++++++++
>  1 file changed, 87 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
> 

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:

dtschema/dtc warnings/errors:
Documentation/devicetree/bindings/display/mediatek/mediatek,dp.example.dts:20:18: fatal error: dt-bindings/power/mt8195-power.h: No such file or directory
   20 |         #include <dt-bindings/power/mt8195-power.h>
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
make[1]: *** [scripts/Makefile.lib:385: Documentation/devicetree/bindings/display/mediatek/mediatek,dp.example.dt.yaml] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:1441: dt_binding_check] Error 2

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/patch/1553428

This check can fail if there are any dependencies. The base for a patch
series is generally the most recent rc1.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit.


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

* Re: [PATCH v6 2/7] dt-bindings: mediatek, dp: Add Display Port binding
@ 2021-11-10 19:44     ` Rob Herring
  0 siblings, 0 replies; 122+ messages in thread
From: Rob Herring @ 2021-11-10 19:44 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Chun-Kuang Hu, devicetree, Rob Herring, Matthias Brugger,
	dri-devel, Markus Schneider-Pargmann, linux-arm-kernel,
	linux-kernel, David Airlie, Jitao shi, Philipp Zabel,
	linux-mediatek, Daniel Vetter, CK Hu

On Wed, 10 Nov 2021 14:06:18 +0100, Guillaume Ranquet wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This controller is present on several mediatek hardware. Currently
> mt8195 and mt8395 have this controller without a functional difference,
> so only one compatible field is added.
> 
> The controller can have two forms, as a normal display port and as an
> embedded display port.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reviewed-by: Rob Herring <robh@kernel.org>
> ---
>  .../display/mediatek/mediatek,dp.yaml         | 87 +++++++++++++++++++
>  1 file changed, 87 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
> 

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:

dtschema/dtc warnings/errors:
Documentation/devicetree/bindings/display/mediatek/mediatek,dp.example.dts:20:18: fatal error: dt-bindings/power/mt8195-power.h: No such file or directory
   20 |         #include <dt-bindings/power/mt8195-power.h>
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
make[1]: *** [scripts/Makefile.lib:385: Documentation/devicetree/bindings/display/mediatek/mediatek,dp.example.dt.yaml] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:1441: dt_binding_check] Error 2

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/patch/1553428

This check can fail if there are any dependencies. The base for a patch
series is generally the most recent rc1.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit.


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 2/7] dt-bindings: mediatek, dp: Add Display Port binding
@ 2021-11-10 19:44     ` Rob Herring
  0 siblings, 0 replies; 122+ messages in thread
From: Rob Herring @ 2021-11-10 19:44 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Chun-Kuang Hu, Jitao shi, devicetree, David Airlie, linux-kernel,
	dri-devel, Markus Schneider-Pargmann, Rob Herring,
	linux-mediatek, Matthias Brugger, linux-arm-kernel

On Wed, 10 Nov 2021 14:06:18 +0100, Guillaume Ranquet wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This controller is present on several mediatek hardware. Currently
> mt8195 and mt8395 have this controller without a functional difference,
> so only one compatible field is added.
> 
> The controller can have two forms, as a normal display port and as an
> embedded display port.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reviewed-by: Rob Herring <robh@kernel.org>
> ---
>  .../display/mediatek/mediatek,dp.yaml         | 87 +++++++++++++++++++
>  1 file changed, 87 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
> 

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:

dtschema/dtc warnings/errors:
Documentation/devicetree/bindings/display/mediatek/mediatek,dp.example.dts:20:18: fatal error: dt-bindings/power/mt8195-power.h: No such file or directory
   20 |         #include <dt-bindings/power/mt8195-power.h>
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
make[1]: *** [scripts/Makefile.lib:385: Documentation/devicetree/bindings/display/mediatek/mediatek,dp.example.dt.yaml] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:1441: dt_binding_check] Error 2

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/patch/1553428

This check can fail if there are any dependencies. The base for a patch
series is generally the most recent rc1.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit.


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

* Re: [PATCH v6 2/7] dt-bindings: mediatek, dp: Add Display Port binding
@ 2021-11-10 19:44     ` Rob Herring
  0 siblings, 0 replies; 122+ messages in thread
From: Rob Herring @ 2021-11-10 19:44 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Chun-Kuang Hu, devicetree, Rob Herring, Matthias Brugger,
	dri-devel, Markus Schneider-Pargmann, linux-arm-kernel,
	linux-kernel, David Airlie, Jitao shi, Philipp Zabel,
	linux-mediatek, Daniel Vetter, CK Hu

On Wed, 10 Nov 2021 14:06:18 +0100, Guillaume Ranquet wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This controller is present on several mediatek hardware. Currently
> mt8195 and mt8395 have this controller without a functional difference,
> so only one compatible field is added.
> 
> The controller can have two forms, as a normal display port and as an
> embedded display port.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reviewed-by: Rob Herring <robh@kernel.org>
> ---
>  .../display/mediatek/mediatek,dp.yaml         | 87 +++++++++++++++++++
>  1 file changed, 87 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
> 

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:

dtschema/dtc warnings/errors:
Documentation/devicetree/bindings/display/mediatek/mediatek,dp.example.dts:20:18: fatal error: dt-bindings/power/mt8195-power.h: No such file or directory
   20 |         #include <dt-bindings/power/mt8195-power.h>
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
make[1]: *** [scripts/Makefile.lib:385: Documentation/devicetree/bindings/display/mediatek/mediatek,dp.example.dt.yaml] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:1441: dt_binding_check] Error 2

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/patch/1553428

This check can fail if there are any dependencies. The base for a patch
series is generally the most recent rc1.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit.


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 6/7] phy: phy-mtk-dp: Add driver for DP phy
  2021-11-10 13:06   ` Guillaume Ranquet
                       ` (2 preceding siblings ...)
  (?)
@ 2021-11-13  7:48     ` Chunfeng Yun
  -1 siblings, 0 replies; 122+ messages in thread
From: Chunfeng Yun @ 2021-11-13  7:48 UTC (permalink / raw)
  To: Guillaume Ranquet, Kishon Vijay Abraham I, Vinod Koul,
	Chun-Kuang Hu, Philipp Zabel, Matthias Brugger
  Cc: Markus Schneider-Pargmann, linux-kernel, linux-arm-kernel,
	linux-mediatek, linux-phy, dri-devel

On Wed, 2021-11-10 at 14:06 +0100, Guillaume Ranquet wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This is a new driver that supports the integrated DisplayPort phy for
> mediatek SoCs, especially the mt8195. The phy is integrated into the
> DisplayPort controller and will be created by the mtk-dp driver. This
> driver expects a struct regmap to be able to work on the same
> registers
> as the DisplayPort controller. It sets the device data to be the
> struct
> phy so that the DisplayPort controller can easily work with it.
> 
> The driver does not have any devicetree bindings because the
> datasheet
> does not list the controller and the phy as distinct units.
> 
> The interaction with the controller can be covered by the configure
> callback of the phy framework and its displayport parameters.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> ---
>  MAINTAINERS                       |   1 +
>  drivers/phy/mediatek/Kconfig      |   8 ++
>  drivers/phy/mediatek/Makefile     |   1 +
>  drivers/phy/mediatek/phy-mtk-dp.c | 219
> ++++++++++++++++++++++++++++++
>  4 files changed, 229 insertions(+)
>  create mode 100644 drivers/phy/mediatek/phy-mtk-dp.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 170bbbeefc3f4..f9a71b6d2a4a9 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6367,6 +6367,7 @@ L:	linux-mediatek@lists.infradead.org
> (moderated for non-subscribers)
>  S:	Supported
>  F:	Documentation/devicetree/bindings/display/mediatek/
>  F:	drivers/gpu/drm/mediatek/
> +F:	drivers/phy/mediatek/phy-mtk-dp.c
>  F:	drivers/phy/mediatek/phy-mtk-hdmi*
>  F:	drivers/phy/mediatek/phy-mtk-mipi*
>  
> diff --git a/drivers/phy/mediatek/Kconfig
> b/drivers/phy/mediatek/Kconfig
> index 55f8e6c048ab3..f7ec860590492 100644
> --- a/drivers/phy/mediatek/Kconfig
> +++ b/drivers/phy/mediatek/Kconfig
> @@ -55,3 +55,11 @@ config PHY_MTK_MIPI_DSI
>  	select GENERIC_PHY
>  	help
>  	  Support MIPI DSI for Mediatek SoCs.
> +
> +config PHY_MTK_DP
> +	tristate "MediaTek DP-PHY Driver"
> +	depends on ARCH_MEDIATEK || COMPILE_TEST
> +	depends on OF
> +	select GENERIC_PHY
> +	help
> +	  Support DisplayPort PHY for Mediatek SoCs.
> diff --git a/drivers/phy/mediatek/Makefile
> b/drivers/phy/mediatek/Makefile
> index ace660fbed3a1..4ba1e06504346 100644
> --- a/drivers/phy/mediatek/Makefile
> +++ b/drivers/phy/mediatek/Makefile
> @@ -3,6 +3,7 @@
>  # Makefile for the phy drivers.
>  #
>  
> +obj-$(CONFIG_PHY_MTK_DP)		+= phy-mtk-dp.o
>  obj-$(CONFIG_PHY_MTK_TPHY)		+= phy-mtk-tphy.o
>  obj-$(CONFIG_PHY_MTK_UFS)		+= phy-mtk-ufs.o
>  obj-$(CONFIG_PHY_MTK_XSPHY)		+= phy-mtk-xsphy.o
> diff --git a/drivers/phy/mediatek/phy-mtk-dp.c
> b/drivers/phy/mediatek/phy-mtk-dp.c
> new file mode 100644
> index 0000000000000..4f8d26ec0346b
> --- /dev/null
> +++ b/drivers/phy/mediatek/phy-mtk-dp.c
> @@ -0,0 +1,219 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 BayLibre
> + * Author: Markus Schneider-Pargmann <msp@baylibre.com>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +#define PHY_OFFSET 0x1000
Why not provide register base address in DTS?
I find that phy's platform device is registered by dp driver but the
one automatically created when parse dts?

> +
> +#define MTK_DP_PHY_DIG_PLL_CTL_1		(PHY_OFFSET + 0x014)
> +# define TPLL_SSC_EN				BIT(3)
> +
> +#define MTK_DP_PHY_DIG_BIT_RATE			(PHY_OFFSET +
> 0x03C)
> +# define BIT_RATE_RBR				0
> +# define BIT_RATE_HBR				1
> +# define BIT_RATE_HBR2				2
> +# define BIT_RATE_HBR3				3
> +
> +#define MTK_DP_PHY_DIG_SW_RST			(PHY_OFFSET +
> 0x038)
> +# define DP_GLB_SW_RST_PHYD			BIT(0)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_3		(PHY_OFFSET + 0x138)
> +#define MTK_DP_LANE1_DRIVING_PARAM_3		(PHY_OFFSET + 0x238)
> +#define MTK_DP_LANE2_DRIVING_PARAM_3		(PHY_OFFSET + 0x338)
> +#define MTK_DP_LANE3_DRIVING_PARAM_3		(PHY_OFFSET + 0x438)
> +# define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT	0x10
> +# define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT	(0x14 << 8)
> +# define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT	(0x18 << 16)
> +# define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT	(0x20 << 24)
> +# define DRIVING_PARAM_3_DEFAULT		(XTP_LN_TX_LCTXC0_SW0_P
> RE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW0_P
> RE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW0_P
> RE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW0_P
> RE3_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_4		(PHY_OFFSET + 0x13C)
> +#define MTK_DP_LANE1_DRIVING_PARAM_4		(PHY_OFFSET + 0x23C)
> +#define MTK_DP_LANE2_DRIVING_PARAM_4		(PHY_OFFSET + 0x33C)
> +#define MTK_DP_LANE3_DRIVING_PARAM_4		(PHY_OFFSET + 0x43C)
> +# define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT	0x18
> +# define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT	(0x1e << 8)
> +# define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT	(0x24 << 16)
> +# define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT	(0x20 << 24)
> +# define DRIVING_PARAM_4_DEFAULT		(XTP_LN_TX_LCTXC0_SW1_P
> RE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW1_P
> RE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW1_P
> RE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW2_P
> RE0_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_5		(PHY_OFFSET + 0x140)
> +#define MTK_DP_LANE1_DRIVING_PARAM_5		(PHY_OFFSET + 0x240)
> +#define MTK_DP_LANE2_DRIVING_PARAM_5		(PHY_OFFSET + 0x340)
> +#define MTK_DP_LANE3_DRIVING_PARAM_5		(PHY_OFFSET + 0x440)
> +# define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT	0x28
> +# define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT	(0x30 << 8)
> +# define DRIVING_PARAM_5_DEFAULT		(XTP_LN_TX_LCTXC0_SW2_P
> RE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW3_P
> RE0_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_6		(PHY_OFFSET + 0x144)
> +#define MTK_DP_LANE1_DRIVING_PARAM_6		(PHY_OFFSET + 0x244)
> +#define MTK_DP_LANE2_DRIVING_PARAM_6		(PHY_OFFSET + 0x344)
> +#define MTK_DP_LANE3_DRIVING_PARAM_6		(PHY_OFFSET + 0x444)
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT	0x00
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT	(0x04 << 8)
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT	(0x08 << 16)
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT	(0x10 << 24)
> +# define DRIVING_PARAM_6_DEFAULT		(XTP_LN_TX_LCTXCP1_SW0_
> PRE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW0_
> PRE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW0_
> PRE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW0_
> PRE3_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_7		(PHY_OFFSET + 0x148)
> +#define MTK_DP_LANE1_DRIVING_PARAM_7		(PHY_OFFSET + 0x248)
> +#define MTK_DP_LANE2_DRIVING_PARAM_7		(PHY_OFFSET + 0x348)
> +#define MTK_DP_LANE3_DRIVING_PARAM_7		(PHY_OFFSET + 0x448)
> +# define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT	0x00
> +# define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT	(0x06 << 8)
> +# define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT	(0x0c << 16)
> +# define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT	(0x00 << 24)
> +# define DRIVING_PARAM_7_DEFAULT		(XTP_LN_TX_LCTXCP1_SW1_
> PRE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW1_
> PRE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW1_
> PRE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW2_
> PRE0_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_8		(PHY_OFFSET + 0x14C)
> +#define MTK_DP_LANE1_DRIVING_PARAM_8		(PHY_OFFSET + 0x24C)
> +#define MTK_DP_LANE2_DRIVING_PARAM_8		(PHY_OFFSET + 0x34C)
> +#define MTK_DP_LANE3_DRIVING_PARAM_8		(PHY_OFFSET + 0x44C)
> +# define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT	0x08
> +# define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT	(0x00 << 8)
> +# define DRIVING_PARAM_8_DEFAULT		(XTP_LN_TX_LCTXCP1_SW2_
> PRE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW3_
> PRE0_DEFAULT)
> +
> +struct mtk_dp_phy {
> +	struct regmap *regs;
> +};
> +
> +static int mtk_dp_phy_init(struct phy *phy)
> +{
> +	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> +	u32 driving_params[] = {
> +		DRIVING_PARAM_3_DEFAULT,
> +		DRIVING_PARAM_4_DEFAULT,
> +		DRIVING_PARAM_5_DEFAULT,
> +		DRIVING_PARAM_6_DEFAULT,
> +		DRIVING_PARAM_7_DEFAULT,
> +		DRIVING_PARAM_8_DEFAULT
> +	};
> +
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_phy_configure(struct phy *phy, union
> phy_configure_opts *opts)
> +{
> +	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> +	u32 val;
> +
> +	if (opts->dp.set_rate) {
> +		switch (opts->dp.link_rate) {
> +		default:
> +			dev_err(&phy->dev,
> +				"Implementation error, unknown linkrate
> %x\n",
> +				opts->dp.link_rate);
> +			return -EINVAL;
> +		case 1620:
> +			val = BIT_RATE_RBR;
> +			break;
> +		case 2700:
> +			val = BIT_RATE_HBR;
> +			break;
> +		case 5400:
> +			val = BIT_RATE_HBR2;
> +			break;
> +		case 8100:
> +			val = BIT_RATE_HBR3;
> +			break;
> +		}
> +		regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE,
> val);
> +	}
> +
> +	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
> +			   TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN :
> 0);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_phy_reset(struct phy *phy)
> +{
> +	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> +
> +	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> +			   DP_GLB_SW_RST_PHYD, 0);
> +	usleep_range(50, 200);
> +	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> +			   DP_GLB_SW_RST_PHYD, 1);
> +
> +	return 0;
> +}
> +
> +static const struct phy_ops mtk_dp_phy_dev_ops = {
> +	.init = mtk_dp_phy_init,
> +	.configure = mtk_dp_phy_configure,
> +	.reset = mtk_dp_phy_reset,
> +	.owner = THIS_MODULE,
> +};
> +
> +static int mtk_dp_phy_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mtk_dp_phy *dp_phy;
> +	struct phy *phy;
> +
> +	dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
> +	if (!dp_phy)
> +		return -ENOMEM;
> +
> +	dp_phy->regs = *(struct regmap **)dev->platform_data;
> +	if (!dp_phy->regs) {
> +		dev_err(dev, "No data passed, requires struct
> regmap**\n");
> +		return -EINVAL;
> +	}
Why not get it from dts?

> +
> +	phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
> +	if (IS_ERR(phy)) {
> +		dev_err(dev, "Failed to create DP PHY: %ld\n",
> PTR_ERR(phy));
> +		return PTR_ERR(phy);
> +	}
> +	phy_set_drvdata(phy, dp_phy);
> +
> +	// Set device data to the phy so that mtk-dp can get it easily
use /* */
> +	dev_set_drvdata(dev, phy);
> +
> +	return 0;
> +}
> +
> +struct platform_driver mtk_dp_phy_driver = {
> +	.probe = mtk_dp_phy_probe,
> +	.driver = {
> +		.name = "mediatek-dp-phy",
> +	},
> +};
> +module_platform_driver(mtk_dp_phy_driver);
> +
> +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> +MODULE_DESCRIPTION("MediaTek DP PHY Driver");
> +MODULE_LICENSE("GPL v2");
_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 6/7] phy: phy-mtk-dp: Add driver for DP phy
@ 2021-11-13  7:48     ` Chunfeng Yun
  0 siblings, 0 replies; 122+ messages in thread
From: Chunfeng Yun @ 2021-11-13  7:48 UTC (permalink / raw)
  To: Guillaume Ranquet, Kishon Vijay Abraham I, Vinod Koul,
	Chun-Kuang Hu, Philipp Zabel, Matthias Brugger
  Cc: linux-kernel, dri-devel, Markus Schneider-Pargmann,
	linux-mediatek, linux-phy, linux-arm-kernel

On Wed, 2021-11-10 at 14:06 +0100, Guillaume Ranquet wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This is a new driver that supports the integrated DisplayPort phy for
> mediatek SoCs, especially the mt8195. The phy is integrated into the
> DisplayPort controller and will be created by the mtk-dp driver. This
> driver expects a struct regmap to be able to work on the same
> registers
> as the DisplayPort controller. It sets the device data to be the
> struct
> phy so that the DisplayPort controller can easily work with it.
> 
> The driver does not have any devicetree bindings because the
> datasheet
> does not list the controller and the phy as distinct units.
> 
> The interaction with the controller can be covered by the configure
> callback of the phy framework and its displayport parameters.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> ---
>  MAINTAINERS                       |   1 +
>  drivers/phy/mediatek/Kconfig      |   8 ++
>  drivers/phy/mediatek/Makefile     |   1 +
>  drivers/phy/mediatek/phy-mtk-dp.c | 219
> ++++++++++++++++++++++++++++++
>  4 files changed, 229 insertions(+)
>  create mode 100644 drivers/phy/mediatek/phy-mtk-dp.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 170bbbeefc3f4..f9a71b6d2a4a9 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6367,6 +6367,7 @@ L:	linux-mediatek@lists.infradead.org
> (moderated for non-subscribers)
>  S:	Supported
>  F:	Documentation/devicetree/bindings/display/mediatek/
>  F:	drivers/gpu/drm/mediatek/
> +F:	drivers/phy/mediatek/phy-mtk-dp.c
>  F:	drivers/phy/mediatek/phy-mtk-hdmi*
>  F:	drivers/phy/mediatek/phy-mtk-mipi*
>  
> diff --git a/drivers/phy/mediatek/Kconfig
> b/drivers/phy/mediatek/Kconfig
> index 55f8e6c048ab3..f7ec860590492 100644
> --- a/drivers/phy/mediatek/Kconfig
> +++ b/drivers/phy/mediatek/Kconfig
> @@ -55,3 +55,11 @@ config PHY_MTK_MIPI_DSI
>  	select GENERIC_PHY
>  	help
>  	  Support MIPI DSI for Mediatek SoCs.
> +
> +config PHY_MTK_DP
> +	tristate "MediaTek DP-PHY Driver"
> +	depends on ARCH_MEDIATEK || COMPILE_TEST
> +	depends on OF
> +	select GENERIC_PHY
> +	help
> +	  Support DisplayPort PHY for Mediatek SoCs.
> diff --git a/drivers/phy/mediatek/Makefile
> b/drivers/phy/mediatek/Makefile
> index ace660fbed3a1..4ba1e06504346 100644
> --- a/drivers/phy/mediatek/Makefile
> +++ b/drivers/phy/mediatek/Makefile
> @@ -3,6 +3,7 @@
>  # Makefile for the phy drivers.
>  #
>  
> +obj-$(CONFIG_PHY_MTK_DP)		+= phy-mtk-dp.o
>  obj-$(CONFIG_PHY_MTK_TPHY)		+= phy-mtk-tphy.o
>  obj-$(CONFIG_PHY_MTK_UFS)		+= phy-mtk-ufs.o
>  obj-$(CONFIG_PHY_MTK_XSPHY)		+= phy-mtk-xsphy.o
> diff --git a/drivers/phy/mediatek/phy-mtk-dp.c
> b/drivers/phy/mediatek/phy-mtk-dp.c
> new file mode 100644
> index 0000000000000..4f8d26ec0346b
> --- /dev/null
> +++ b/drivers/phy/mediatek/phy-mtk-dp.c
> @@ -0,0 +1,219 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 BayLibre
> + * Author: Markus Schneider-Pargmann <msp@baylibre.com>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +#define PHY_OFFSET 0x1000
Why not provide register base address in DTS?
I find that phy's platform device is registered by dp driver but the
one automatically created when parse dts?

> +
> +#define MTK_DP_PHY_DIG_PLL_CTL_1		(PHY_OFFSET + 0x014)
> +# define TPLL_SSC_EN				BIT(3)
> +
> +#define MTK_DP_PHY_DIG_BIT_RATE			(PHY_OFFSET +
> 0x03C)
> +# define BIT_RATE_RBR				0
> +# define BIT_RATE_HBR				1
> +# define BIT_RATE_HBR2				2
> +# define BIT_RATE_HBR3				3
> +
> +#define MTK_DP_PHY_DIG_SW_RST			(PHY_OFFSET +
> 0x038)
> +# define DP_GLB_SW_RST_PHYD			BIT(0)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_3		(PHY_OFFSET + 0x138)
> +#define MTK_DP_LANE1_DRIVING_PARAM_3		(PHY_OFFSET + 0x238)
> +#define MTK_DP_LANE2_DRIVING_PARAM_3		(PHY_OFFSET + 0x338)
> +#define MTK_DP_LANE3_DRIVING_PARAM_3		(PHY_OFFSET + 0x438)
> +# define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT	0x10
> +# define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT	(0x14 << 8)
> +# define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT	(0x18 << 16)
> +# define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT	(0x20 << 24)
> +# define DRIVING_PARAM_3_DEFAULT		(XTP_LN_TX_LCTXC0_SW0_P
> RE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW0_P
> RE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW0_P
> RE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW0_P
> RE3_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_4		(PHY_OFFSET + 0x13C)
> +#define MTK_DP_LANE1_DRIVING_PARAM_4		(PHY_OFFSET + 0x23C)
> +#define MTK_DP_LANE2_DRIVING_PARAM_4		(PHY_OFFSET + 0x33C)
> +#define MTK_DP_LANE3_DRIVING_PARAM_4		(PHY_OFFSET + 0x43C)
> +# define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT	0x18
> +# define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT	(0x1e << 8)
> +# define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT	(0x24 << 16)
> +# define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT	(0x20 << 24)
> +# define DRIVING_PARAM_4_DEFAULT		(XTP_LN_TX_LCTXC0_SW1_P
> RE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW1_P
> RE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW1_P
> RE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW2_P
> RE0_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_5		(PHY_OFFSET + 0x140)
> +#define MTK_DP_LANE1_DRIVING_PARAM_5		(PHY_OFFSET + 0x240)
> +#define MTK_DP_LANE2_DRIVING_PARAM_5		(PHY_OFFSET + 0x340)
> +#define MTK_DP_LANE3_DRIVING_PARAM_5		(PHY_OFFSET + 0x440)
> +# define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT	0x28
> +# define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT	(0x30 << 8)
> +# define DRIVING_PARAM_5_DEFAULT		(XTP_LN_TX_LCTXC0_SW2_P
> RE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW3_P
> RE0_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_6		(PHY_OFFSET + 0x144)
> +#define MTK_DP_LANE1_DRIVING_PARAM_6		(PHY_OFFSET + 0x244)
> +#define MTK_DP_LANE2_DRIVING_PARAM_6		(PHY_OFFSET + 0x344)
> +#define MTK_DP_LANE3_DRIVING_PARAM_6		(PHY_OFFSET + 0x444)
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT	0x00
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT	(0x04 << 8)
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT	(0x08 << 16)
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT	(0x10 << 24)
> +# define DRIVING_PARAM_6_DEFAULT		(XTP_LN_TX_LCTXCP1_SW0_
> PRE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW0_
> PRE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW0_
> PRE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW0_
> PRE3_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_7		(PHY_OFFSET + 0x148)
> +#define MTK_DP_LANE1_DRIVING_PARAM_7		(PHY_OFFSET + 0x248)
> +#define MTK_DP_LANE2_DRIVING_PARAM_7		(PHY_OFFSET + 0x348)
> +#define MTK_DP_LANE3_DRIVING_PARAM_7		(PHY_OFFSET + 0x448)
> +# define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT	0x00
> +# define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT	(0x06 << 8)
> +# define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT	(0x0c << 16)
> +# define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT	(0x00 << 24)
> +# define DRIVING_PARAM_7_DEFAULT		(XTP_LN_TX_LCTXCP1_SW1_
> PRE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW1_
> PRE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW1_
> PRE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW2_
> PRE0_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_8		(PHY_OFFSET + 0x14C)
> +#define MTK_DP_LANE1_DRIVING_PARAM_8		(PHY_OFFSET + 0x24C)
> +#define MTK_DP_LANE2_DRIVING_PARAM_8		(PHY_OFFSET + 0x34C)
> +#define MTK_DP_LANE3_DRIVING_PARAM_8		(PHY_OFFSET + 0x44C)
> +# define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT	0x08
> +# define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT	(0x00 << 8)
> +# define DRIVING_PARAM_8_DEFAULT		(XTP_LN_TX_LCTXCP1_SW2_
> PRE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW3_
> PRE0_DEFAULT)
> +
> +struct mtk_dp_phy {
> +	struct regmap *regs;
> +};
> +
> +static int mtk_dp_phy_init(struct phy *phy)
> +{
> +	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> +	u32 driving_params[] = {
> +		DRIVING_PARAM_3_DEFAULT,
> +		DRIVING_PARAM_4_DEFAULT,
> +		DRIVING_PARAM_5_DEFAULT,
> +		DRIVING_PARAM_6_DEFAULT,
> +		DRIVING_PARAM_7_DEFAULT,
> +		DRIVING_PARAM_8_DEFAULT
> +	};
> +
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_phy_configure(struct phy *phy, union
> phy_configure_opts *opts)
> +{
> +	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> +	u32 val;
> +
> +	if (opts->dp.set_rate) {
> +		switch (opts->dp.link_rate) {
> +		default:
> +			dev_err(&phy->dev,
> +				"Implementation error, unknown linkrate
> %x\n",
> +				opts->dp.link_rate);
> +			return -EINVAL;
> +		case 1620:
> +			val = BIT_RATE_RBR;
> +			break;
> +		case 2700:
> +			val = BIT_RATE_HBR;
> +			break;
> +		case 5400:
> +			val = BIT_RATE_HBR2;
> +			break;
> +		case 8100:
> +			val = BIT_RATE_HBR3;
> +			break;
> +		}
> +		regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE,
> val);
> +	}
> +
> +	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
> +			   TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN :
> 0);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_phy_reset(struct phy *phy)
> +{
> +	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> +
> +	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> +			   DP_GLB_SW_RST_PHYD, 0);
> +	usleep_range(50, 200);
> +	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> +			   DP_GLB_SW_RST_PHYD, 1);
> +
> +	return 0;
> +}
> +
> +static const struct phy_ops mtk_dp_phy_dev_ops = {
> +	.init = mtk_dp_phy_init,
> +	.configure = mtk_dp_phy_configure,
> +	.reset = mtk_dp_phy_reset,
> +	.owner = THIS_MODULE,
> +};
> +
> +static int mtk_dp_phy_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mtk_dp_phy *dp_phy;
> +	struct phy *phy;
> +
> +	dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
> +	if (!dp_phy)
> +		return -ENOMEM;
> +
> +	dp_phy->regs = *(struct regmap **)dev->platform_data;
> +	if (!dp_phy->regs) {
> +		dev_err(dev, "No data passed, requires struct
> regmap**\n");
> +		return -EINVAL;
> +	}
Why not get it from dts?

> +
> +	phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
> +	if (IS_ERR(phy)) {
> +		dev_err(dev, "Failed to create DP PHY: %ld\n",
> PTR_ERR(phy));
> +		return PTR_ERR(phy);
> +	}
> +	phy_set_drvdata(phy, dp_phy);
> +
> +	// Set device data to the phy so that mtk-dp can get it easily
use /* */
> +	dev_set_drvdata(dev, phy);
> +
> +	return 0;
> +}
> +
> +struct platform_driver mtk_dp_phy_driver = {
> +	.probe = mtk_dp_phy_probe,
> +	.driver = {
> +		.name = "mediatek-dp-phy",
> +	},
> +};
> +module_platform_driver(mtk_dp_phy_driver);
> +
> +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> +MODULE_DESCRIPTION("MediaTek DP PHY Driver");
> +MODULE_LICENSE("GPL v2");

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

* Re: [PATCH v6 6/7] phy: phy-mtk-dp: Add driver for DP phy
@ 2021-11-13  7:48     ` Chunfeng Yun
  0 siblings, 0 replies; 122+ messages in thread
From: Chunfeng Yun @ 2021-11-13  7:48 UTC (permalink / raw)
  To: Guillaume Ranquet, Kishon Vijay Abraham I, Vinod Koul,
	Chun-Kuang Hu, Philipp Zabel, Matthias Brugger
  Cc: Markus Schneider-Pargmann, linux-kernel, linux-arm-kernel,
	linux-mediatek, linux-phy, dri-devel

On Wed, 2021-11-10 at 14:06 +0100, Guillaume Ranquet wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This is a new driver that supports the integrated DisplayPort phy for
> mediatek SoCs, especially the mt8195. The phy is integrated into the
> DisplayPort controller and will be created by the mtk-dp driver. This
> driver expects a struct regmap to be able to work on the same
> registers
> as the DisplayPort controller. It sets the device data to be the
> struct
> phy so that the DisplayPort controller can easily work with it.
> 
> The driver does not have any devicetree bindings because the
> datasheet
> does not list the controller and the phy as distinct units.
> 
> The interaction with the controller can be covered by the configure
> callback of the phy framework and its displayport parameters.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> ---
>  MAINTAINERS                       |   1 +
>  drivers/phy/mediatek/Kconfig      |   8 ++
>  drivers/phy/mediatek/Makefile     |   1 +
>  drivers/phy/mediatek/phy-mtk-dp.c | 219
> ++++++++++++++++++++++++++++++
>  4 files changed, 229 insertions(+)
>  create mode 100644 drivers/phy/mediatek/phy-mtk-dp.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 170bbbeefc3f4..f9a71b6d2a4a9 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6367,6 +6367,7 @@ L:	linux-mediatek@lists.infradead.org
> (moderated for non-subscribers)
>  S:	Supported
>  F:	Documentation/devicetree/bindings/display/mediatek/
>  F:	drivers/gpu/drm/mediatek/
> +F:	drivers/phy/mediatek/phy-mtk-dp.c
>  F:	drivers/phy/mediatek/phy-mtk-hdmi*
>  F:	drivers/phy/mediatek/phy-mtk-mipi*
>  
> diff --git a/drivers/phy/mediatek/Kconfig
> b/drivers/phy/mediatek/Kconfig
> index 55f8e6c048ab3..f7ec860590492 100644
> --- a/drivers/phy/mediatek/Kconfig
> +++ b/drivers/phy/mediatek/Kconfig
> @@ -55,3 +55,11 @@ config PHY_MTK_MIPI_DSI
>  	select GENERIC_PHY
>  	help
>  	  Support MIPI DSI for Mediatek SoCs.
> +
> +config PHY_MTK_DP
> +	tristate "MediaTek DP-PHY Driver"
> +	depends on ARCH_MEDIATEK || COMPILE_TEST
> +	depends on OF
> +	select GENERIC_PHY
> +	help
> +	  Support DisplayPort PHY for Mediatek SoCs.
> diff --git a/drivers/phy/mediatek/Makefile
> b/drivers/phy/mediatek/Makefile
> index ace660fbed3a1..4ba1e06504346 100644
> --- a/drivers/phy/mediatek/Makefile
> +++ b/drivers/phy/mediatek/Makefile
> @@ -3,6 +3,7 @@
>  # Makefile for the phy drivers.
>  #
>  
> +obj-$(CONFIG_PHY_MTK_DP)		+= phy-mtk-dp.o
>  obj-$(CONFIG_PHY_MTK_TPHY)		+= phy-mtk-tphy.o
>  obj-$(CONFIG_PHY_MTK_UFS)		+= phy-mtk-ufs.o
>  obj-$(CONFIG_PHY_MTK_XSPHY)		+= phy-mtk-xsphy.o
> diff --git a/drivers/phy/mediatek/phy-mtk-dp.c
> b/drivers/phy/mediatek/phy-mtk-dp.c
> new file mode 100644
> index 0000000000000..4f8d26ec0346b
> --- /dev/null
> +++ b/drivers/phy/mediatek/phy-mtk-dp.c
> @@ -0,0 +1,219 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 BayLibre
> + * Author: Markus Schneider-Pargmann <msp@baylibre.com>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +#define PHY_OFFSET 0x1000
Why not provide register base address in DTS?
I find that phy's platform device is registered by dp driver but the
one automatically created when parse dts?

> +
> +#define MTK_DP_PHY_DIG_PLL_CTL_1		(PHY_OFFSET + 0x014)
> +# define TPLL_SSC_EN				BIT(3)
> +
> +#define MTK_DP_PHY_DIG_BIT_RATE			(PHY_OFFSET +
> 0x03C)
> +# define BIT_RATE_RBR				0
> +# define BIT_RATE_HBR				1
> +# define BIT_RATE_HBR2				2
> +# define BIT_RATE_HBR3				3
> +
> +#define MTK_DP_PHY_DIG_SW_RST			(PHY_OFFSET +
> 0x038)
> +# define DP_GLB_SW_RST_PHYD			BIT(0)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_3		(PHY_OFFSET + 0x138)
> +#define MTK_DP_LANE1_DRIVING_PARAM_3		(PHY_OFFSET + 0x238)
> +#define MTK_DP_LANE2_DRIVING_PARAM_3		(PHY_OFFSET + 0x338)
> +#define MTK_DP_LANE3_DRIVING_PARAM_3		(PHY_OFFSET + 0x438)
> +# define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT	0x10
> +# define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT	(0x14 << 8)
> +# define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT	(0x18 << 16)
> +# define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT	(0x20 << 24)
> +# define DRIVING_PARAM_3_DEFAULT		(XTP_LN_TX_LCTXC0_SW0_P
> RE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW0_P
> RE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW0_P
> RE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW0_P
> RE3_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_4		(PHY_OFFSET + 0x13C)
> +#define MTK_DP_LANE1_DRIVING_PARAM_4		(PHY_OFFSET + 0x23C)
> +#define MTK_DP_LANE2_DRIVING_PARAM_4		(PHY_OFFSET + 0x33C)
> +#define MTK_DP_LANE3_DRIVING_PARAM_4		(PHY_OFFSET + 0x43C)
> +# define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT	0x18
> +# define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT	(0x1e << 8)
> +# define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT	(0x24 << 16)
> +# define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT	(0x20 << 24)
> +# define DRIVING_PARAM_4_DEFAULT		(XTP_LN_TX_LCTXC0_SW1_P
> RE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW1_P
> RE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW1_P
> RE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW2_P
> RE0_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_5		(PHY_OFFSET + 0x140)
> +#define MTK_DP_LANE1_DRIVING_PARAM_5		(PHY_OFFSET + 0x240)
> +#define MTK_DP_LANE2_DRIVING_PARAM_5		(PHY_OFFSET + 0x340)
> +#define MTK_DP_LANE3_DRIVING_PARAM_5		(PHY_OFFSET + 0x440)
> +# define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT	0x28
> +# define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT	(0x30 << 8)
> +# define DRIVING_PARAM_5_DEFAULT		(XTP_LN_TX_LCTXC0_SW2_P
> RE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW3_P
> RE0_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_6		(PHY_OFFSET + 0x144)
> +#define MTK_DP_LANE1_DRIVING_PARAM_6		(PHY_OFFSET + 0x244)
> +#define MTK_DP_LANE2_DRIVING_PARAM_6		(PHY_OFFSET + 0x344)
> +#define MTK_DP_LANE3_DRIVING_PARAM_6		(PHY_OFFSET + 0x444)
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT	0x00
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT	(0x04 << 8)
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT	(0x08 << 16)
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT	(0x10 << 24)
> +# define DRIVING_PARAM_6_DEFAULT		(XTP_LN_TX_LCTXCP1_SW0_
> PRE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW0_
> PRE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW0_
> PRE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW0_
> PRE3_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_7		(PHY_OFFSET + 0x148)
> +#define MTK_DP_LANE1_DRIVING_PARAM_7		(PHY_OFFSET + 0x248)
> +#define MTK_DP_LANE2_DRIVING_PARAM_7		(PHY_OFFSET + 0x348)
> +#define MTK_DP_LANE3_DRIVING_PARAM_7		(PHY_OFFSET + 0x448)
> +# define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT	0x00
> +# define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT	(0x06 << 8)
> +# define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT	(0x0c << 16)
> +# define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT	(0x00 << 24)
> +# define DRIVING_PARAM_7_DEFAULT		(XTP_LN_TX_LCTXCP1_SW1_
> PRE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW1_
> PRE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW1_
> PRE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW2_
> PRE0_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_8		(PHY_OFFSET + 0x14C)
> +#define MTK_DP_LANE1_DRIVING_PARAM_8		(PHY_OFFSET + 0x24C)
> +#define MTK_DP_LANE2_DRIVING_PARAM_8		(PHY_OFFSET + 0x34C)
> +#define MTK_DP_LANE3_DRIVING_PARAM_8		(PHY_OFFSET + 0x44C)
> +# define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT	0x08
> +# define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT	(0x00 << 8)
> +# define DRIVING_PARAM_8_DEFAULT		(XTP_LN_TX_LCTXCP1_SW2_
> PRE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW3_
> PRE0_DEFAULT)
> +
> +struct mtk_dp_phy {
> +	struct regmap *regs;
> +};
> +
> +static int mtk_dp_phy_init(struct phy *phy)
> +{
> +	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> +	u32 driving_params[] = {
> +		DRIVING_PARAM_3_DEFAULT,
> +		DRIVING_PARAM_4_DEFAULT,
> +		DRIVING_PARAM_5_DEFAULT,
> +		DRIVING_PARAM_6_DEFAULT,
> +		DRIVING_PARAM_7_DEFAULT,
> +		DRIVING_PARAM_8_DEFAULT
> +	};
> +
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_phy_configure(struct phy *phy, union
> phy_configure_opts *opts)
> +{
> +	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> +	u32 val;
> +
> +	if (opts->dp.set_rate) {
> +		switch (opts->dp.link_rate) {
> +		default:
> +			dev_err(&phy->dev,
> +				"Implementation error, unknown linkrate
> %x\n",
> +				opts->dp.link_rate);
> +			return -EINVAL;
> +		case 1620:
> +			val = BIT_RATE_RBR;
> +			break;
> +		case 2700:
> +			val = BIT_RATE_HBR;
> +			break;
> +		case 5400:
> +			val = BIT_RATE_HBR2;
> +			break;
> +		case 8100:
> +			val = BIT_RATE_HBR3;
> +			break;
> +		}
> +		regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE,
> val);
> +	}
> +
> +	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
> +			   TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN :
> 0);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_phy_reset(struct phy *phy)
> +{
> +	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> +
> +	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> +			   DP_GLB_SW_RST_PHYD, 0);
> +	usleep_range(50, 200);
> +	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> +			   DP_GLB_SW_RST_PHYD, 1);
> +
> +	return 0;
> +}
> +
> +static const struct phy_ops mtk_dp_phy_dev_ops = {
> +	.init = mtk_dp_phy_init,
> +	.configure = mtk_dp_phy_configure,
> +	.reset = mtk_dp_phy_reset,
> +	.owner = THIS_MODULE,
> +};
> +
> +static int mtk_dp_phy_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mtk_dp_phy *dp_phy;
> +	struct phy *phy;
> +
> +	dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
> +	if (!dp_phy)
> +		return -ENOMEM;
> +
> +	dp_phy->regs = *(struct regmap **)dev->platform_data;
> +	if (!dp_phy->regs) {
> +		dev_err(dev, "No data passed, requires struct
> regmap**\n");
> +		return -EINVAL;
> +	}
Why not get it from dts?

> +
> +	phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
> +	if (IS_ERR(phy)) {
> +		dev_err(dev, "Failed to create DP PHY: %ld\n",
> PTR_ERR(phy));
> +		return PTR_ERR(phy);
> +	}
> +	phy_set_drvdata(phy, dp_phy);
> +
> +	// Set device data to the phy so that mtk-dp can get it easily
use /* */
> +	dev_set_drvdata(dev, phy);
> +
> +	return 0;
> +}
> +
> +struct platform_driver mtk_dp_phy_driver = {
> +	.probe = mtk_dp_phy_probe,
> +	.driver = {
> +		.name = "mediatek-dp-phy",
> +	},
> +};
> +module_platform_driver(mtk_dp_phy_driver);
> +
> +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> +MODULE_DESCRIPTION("MediaTek DP PHY Driver");
> +MODULE_LICENSE("GPL v2");
-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

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

* Re: [PATCH v6 6/7] phy: phy-mtk-dp: Add driver for DP phy
@ 2021-11-13  7:48     ` Chunfeng Yun
  0 siblings, 0 replies; 122+ messages in thread
From: Chunfeng Yun @ 2021-11-13  7:48 UTC (permalink / raw)
  To: Guillaume Ranquet, Kishon Vijay Abraham I, Vinod Koul,
	Chun-Kuang Hu, Philipp Zabel, Matthias Brugger
  Cc: Markus Schneider-Pargmann, linux-kernel, linux-arm-kernel,
	linux-mediatek, linux-phy, dri-devel

On Wed, 2021-11-10 at 14:06 +0100, Guillaume Ranquet wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This is a new driver that supports the integrated DisplayPort phy for
> mediatek SoCs, especially the mt8195. The phy is integrated into the
> DisplayPort controller and will be created by the mtk-dp driver. This
> driver expects a struct regmap to be able to work on the same
> registers
> as the DisplayPort controller. It sets the device data to be the
> struct
> phy so that the DisplayPort controller can easily work with it.
> 
> The driver does not have any devicetree bindings because the
> datasheet
> does not list the controller and the phy as distinct units.
> 
> The interaction with the controller can be covered by the configure
> callback of the phy framework and its displayport parameters.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> ---
>  MAINTAINERS                       |   1 +
>  drivers/phy/mediatek/Kconfig      |   8 ++
>  drivers/phy/mediatek/Makefile     |   1 +
>  drivers/phy/mediatek/phy-mtk-dp.c | 219
> ++++++++++++++++++++++++++++++
>  4 files changed, 229 insertions(+)
>  create mode 100644 drivers/phy/mediatek/phy-mtk-dp.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 170bbbeefc3f4..f9a71b6d2a4a9 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6367,6 +6367,7 @@ L:	linux-mediatek@lists.infradead.org
> (moderated for non-subscribers)
>  S:	Supported
>  F:	Documentation/devicetree/bindings/display/mediatek/
>  F:	drivers/gpu/drm/mediatek/
> +F:	drivers/phy/mediatek/phy-mtk-dp.c
>  F:	drivers/phy/mediatek/phy-mtk-hdmi*
>  F:	drivers/phy/mediatek/phy-mtk-mipi*
>  
> diff --git a/drivers/phy/mediatek/Kconfig
> b/drivers/phy/mediatek/Kconfig
> index 55f8e6c048ab3..f7ec860590492 100644
> --- a/drivers/phy/mediatek/Kconfig
> +++ b/drivers/phy/mediatek/Kconfig
> @@ -55,3 +55,11 @@ config PHY_MTK_MIPI_DSI
>  	select GENERIC_PHY
>  	help
>  	  Support MIPI DSI for Mediatek SoCs.
> +
> +config PHY_MTK_DP
> +	tristate "MediaTek DP-PHY Driver"
> +	depends on ARCH_MEDIATEK || COMPILE_TEST
> +	depends on OF
> +	select GENERIC_PHY
> +	help
> +	  Support DisplayPort PHY for Mediatek SoCs.
> diff --git a/drivers/phy/mediatek/Makefile
> b/drivers/phy/mediatek/Makefile
> index ace660fbed3a1..4ba1e06504346 100644
> --- a/drivers/phy/mediatek/Makefile
> +++ b/drivers/phy/mediatek/Makefile
> @@ -3,6 +3,7 @@
>  # Makefile for the phy drivers.
>  #
>  
> +obj-$(CONFIG_PHY_MTK_DP)		+= phy-mtk-dp.o
>  obj-$(CONFIG_PHY_MTK_TPHY)		+= phy-mtk-tphy.o
>  obj-$(CONFIG_PHY_MTK_UFS)		+= phy-mtk-ufs.o
>  obj-$(CONFIG_PHY_MTK_XSPHY)		+= phy-mtk-xsphy.o
> diff --git a/drivers/phy/mediatek/phy-mtk-dp.c
> b/drivers/phy/mediatek/phy-mtk-dp.c
> new file mode 100644
> index 0000000000000..4f8d26ec0346b
> --- /dev/null
> +++ b/drivers/phy/mediatek/phy-mtk-dp.c
> @@ -0,0 +1,219 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 BayLibre
> + * Author: Markus Schneider-Pargmann <msp@baylibre.com>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +#define PHY_OFFSET 0x1000
Why not provide register base address in DTS?
I find that phy's platform device is registered by dp driver but the
one automatically created when parse dts?

> +
> +#define MTK_DP_PHY_DIG_PLL_CTL_1		(PHY_OFFSET + 0x014)
> +# define TPLL_SSC_EN				BIT(3)
> +
> +#define MTK_DP_PHY_DIG_BIT_RATE			(PHY_OFFSET +
> 0x03C)
> +# define BIT_RATE_RBR				0
> +# define BIT_RATE_HBR				1
> +# define BIT_RATE_HBR2				2
> +# define BIT_RATE_HBR3				3
> +
> +#define MTK_DP_PHY_DIG_SW_RST			(PHY_OFFSET +
> 0x038)
> +# define DP_GLB_SW_RST_PHYD			BIT(0)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_3		(PHY_OFFSET + 0x138)
> +#define MTK_DP_LANE1_DRIVING_PARAM_3		(PHY_OFFSET + 0x238)
> +#define MTK_DP_LANE2_DRIVING_PARAM_3		(PHY_OFFSET + 0x338)
> +#define MTK_DP_LANE3_DRIVING_PARAM_3		(PHY_OFFSET + 0x438)
> +# define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT	0x10
> +# define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT	(0x14 << 8)
> +# define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT	(0x18 << 16)
> +# define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT	(0x20 << 24)
> +# define DRIVING_PARAM_3_DEFAULT		(XTP_LN_TX_LCTXC0_SW0_P
> RE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW0_P
> RE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW0_P
> RE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW0_P
> RE3_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_4		(PHY_OFFSET + 0x13C)
> +#define MTK_DP_LANE1_DRIVING_PARAM_4		(PHY_OFFSET + 0x23C)
> +#define MTK_DP_LANE2_DRIVING_PARAM_4		(PHY_OFFSET + 0x33C)
> +#define MTK_DP_LANE3_DRIVING_PARAM_4		(PHY_OFFSET + 0x43C)
> +# define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT	0x18
> +# define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT	(0x1e << 8)
> +# define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT	(0x24 << 16)
> +# define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT	(0x20 << 24)
> +# define DRIVING_PARAM_4_DEFAULT		(XTP_LN_TX_LCTXC0_SW1_P
> RE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW1_P
> RE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW1_P
> RE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW2_P
> RE0_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_5		(PHY_OFFSET + 0x140)
> +#define MTK_DP_LANE1_DRIVING_PARAM_5		(PHY_OFFSET + 0x240)
> +#define MTK_DP_LANE2_DRIVING_PARAM_5		(PHY_OFFSET + 0x340)
> +#define MTK_DP_LANE3_DRIVING_PARAM_5		(PHY_OFFSET + 0x440)
> +# define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT	0x28
> +# define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT	(0x30 << 8)
> +# define DRIVING_PARAM_5_DEFAULT		(XTP_LN_TX_LCTXC0_SW2_P
> RE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW3_P
> RE0_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_6		(PHY_OFFSET + 0x144)
> +#define MTK_DP_LANE1_DRIVING_PARAM_6		(PHY_OFFSET + 0x244)
> +#define MTK_DP_LANE2_DRIVING_PARAM_6		(PHY_OFFSET + 0x344)
> +#define MTK_DP_LANE3_DRIVING_PARAM_6		(PHY_OFFSET + 0x444)
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT	0x00
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT	(0x04 << 8)
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT	(0x08 << 16)
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT	(0x10 << 24)
> +# define DRIVING_PARAM_6_DEFAULT		(XTP_LN_TX_LCTXCP1_SW0_
> PRE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW0_
> PRE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW0_
> PRE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW0_
> PRE3_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_7		(PHY_OFFSET + 0x148)
> +#define MTK_DP_LANE1_DRIVING_PARAM_7		(PHY_OFFSET + 0x248)
> +#define MTK_DP_LANE2_DRIVING_PARAM_7		(PHY_OFFSET + 0x348)
> +#define MTK_DP_LANE3_DRIVING_PARAM_7		(PHY_OFFSET + 0x448)
> +# define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT	0x00
> +# define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT	(0x06 << 8)
> +# define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT	(0x0c << 16)
> +# define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT	(0x00 << 24)
> +# define DRIVING_PARAM_7_DEFAULT		(XTP_LN_TX_LCTXCP1_SW1_
> PRE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW1_
> PRE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW1_
> PRE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW2_
> PRE0_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_8		(PHY_OFFSET + 0x14C)
> +#define MTK_DP_LANE1_DRIVING_PARAM_8		(PHY_OFFSET + 0x24C)
> +#define MTK_DP_LANE2_DRIVING_PARAM_8		(PHY_OFFSET + 0x34C)
> +#define MTK_DP_LANE3_DRIVING_PARAM_8		(PHY_OFFSET + 0x44C)
> +# define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT	0x08
> +# define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT	(0x00 << 8)
> +# define DRIVING_PARAM_8_DEFAULT		(XTP_LN_TX_LCTXCP1_SW2_
> PRE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW3_
> PRE0_DEFAULT)
> +
> +struct mtk_dp_phy {
> +	struct regmap *regs;
> +};
> +
> +static int mtk_dp_phy_init(struct phy *phy)
> +{
> +	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> +	u32 driving_params[] = {
> +		DRIVING_PARAM_3_DEFAULT,
> +		DRIVING_PARAM_4_DEFAULT,
> +		DRIVING_PARAM_5_DEFAULT,
> +		DRIVING_PARAM_6_DEFAULT,
> +		DRIVING_PARAM_7_DEFAULT,
> +		DRIVING_PARAM_8_DEFAULT
> +	};
> +
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_phy_configure(struct phy *phy, union
> phy_configure_opts *opts)
> +{
> +	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> +	u32 val;
> +
> +	if (opts->dp.set_rate) {
> +		switch (opts->dp.link_rate) {
> +		default:
> +			dev_err(&phy->dev,
> +				"Implementation error, unknown linkrate
> %x\n",
> +				opts->dp.link_rate);
> +			return -EINVAL;
> +		case 1620:
> +			val = BIT_RATE_RBR;
> +			break;
> +		case 2700:
> +			val = BIT_RATE_HBR;
> +			break;
> +		case 5400:
> +			val = BIT_RATE_HBR2;
> +			break;
> +		case 8100:
> +			val = BIT_RATE_HBR3;
> +			break;
> +		}
> +		regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE,
> val);
> +	}
> +
> +	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
> +			   TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN :
> 0);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_phy_reset(struct phy *phy)
> +{
> +	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> +
> +	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> +			   DP_GLB_SW_RST_PHYD, 0);
> +	usleep_range(50, 200);
> +	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> +			   DP_GLB_SW_RST_PHYD, 1);
> +
> +	return 0;
> +}
> +
> +static const struct phy_ops mtk_dp_phy_dev_ops = {
> +	.init = mtk_dp_phy_init,
> +	.configure = mtk_dp_phy_configure,
> +	.reset = mtk_dp_phy_reset,
> +	.owner = THIS_MODULE,
> +};
> +
> +static int mtk_dp_phy_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mtk_dp_phy *dp_phy;
> +	struct phy *phy;
> +
> +	dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
> +	if (!dp_phy)
> +		return -ENOMEM;
> +
> +	dp_phy->regs = *(struct regmap **)dev->platform_data;
> +	if (!dp_phy->regs) {
> +		dev_err(dev, "No data passed, requires struct
> regmap**\n");
> +		return -EINVAL;
> +	}
Why not get it from dts?

> +
> +	phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
> +	if (IS_ERR(phy)) {
> +		dev_err(dev, "Failed to create DP PHY: %ld\n",
> PTR_ERR(phy));
> +		return PTR_ERR(phy);
> +	}
> +	phy_set_drvdata(phy, dp_phy);
> +
> +	// Set device data to the phy so that mtk-dp can get it easily
use /* */
> +	dev_set_drvdata(dev, phy);
> +
> +	return 0;
> +}
> +
> +struct platform_driver mtk_dp_phy_driver = {
> +	.probe = mtk_dp_phy_probe,
> +	.driver = {
> +		.name = "mediatek-dp-phy",
> +	},
> +};
> +module_platform_driver(mtk_dp_phy_driver);
> +
> +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> +MODULE_DESCRIPTION("MediaTek DP PHY Driver");
> +MODULE_LICENSE("GPL v2");
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 6/7] phy: phy-mtk-dp: Add driver for DP phy
@ 2021-11-13  7:48     ` Chunfeng Yun
  0 siblings, 0 replies; 122+ messages in thread
From: Chunfeng Yun @ 2021-11-13  7:48 UTC (permalink / raw)
  To: Guillaume Ranquet, Kishon Vijay Abraham I, Vinod Koul,
	Chun-Kuang Hu, Philipp Zabel, Matthias Brugger
  Cc: Markus Schneider-Pargmann, linux-kernel, linux-arm-kernel,
	linux-mediatek, linux-phy, dri-devel

On Wed, 2021-11-10 at 14:06 +0100, Guillaume Ranquet wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This is a new driver that supports the integrated DisplayPort phy for
> mediatek SoCs, especially the mt8195. The phy is integrated into the
> DisplayPort controller and will be created by the mtk-dp driver. This
> driver expects a struct regmap to be able to work on the same
> registers
> as the DisplayPort controller. It sets the device data to be the
> struct
> phy so that the DisplayPort controller can easily work with it.
> 
> The driver does not have any devicetree bindings because the
> datasheet
> does not list the controller and the phy as distinct units.
> 
> The interaction with the controller can be covered by the configure
> callback of the phy framework and its displayport parameters.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> ---
>  MAINTAINERS                       |   1 +
>  drivers/phy/mediatek/Kconfig      |   8 ++
>  drivers/phy/mediatek/Makefile     |   1 +
>  drivers/phy/mediatek/phy-mtk-dp.c | 219
> ++++++++++++++++++++++++++++++
>  4 files changed, 229 insertions(+)
>  create mode 100644 drivers/phy/mediatek/phy-mtk-dp.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 170bbbeefc3f4..f9a71b6d2a4a9 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6367,6 +6367,7 @@ L:	linux-mediatek@lists.infradead.org
> (moderated for non-subscribers)
>  S:	Supported
>  F:	Documentation/devicetree/bindings/display/mediatek/
>  F:	drivers/gpu/drm/mediatek/
> +F:	drivers/phy/mediatek/phy-mtk-dp.c
>  F:	drivers/phy/mediatek/phy-mtk-hdmi*
>  F:	drivers/phy/mediatek/phy-mtk-mipi*
>  
> diff --git a/drivers/phy/mediatek/Kconfig
> b/drivers/phy/mediatek/Kconfig
> index 55f8e6c048ab3..f7ec860590492 100644
> --- a/drivers/phy/mediatek/Kconfig
> +++ b/drivers/phy/mediatek/Kconfig
> @@ -55,3 +55,11 @@ config PHY_MTK_MIPI_DSI
>  	select GENERIC_PHY
>  	help
>  	  Support MIPI DSI for Mediatek SoCs.
> +
> +config PHY_MTK_DP
> +	tristate "MediaTek DP-PHY Driver"
> +	depends on ARCH_MEDIATEK || COMPILE_TEST
> +	depends on OF
> +	select GENERIC_PHY
> +	help
> +	  Support DisplayPort PHY for Mediatek SoCs.
> diff --git a/drivers/phy/mediatek/Makefile
> b/drivers/phy/mediatek/Makefile
> index ace660fbed3a1..4ba1e06504346 100644
> --- a/drivers/phy/mediatek/Makefile
> +++ b/drivers/phy/mediatek/Makefile
> @@ -3,6 +3,7 @@
>  # Makefile for the phy drivers.
>  #
>  
> +obj-$(CONFIG_PHY_MTK_DP)		+= phy-mtk-dp.o
>  obj-$(CONFIG_PHY_MTK_TPHY)		+= phy-mtk-tphy.o
>  obj-$(CONFIG_PHY_MTK_UFS)		+= phy-mtk-ufs.o
>  obj-$(CONFIG_PHY_MTK_XSPHY)		+= phy-mtk-xsphy.o
> diff --git a/drivers/phy/mediatek/phy-mtk-dp.c
> b/drivers/phy/mediatek/phy-mtk-dp.c
> new file mode 100644
> index 0000000000000..4f8d26ec0346b
> --- /dev/null
> +++ b/drivers/phy/mediatek/phy-mtk-dp.c
> @@ -0,0 +1,219 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 BayLibre
> + * Author: Markus Schneider-Pargmann <msp@baylibre.com>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +#define PHY_OFFSET 0x1000
Why not provide register base address in DTS?
I find that phy's platform device is registered by dp driver but the
one automatically created when parse dts?

> +
> +#define MTK_DP_PHY_DIG_PLL_CTL_1		(PHY_OFFSET + 0x014)
> +# define TPLL_SSC_EN				BIT(3)
> +
> +#define MTK_DP_PHY_DIG_BIT_RATE			(PHY_OFFSET +
> 0x03C)
> +# define BIT_RATE_RBR				0
> +# define BIT_RATE_HBR				1
> +# define BIT_RATE_HBR2				2
> +# define BIT_RATE_HBR3				3
> +
> +#define MTK_DP_PHY_DIG_SW_RST			(PHY_OFFSET +
> 0x038)
> +# define DP_GLB_SW_RST_PHYD			BIT(0)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_3		(PHY_OFFSET + 0x138)
> +#define MTK_DP_LANE1_DRIVING_PARAM_3		(PHY_OFFSET + 0x238)
> +#define MTK_DP_LANE2_DRIVING_PARAM_3		(PHY_OFFSET + 0x338)
> +#define MTK_DP_LANE3_DRIVING_PARAM_3		(PHY_OFFSET + 0x438)
> +# define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT	0x10
> +# define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT	(0x14 << 8)
> +# define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT	(0x18 << 16)
> +# define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT	(0x20 << 24)
> +# define DRIVING_PARAM_3_DEFAULT		(XTP_LN_TX_LCTXC0_SW0_P
> RE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW0_P
> RE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW0_P
> RE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW0_P
> RE3_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_4		(PHY_OFFSET + 0x13C)
> +#define MTK_DP_LANE1_DRIVING_PARAM_4		(PHY_OFFSET + 0x23C)
> +#define MTK_DP_LANE2_DRIVING_PARAM_4		(PHY_OFFSET + 0x33C)
> +#define MTK_DP_LANE3_DRIVING_PARAM_4		(PHY_OFFSET + 0x43C)
> +# define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT	0x18
> +# define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT	(0x1e << 8)
> +# define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT	(0x24 << 16)
> +# define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT	(0x20 << 24)
> +# define DRIVING_PARAM_4_DEFAULT		(XTP_LN_TX_LCTXC0_SW1_P
> RE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW1_P
> RE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW1_P
> RE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW2_P
> RE0_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_5		(PHY_OFFSET + 0x140)
> +#define MTK_DP_LANE1_DRIVING_PARAM_5		(PHY_OFFSET + 0x240)
> +#define MTK_DP_LANE2_DRIVING_PARAM_5		(PHY_OFFSET + 0x340)
> +#define MTK_DP_LANE3_DRIVING_PARAM_5		(PHY_OFFSET + 0x440)
> +# define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT	0x28
> +# define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT	(0x30 << 8)
> +# define DRIVING_PARAM_5_DEFAULT		(XTP_LN_TX_LCTXC0_SW2_P
> RE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXC0_SW3_P
> RE0_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_6		(PHY_OFFSET + 0x144)
> +#define MTK_DP_LANE1_DRIVING_PARAM_6		(PHY_OFFSET + 0x244)
> +#define MTK_DP_LANE2_DRIVING_PARAM_6		(PHY_OFFSET + 0x344)
> +#define MTK_DP_LANE3_DRIVING_PARAM_6		(PHY_OFFSET + 0x444)
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT	0x00
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT	(0x04 << 8)
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT	(0x08 << 16)
> +# define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT	(0x10 << 24)
> +# define DRIVING_PARAM_6_DEFAULT		(XTP_LN_TX_LCTXCP1_SW0_
> PRE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW0_
> PRE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW0_
> PRE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW0_
> PRE3_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_7		(PHY_OFFSET + 0x148)
> +#define MTK_DP_LANE1_DRIVING_PARAM_7		(PHY_OFFSET + 0x248)
> +#define MTK_DP_LANE2_DRIVING_PARAM_7		(PHY_OFFSET + 0x348)
> +#define MTK_DP_LANE3_DRIVING_PARAM_7		(PHY_OFFSET + 0x448)
> +# define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT	0x00
> +# define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT	(0x06 << 8)
> +# define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT	(0x0c << 16)
> +# define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT	(0x00 << 24)
> +# define DRIVING_PARAM_7_DEFAULT		(XTP_LN_TX_LCTXCP1_SW1_
> PRE0_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW1_
> PRE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW1_
> PRE2_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW2_
> PRE0_DEFAULT)
> +
> +#define MTK_DP_LANE0_DRIVING_PARAM_8		(PHY_OFFSET + 0x14C)
> +#define MTK_DP_LANE1_DRIVING_PARAM_8		(PHY_OFFSET + 0x24C)
> +#define MTK_DP_LANE2_DRIVING_PARAM_8		(PHY_OFFSET + 0x34C)
> +#define MTK_DP_LANE3_DRIVING_PARAM_8		(PHY_OFFSET + 0x44C)
> +# define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT	0x08
> +# define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT	(0x00 << 8)
> +# define DRIVING_PARAM_8_DEFAULT		(XTP_LN_TX_LCTXCP1_SW2_
> PRE1_DEFAULT | \
> +						 XTP_LN_TX_LCTXCP1_SW3_
> PRE0_DEFAULT)
> +
> +struct mtk_dp_phy {
> +	struct regmap *regs;
> +};
> +
> +static int mtk_dp_phy_init(struct phy *phy)
> +{
> +	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> +	u32 driving_params[] = {
> +		DRIVING_PARAM_3_DEFAULT,
> +		DRIVING_PARAM_4_DEFAULT,
> +		DRIVING_PARAM_5_DEFAULT,
> +		DRIVING_PARAM_6_DEFAULT,
> +		DRIVING_PARAM_7_DEFAULT,
> +		DRIVING_PARAM_8_DEFAULT
> +	};
> +
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +	regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
> +			  driving_params, ARRAY_SIZE(driving_params));
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_phy_configure(struct phy *phy, union
> phy_configure_opts *opts)
> +{
> +	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> +	u32 val;
> +
> +	if (opts->dp.set_rate) {
> +		switch (opts->dp.link_rate) {
> +		default:
> +			dev_err(&phy->dev,
> +				"Implementation error, unknown linkrate
> %x\n",
> +				opts->dp.link_rate);
> +			return -EINVAL;
> +		case 1620:
> +			val = BIT_RATE_RBR;
> +			break;
> +		case 2700:
> +			val = BIT_RATE_HBR;
> +			break;
> +		case 5400:
> +			val = BIT_RATE_HBR2;
> +			break;
> +		case 8100:
> +			val = BIT_RATE_HBR3;
> +			break;
> +		}
> +		regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE,
> val);
> +	}
> +
> +	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
> +			   TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN :
> 0);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_phy_reset(struct phy *phy)
> +{
> +	struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> +
> +	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> +			   DP_GLB_SW_RST_PHYD, 0);
> +	usleep_range(50, 200);
> +	regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> +			   DP_GLB_SW_RST_PHYD, 1);
> +
> +	return 0;
> +}
> +
> +static const struct phy_ops mtk_dp_phy_dev_ops = {
> +	.init = mtk_dp_phy_init,
> +	.configure = mtk_dp_phy_configure,
> +	.reset = mtk_dp_phy_reset,
> +	.owner = THIS_MODULE,
> +};
> +
> +static int mtk_dp_phy_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mtk_dp_phy *dp_phy;
> +	struct phy *phy;
> +
> +	dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
> +	if (!dp_phy)
> +		return -ENOMEM;
> +
> +	dp_phy->regs = *(struct regmap **)dev->platform_data;
> +	if (!dp_phy->regs) {
> +		dev_err(dev, "No data passed, requires struct
> regmap**\n");
> +		return -EINVAL;
> +	}
Why not get it from dts?

> +
> +	phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
> +	if (IS_ERR(phy)) {
> +		dev_err(dev, "Failed to create DP PHY: %ld\n",
> PTR_ERR(phy));
> +		return PTR_ERR(phy);
> +	}
> +	phy_set_drvdata(phy, dp_phy);
> +
> +	// Set device data to the phy so that mtk-dp can get it easily
use /* */
> +	dev_set_drvdata(dev, phy);
> +
> +	return 0;
> +}
> +
> +struct platform_driver mtk_dp_phy_driver = {
> +	.probe = mtk_dp_phy_probe,
> +	.driver = {
> +		.name = "mediatek-dp-phy",
> +	},
> +};
> +module_platform_driver(mtk_dp_phy_driver);
> +
> +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> +MODULE_DESCRIPTION("MediaTek DP PHY Driver");
> +MODULE_LICENSE("GPL v2");

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-11-10 13:06   ` Guillaume Ranquet
  (?)
@ 2021-11-15 10:11     ` Maxime Ripard
  -1 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-11-15 10:11 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Chun-Kuang Hu, David Airlie, Thomas Zimmermann, linux-kernel,
	dri-devel, Markus Schneider-Pargmann, linux-mediatek,
	Matthias Brugger, linux-arm-kernel, kernel test robot

[-- Attachment #1: Type: text/plain, Size: 134325 bytes --]

Hi,

On Wed, Nov 10, 2021 at 02:06:23PM +0100, Guillaume Ranquet wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> according phy driver mediatek-dp-phy.
> 
> It supports both functional units on the mt8195, the embedded
> DisplayPort as well as the external DisplayPort units. It offers
> hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> to 4 lanes.

There's a number of checkpatch --strict errors in there, make sure to fix them.

> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.

If the PHY is in the same register space than the DP controller, why do
you need a separate PHY driver in the first place?

> This driver is based on an initial version by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reported-by: kernel test robot <lkp@intel.com>

> ---
>  drivers/gpu/drm/drm_edid.c              |    2 +-
>  drivers/gpu/drm/mediatek/Kconfig        |    7 +
>  drivers/gpu/drm/mediatek/Makefile       |    2 +
>  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
>  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
>  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
>  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
>  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
>  9 files changed, 3799 insertions(+), 13 deletions(-)
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> 
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 500279a82167a..bfd98b50ceb5b 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
>  	 * modes and forbids YCRCB422 support for all video modes per
>  	 * HDMI 1.3 spec.
>  	 */
> -	info->color_formats = DRM_COLOR_FORMAT_RGB444;
> +	info->color_formats |= DRM_COLOR_FORMAT_RGB444;
>  
>  	/* YCRCB444 is optional according to spec. */
>  	if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {

This looks unrelated?

> diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> index 2976d21e9a34a..029b94c716131 100644
> --- a/drivers/gpu/drm/mediatek/Kconfig
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
>  	select PHY_MTK_HDMI
>  	help
>  	  DRM/KMS HDMI driver for Mediatek SoCs
> +
> +config MTK_DPTX_SUPPORT
> +	tristate "DRM DPTX Support for Mediatek SoCs"
> +	depends on DRM_MEDIATEK
> +	select PHY_MTK_DP
> +	help
> +	  DRM/KMS Display Port driver for Mediatek SoCs.
> diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> index 29098d7c8307c..d86a6406055e6 100644
> --- a/drivers/gpu/drm/mediatek/Makefile
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
>  			  mtk_hdmi_ddc.o
>  
>  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> +
> +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> new file mode 100644
> index 0000000000000..83087219d5a5e
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -0,0 +1,3094 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019 MediaTek Inc.
> + * Copyright (c) 2021 BayLibre
> + */
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_dp_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <linux/arm-smccc.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <sound/hdmi-codec.h>
> +#include <video/videomode.h>
> +
> +#include "mtk_dp_reg.h"
> +
> +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> +
> +#define MTK_DP_MAX_LANES 4
> +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> +
> +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> +
> +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> +
> +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> +
> +#define MTK_DP_DP_VERSION_11 0x11
> +
> +enum mtk_dp_state {
> +	MTK_DP_STATE_INITIAL,
> +	MTK_DP_STATE_IDLE,
> +	MTK_DP_STATE_PREPARE,
> +	MTK_DP_STATE_NORMAL,
> +};
> +
> +enum mtk_dp_train_state {
> +	MTK_DP_TRAIN_STATE_STARTUP = 0,
> +	MTK_DP_TRAIN_STATE_CHECKCAP,
> +	MTK_DP_TRAIN_STATE_CHECKEDID,
> +	MTK_DP_TRAIN_STATE_TRAINING_PRE,
> +	MTK_DP_TRAIN_STATE_TRAINING,
> +	MTK_DP_TRAIN_STATE_CHECKTIMING,
> +	MTK_DP_TRAIN_STATE_NORMAL,
> +	MTK_DP_TRAIN_STATE_POWERSAVE,
> +	MTK_DP_TRAIN_STATE_DPIDLE,
> +};
> +
> +struct mtk_dp_timings {
> +	struct videomode vm;
> +
> +	u16 htotal;
> +	u16 vtotal;
> +	u8 frame_rate;
> +	u32 pix_rate_khz;
> +};
> +
> +struct mtk_dp_train_info {
> +	bool tps3;
> +	bool tps4;
> +	bool sink_ssc;
> +	bool cable_plugged_in;
> +	bool cable_state_change;
> +	bool cr_done;
> +	bool eq_done;
> +
> +	// link_rate is in multiple of 0.27Gbps
> +	int link_rate;
> +	int lane_count;
> +
> +	int irq_status;
> +	int check_cap_count;
> +};
> +
> +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> +enum mtk_dp_color_format {
> +	MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> +	MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> +	MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> +	MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> +	MTK_DP_COLOR_FORMAT_YONLY = 4,
> +	MTK_DP_COLOR_FORMAT_RAW = 5,
> +	MTK_DP_COLOR_FORMAT_RESERVED = 6,
> +	MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> +	MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> +};
> +
> +// Multiple of 0.27Gbps
> +enum mtk_dp_linkrate {
> +	MTK_DP_LINKRATE_RBR = 0x6,
> +	MTK_DP_LINKRATE_HBR = 0xA,
> +	MTK_DP_LINKRATE_HBR2 = 0x14,
> +	MTK_DP_LINKRATE_HBR25 = 0x19,
> +	MTK_DP_LINKRATE_HBR3 = 0x1E,
> +};
> +
> +// Same values as used for DP Spec MISC0 bits 5,6,7
> +enum mtk_dp_color_depth {
> +	MTK_DP_COLOR_DEPTH_6BIT = 0,
> +	MTK_DP_COLOR_DEPTH_8BIT = 1,
> +	MTK_DP_COLOR_DEPTH_10BIT = 2,
> +	MTK_DP_COLOR_DEPTH_12BIT = 3,
> +	MTK_DP_COLOR_DEPTH_16BIT = 4,
> +	MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> +};
> +
> +struct mtk_dp_audio_cfg {
> +	int sample_rate;
> +	int word_length_bits;
> +	int channels;
> +};
> +
> +struct mtk_dp_info {
> +	enum mtk_dp_color_depth depth;
> +	enum mtk_dp_color_format format;
> +	struct mtk_dp_audio_cfg audio_caps;
> +	struct mtk_dp_timings timings;
> +};
> +
> +struct dp_cal_data {
> +	unsigned int glb_bias_trim;
> +	unsigned int clktx_impse;
> +
> +	unsigned int ln0_tx_impsel_pmos;
> +	unsigned int ln0_tx_impsel_nmos;
> +	unsigned int ln1_tx_impsel_pmos;
> +	unsigned int ln1_tx_impsel_nmos;
> +	unsigned int ln2_tx_impsel_pmos;
> +	unsigned int ln2_tx_impsel_nmos;
> +	unsigned int ln3_tx_impsel_pmos;
> +	unsigned int ln3_tx_impsel_nmos;
> +};
> +
> +struct mtk_dp {
> +	struct device *dev;
> +	struct platform_device *phy_dev;
> +	struct phy *phy;
> +	struct dp_cal_data cal_data;
> +
> +	struct drm_device *drm_dev;
> +	struct drm_bridge bridge;
> +	struct drm_bridge *next_bridge;
> +	struct drm_dp_aux aux;
> +
> +	struct mutex edid_lock;
> +	struct edid *edid;
> +
> +	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> +
> +	struct mtk_dp_info info;
> +	enum mtk_dp_state state;
> +
> +	struct mtk_dp_train_info train_info;
> +	enum mtk_dp_train_state train_state;
> +	unsigned int input_fmt;
> +
> +	struct regmap *regs;
> +	struct clk *dp_tx_clk;
> +
> +	bool enabled;
> +	bool audio_enable;
> +
> +	bool has_fec;
> +	struct mutex dp_lock;
> +
> +	struct mutex update_plugged_status_lock;
> +
> +	hdmi_codec_plugged_cb plugged_cb;
> +	struct device *codec_dev;
> +	u8 connector_eld[MAX_ELD_BYTES];
> +};
> +
> +enum mtk_dp_sdp_type {
> +	MTK_DP_SDP_NONE = 0x00,
> +	MTK_DP_SDP_ACM = 0x01,
> +	MTK_DP_SDP_ISRC = 0x02,
> +	MTK_DP_SDP_AVI = 0x03,
> +	MTK_DP_SDP_AUI = 0x04,
> +	MTK_DP_SDP_SPD = 0x05,
> +	MTK_DP_SDP_MPEG = 0x06,
> +	MTK_DP_SDP_NTSC = 0x07,
> +	MTK_DP_SDP_VSP = 0x08,
> +	MTK_DP_SDP_VSC = 0x09,
> +	MTK_DP_SDP_EXT = 0x0A,
> +	MTK_DP_SDP_PPS0 = 0x0B,
> +	MTK_DP_SDP_PPS1 = 0x0C,
> +	MTK_DP_SDP_PPS2 = 0x0D,
> +	MTK_DP_SDP_PPS3 = 0x0E,
> +	MTK_DP_SDP_DRM = 0x10,
> +	MTK_DP_SDP_MAX_NUM
> +};
> +
> +struct mtk_dp_sdp_packet {
> +	enum mtk_dp_sdp_type type;
> +	struct dp_sdp sdp;
> +};
> +
> +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> +union mtk_dp_audio_channel_status {
> +	struct {
> +		u8 rev : 1;
> +		u8 is_lpcm : 1;
> +		u8 copy_right : 1;
> +		u8 additional_format_info : 3;
> +		u8 channel_status_mode : 2;
> +		u8 category_code;
> +		u8 src_num : 4;
> +		u8 channel_num : 4;
> +		u8 sampling_freq : 4;
> +		u8 clk_accuracy : 2;
> +		u8 rev2 : 2;
> +		u8 word_len : 4;
> +		u8 original_sampling_freq : 4;
> +	} iec;
> +
> +	u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> +};

Why don't you leverage hdmi_codec_params and snd_aes_iec958?

> +static struct regmap_config mtk_dp_regmap_config = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +	.max_register = SEC_OFFSET + 0x90,
> +	.name = "mtk-dp-registers",
> +};
> +
> +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
> +{
> +	return mtk_dp->next_bridge != NULL;
> +}
> +
> +static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
> +{
> +	return container_of(b, struct mtk_dp, bridge);
> +}
> +
> +static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
> +{
> +	u32 read_val;
> +	int ret;
> +
> +	ret = regmap_read(mtk_dp->regs, offset, &read_val);
> +	if (ret) {
> +		dev_err(mtk_dp->dev, "Failed to read register 0x%x: %d\n",
> +			offset, ret);
> +		return 0;
> +	}
> +
> +	return read_val;
> +}
> +
> +static void mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
> +{
> +	int ret;
> +
> +	ret = regmap_write(mtk_dp->regs, offset, val);
> +	if (ret)
> +		dev_err(mtk_dp->dev,
> +			"Failed to write register 0x%x with value 0x%x: %d\n",
> +			offset, val, ret);
> +}
> +
> +static void mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, u32 val,
> +			       u32 mask)
> +{
> +	int ret;
> +
> +	ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
> +	if (ret)
> +		dev_err(mtk_dp->dev,
> +			"Failed to update register 0x%x with value 0x%x, mask 0x%x: %d\n",
> +			offset, val, mask, ret);
> +}
> +
> +static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32 offset, u8 *buf,
> +				    size_t length)
> +{
> +	int i;
> +	int num_regs = (length + 1) / 2;
> +
> +	// 2 bytes per register
> +	for (i = 0; i < num_regs; i++) {
> +		u32 val = buf[i * 2] |
> +			  (i * 2 + 1 < length ? buf[i * 2 + 1] << 8 : 0);
> +
> +		mtk_dp_write(mtk_dp, offset + i * 4, val);
> +	}
> +}
> +
> +static unsigned long mtk_dp_sip_atf_call(unsigned int cmd, unsigned int para)
> +{
> +	struct arm_smccc_res res;
> +
> +	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32, cmd, para, 0, 0, 0, 0, 0,
> +		      &res);
> +
> +	pr_debug("[DPTX]%s cmd 0x%x, p1 0x%x, ret 0x%lx-0x%lx", __func__, cmd,
> +		 para, res.a0, res.a1);
> +	return res.a1;
> +}
> +
> +static void mtk_dp_msa_bypass_disable(struct mtk_dp *mtk_dp)
> +{
> +	const u16 bits_to_set =
> +		BIT(HTOTAL_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(VTOTAL_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(HSTART_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(VSTART_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(HWIDTH_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(VHEIGHT_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(HSP_SEL_DP_ENC0_P0_SHIFT) | BIT(HSW_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(VSP_SEL_DP_ENC0_P0_SHIFT) | BIT(VSW_SEL_DP_ENC0_P0_SHIFT);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, bits_to_set,
> +			   bits_to_set);
> +}
> +
> +static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
> +{
> +	struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010, timings->htotal,
> +			   HTOTAL_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
> +			   timings->vm.hsync_len + timings->vm.hback_porch,
> +			   HSTART_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> +			   timings->vm.hsync_len << HSW_SW_DP_ENC0_P0_SHIFT,
> +			   HSW_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028, 0,
> +			   HSP_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020, timings->vm.hactive,
> +			   HWIDTH_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014, timings->vtotal,
> +			   VTOTAL_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
> +			   timings->vm.vsync_len + timings->vm.vback_porch,
> +			   VSTART_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> +			   timings->vm.vsync_len << VSW_SW_DP_ENC0_P0_SHIFT,
> +			   VSW_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C, 0,
> +			   VSP_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024, timings->vm.vactive,
> +			   VHEIGHT_SW_DP_ENC0_P0_MASK);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064, timings->vm.hactive,
> +			   HDE_NUM_LAST_DP_ENC0_P0_MASK);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154, timings->htotal,
> +			   PGEN_HTOTAL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
> +			   timings->vm.hfront_porch,
> +			   PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C, timings->vm.hsync_len,
> +			   PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
> +			   timings->vm.hback_porch + timings->vm.hsync_len,
> +			   PGEN_HFDE_START_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164, timings->vm.hactive,
> +			   PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168, timings->vtotal,
> +			   PGEN_VTOTAL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
> +			   timings->vm.vfront_porch,
> +			   PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170, timings->vm.vsync_len,
> +			   PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
> +			   timings->vm.vback_porch + timings->vm.vsync_len,
> +			   PGEN_VFDE_START_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178, timings->vm.vactive,
> +			   PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
> +				    enum mtk_dp_color_format color_format)
> +{
> +	u32 val;
> +
> +	mtk_dp->info.format = color_format;
> +
> +	// Update MISC0
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +			   color_format << DP_TEST_COLOR_FORMAT_SHIFT,
> +			   DP_TEST_COLOR_FORMAT_MASK);
> +
> +	switch (color_format) {
> +	case MTK_DP_COLOR_FORMAT_YUV_422:
> +		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
> +		break;
> +	case MTK_DP_COLOR_FORMAT_YUV_420:
> +		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420;
> +		break;
> +	case MTK_DP_COLOR_FORMAT_YONLY:
> +	case MTK_DP_COLOR_FORMAT_RAW:
> +	case MTK_DP_COLOR_FORMAT_RESERVED:
> +	case MTK_DP_COLOR_FORMAT_UNKNOWN:
> +		drm_warn(mtk_dp->drm_dev, "Unsupported color format: %d\n",
> +			 color_format);
> +		fallthrough;
> +	case MTK_DP_COLOR_FORMAT_RGB_444:
> +	case MTK_DP_COLOR_FORMAT_YUV_444:
> +		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
> +		break;
> +	}
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
> +			   PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp,
> +				   enum mtk_dp_color_depth color_depth)
> +{
> +	u32 val;
> +
> +	mtk_dp->info.depth = color_depth;
> +
> +	// Update MISC0
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +			   color_depth << DP_TEST_BIT_DEPTH_SHIFT,
> +			   DP_TEST_BIT_DEPTH_MASK);
> +
> +	switch (color_depth) {
> +	case MTK_DP_COLOR_DEPTH_6BIT:
> +		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT;
> +		break;
> +	case MTK_DP_COLOR_DEPTH_8BIT:
> +		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT;
> +		break;
> +	case MTK_DP_COLOR_DEPTH_10BIT:
> +		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT;
> +		break;
> +	case MTK_DP_COLOR_DEPTH_12BIT:
> +		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT;
> +		break;
> +	case MTK_DP_COLOR_DEPTH_16BIT:
> +		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT;
> +		break;
> +	case MTK_DP_COLOR_DEPTH_UNKNOWN:
> +		drm_warn(mtk_dp->drm_dev, "Unsupported color depth %d\n",
> +			 color_depth);
> +		return;
> +	}
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
> +			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_mn_overwrite_disable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
> +			   VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32 val)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   val << SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT,
> +			   SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   BIT(VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT),
> +			   VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> +			   0x20 << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
> +			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +			   0x20 << SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT,
> +			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
> +			   2 << VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT,
> +			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +			   4 << FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT,
> +			   FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
> +	mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368,
> +		     1 << VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT |
> +			     1 << VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT |
> +			     BIT(SDP_DP13_EN_DP_ENC1_P0_SHIFT) |
> +			     1 << BS2BS_MODE_DP_ENC1_P0_SHIFT);
> +}
> +
> +static void mtk_dp_pg_disable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038, 0,
> +			   VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
> +			   4 << PGEN_PATTERN_SEL_SHIFT, PGEN_PATTERN_SEL_MASK);
> +}
> +
> +static void mtk_dp_audio_setup_channels(struct mtk_dp *mtk_dp,
> +					struct mtk_dp_audio_cfg *cfg)
> +{
> +	u32 channel_enable_bits;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3324,
> +			   AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX,
> +			   AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK);
> +
> +	//audio channel count change reset
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(9), BIT(9));
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> +			   AU_PRTY_REGEN_DP_ENC1_P0_MASK,
> +			   AU_PRTY_REGEN_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> +			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK,
> +			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> +			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK,
> +			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK);
> +
> +	switch (cfg->channels) {
> +	case 2:
> +		channel_enable_bits = AUDIO_2CH_SEL_DP_ENC0_P0_MASK |
> +				      AUDIO_2CH_EN_DP_ENC0_P0_MASK;
> +		break;
> +	case 8:
> +	default:
> +		channel_enable_bits = AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
> +				      AUDIO_8CH_EN_DP_ENC0_P0_MASK;
> +		break;
> +	}
> +	mtk_dp_update_bits(
> +		mtk_dp, MTK_DP_ENC0_P0_3088,
> +		channel_enable_bits | AU_EN_DP_ENC0_P0_MASK,
> +		AUDIO_2CH_SEL_DP_ENC0_P0_MASK | AUDIO_2CH_EN_DP_ENC0_P0_MASK |
> +			AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
> +			AUDIO_8CH_EN_DP_ENC0_P0_MASK | AU_EN_DP_ENC0_P0_MASK);
> +
> +	//audio channel count change reset
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, 0, BIT(9));
> +
> +	//enable audio reset
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(0), BIT(0));
> +}
> +
> +static void mtk_dp_audio_channel_status_set(struct mtk_dp *mtk_dp,
> +					    struct mtk_dp_audio_cfg *cfg)
> +{
> +	union mtk_dp_audio_channel_status channel_status;
> +
> +	memset(&channel_status, 0, sizeof(channel_status));
> +
> +	switch (cfg->sample_rate) {
> +	case 32000:
> +		channel_status.iec.sampling_freq = 3;
> +		break;
> +	case 44100:
> +		channel_status.iec.sampling_freq = 0;
> +		break;
> +	case 48000:
> +		channel_status.iec.sampling_freq = 2;
> +		break;
> +	case 88200:
> +		channel_status.iec.sampling_freq = 8;
> +		break;
> +	case 96000:
> +		channel_status.iec.sampling_freq = 0xA;
> +		break;
> +	case 192000:
> +		channel_status.iec.sampling_freq = 0xE;
> +		break;
> +	default:
> +		channel_status.iec.sampling_freq = 0x1;
> +		break;
> +	}
> +
> +	switch (cfg->word_length_bits) {
> +	case 16:
> +		channel_status.iec.word_len = 0x02;
> +		break;
> +	case 20:
> +		channel_status.iec.word_len = 0x03;
> +		break;
> +	case 24:
> +		channel_status.iec.word_len = 0x0B;
> +		break;
> +	}

Yeah, this is provided by hdmi-codec through hdmi_codec_params in
prepare.

> +	// IEC 60958 consumer channel status bits
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_308C,
> +			   channel_status.buf[1] << 8 | channel_status.buf[0],
> +			   CH_STATUS_0_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3090,
> +			   channel_status.buf[3] << 8 | channel_status.buf[2],
> +			   CH_STATUS_1_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3094, channel_status.buf[4],
> +			   CH_STATUS_2_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_audio_sdp_asp_set_channels(struct mtk_dp *mtk_dp,
> +					      int channels)
> +{
> +	if (channels != 2 && channels != 8)
> +		channels = 8;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_312C,
> +			   (channels - 1) << ASP_HB3_DP_ENC0_P0_SHIFT,
> +			   ASP_HB2_DP_ENC0_P0_MASK | ASP_HB3_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_audio_set_divider(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
> +			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2,
> +			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK);
> +}
> +
> +static bool mtk_dp_plug_state(struct mtk_dp *mtk_dp)
> +{
> +	return !!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
> +		  HPD_DB_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_sdp_trigger_packet(struct mtk_dp *mtk_dp,
> +				      enum mtk_dp_sdp_type type)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, type,
> +			   SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, SDP_PACKET_W_DP_ENC1_P0,
> +			   SDP_PACKET_W_DP_ENC1_P0);
> +}
> +
> +static void mtk_dp_sdp_set_data(struct mtk_dp *mtk_dp, u8 *data_bytes)
> +{
> +	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_ENC1_P0_3200, data_bytes, 0x10);
> +}
> +
> +static void mtk_dp_sdp_set_header(struct mtk_dp *mtk_dp,
> +				  enum mtk_dp_sdp_type type,
> +				  struct dp_sdp_header *header)
> +{
> +	u32 db_addr;
> +
> +	switch (type) {
> +	case MTK_DP_SDP_DRM:
> +		db_addr = MTK_DP_ENC0_P0_3138;
> +		break;
> +	case MTK_DP_SDP_PPS0:
> +	case MTK_DP_SDP_PPS1:
> +	case MTK_DP_SDP_PPS2:
> +	case MTK_DP_SDP_PPS3:
> +		db_addr = MTK_DP_ENC0_P0_3130;
> +		break;
> +	default:
> +		db_addr = MTK_DP_ENC0_P0_30D8 + (type - MTK_DP_SDP_ACM) * 8;
> +	}
> +
> +	mtk_dp_bulk_16bit_write(mtk_dp, db_addr, (u8 *)header, 4);
> +}
> +
> +static const u32 mtk_dp_sdp_type_to_reg[MTK_DP_SDP_MAX_NUM] = {
> +	/* MTK_DP_SDP_NONE => */ 0x0,
> +	/* MTK_DP_SDP_ACM  => */ MTK_DP_ENC0_P0_30B4,
> +	/* MTK_DP_SDP_ISRC => */ MTK_DP_ENC0_P0_30B4 + 1,
> +	/* MTK_DP_SDP_AVI  => */ MTK_DP_ENC0_P0_30A4 + 1,
> +	/* MTK_DP_SDP_AUI  => */ MTK_DP_ENC0_P0_30A8,
> +	/* MTK_DP_SDP_SPD  => */ MTK_DP_ENC0_P0_30A8 + 1,
> +	/* MTK_DP_SDP_MPEG => */ MTK_DP_ENC0_P0_30AC,
> +	/* MTK_DP_SDP_NTSC => */ MTK_DP_ENC0_P0_30AC + 1,
> +	/* MTK_DP_SDP_VSP  => */ MTK_DP_ENC0_P0_30B0,
> +	/* MTK_DP_SDP_VSC  => */ MTK_DP_ENC0_P0_30B8,
> +	/* MTK_DP_SDP_EXT  => */ MTK_DP_ENC0_P0_30B0 + 1,
> +	/* MTK_DP_SDP_PPS0 => */ MTK_DP_ENC0_P0_31E8,
> +	/* MTK_DP_SDP_PPS1 => */ MTK_DP_ENC0_P0_31E8,
> +	/* MTK_DP_SDP_PPS2 => */ MTK_DP_ENC0_P0_31E8,
> +	/* MTK_DP_SDP_PPS3 => */ MTK_DP_ENC0_P0_31E8,
> +	/* MTK_DP_SDP_DRM  => */ MTK_DP_ENC0_P0_31DC,
> +};
> +
> +static void mtk_dp_disable_sdp(struct mtk_dp *mtk_dp, enum mtk_dp_sdp_type type)
> +{
> +	if (type == MTK_DP_SDP_NONE)
> +		return;
> +
> +	// Disable periodic send
> +	mtk_dp_update_bits(mtk_dp, mtk_dp_sdp_type_to_reg[type] & 0xfffc, 0,
> +			   0xFF << ((mtk_dp_sdp_type_to_reg[type] & 3) * 8));
> +}
> +
> +static void mtk_dp_setup_sdp(struct mtk_dp *mtk_dp,
> +			     struct mtk_dp_sdp_packet *packet)
> +{
> +	mtk_dp_sdp_set_data(mtk_dp, packet->sdp.db);
> +	mtk_dp_sdp_set_header(mtk_dp, packet->type, &packet->sdp.sdp_header);
> +
> +	mtk_dp_disable_sdp(mtk_dp, packet->type);
> +
> +	switch (packet->type) {
> +	case MTK_DP_SDP_NONE:
> +		break;
> +	case MTK_DP_SDP_ISRC:
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> +				   0x1C << ISRC1_HB3_DP_ENC0_P0_SHIFT,
> +				   ISRC1_HB3_DP_ENC0_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, MTK_DP_SDP_ISRC,
> +				   SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
> +
> +		if (packet->sdp.sdp_header.HB3 & BIT(2))
> +			mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
> +					   BIT(ISRC_CONT_DP_ENC0_P0_SHIFT),
> +					   ISRC_CONT_DP_ENC0_P0_MASK);
> +		else
> +			mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC, 0,
> +					   ISRC_CONT_DP_ENC0_P0_MASK);
> +
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280,
> +				   SDP_PACKET_W_DP_ENC1_P0,
> +				   SDP_PACKET_W_DP_ENC1_P0);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30B4,
> +				   5 << ISRC_CFG_DP_ENC0_P0_SHIFT,
> +				   ISRC_CFG_DP_ENC0_P0_MASK);
> +		break;
> +	case MTK_DP_SDP_DRM:
> +		mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31DC,
> +				   5 << HDR0_CFG_DP_ENC0_P0_SHIFT,
> +				   HDR0_CFG_DP_ENC0_P0_MASK);
> +		break;
> +	case MTK_DP_SDP_ACM:
> +	case MTK_DP_SDP_AVI:
> +	case MTK_DP_SDP_AUI:
> +	case MTK_DP_SDP_SPD:
> +	case MTK_DP_SDP_MPEG:
> +	case MTK_DP_SDP_NTSC:
> +	case MTK_DP_SDP_VSP:
> +	case MTK_DP_SDP_VSC:
> +	case MTK_DP_SDP_EXT:
> +	case MTK_DP_SDP_PPS0:
> +	case MTK_DP_SDP_PPS1:
> +	case MTK_DP_SDP_PPS2:
> +	case MTK_DP_SDP_PPS3:
> +		mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
> +		// Enable periodic sending
> +		mtk_dp_update_bits(
> +			mtk_dp, mtk_dp_sdp_type_to_reg[packet->type] & 0xfffc,
> +			0x05 << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
> +				 8),
> +			0xff << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
> +				 8));
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static void mtk_dp_sdp_vsc_ext_disable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A0, 0,
> +			   BIT(7) | BIT(8) | BIT(12));
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_328C, 0, BIT(7));
> +}
> +
> +static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_write(
> +		mtk_dp, MTK_DP_AUX_P0_3640,
> +		BIT(AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT));
> +}
> +
> +static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32 addr)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644, cmd,
> +			   MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648, addr,
> +			   MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C, addr >> 16,
> +			   MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_cmd_complete(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +			   BIT(MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT),
> +			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK |
> +				   PHY_FIFO_RST_AUX_TX_P0_MASK |
> +				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
> +			   BIT(AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT),
> +			   AUX_TX_REQUEST_READY_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> +				       size_t length)
> +{
> +	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf, length);
> +}
> +
> +static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> +				    size_t length, int read_delay)
> +{
> +	int read_pos;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620, 0,
> +			   AUX_RD_MODE_AUX_TX_P0_MASK);
> +
> +	for (read_pos = 0; read_pos < length; read_pos++) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> +				   BIT(AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT),
> +				   AUX_RX_FIFO_READ_PULSE_TX_P0_MASK);
> +		usleep_range(read_delay, read_delay * 2);
> +		buf[read_pos] =
> +			(u8)(mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3620) &
> +			     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK >>
> +				     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT);
> +	}
> +}
> +
> +static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t length)
> +{
> +	if (length > 0) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +				   (length - 1)
> +					   << MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT,
> +				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
> +				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
> +					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	} else {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +				   BIT(AUX_NO_LENGTH_AUX_TX_P0_SHIFT),
> +				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
> +					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	}
> +}
> +
> +static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp, bool is_read)
> +{
> +	int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
> +
> +	while (--wait_reply) {
> +		u32 aux_irq_status;
> +
> +		if (is_read) {
> +			u32 fifo_status =
> +				mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3618);
> +
> +			if (fifo_status &
> +			    (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
> +			     AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
> +				return 0;
> +			}
> +		}
> +
> +		aux_irq_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3640);
> +		if (aux_irq_status & AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK)
> +			return 0;
> +
> +		if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK)
> +			return -ETIMEDOUT;
> +
> +		usleep_range(100, 500);
> +	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool is_read, u8 cmd,
> +				  u32 addr, u8 *buf, size_t length)
> +{
> +	int ret;
> +	u32 reply_cmd;
> +
> +	if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
> +			(cmd == DP_AUX_NATIVE_READ && !length)))
> +		return -EINVAL;
> +
> +	if (!is_read)
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> +				   BIT(AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT),
> +				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK);
> +
> +	mtk_dp_aux_cmd_complete(mtk_dp);
> +	mtk_dp_aux_irq_clear(mtk_dp);
> +	usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> +		     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> +
> +	mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
> +	mtk_dp_aux_set_length(mtk_dp, length);
> +
> +	if (!is_read) {
> +		if (length)
> +			mtk_dp_aux_fill_write_fifo(mtk_dp, buf, length);
> +
> +		mtk_dp_update_bits(
> +			mtk_dp, MTK_DP_AUX_P0_3704,
> +			AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK,
> +			AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK);
> +	}
> +
> +	mtk_dp_aux_request_ready(mtk_dp);
> +
> +	ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
> +
> +	reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
> +		    AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
> +
> +	if (ret || reply_cmd) {
> +		u32 phy_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3628) &
> +				 AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
> +		if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
> +			drm_err(mtk_dp->drm_dev,
> +				"AUX Rx Aux hang, need SW reset\n");
> +			ret = -EIO;
> +		}
> +
> +		mtk_dp_aux_cmd_complete(mtk_dp);
> +		mtk_dp_aux_irq_clear(mtk_dp);
> +
> +		usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> +			     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> +		return -ETIMEDOUT;
> +	}
> +
> +	if (!length) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
> +				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
> +					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	} else if (is_read) {
> +		int read_delay;
> +
> +		if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
> +		    cmd == DP_AUX_I2C_READ)
> +			read_delay = 500;
> +		else
> +			read_delay = 100;
> +
> +		mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length, read_delay);
> +	}
> +
> +	mtk_dp_aux_cmd_complete(mtk_dp);
> +	mtk_dp_aux_irq_clear(mtk_dp);
> +	usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> +		     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> +
> +	return 0;
> +}
> +
> +static bool mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int lane_num,
> +					  int swing_val, int preemphasis)
> +{
> +	u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
> +
> +	if (lane_num < 0 || lane_num > 3)
> +		return false;
> +
> +	dev_dbg(mtk_dp->dev,
> +		"link training swing_val= 0x%x, preemphasis = 0x%x\n",
> +		swing_val, preemphasis);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +			   swing_val << (DP_TX0_VOLT_SWING_SHIFT + lane_shift),
> +			   DP_TX0_VOLT_SWING_MASK << lane_shift);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +			   preemphasis << (DP_TX0_PRE_EMPH_SHIFT + lane_shift),
> +			   DP_TX0_PRE_EMPH_MASK << lane_shift);
> +
> +	return true;
> +}
> +
> +static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP, 0,
> +			   DP_TX0_VOLT_SWING_MASK | DP_TX1_VOLT_SWING_MASK |
> +				   DP_TX2_VOLT_SWING_MASK |
> +				   DP_TX3_VOLT_SWING_MASK |
> +				   DP_TX0_PRE_EMPH_MASK | DP_TX1_PRE_EMPH_MASK |
> +				   DP_TX2_PRE_EMPH_MASK | DP_TX3_PRE_EMPH_MASK);
> +}
> +
> +static void mtk_dp_fec_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> +			   enable ? BIT(FEC_EN_DP_TRANS_P0_SHIFT) : 0,
> +			   FEC_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +	u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
> +			 SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
> +
> +	if (irq_status) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, irq_status,
> +				   SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, 0,
> +				   SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +	}
> +
> +	return irq_status;
> +}
> +
> +static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +	u8 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
> +			 IRQ_STATUS_DP_TRANS_P0_MASK) >>
> +			IRQ_STATUS_DP_TRANS_P0_SHIFT;
> +
> +	if (irq_status) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, irq_status,
> +				   IRQ_CLR_DP_TRANS_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, 0,
> +				   IRQ_CLR_DP_TRANS_P0_MASK);
> +	}
> +
> +	return irq_status;
> +}
> +
> +static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	u32 val = 0;
> +
> +	if (!enable)
> +		val = IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
> +		      IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
> +		      IRQ_MASK_DP_TRANS_P0_INT_IRQ;
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, val,
> +			   IRQ_MASK_DP_TRANS_P0_MASK);
> +}
> +
> +void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
> +			   XTAL_FREQ_DP_TRANS_P0_DEFAULT,
> +			   XTAL_FREQ_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> +			   BIT(FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT),
> +			   FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> +			   BIT(AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT),
> +			   AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
> +			   SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK, IRQ_MASK_AUX_TOP_IRQ,
> +			   IRQ_MASK_AUX_TOP_IRQ);
> +}
> +
> +static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp *mtk_dp)
> +{
> +	// Debounce threshold
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   8 << HPD_DEB_THD_DP_TRANS_P0_SHIFT,
> +			   HPD_DEB_THD_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
> +			    HPD_INT_THD_DP_TRANS_P0_UPPER_1100US)
> +				   << HPD_INT_THD_DP_TRANS_P0_SHIFT,
> +			   HPD_INT_THD_DP_TRANS_P0_MASK);
> +
> +	// Connect threshold 1.5ms + 5 x 0.1ms = 2ms
> +	// Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   (5 << HPD_DISC_THD_DP_TRANS_P0_SHIFT) |
> +				   (5 << HPD_CONN_THD_DP_TRANS_P0_SHIFT),
> +			   HPD_DISC_THD_DP_TRANS_P0_MASK |
> +				   HPD_CONN_THD_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
> +			   HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
> +			   HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
> +{
> +	// modify timeout threshold = 1595 [12 : 8]
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C, 0x1595,
> +			   AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658, 0,
> +			   AUX_TX_OV_EN_AUX_TX_P0_MASK);
> +	// 25 for 26M
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
> +			   25 << AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT,
> +			   AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
> +	// 13 for 26M
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
> +			   13 << AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT,
> +			   AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
> +			   BIT(MTK_ATOP_EN_AUX_TX_P0_SHIFT),
> +			   MTK_ATOP_EN_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_digital_settings(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
> +			   VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
> +	mtk_dp_set_color_format(mtk_dp, MTK_DP_COLOR_FORMAT_RGB_444);
> +	mtk_dp_set_color_depth(mtk_dp, MTK_DP_COLOR_DEPTH_8BIT);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
> +			   1 << BS2BS_MODE_DP_ENC1_P0_SHIFT,
> +			   BS2BS_MODE_DP_ENC1_P0_MASK);
> +
> +	// dp tx encoder reset all sw
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +			   BIT(DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT),
> +			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
> +	usleep_range(1000, 5000);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
> +			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> +			   BIT(DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT),
> +			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
> +	usleep_range(1000, 5000);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C, 0,
> +			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
> +			   lanes == 0 ? 0 : BIT(3), BIT(3) | BIT(2));
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, lanes,
> +			   LANE_NUM_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
> +			   lanes << LANE_NUM_DP_TRANS_P0_SHIFT,
> +			   LANE_NUM_DP_TRANS_P0_MASK);
> +}
> +
> +static int link_rate_to_mb_per_s(struct mtk_dp *mtk_dp,
> +				 enum mtk_dp_linkrate linkrate)
> +{
> +	switch (linkrate) {
> +	default:
> +		drm_err(mtk_dp->drm_dev,
> +			"Implementation error, unknown linkrate %d\n",
> +			linkrate);
> +		fallthrough;
> +	case MTK_DP_LINKRATE_RBR:
> +		return 1620;
> +	case MTK_DP_LINKRATE_HBR:
> +		return 2700;
> +	case MTK_DP_LINKRATE_HBR2:
> +		return 5400;
> +	case MTK_DP_LINKRATE_HBR3:
> +		return 8100;
> +	}
> +}
> +
> +static u32 check_cal_data_valid(u32 min, u32 max, u32 val, u32 default_val)
> +{
> +	if (val < min || val > max)
> +		return default_val;
> +
> +	return val;
> +}
> +
> +static int mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
> +{
> +	struct dp_cal_data *cal_data = &mtk_dp->cal_data;
> +	struct device *dev = mtk_dp->dev;
> +	struct nvmem_cell *cell;
> +	u32 *buf;
> +	size_t len;
> +
> +	cell = nvmem_cell_get(dev, "dp_calibration_data");
> +	if (IS_ERR(cell)) {
> +		dev_err(dev,
> +			"Error: Failed to get nvmem cell dp_calibration_data\n");
> +		return PTR_ERR(cell);
> +	}
> +
> +	buf = (u32 *)nvmem_cell_read(cell, &len);
> +	nvmem_cell_put(cell);
> +
> +	if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
> +		dev_err(dev,
> +			"Error: Failed to read nvmem_cell_read fail len %ld\n",
> +			(len / sizeof(u32)));
> +		return PTR_ERR(buf);
> +	}
> +
> +	if (mtk_dp_is_edp(mtk_dp)) {
> +		cal_data->glb_bias_trim = check_cal_data_valid(
> +			1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf);
> +		cal_data->clktx_impse =
> +			check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8);
> +		cal_data->ln0_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8);
> +		cal_data->ln0_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8);
> +		cal_data->ln1_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8);
> +		cal_data->ln1_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8);
> +		cal_data->ln2_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8);
> +		cal_data->ln2_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8);
> +		cal_data->ln3_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8);
> +		cal_data->ln3_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8);
> +	} else {
> +		cal_data->glb_bias_trim = check_cal_data_valid(
> +			1, 0x1e, (buf[0] >> 27) & 0x1f, 0xf);
> +		cal_data->clktx_impse =
> +			check_cal_data_valid(1, 0xe, (buf[0] >> 13) & 0xf, 0x8);
> +		cal_data->ln0_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 28) & 0xf, 0x8);
> +		cal_data->ln0_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 24) & 0xf, 0x8);
> +		cal_data->ln1_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 20) & 0xf, 0x8);
> +		cal_data->ln1_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 16) & 0xf, 0x8);
> +		cal_data->ln2_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 12) & 0xf, 0x8);
> +		cal_data->ln2_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 8) & 0xf, 0x8);
> +		cal_data->ln3_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 4) & 0xf, 0x8);
> +		cal_data->ln3_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, buf[1] & 0xf, 0x8);
> +	}
> +
> +	kfree(buf);
> +
> +	return 0;
> +}
> +
> +void mtk_dp_set_cal_data(struct mtk_dp *mtk_dp)
> +{
> +	struct dp_cal_data *cal_data = &mtk_dp->cal_data;
> +
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
> +			   cal_data->clktx_impse << 20, RG_CKM_PT0_CKTX_IMPSEL);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
> +			   cal_data->glb_bias_trim << 16,
> +			   RG_XTP_GLB_BIAS_INTR_CTRL);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +			   cal_data->ln0_tx_impsel_pmos << 12,
> +			   RG_XTP_LN0_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +			   cal_data->ln0_tx_impsel_nmos << 16,
> +			   RG_XTP_LN0_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +			   cal_data->ln1_tx_impsel_pmos << 12,
> +			   RG_XTP_LN1_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +			   cal_data->ln1_tx_impsel_nmos << 16,
> +			   RG_XTP_LN1_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +			   cal_data->ln2_tx_impsel_pmos << 12,
> +			   RG_XTP_LN2_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +			   cal_data->ln2_tx_impsel_nmos << 16,
> +			   RG_XTP_LN2_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +			   cal_data->ln3_tx_impsel_pmos << 12,
> +			   RG_XTP_LN3_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +			   cal_data->ln3_tx_impsel_nmos << 16,
> +			   RG_XTP_LN3_TX_IMPSEL_NMOS);
> +}
> +
> +static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
> +				enum mtk_dp_linkrate link_rate, int lane_count)
> +{
> +	int ret;
> +	union phy_configure_opts
> +		phy_opts = { .dp = {
> +				     .link_rate = link_rate_to_mb_per_s(
> +					     mtk_dp, link_rate),
> +				     .set_rate = 1,
> +				     .lanes = lane_count,
> +				     .set_lanes = 1,
> +				     .ssc = mtk_dp->train_info.sink_ssc,
> +			     } };
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP,
> +			   DP_PWR_STATE_MASK);
> +
> +	ret = phy_configure(mtk_dp->phy, &phy_opts);
> +
> +	mtk_dp_set_cal_data(mtk_dp);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL_LANE, DP_PWR_STATE_MASK);
> +	return ret;
> +}
> +
> +static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	const u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
> +			POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
> +			POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
> +			POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580, enable ? val : 0, val);
> +}
> +
> +static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int pattern)
> +{
> +	if (pattern < 0 || pattern > 4) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Implementation error, no such pattern %d\n", pattern);
> +		return;
> +	}
> +
> +	if (pattern == 1) // TPS1
> +		mtk_dp_set_idle_pattern(mtk_dp, false);
> +
> +	mtk_dp_update_bits(
> +		mtk_dp, MTK_DP_TRANS_P0_3400,
> +		pattern ? BIT(pattern - 1) << PATTERN1_EN_DP_TRANS_P0_SHIFT : 0,
> +		PATTERN1_EN_DP_TRANS_P0_MASK | PATTERN2_EN_DP_TRANS_P0_MASK |
> +			PATTERN3_EN_DP_TRANS_P0_MASK |
> +			PATTERN4_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +			   enable ? BIT(ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT) : 0,
> +			   ENHANCED_FRAME_EN_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
> +			   enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
> +			   DP_SCR_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	u32 val = BIT(VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT);
> +
> +	if (enable)
> +		val |= BIT(VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, val,
> +			   VIDEO_MUTE_SEL_DP_ENC0_P0_MASK |
> +				   VIDEO_MUTE_SW_DP_ENC0_P0_MASK);
> +
> +	if (mtk_dp_is_edp(mtk_dp))
> +		mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable);
> +	else
> +		mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable);
> +}
> +
> +static void mtk_dp_audio_mute(struct mtk_dp *mtk_dp, bool mute)
> +{
> +	if (mute) {
> +		mtk_dp_update_bits(
> +			mtk_dp, MTK_DP_ENC0_P0_3030,
> +			BIT(VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT) |
> +				BIT(VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT),
> +			VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
> +				VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
> +
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088, 0,
> +				   AU_EN_DP_ENC0_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0,
> +				   AU_TS_CFG_DP_ENC0_P0_MASK);
> +
> +	} else {
> +		mtk_dp_update_bits(
> +			mtk_dp, MTK_DP_ENC0_P0_3030, 0,
> +			VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
> +				VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
> +
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088,
> +				   BIT(AU_EN_DP_ENC0_P0_SHIFT),
> +				   AU_EN_DP_ENC0_P0_MASK);
> +		// Send one every two frames
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0x0F,
> +				   AU_TS_CFG_DP_ENC0_P0_MASK);
> +	}
> +}
> +
> +static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, 0,
> +			   SW_RST_B_PHYD);
> +	usleep_range(10, 200);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, SW_RST_B_PHYD,
> +			   SW_RST_B_PHYD);
> +}
> +
> +static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
> +	usleep_range(10, 200);
> +	mtk_dp_write(mtk_dp, MTK_DP_0034,
> +		     DA_CKM_CKTX0_EN_FORCE_EN | DA_CKM_BIAS_LPF_EN_FORCE_VAL |
> +			     DA_CKM_BIAS_EN_FORCE_VAL |
> +			     DA_XTP_GLB_LDO_EN_FORCE_VAL |
> +			     DA_XTP_GLB_AVD10_ON_FORCE_VAL);
> +	// Disable RX
> +	mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
> +	mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
> +		     0x550 | BIT(FUSE_SEL_SHIFT) | BIT(MEM_ISO_EN_SHIFT));
> +}
> +
> +static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR2;
> +	mtk_dp->train_info.lane_count = MTK_DP_MAX_LANES;
> +	mtk_dp->train_info.irq_status = 0;
> +	mtk_dp->train_info.cable_plugged_in = false;
> +	mtk_dp->train_info.cable_state_change = false;
> +	mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> +	mtk_dp->state = MTK_DP_STATE_INITIAL;
> +
> +	mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
> +	mtk_dp->info.depth = MTK_DP_COLOR_DEPTH_8BIT;
> +	memset(&mtk_dp->info.timings, 0, sizeof(struct mtk_dp_timings));
> +	mtk_dp->info.timings.frame_rate = 60;
> +
> +	mtk_dp->has_fec = false;
> +	mtk_dp->audio_enable = false;
> +}
> +
> +static void mtk_dp_sdp_set_down_cnt_init(struct mtk_dp *mtk_dp,
> +					 u32 sram_read_start)
> +{
> +	u32 sdp_down_cnt_init = 0;
> +	u32 dc_offset;
> +
> +	if (mtk_dp->info.timings.pix_rate_khz > 0)
> +		sdp_down_cnt_init = sram_read_start *
> +				    mtk_dp->train_info.link_rate * 2700 * 8 /
> +				    (mtk_dp->info.timings.pix_rate_khz * 4);
> +
> +	switch (mtk_dp->train_info.lane_count) {
> +	case 1:
> +		sdp_down_cnt_init = sdp_down_cnt_init > 0x1A ?
> +						  sdp_down_cnt_init :
> +						  0x1A; //26
> +		break;
> +	case 2:
> +		// case for LowResolution && High Audio Sample Rate
> +		dc_offset = mtk_dp->info.timings.vtotal <= 525 ? 0x04 : 0x00;
> +		sdp_down_cnt_init = sdp_down_cnt_init > 0x10 ?
> +						  sdp_down_cnt_init :
> +						  0x10 + dc_offset; //20 or 16
> +		break;
> +	case 4:
> +	default:
> +		sdp_down_cnt_init =
> +			sdp_down_cnt_init > 0x06 ? sdp_down_cnt_init : 0x06; //6
> +		break;
> +	}
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> +			   sdp_down_cnt_init
> +				   << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
> +			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_sdp_set_down_cnt_init_in_hblank(struct mtk_dp *mtk_dp)
> +{
> +	int pix_clk_mhz;
> +	u32 dc_offset;
> +	u32 spd_down_cnt_init = 0;
> +
> +	pix_clk_mhz = mtk_dp->info.format == MTK_DP_COLOR_FORMAT_YUV_420 ?
> +				    mtk_dp->info.timings.pix_rate_khz / 2000 :
> +				    mtk_dp->info.timings.pix_rate_khz / 1000;
> +
> +	switch (mtk_dp->train_info.lane_count) {
> +	case 1:
> +		spd_down_cnt_init = 0x20;
> +		break;
> +	case 2:
> +		dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x14 : 0x00;
> +		spd_down_cnt_init = 0x18 + dc_offset;
> +		break;
> +	case 4:
> +	default:
> +		dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x08 : 0x00;
> +		if (pix_clk_mhz > mtk_dp->train_info.link_rate * 27)
> +			spd_down_cnt_init = 0x8;
> +		else
> +			spd_down_cnt_init = 0x10 + dc_offset;
> +		break;
> +	}
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364, spd_down_cnt_init,
> +			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
> +}
> +
> +static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
> +{
> +	u32 sram_read_start = MTK_DP_TBC_BUF_READ_START_ADDR;
> +
> +	if (mtk_dp->train_info.lane_count > 0) {
> +		sram_read_start = min_t(
> +			u32, MTK_DP_TBC_BUF_READ_START_ADDR,
> +			mtk_dp->info.timings.vm.hactive /
> +				(mtk_dp->train_info.lane_count * 4 * 2 * 2));
> +		mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
> +	}
> +
> +	mtk_dp_setup_encoder(mtk_dp);
> +	mtk_dp_sdp_set_down_cnt_init_in_hblank(mtk_dp);
> +	mtk_dp_sdp_set_down_cnt_init(mtk_dp, sram_read_start);
> +}
> +
> +static void mtk_dp_calculate_pixrate(struct mtk_dp *mtk_dp)
> +{
> +	int target_frame_rate = 60;
> +	int target_pixel_clk;
> +
> +	if (mtk_dp->info.timings.frame_rate > 0) {
> +		target_frame_rate = mtk_dp->info.timings.frame_rate;
> +		target_pixel_clk = (int)mtk_dp->info.timings.htotal *
> +				   (int)mtk_dp->info.timings.vtotal *
> +				   target_frame_rate;
> +	} else if (mtk_dp->info.timings.pix_rate_khz > 0) {
> +		target_pixel_clk = mtk_dp->info.timings.pix_rate_khz * 1000;
> +	} else {
> +		target_pixel_clk = (int)mtk_dp->info.timings.htotal *
> +				   (int)mtk_dp->info.timings.vtotal *
> +				   target_frame_rate;
> +	}
> +
> +	if (target_pixel_clk > 0)
> +		mtk_dp->info.timings.pix_rate_khz = target_pixel_clk / 1000;
> +}
> +
> +static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_msa_bypass_disable(mtk_dp);
> +	mtk_dp_calculate_pixrate(mtk_dp);
> +	mtk_dp_pg_disable(mtk_dp);
> +	mtk_dp_setup_tu(mtk_dp);
> +}
> +
> +static void mtk_dp_edid_free(struct mtk_dp *mtk_dp)
> +{
> +	mutex_lock(&mtk_dp->edid_lock);
> +	kfree(mtk_dp->edid);
> +	mtk_dp->edid = NULL;
> +	mutex_unlock(&mtk_dp->edid_lock);
> +}
> +
> +static void mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp)
> +{
> +	ssize_t ret;
> +	u8 sink_count;
> +	u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +	u32 sink_count_reg;
> +	u32 link_status_reg;
> +	bool locked;
> +
> +	sink_count_reg = DP_SINK_COUNT_ESI;
> +	link_status_reg = DP_LANE0_1_STATUS;
> +
> +	ret = drm_dp_dpcd_readb(&mtk_dp->aux, sink_count_reg, &sink_count);
> +	if (ret < 0) {
> +		drm_info(mtk_dp->drm_dev, "Read sink count failed: %ld\n", ret);
> +		return;
> +	}
> +
> +	ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
> +	if (ret < 0) {
> +		drm_info(mtk_dp->drm_dev,
> +			 "Read DP_SINK_COUNT_ESI failed: %ld\n", ret);
> +		return;
> +	}
> +
> +	ret = drm_dp_dpcd_read(&mtk_dp->aux, link_status_reg, link_status,
> +			       sizeof(link_status));
> +	if (!ret) {
> +		drm_info(mtk_dp->drm_dev, "Read link status failed: %ld\n",
> +			 ret);
> +		return;
> +	}
> +
> +	locked = drm_dp_channel_eq_ok(link_status,
> +				      mtk_dp->train_info.lane_count);
> +	if (!locked && mtk_dp->train_state > MTK_DP_TRAIN_STATE_TRAINING_PRE)
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
> +
> +	if (link_status[1] & DP_REMOTE_CONTROL_COMMAND_PENDING)
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR,
> +				   DP_REMOTE_CONTROL_COMMAND_PENDING);
> +
> +	if (DP_GET_SINK_COUNT(sink_count) &&
> +	    (link_status[2] & DP_DOWNSTREAM_PORT_STATUS_CHANGED)) {
> +		mtk_dp_edid_free(mtk_dp);
> +		mtk_dp->train_info.check_cap_count = 0;
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
> +		msleep(20);
> +	}
> +}
> +
> +static void mtk_dp_sdp_stop_sending(struct mtk_dp *mtk_dp)
> +{
> +	u8 packet_type;
> +
> +	for (packet_type = MTK_DP_SDP_ACM; packet_type < MTK_DP_SDP_MAX_NUM;
> +	     packet_type++)
> +		mtk_dp_disable_sdp(mtk_dp, packet_type);
> +
> +	mtk_dp_sdp_vsc_ext_disable(mtk_dp);
> +}
> +
> +static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes,
> +					  u8 dpcd_adjust_req[2])
> +{
> +	int lane;
> +
> +	for (lane = 0; lane < lanes; ++lane) {
> +		u8 val;
> +		u8 swing;
> +		u8 preemphasis;
> +		int index = lane / 2;
> +		int shift = lane % 2 ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
> +
> +		swing = (dpcd_adjust_req[index] >> shift) &
> +			DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
> +		preemphasis = ((dpcd_adjust_req[index] >> shift) &
> +			       DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
> +			      DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
> +		val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
> +		      preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +
> +		if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
> +			val |= DP_TRAIN_MAX_SWING_REACHED;
> +		if (preemphasis == 3)
> +			val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> +
> +		mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing, preemphasis);
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET + lane,
> +				   val);
> +	}
> +
> +	// Wait for the signal to be stable enough
> +	usleep_range(2000, 5000);
> +}
> +
> +static void mtk_dp_read_link_status(struct mtk_dp *mtk_dp,
> +				    u8 link_status[DP_LINK_STATUS_SIZE])
> +{
> +	drm_dp_dpcd_read(&mtk_dp->aux, DP_LANE0_1_STATUS, link_status,
> +			 DP_LINK_STATUS_SIZE);
> +}
> +
> +static int mtk_dp_train_flow(struct mtk_dp *mtk_dp, int target_link_rate,
> +			     int target_lane_count)
> +{
> +	u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +	u8 lane_adjust[2] = {};
> +	bool pass_tps1 = false;
> +	bool pass_tps2_3 = false;
> +	int train_retries;
> +	int status_control;
> +	int iteration_count;
> +	u8 prev_lane_adjust;
> +	u8 val;
> +
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET, target_link_rate);
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> +			   target_lane_count | DP_LANE_COUNT_ENHANCED_FRAME_EN);
> +
> +	if (mtk_dp->train_info.sink_ssc)
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
> +				   DP_SPREAD_AMP_0_5);
> +
> +	train_retries = 0;
> +	status_control = 0;
> +	iteration_count = 1;
> +	prev_lane_adjust = 0xFF;
> +
> +	mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
> +	mtk_dp_phy_configure(mtk_dp, target_link_rate, target_lane_count);
> +
> +	dev_dbg(mtk_dp->dev,
> +		"Link train target_link_rate = 0x%x, target_lane_count = 0x%x\n",
> +		target_link_rate, target_lane_count);
> +
> +	do {
> +		train_retries++;
> +		if (!mtk_dp->train_info.cable_plugged_in ||
> +		    ((mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) !=
> +		     0x0)) {
> +			return -ENODEV;
> +		}
> +
> +		if (mtk_dp->train_state < MTK_DP_TRAIN_STATE_TRAINING)
> +			return -EAGAIN;
> +
> +		if (!pass_tps1) {
> +			mtk_dp_training_set_scramble(mtk_dp, false);
> +
> +			if (status_control == 0) {
> +				status_control = 1;
> +				mtk_dp_train_set_pattern(mtk_dp, 1);
> +				val = DP_LINK_SCRAMBLING_DISABLE |
> +				      DP_TRAINING_PATTERN_1;
> +				drm_dp_dpcd_writeb(
> +					&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +					DP_LINK_SCRAMBLING_DISABLE |
> +						DP_TRAINING_PATTERN_1);
> +				drm_dp_dpcd_read(&mtk_dp->aux,
> +						 DP_ADJUST_REQUEST_LANE0_1,
> +						 lane_adjust,
> +						 sizeof(lane_adjust));
> +				iteration_count++;
> +
> +				mtk_dp_train_update_swing_pre(
> +					mtk_dp, target_lane_count, lane_adjust);
> +			}
> +
> +			drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
> +							       mtk_dp->rx_cap);
> +			mtk_dp_read_link_status(mtk_dp, link_status);
> +
> +			if (drm_dp_clock_recovery_ok(link_status,
> +						     target_lane_count)) {
> +				mtk_dp->train_info.cr_done = true;
> +				pass_tps1 = true;
> +				train_retries = 0;
> +				iteration_count = 1;
> +				dev_dbg(mtk_dp->dev, "Link train CR pass\n");
> +			} else if (prev_lane_adjust == link_status[4]) {
> +				iteration_count++;
> +				if (prev_lane_adjust &
> +				    DP_ADJUST_VOLTAGE_SWING_LANE0_MASK)
> +					break;
> +			} else {
> +				prev_lane_adjust = link_status[4];
> +			}
> +			dev_dbg(mtk_dp->dev, "Link train CQ fail\n");
> +		} else if (pass_tps1 && !pass_tps2_3) {
> +			if (status_control == 1) {
> +				status_control = 2;
> +				if (mtk_dp->train_info.tps4) {
> +					mtk_dp_train_set_pattern(mtk_dp, 4);
> +					val = DP_TRAINING_PATTERN_4;
> +				} else if (mtk_dp->train_info.tps3) {
> +					mtk_dp_train_set_pattern(mtk_dp, 3);
> +					val = DP_LINK_SCRAMBLING_DISABLE |
> +					      DP_TRAINING_PATTERN_3;
> +				} else {
> +					mtk_dp_train_set_pattern(mtk_dp, 2);
> +					val = DP_LINK_SCRAMBLING_DISABLE |
> +					      DP_TRAINING_PATTERN_2;
> +				}
> +				drm_dp_dpcd_writeb(&mtk_dp->aux,
> +						   DP_TRAINING_PATTERN_SET,
> +						   val);
> +
> +				drm_dp_dpcd_read(&mtk_dp->aux,
> +						 DP_ADJUST_REQUEST_LANE0_1,
> +						 lane_adjust,
> +						 sizeof(lane_adjust));
> +
> +				iteration_count++;
> +				mtk_dp_train_update_swing_pre(
> +					mtk_dp, target_lane_count, lane_adjust);
> +			}
> +
> +			drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
> +							   mtk_dp->rx_cap);
> +
> +			mtk_dp_read_link_status(mtk_dp, link_status);
> +
> +			if (!drm_dp_clock_recovery_ok(link_status,
> +						      target_lane_count)) {
> +				mtk_dp->train_info.cr_done = false;
> +				mtk_dp->train_info.eq_done = false;
> +				dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
> +				break;
> +			}
> +
> +			if (drm_dp_channel_eq_ok(link_status,
> +						 target_lane_count)) {
> +				mtk_dp->train_info.eq_done = true;
> +				pass_tps2_3 = true;
> +				dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
> +				break;
> +			}
> +
> +			if (prev_lane_adjust == link_status[4])
> +				iteration_count++;
> +			else
> +				prev_lane_adjust = link_status[4];
> +		}
> +
> +		drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
> +				 lane_adjust, sizeof(lane_adjust));
> +		mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
> +					      lane_adjust);
> +	} while (train_retries < MTK_DP_TRAIN_RETRY_LIMIT &&
> +		 iteration_count < MTK_DP_TRAIN_MAX_ITERATIONS);
> +
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +			   DP_TRAINING_PATTERN_DISABLE);
> +	mtk_dp_train_set_pattern(mtk_dp, 0);
> +
> +	if (pass_tps2_3) {
> +		mtk_dp->train_info.link_rate = target_link_rate;
> +		mtk_dp->train_info.lane_count = target_lane_count;
> +
> +		mtk_dp_training_set_scramble(mtk_dp, true);
> +
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> +				   target_lane_count |
> +					   DP_LANE_COUNT_ENHANCED_FRAME_EN);
> +		mtk_dp_set_enhanced_frame_mode(mtk_dp, true);
> +
> +		return 0;
> +	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static bool mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
> +{
> +	u8 buf[DP_RECEIVER_CAP_SIZE] = {};
> +	u8 val;
> +	struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
> +
> +	if (!mtk_dp_plug_state(mtk_dp))
> +		return false;
> +
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> +	usleep_range(2000, 5000);
> +
> +	drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
> +
> +	memcpy(mtk_dp->rx_cap, buf, min(sizeof(mtk_dp->rx_cap), sizeof(buf)));
> +	mtk_dp->rx_cap[DP_TRAINING_AUX_RD_INTERVAL] &= DP_TRAINING_AUX_RD_MASK;
> +
> +	train_info->link_rate =
> +		min_t(int, MTK_DP_MAX_LINK_RATE, buf[DP_MAX_LINK_RATE]);
> +	train_info->lane_count =
> +		min_t(int, MTK_DP_MAX_LANES, drm_dp_max_lane_count(buf));
> +
> +	train_info->tps3 = drm_dp_tps3_supported(buf);
> +	train_info->tps4 = drm_dp_tps4_supported(buf);
> +
> +	train_info->sink_ssc =
> +		!!(buf[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5);
> +
> +	train_info->sink_ssc = false;
> +
> +	drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
> +	if (val & DP_MST_CAP) {
> +		// Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0
> +		drm_dp_dpcd_readb(&mtk_dp->aux,
> +				  DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0, &val);
> +		if (val)
> +			drm_dp_dpcd_writeb(&mtk_dp->aux,
> +					   DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
> +					   val);
> +	}
> +
> +	return true;
> +}
> +
> +static int mtk_dp_edid_parse_audio_capabilities(struct mtk_dp *mtk_dp,
> +						struct mtk_dp_audio_cfg *cfg)
> +{
> +	struct cea_sad *sads;
> +	int sad_count;
> +	int i;
> +	int ret = 0;
> +
> +	mutex_lock(&mtk_dp->edid_lock);
> +	if (!mtk_dp->edid) {
> +		mutex_unlock(&mtk_dp->edid_lock);
> +		dev_err(mtk_dp->dev, "EDID not found!\n");
> +		return -EINVAL;
> +	}
> +
> +	sad_count = drm_edid_to_sad(mtk_dp->edid, &sads);
> +	mutex_unlock(&mtk_dp->edid_lock);
> +	if (sad_count <= 0) {
> +		drm_info(mtk_dp->drm_dev, "The SADs is NULL\n");
> +		return 0;
> +	}
> +
> +	for (i = 0; i < sad_count; i++) {
> +		int sample_rate;
> +		int word_length;
> +		// Only PCM supported at the moment
> +		if (sads[i].format != HDMI_AUDIO_CODING_TYPE_PCM)
> +			continue;
> +
> +		sample_rate = drm_cea_sad_get_sample_rate(&sads[i]);
> +		word_length =
> +			drm_cea_sad_get_uncompressed_word_length(&sads[i]);
> +		if (sample_rate <= 0 || word_length <= 0)
> +			continue;
> +
> +		cfg->channels = sads[i].channels;
> +		cfg->word_length_bits = word_length;
> +		cfg->sample_rate = sample_rate;
> +		ret = 1;
> +		break;
> +	}
> +	kfree(sads);
> +
> +	return ret;
> +}
> +
> +static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
> +{
> +	phy_reset(mtk_dp->phy);
> +	mtk_dp_reset_swing_pre_emphasis(mtk_dp);
> +
> +	usleep_range(2000, 5000);
> +}
> +
> +static int mtk_dp_train_start(struct mtk_dp *mtk_dp)
> +{
> +	int ret = 0;
> +	int lane_count;
> +	int link_rate;
> +	int train_limit;
> +	int max_link_rate;
> +	int plug_wait;
> +
> +	for (plug_wait = 7; !mtk_dp_plug_state(mtk_dp) && plug_wait > 0;
> +	     --plug_wait)
> +		usleep_range(1000, 5000);
> +	if (plug_wait == 0) {
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> +		return -ENODEV;
> +	}
> +
> +	link_rate = mtk_dp->rx_cap[1];
> +	lane_count = mtk_dp->rx_cap[2] & 0x1F;
> +
> +	mtk_dp->train_info.link_rate = min(MTK_DP_MAX_LINK_RATE, link_rate);
> +	mtk_dp->train_info.lane_count = min(MTK_DP_MAX_LANES, lane_count);
> +	link_rate = mtk_dp->train_info.link_rate;
> +	lane_count = mtk_dp->train_info.lane_count;
> +
> +	switch (link_rate) {
> +	case MTK_DP_LINKRATE_RBR:
> +	case MTK_DP_LINKRATE_HBR:
> +	case MTK_DP_LINKRATE_HBR2:
> +	case MTK_DP_LINKRATE_HBR25:
> +	case MTK_DP_LINKRATE_HBR3:
> +		break;
> +	default:
> +		mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR3;
> +		break;
> +	};
> +
> +	max_link_rate = link_rate;
> +	for (train_limit = 0; train_limit <= 6; ++train_limit) {
> +		mtk_dp->train_info.cr_done = false;
> +		mtk_dp->train_info.eq_done = false;
> +
> +		mtk_dp_train_change_mode(mtk_dp);
> +		ret = mtk_dp_train_flow(mtk_dp, link_rate, lane_count);
> +		if (ret == -ENODEV || ret == -EAGAIN)
> +			return ret;
> +
> +		if (!mtk_dp->train_info.cr_done) {
> +			switch (link_rate) {
> +			case MTK_DP_LINKRATE_RBR:
> +				lane_count = lane_count / 2;
> +				link_rate = max_link_rate;
> +				if (lane_count == 0x0) {
> +					mtk_dp->train_state =
> +						MTK_DP_TRAIN_STATE_DPIDLE;
> +					return -EIO;
> +				}
> +				break;
> +			case MTK_DP_LINKRATE_HBR:
> +				link_rate = MTK_DP_LINKRATE_RBR;
> +				break;
> +			case MTK_DP_LINKRATE_HBR2:
> +				link_rate = MTK_DP_LINKRATE_HBR;
> +				break;
> +			case MTK_DP_LINKRATE_HBR3:
> +				link_rate = MTK_DP_LINKRATE_HBR2;
> +				break;
> +			default:
> +				return -EINVAL;
> +			};
> +		} else if (!mtk_dp->train_info.eq_done) {
> +			lane_count /= 2;
> +			if (lane_count == 0)
> +				return -EIO;
> +		} else {
> +			return 0;
> +		}
> +	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_train_handler(struct mtk_dp *mtk_dp)
> +{
> +	int ret = 0;
> +
> +	if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
> +		return ret;
> +
> +	switch (mtk_dp->train_state) {
> +	case MTK_DP_TRAIN_STATE_STARTUP:
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> +		break;
> +
> +	case MTK_DP_TRAIN_STATE_CHECKCAP:
> +		if (mtk_dp_parse_capabilities(mtk_dp)) {
> +			mtk_dp->train_info.check_cap_count = 0;
> +			mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
> +		} else {
> +			mtk_dp->train_info.check_cap_count++;
> +
> +			if (mtk_dp->train_info.check_cap_count >
> +			    MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT) {
> +				mtk_dp->train_info.check_cap_count = 0;
> +				mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> +				ret = -ETIMEDOUT;
> +			}
> +		}
> +		break;
> +
> +	case MTK_DP_TRAIN_STATE_CHECKEDID: {
> +		int caps_found = mtk_dp_edid_parse_audio_capabilities(
> +			mtk_dp, &mtk_dp->info.audio_caps);
> +		mtk_dp->audio_enable = caps_found > 0;
> +		if (!mtk_dp->audio_enable)
> +			memset(&mtk_dp->info.audio_caps, 0,
> +			       sizeof(mtk_dp->info.audio_caps));
> +	}
> +
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
> +		break;
> +
> +	case MTK_DP_TRAIN_STATE_TRAINING_PRE:
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING;
> +		break;
> +
> +	case MTK_DP_TRAIN_STATE_TRAINING:
> +		ret = mtk_dp_train_start(mtk_dp);
> +		if (!ret) {
> +			mtk_dp_video_mute(mtk_dp, true);
> +			mtk_dp_audio_mute(mtk_dp, true);
> +			mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKTIMING;
> +			mtk_dp_fec_enable(mtk_dp, mtk_dp->has_fec);
> +		} else if (ret != -EAGAIN) {
> +			mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> +		}
> +
> +		ret = 0;
> +		break;
> +
> +	case MTK_DP_TRAIN_STATE_CHECKTIMING:
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_NORMAL;
> +		break;
> +	case MTK_DP_TRAIN_STATE_NORMAL:
> +		break;
> +	case MTK_DP_TRAIN_STATE_POWERSAVE:
> +		break;
> +	case MTK_DP_TRAIN_STATE_DPIDLE:
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	if (enable) {
> +		mtk_dp_set_tx_out(mtk_dp);
> +		mtk_dp_video_mute(mtk_dp, false);
> +	} else {
> +		mtk_dp_video_mute(mtk_dp, true);
> +	}
> +}
> +
> +static void mtk_dp_audio_sdp_setup(struct mtk_dp *mtk_dp,
> +				   struct mtk_dp_audio_cfg *cfg)
> +{
> +	struct mtk_dp_sdp_packet packet;
> +	struct hdmi_audio_infoframe frame;
> +
> +	hdmi_audio_infoframe_init(&frame);
> +	frame.coding_type = HDMI_AUDIO_CODING_TYPE_PCM;
> +	frame.channels = cfg->channels;
> +	frame.sample_frequency = cfg->sample_rate;
> +
> +	switch (cfg->word_length_bits) {
> +	case 16:
> +		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
> +		break;
> +	case 20:
> +		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_20;
> +		break;
> +	case 24:
> +	default:
> +		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_24;
> +		break;
> +	}
> +
> +	packet.type = MTK_DP_SDP_AUI;
> +	hdmi_audio_infoframe_pack_for_dp(&frame, &packet.sdp,
> +					 MTK_DP_DP_VERSION_11);
> +
> +	mtk_dp_audio_sdp_asp_set_channels(mtk_dp, cfg->channels);
> +	mtk_dp_setup_sdp(mtk_dp, &packet);
> +}
> +
> +static void mtk_dp_audio_setup(struct mtk_dp *mtk_dp,
> +			       struct mtk_dp_audio_cfg *cfg)
> +{
> +	mtk_dp_audio_sdp_setup(mtk_dp, cfg);
> +	mtk_dp_audio_channel_status_set(mtk_dp, cfg);
> +
> +	mtk_dp_audio_setup_channels(mtk_dp, cfg);
> +	mtk_dp_audio_set_divider(mtk_dp);
> +}
> +
> +static void mtk_dp_video_config(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_mn_overwrite_disable(mtk_dp);
> +
> +	mtk_dp_set_msa(mtk_dp);
> +
> +	mtk_dp_set_color_depth(mtk_dp, mtk_dp->info.depth);
> +	mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
> +}
> +
> +static int mtk_dp_state_handler(struct mtk_dp *mtk_dp)
> +{
> +	int ret = 0;
> +
> +	switch (mtk_dp->state) {
> +	case MTK_DP_STATE_INITIAL:
> +		mtk_dp_video_mute(mtk_dp, true);
> +		mtk_dp_audio_mute(mtk_dp, true);
> +		mtk_dp->state = MTK_DP_STATE_IDLE;
> +		break;
> +
> +	case MTK_DP_STATE_IDLE:
> +		if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
> +			mtk_dp->state = MTK_DP_STATE_PREPARE;
> +		break;
> +
> +	case MTK_DP_STATE_PREPARE:
> +		mtk_dp_video_config(mtk_dp);
> +		mtk_dp_video_enable(mtk_dp, true);
> +
> +		if (mtk_dp->audio_enable) {
> +			mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_caps);
> +			mtk_dp_audio_mute(mtk_dp, false);
> +		}
> +
> +		mtk_dp->state = MTK_DP_STATE_NORMAL;
> +		break;
> +
> +	case MTK_DP_STATE_NORMAL:
> +		if (mtk_dp->train_state != MTK_DP_TRAIN_STATE_NORMAL) {
> +			mtk_dp_video_mute(mtk_dp, true);
> +			mtk_dp_audio_mute(mtk_dp, true);
> +			mtk_dp_sdp_stop_sending(mtk_dp);
> +			mtk_dp->state = MTK_DP_STATE_IDLE;
> +		}
> +		break;
> +
> +	default:
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_set_idle_pattern(mtk_dp, true);
> +	mtk_dp_initialize_priv_data(mtk_dp);
> +
> +	mtk_dp_initialize_settings(mtk_dp);
> +	mtk_dp_initialize_aux_settings(mtk_dp);
> +	mtk_dp_initialize_digital_settings(mtk_dp);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
> +			   BIT(RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT),
> +			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK);
> +	mtk_dp_initialize_hpd_detect_settings(mtk_dp);
> +
> +	mtk_dp_digital_sw_reset(mtk_dp);
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev;
> +	int event;
> +	u8 buf[DP_RECEIVER_CAP_SIZE] = {};
> +
> +	event = mtk_dp_plug_state(mtk_dp) ? connector_status_connected :
> +						  connector_status_disconnected;
> +
> +	if (event < 0)
> +		return IRQ_HANDLED;
> +
> +	if (mtk_dp->drm_dev) {
> +		dev_info(mtk_dp->dev, "drm_helper_hpd_irq_event\n");
> +		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
> +	}
> +
> +	if (mtk_dp->train_info.cable_state_change) {
> +		mtk_dp->train_info.cable_state_change = false;
> +
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> +
> +		if (!mtk_dp->train_info.cable_plugged_in ||
> +		    !mtk_dp_plug_state(mtk_dp)) {
> +			mtk_dp_video_mute(mtk_dp, true);
> +			mtk_dp_audio_mute(mtk_dp, true);
> +
> +			mtk_dp_initialize_priv_data(mtk_dp);
> +			mtk_dp_set_idle_pattern(mtk_dp, true);
> +			if (mtk_dp->has_fec)
> +				mtk_dp_fec_enable(mtk_dp, false);
> +			mtk_dp_sdp_stop_sending(mtk_dp);
> +
> +			mtk_dp_edid_free(mtk_dp);
> +			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +					   DP_PWR_STATE_BANDGAP_TPLL,
> +					   DP_PWR_STATE_MASK);
> +		} else {
> +			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +					   DP_PWR_STATE_BANDGAP_TPLL_LANE,
> +					   DP_PWR_STATE_MASK);
> +			drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
> +			mtk_dp->train_info.link_rate =
> +				min_t(int, MTK_DP_MAX_LINK_RATE,
> +				      buf[DP_MAX_LINK_RATE]);
> +			mtk_dp->train_info.lane_count =
> +				min_t(int, MTK_DP_MAX_LANES,
> +				      drm_dp_max_lane_count(buf));
> +		}
> +	}
> +
> +	if (mtk_dp->train_info.irq_status & MTK_DP_HPD_INTERRUPT) {
> +		dev_info(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n");
> +		mtk_dp->train_info.irq_status &= ~MTK_DP_HPD_INTERRUPT;
> +		mtk_dp_hpd_sink_event(mtk_dp);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mtk_dp_hpd_isr_handler(struct mtk_dp *mtk_dp)
> +{
> +	bool connected;
> +	u16 swirq_status = mtk_dp_swirq_get_clear(mtk_dp);
> +	u8 hwirq_status = mtk_dp_hwirq_get_clear(mtk_dp);
> +	struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
> +
> +	train_info->irq_status |= hwirq_status | swirq_status;
> +
> +	if (!train_info->irq_status)
> +		return IRQ_HANDLED;
> +
> +	connected = mtk_dp_plug_state(mtk_dp);
> +	if (connected || !train_info->cable_plugged_in)
> +		train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
> +	else if (!connected || train_info->cable_plugged_in)
> +		train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
> +
> +	if (!(train_info->irq_status &
> +	      (MTK_DP_HPD_CONNECT | MTK_DP_HPD_DISCONNECT)))
> +		return IRQ_HANDLED;
> +
> +	if (train_info->irq_status & MTK_DP_HPD_CONNECT) {
> +		train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
> +		train_info->cable_plugged_in = true;
> +	} else {
> +		train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
> +		train_info->cable_plugged_in = false;
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> +	}
> +	train_info->cable_state_change = true;
> +	return IRQ_WAKE_THREAD;
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev;
> +	u32 irq_status;
> +
> +	irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS);
> +
> +	if (!irq_status)
> +		return IRQ_HANDLED;
> +
> +	if (irq_status & RGS_IRQ_STATUS_TRANSMITTER)
> +		return mtk_dp_hpd_isr_handler(mtk_dp);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int mtk_dp_dt_parse_pdata(struct mtk_dp *mtk_dp,
> +				 struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	int ret = 0;
> +	void __iomem *base;
> +
> +	base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(base))
> +		return PTR_ERR(base);
> +
> +	mtk_dp->regs = devm_regmap_init_mmio(dev, base, &mtk_dp_regmap_config);
> +	if (IS_ERR(mtk_dp->regs))
> +		return PTR_ERR(mtk_dp->regs);
> +
> +	mtk_dp->dp_tx_clk = devm_clk_get(dev, "faxi");
> +	if (IS_ERR(mtk_dp->dp_tx_clk)) {
> +		ret = PTR_ERR(mtk_dp->dp_tx_clk);
> +		dev_err(dev, "Failed to get dptx clock: %d\n", ret);
> +		mtk_dp->dp_tx_clk = NULL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_update_plugged_status(struct mtk_dp *mtk_dp)
> +{
> +	bool connected, has_audio;
> +
> +	mutex_lock(&mtk_dp->update_plugged_status_lock);
> +	connected = mtk_dp_plug_state(mtk_dp);
> +	has_audio = drm_detect_monitor_audio(mtk_dp->edid);
> +	if (mtk_dp->plugged_cb && mtk_dp->codec_dev)
> +		mtk_dp->plugged_cb(mtk_dp->codec_dev, connected & has_audio);
> +	mutex_unlock(&mtk_dp->update_plugged_status_lock);
> +}
> +
> +static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	enum drm_connector_status ret = connector_status_disconnected;
> +	u8 sink_count = 0;
> +
> +	if (mtk_dp_is_edp(mtk_dp))
> +		return connector_status_connected;
> +
> +	if (mtk_dp_plug_state(mtk_dp)) {
> +		drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
> +		if (DP_GET_SINK_COUNT(sink_count))
> +			ret = connector_status_connected;
> +	}
> +
> +	mtk_dp_update_plugged_status(mtk_dp);
> +	return ret;
> +}
> +
> +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> +				    struct drm_connector *connector)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	bool enabled = mtk_dp->enabled;
> +	struct edid *new_edid = NULL;
> +
> +	if (!enabled)
> +		drm_bridge_chain_pre_enable(bridge);
> +
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> +	usleep_range(2000, 5000);
> +
> +	if (mtk_dp_plug_state(mtk_dp))
> +		new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> +
> +	if (!enabled)
> +		drm_bridge_chain_post_disable(bridge);
> +
> +	mutex_lock(&mtk_dp->edid_lock);
> +	kfree(mtk_dp->edid);
> +	mtk_dp->edid = NULL;
> +
> +	if (!new_edid) {
> +		mutex_unlock(&mtk_dp->edid_lock);
> +		return NULL;
> +	}
> +
> +	mtk_dp->edid = drm_edid_duplicate(new_edid);
> +	mutex_unlock(&mtk_dp->edid_lock);
> +
> +	return new_edid;
> +}
> +
> +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> +				   struct drm_dp_aux_msg *msg)
> +{
> +	ssize_t err = -EAGAIN;
> +	struct mtk_dp *mtk_dp;
> +	bool is_read;
> +	u8 request;
> +	size_t accessed_bytes = 0;
> +	int retry = 3, ret = 0;
> +
> +	mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> +
> +	if (!mtk_dp->train_info.cable_plugged_in ||
> +	    mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> +		err = -EAGAIN;
> +		goto err;
> +	}
> +
> +	switch (msg->request) {
> +	case DP_AUX_I2C_MOT:
> +	case DP_AUX_I2C_WRITE:
> +	case DP_AUX_NATIVE_WRITE:
> +	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> +	case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> +		request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> +		is_read = false;
> +		break;
> +	case DP_AUX_I2C_READ:
> +	case DP_AUX_NATIVE_READ:
> +	case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> +		request = msg->request;
> +		is_read = true;
> +		break;
> +	default:
> +		drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> +			msg->request);
> +		err = -EINVAL;
> +		goto err;
> +	}
> +
> +	if (msg->size == 0) {
> +		mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> +				       msg->address + accessed_bytes,
> +				       msg->buffer + accessed_bytes, 0);
> +	} else {
> +		while (accessed_bytes < msg->size) {
> +			size_t to_access =
> +				min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> +				      msg->size - accessed_bytes);
> +			retry = 3;
> +			while (retry--) {
> +				ret = mtk_dp_aux_do_transfer(
> +					mtk_dp, is_read, request,
> +					msg->address + accessed_bytes,
> +					msg->buffer + accessed_bytes,
> +					to_access);
> +				if (ret == 0)
> +					break;
> +				udelay(50);
> +			}
> +			if (!retry && ret) {
> +				drm_info(mtk_dp->drm_dev,
> +					 "Failed to do AUX transfer: %d\n",
> +					 ret);
> +				break;
> +			}
> +			accessed_bytes += to_access;
> +		}
> +	}
> +err:
> +	if (!ret) {
> +		msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +		ret = msg->size;
> +	} else {
> +		msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> +		return err;
> +	}
> +
> +	msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +	return msg->size;
> +}
> +
> +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> +{
> +	drm_dp_aux_init(&mtk_dp->aux);
> +	mtk_dp->aux.name = "aux_mtk_dp";
> +	mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> +}
> +
> +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> +{
> +	mutex_lock(&mtk_dp->dp_lock);
> +
> +	mtk_dp_hwirq_enable(mtk_dp, false);
> +	mtk_dp_power_disable(mtk_dp);
> +	phy_exit(mtk_dp->phy);
> +	clk_disable_unprepare(mtk_dp->dp_tx_clk);
> +
> +	mutex_unlock(&mtk_dp->dp_lock);
> +}
> +
> +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> +{
> +	int ret = 0;
> +
> +	mutex_lock(&mtk_dp->dp_lock);
> +
> +	ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> +	if (ret < 0) {
> +		dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> +		goto err;
> +	}
> +	ret = phy_init(mtk_dp->phy);
> +	if (ret) {
> +		dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> +		goto err_phy_init;
> +	}
> +	ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> +	if (ret) {
> +		dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> +		goto err_phy_config;
> +	}
> +
> +	mtk_dp_init_port(mtk_dp);
> +	mtk_dp_power_enable(mtk_dp);
> +	mtk_dp_hwirq_enable(mtk_dp, true);
> +
> +err_phy_config:
> +	phy_exit(mtk_dp->phy);
> +err_phy_init:
> +	clk_disable_unprepare(mtk_dp->dp_tx_clk);
> +err:
> +	mutex_unlock(&mtk_dp->dp_lock);
> +	return ret;
> +}
> +
> +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> +				enum drm_bridge_attach_flags flags)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	int ret;
> +
> +	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> +		dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> +		return -EINVAL;
> +	}
> +
> +	ret = mtk_dp_poweron(mtk_dp);
> +	if (ret)
> +		return ret;
> +
> +	if (mtk_dp->next_bridge) {
> +		ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> +					&mtk_dp->bridge, flags);
> +		if (ret) {
> +			drm_warn(mtk_dp->drm_dev,
> +				 "Failed to attach external bridge: %d\n", ret);
> +			goto err_bridge_attach;
> +		}
> +	}
> +
> +	mtk_dp->drm_dev = bridge->dev;
> +
> +	return 0;
> +
> +err_bridge_attach:
> +	mtk_dp_poweroff(mtk_dp);
> +	return ret;
> +}
> +
> +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +	mtk_dp->drm_dev = NULL;
> +
> +	mtk_dp_poweroff(mtk_dp);
> +}
> +
> +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> +					 struct drm_bridge_state *old_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +	mtk_dp_video_mute(mtk_dp, true);
> +	mtk_dp_audio_mute(mtk_dp, true);
> +	mtk_dp->state = MTK_DP_STATE_IDLE;
> +	mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> +
> +	mtk_dp->enabled = false;
> +	msleep(100);
> +}
> +
> +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> +					  struct drm_display_mode *mode)
> +{
> +	struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> +
> +	drm_display_mode_to_videomode(mode, &timings->vm);
> +	timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> +	timings->htotal = mode->htotal;
> +	timings->vtotal = mode->vtotal;
> +}
> +
> +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> +					struct drm_bridge_state *old_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	struct drm_connector *conn;
> +	struct drm_connector_state *conn_state;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *crtc_state;
> +	int ret = 0;
> +	int i;
> +
> +	conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> +							bridge->encoder);
> +	if (!conn) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as connector is missing\n");
> +		return;
> +	}
> +
> +	memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);

This should be protected by a mutex (just like any resource shared
between KMS and ALSA)

> +	conn_state =
> +		drm_atomic_get_new_connector_state(old_state->base.state, conn);
> +	if (!conn_state) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as connector state is missing\n");
> +		return;
> +	}
> +
> +	crtc = conn_state->crtc;
> +	if (!crtc) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as connector state doesn't have a crtc\n");
> +		return;
> +	}
> +
> +	crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> +	if (!crtc_state) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as crtc state is missing\n");
> +		return;
> +	}
> +
> +	mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);
> +	if (!mtk_dp_parse_capabilities(mtk_dp)) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as nothing is plugged in\n");
> +		return;
> +	}
> +
> +	//training
> +	for (i = 0; i < 50; i++) {
> +		ret = mtk_dp_train_handler(mtk_dp);
> +		if (ret) {
> +			drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> +				ret);
> +			return;
> +		}
> +
> +		ret = mtk_dp_state_handler(mtk_dp);
> +		if (ret) {
> +			drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> +				ret);
> +			return;
> +		}
> +	}
> +
> +	mtk_dp->enabled = true;
> +	mtk_dp_update_plugged_status(mtk_dp);
> +}
> +
> +static enum drm_mode_status
> +mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
> +			 const struct drm_display_info *info,
> +			 const struct drm_display_mode *mode)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	u32 rx_linkrate;
> +	u32 bpp;
> +
> +	bpp = info->color_formats & DRM_COLOR_FORMAT_YCRCB422 ? 16 : 24;
> +	rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
> +	if (rx_linkrate * mtk_dp->train_info.lane_count < mode->clock * bpp / 8)
> +		return MODE_CLOCK_HIGH;
> +
> +	if (mode->clock > 600000)
> +		return MODE_CLOCK_HIGH;
> +
> +	if ((mode->clock * 1000) / (mode->htotal * mode->vtotal) > 62)
> +		return MODE_CLOCK_HIGH;
> +
> +	return MODE_OK;
> +}
> +
> +static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(
> +	struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
> +	struct drm_crtc_state *crtc_state,
> +	struct drm_connector_state *conn_state, unsigned int *num_output_fmts)
> +{
> +	u32 *output_fmts;
> +
> +	*num_output_fmts = 0;
> +	output_fmts = kcalloc(1, sizeof(*output_fmts), GFP_KERNEL);
> +	if (!output_fmts)
> +		return NULL;
> +	*num_output_fmts = 1;
> +	output_fmts[0] = MEDIA_BUS_FMT_FIXED;
> +	return output_fmts;
> +}
> +
> +static const u32 mt8195_input_fmts[] = {
> +	MEDIA_BUS_FMT_RGB888_1X24,
> +	MEDIA_BUS_FMT_YUV8_1X24,
> +	MEDIA_BUS_FMT_YUYV8_1X16,
> +};
> +
> +static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(
> +	struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
> +	struct drm_crtc_state *crtc_state,
> +	struct drm_connector_state *conn_state, u32 output_fmt,
> +	unsigned int *num_input_fmts)
> +{
> +	u32 *input_fmts;
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> +	struct drm_display_info *display_info =
> +		&conn_state->connector->display_info;
> +	u32 rx_linkrate;
> +	u32 bpp;
> +
> +	bpp = (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422) ? 16 :
> +										24;
> +	rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
> +	*num_input_fmts = 0;
> +	input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts), sizeof(*input_fmts),
> +			     GFP_KERNEL);
> +	if (!input_fmts)
> +		return NULL;
> +
> +	*num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
> +
> +	memcpy(input_fmts, mt8195_input_fmts,
> +	       sizeof(*input_fmts) * ARRAY_SIZE(mt8195_input_fmts));
> +
> +	if (((rx_linkrate * mtk_dp->train_info.lane_count) <
> +	     (mode->clock * 24 / 8)) &&
> +	    ((rx_linkrate * mtk_dp->train_info.lane_count) >
> +	     (mode->clock * 16 / 8)) &&
> +	    (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422)) {
> +		kfree(input_fmts);
> +		input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL);
> +		*num_input_fmts = 1;
> +		input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
> +	}
> +
> +	return input_fmts;
> +}
> +
> +static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
> +				      struct drm_bridge_state *bridge_state,
> +				      struct drm_crtc_state *crtc_state,
> +				      struct drm_connector_state *conn_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	unsigned int input_bus_format;
> +
> +	input_bus_format = bridge_state->input_bus_cfg.format;
> +
> +	dev_info(mtk_dp->dev, "input format 0x%04x, output format 0x%04x\n",
> +		 bridge_state->input_bus_cfg.format,
> +		 bridge_state->output_bus_cfg.format);
> +
> +	mtk_dp->input_fmt = input_bus_format;
> +	if (mtk_dp->input_fmt == MEDIA_BUS_FMT_YUYV8_1X16)
> +		mtk_dp->info.format = MTK_DP_COLOR_FORMAT_YUV_422;
> +	else
> +		mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
> +
> +	return 0;
> +}
> +
> +static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
> +	.atomic_check = mtk_dp_bridge_atomic_check,
> +	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> +	.atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts,
> +	.atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts,
> +	.atomic_reset = drm_atomic_helper_bridge_reset,
> +	.attach = mtk_dp_bridge_attach,
> +	.detach = mtk_dp_bridge_detach,
> +	.atomic_enable = mtk_dp_bridge_atomic_enable,
> +	.atomic_disable = mtk_dp_bridge_atomic_disable,
> +	.mode_valid = mtk_dp_bridge_mode_valid,
> +	.get_edid = mtk_dp_get_edid,
> +	.detect = mtk_dp_bdg_detect,
> +};
> +
> +/*
> + * HDMI audio codec callbacks
> + */
> +static int mtk_dp_audio_hw_params(struct device *dev, void *data,
> +				  struct hdmi_codec_daifmt *daifmt,
> +				  struct hdmi_codec_params *params)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +	struct mtk_dp_audio_cfg cfg;
> +
> +	if (!mtk_dp->enabled) {
> +		pr_err("%s, DP is not ready!\n", __func__);
> +		return -ENODEV;
> +	}
> +
> +	cfg.channels = params->cea.channels;
> +	cfg.sample_rate = params->sample_rate;
> +	cfg.word_length_bits = 24;
> +
> +	mtk_dp_audio_setup(mtk_dp, &cfg);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_audio_startup(struct device *dev, void *data)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	mtk_dp_audio_mute(mtk_dp, false);
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_audio_shutdown(struct device *dev, void *data)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	mtk_dp_audio_mute(mtk_dp, true);
> +}
> +
> +static int mtk_dp_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
> +				size_t len)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	if (mtk_dp->enabled)
> +		memcpy(buf, mtk_dp->connector_eld, len);
> +	else
> +		memset(buf, 0, len);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_audio_hook_plugged_cb(struct device *dev, void *data,
> +					hdmi_codec_plugged_cb fn,
> +					struct device *codec_dev)
> +{
> +	struct mtk_dp *mtk_dp = data;
> +
> +	mutex_lock(&mtk_dp->update_plugged_status_lock);
> +	mtk_dp->plugged_cb = fn;
> +	mtk_dp->codec_dev = codec_dev;
> +	mutex_unlock(&mtk_dp->update_plugged_status_lock);
> +
> +	mtk_dp_update_plugged_status(mtk_dp);
> +
> +	return 0;
> +}
> +
> +static const struct hdmi_codec_ops mtk_dp_audio_codec_ops = {
> +	.hw_params = mtk_dp_audio_hw_params,
> +	.audio_startup = mtk_dp_audio_startup,
> +	.audio_shutdown = mtk_dp_audio_shutdown,
> +	.get_eld = mtk_dp_audio_get_eld,
> +	.hook_plugged_cb = mtk_dp_audio_hook_plugged_cb,
> +	.no_capture_mute = 1,
> +};
> +
> +static int mtk_dp_register_audio_driver(struct device *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +	struct hdmi_codec_pdata codec_data = {
> +		.ops = &mtk_dp_audio_codec_ops,
> +		.max_i2s_channels = 8,
> +		.i2s = 1,
> +		.data = mtk_dp,
> +	};
> +	struct platform_device *pdev;
> +
> +	pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
> +					     PLATFORM_DEVID_AUTO, &codec_data,
> +					     sizeof(codec_data));
> +	if (IS_ERR(pdev))
> +		return PTR_ERR(pdev);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_probe(struct platform_device *pdev)
> +{
> +	struct mtk_dp *mtk_dp;
> +	struct device *dev = &pdev->dev;
> +	int ret;
> +	int irq_num = 0;
> +	struct drm_panel *panel = NULL;
> +
> +	mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> +	if (!mtk_dp)
> +		return -ENOMEM;
> +
> +	mtk_dp->dev = dev;
> +
> +	irq_num = platform_get_irq(pdev, 0);
> +	if (irq_num < 0) {
> +		dev_err(dev, "failed to request dp irq resource\n");
> +		return -EPROBE_DEFER;
> +	}
> +
> +	ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel,
> +					  &mtk_dp->next_bridge);
> +	if (ret == -ENODEV) {
> +		dev_info(
> +			dev,
> +			"No panel connected in devicetree, continuing as external DP\n");
> +		mtk_dp->next_bridge = NULL;
> +	} else if (ret) {
> +		dev_err(dev, "Failed to find panel or bridge: %d\n", ret);
> +		return ret;
> +	}
> +
> +	if (panel) {
> +		mtk_dp->next_bridge = devm_drm_panel_bridge_add(dev, panel);
> +		if (IS_ERR(mtk_dp->next_bridge)) {
> +			ret = PTR_ERR(mtk_dp->next_bridge);
> +			dev_err(dev, "Failed to create bridge: %d\n", ret);
> +			return -EPROBE_DEFER;
> +		}
> +	}

You should be using devm_drm_of_get_bridge here

> +	ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> +	if (ret)
> +		return ret;

pdata?

> +
> +	mtk_dp_aux_init(mtk_dp);
> +
> +	ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
> +					mtk_dp_hpd_event_thread,
> +					IRQ_TYPE_LEVEL_HIGH, dev_name(dev),
> +					mtk_dp);
> +	if (ret) {
> +		dev_err(dev, "failed to request mediatek dptx irq\n");
> +		return -EPROBE_DEFER;
> +	}
> +
> +	mutex_init(&mtk_dp->dp_lock);
> +	mutex_init(&mtk_dp->edid_lock);
> +
> +	platform_set_drvdata(pdev, mtk_dp);
> +
> +	if (!mtk_dp_is_edp(mtk_dp)) {
> +		mutex_init(&mtk_dp->update_plugged_status_lock);
> +		ret = mtk_dp_register_audio_driver(dev);
> +		if (ret) {
> +			dev_err(dev, "Failed to register audio driver: %d\n",
> +				ret);
> +			return ret;
> +		}
> +	}
> +
> +	mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
> +							PLATFORM_DEVID_AUTO,
> +							&mtk_dp->regs,
> +							sizeof(&mtk_dp->regs));
> +	if (IS_ERR(mtk_dp->phy_dev)) {
> +		dev_err(dev, "Failed to create device mediatek-dp-phy: %ld\n",
> +			PTR_ERR(mtk_dp->phy_dev));
> +		return PTR_ERR(mtk_dp->phy_dev);
> +	}
> +
> +	mtk_dp_get_calibration_data(mtk_dp);
> +
> +	mtk_dp->phy = dev_get_drvdata(&mtk_dp->phy_dev->dev);
> +	if (IS_ERR(mtk_dp->phy)) {
> +		dev_err(dev, "Failed to get phy: %ld\n", PTR_ERR(mtk_dp->phy));
> +		platform_device_unregister(mtk_dp->phy_dev);
> +		return PTR_ERR(mtk_dp->phy);
> +	}
> +
> +	mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
> +	mtk_dp->bridge.of_node = dev->of_node;
> +	if (mtk_dp_is_edp(mtk_dp))
> +		mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> +	else
> +		mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
> +
> +	mtk_dp->bridge.ops =
> +		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
> +	drm_bridge_add(&mtk_dp->bridge);
> +
> +	pm_runtime_enable(dev);
> +	pm_runtime_get_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_remove(struct platform_device *pdev)
> +{
> +	struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
> +
> +	platform_device_unregister(mtk_dp->phy_dev);
> +
> +	mtk_dp_video_mute(mtk_dp, true);
> +	mtk_dp_audio_mute(mtk_dp, true);
> +
> +	pm_runtime_disable(&pdev->dev);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int mtk_dp_suspend(struct device *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	mtk_dp_power_disable(mtk_dp);
> +	mtk_dp_hwirq_enable(mtk_dp, false);
> +
> +	pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_resume(struct device *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	pm_runtime_get_sync(dev);
> +
> +	mtk_dp_init_port(mtk_dp);
> +	mtk_dp_power_enable(mtk_dp);
> +	mtk_dp_hwirq_enable(mtk_dp, true);
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
> +
> +static const struct of_device_id mtk_dp_of_match[] = {
> +	{
> +		.compatible = "mediatek,mt8195-dp-tx",
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
> +
> +struct platform_driver mtk_dp_driver = {
> +	.probe = mtk_dp_probe,
> +	.remove = mtk_dp_remove,
> +	.driver = {
> +		.name = "mediatek-drm-dp",
> +		.of_match_table = mtk_dp_of_match,
> +		.pm = &mtk_dp_pm_ops,
> +	},
> +};
> +
> +MODULE_AUTHOR("Jason-JH.Lin <jason-jh.lin@mediatek.com>");
> +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> +MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> new file mode 100644
> index 0000000000000..79952ac30e9e6
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> @@ -0,0 +1,568 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019 MediaTek Inc.
> + * Copyright (c) 2021 BayLibre
> + */
> +#ifndef _MTK_DP_REG_H_
> +#define _MTK_DP_REG_H_
> +
> +#define MTK_DP_SIP_CONTROL_AARCH32 0x82000523
> +#define MTK_DP_SIP_ATF_VIDEO_UNMUTE 0x20
> +#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE 0x21
> +
> +#define DP_PHY_GLB_BIAS_GEN_00 0x0000
> +#define RG_XTP_GLB_BIAS_INTR_CTRL (0x1f << 16)
> +
> +#define DP_PHY_GLB_DPAUX_TX 0x0008
> +#define RG_CKM_PT0_CKTX_IMPSEL (0xf << 20)
> +
> +#define DP_PHY_LANE_TX_0 0x104
> +#define RG_XTP_LN0_TX_IMPSEL_PMOS (0xf << 12)
> +#define RG_XTP_LN0_TX_IMPSEL_NMOS (0xf << 16)
> +
> +#define DP_PHY_LANE_TX_1 0x204
> +#define RG_XTP_LN1_TX_IMPSEL_PMOS (0xf << 12)
> +#define RG_XTP_LN1_TX_IMPSEL_NMOS (0xf << 16)
> +
> +#define DP_PHY_LANE_TX_2 0x304
> +#define RG_XTP_LN2_TX_IMPSEL_PMOS (0xf << 12)
> +#define RG_XTP_LN2_TX_IMPSEL_NMOS (0xf << 16)
> +
> +#define DP_PHY_LANE_TX_3 0x404
> +#define RG_XTP_LN3_TX_IMPSEL_PMOS (0xf << 12)
> +#define RG_XTP_LN3_TX_IMPSEL_NMOS (0xf << 16)
> +
> +#define TOP_OFFSET 0x2000
> +#define ENC0_OFFSET 0x3000
> +#define ENC1_OFFSET 0x3200
> +#define TRANS_OFFSET 0x3400
> +#define AUX_OFFSET 0x3600
> +#define SEC_OFFSET 0x4000
> +
> +#define MTK_DP_HPD_DISCONNECT BIT(1)
> +#define MTK_DP_HPD_CONNECT BIT(2)
> +#define MTK_DP_HPD_INTERRUPT BIT(3)
> +
> +#define MTK_DP_0034 0x034
> +#define DA_XTP_GLB_CKDET_EN_FORCE_VAL BIT(15)
> +#define DA_XTP_GLB_CKDET_EN_FORCE_EN BIT(14)
> +#define DA_CKM_INTCKTX_EN_FORCE_VAL BIT(13)
> +#define DA_CKM_INTCKTX_EN_FORCE_EN BIT(12)
> +#define DA_CKM_CKTX0_EN_FORCE_VAL BIT(11)
> +#define DA_CKM_CKTX0_EN_FORCE_EN BIT(10)
> +#define DA_CKM_XTAL_CK_FORCE_VAL BIT(9)
> +#define DA_CKM_XTAL_CK_FORCE_EN BIT(8)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_VAL BIT(7)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_EN BIT(6)
> +#define DA_CKM_BIAS_EN_FORCE_VAL BIT(5)
> +#define DA_CKM_BIAS_EN_FORCE_EN BIT(4)
> +#define DA_XTP_GLB_AVD10_ON_FORCE_VAL BIT(3)
> +#define DA_XTP_GLB_AVD10_ON_FORCE BIT(2)
> +#define DA_XTP_GLB_LDO_EN_FORCE_VAL BIT(1)
> +#define DA_XTP_GLB_LDO_EN_FORCE_EN BIT(0)
> +
> +#define MTK_DP_1040 0x1040
> +#define RG_DPAUX_RX_VALID_DEGLITCH_EN BIT(2)
> +#define RG_XTP_GLB_CKDET_EN BIT(1)
> +#define RG_DPAUX_RX_EN BIT(0)
> +
> +#define MTK_DP_ENC0_P0_3000 (ENC0_OFFSET + 0x000)
> +#define LANE_NUM_DP_ENC0_P0_MASK 0x3
> +#define VIDEO_MUTE_SW_DP_ENC0_P0_MASK 0x4
> +#define VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT 2
> +#define VIDEO_MUTE_SEL_DP_ENC0_P0_MASK 0x8
> +#define VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT 3
> +#define ENHANCED_FRAME_EN_DP_ENC0_P0_MASK 0x10
> +#define ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT 4
> +
> +#define MTK_DP_ENC0_P0_3004 (ENC0_OFFSET + 0x004)
> +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK 0x100
> +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_SHIFT 8
> +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK 0x200
> +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT 9
> +
> +#define MTK_DP_ENC0_P0_3008 (ENC0_OFFSET + 0x008)
> +#define VIDEO_M_CODE_SW_0_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_300C (ENC0_OFFSET + 0x00C)
> +#define VIDEO_M_CODE_SW_1_DP_ENC0_P0_MASK 0xff
> +
> +#define MTK_DP_ENC0_P0_3010 (ENC0_OFFSET + 0x010)
> +#define HTOTAL_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3014 (ENC0_OFFSET + 0x014)
> +#define VTOTAL_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3018 (ENC0_OFFSET + 0x018)
> +#define HSTART_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_301C (ENC0_OFFSET + 0x01C)
> +#define VSTART_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3020 (ENC0_OFFSET + 0x020)
> +#define HWIDTH_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3024 (ENC0_OFFSET + 0x024)
> +#define VHEIGHT_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3028 (ENC0_OFFSET + 0x028)
> +#define HSW_SW_DP_ENC0_P0_MASK 0x7fff
> +#define HSW_SW_DP_ENC0_P0_SHIFT 0
> +#define HSP_SW_DP_ENC0_P0_MASK 0x8000
> +
> +#define MTK_DP_ENC0_P0_302C (ENC0_OFFSET + 0x02C)
> +#define VSW_SW_DP_ENC0_P0_MASK 0x7fff
> +#define VSW_SW_DP_ENC0_P0_SHIFT 0
> +#define VSP_SW_DP_ENC0_P0_MASK 0x8000
> +
> +#define MTK_DP_ENC0_P0_3030 (ENC0_OFFSET + 0x030)
> +#define HTOTAL_SEL_DP_ENC0_P0_SHIFT 0
> +#define VTOTAL_SEL_DP_ENC0_P0_SHIFT 1
> +#define HSTART_SEL_DP_ENC0_P0_SHIFT 2
> +#define VSTART_SEL_DP_ENC0_P0_SHIFT 3
> +#define HWIDTH_SEL_DP_ENC0_P0_SHIFT 4
> +#define VHEIGHT_SEL_DP_ENC0_P0_SHIFT 5
> +#define HSP_SEL_DP_ENC0_P0_SHIFT 6
> +#define HSW_SEL_DP_ENC0_P0_SHIFT 7
> +#define VSP_SEL_DP_ENC0_P0_SHIFT 8
> +#define VSW_SEL_DP_ENC0_P0_SHIFT 9
> +#define VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK 0x800
> +#define VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT 11
> +#define VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK 0x1000
> +#define VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT 12
> +
> +#define MTK_DP_ENC0_P0_3034 (ENC0_OFFSET + 0x034)
> +
> +#define MTK_DP_ENC0_P0_3038 (ENC0_OFFSET + 0x038)
> +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK 0x800
> +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_SHIFT 11
> +
> +#define MTK_DP_ENC0_P0_303C (ENC0_OFFSET + 0x03C)
> +#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK 0x3f
> +#define SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT 0
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK 0x700
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT 8
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT                                     \
> +	(0 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT                                     \
> +	(1 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT                                     \
> +	(2 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT                                      \
> +	(3 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT                                      \
> +	(4 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK 0x7000
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT 12
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB                                     \
> +	(0 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422                                \
> +	(1 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420                                \
> +	(2 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> +#define VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK 0x8000
> +#define VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT 15
> +
> +#define MTK_DP_ENC0_P0_3040 (ENC0_OFFSET + 0x040)
> +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK 0xfff
> +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT 0
> +
> +#define MTK_DP_ENC0_P0_3044 (ENC0_OFFSET + 0x044)
> +#define VIDEO_N_CODE_0_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3048 (ENC0_OFFSET + 0x048)
> +#define VIDEO_N_CODE_1_DP_ENC0_P0_MASK 0xff
> +
> +#define MTK_DP_ENC0_P0_304C (ENC0_OFFSET + 0x04C)
> +#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK 0x4
> +#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK 0x100
> +
> +#define MTK_DP_ENC0_P0_3050 (ENC0_OFFSET + 0x050)
> +#define VIDEO_N_CODE_MN_GEN_0_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3054 (ENC0_OFFSET + 0x054)
> +#define VIDEO_N_CODE_MN_GEN_1_DP_ENC0_P0_MASK 0xff
> +
> +#define MTK_DP_ENC0_P0_3064 (ENC0_OFFSET + 0x064)
> +#define HDE_NUM_LAST_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3088 (ENC0_OFFSET + 0x088)
> +#define AU_EN_DP_ENC0_P0_MASK 0x40
> +#define AU_EN_DP_ENC0_P0_SHIFT 6
> +#define AUDIO_8CH_EN_DP_ENC0_P0_MASK 0x80
> +#define AUDIO_8CH_SEL_DP_ENC0_P0_MASK 0x100
> +#define AUDIO_2CH_EN_DP_ENC0_P0_MASK 0x4000
> +#define AUDIO_2CH_SEL_DP_ENC0_P0_MASK 0x8000
> +
> +#define MTK_DP_ENC0_P0_308C (ENC0_OFFSET + 0x08C)
> +#define CH_STATUS_0_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3090 (ENC0_OFFSET + 0x090)
> +#define CH_STATUS_1_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3094 (ENC0_OFFSET + 0x094)
> +#define CH_STATUS_2_DP_ENC0_P0_MASK 0xff
> +
> +#define MTK_DP_ENC0_P0_30A0 (ENC0_OFFSET + 0x0A0)
> +
> +#define MTK_DP_ENC0_P0_30A4 (ENC0_OFFSET + 0x0A4)
> +#define AU_TS_CFG_DP_ENC0_P0_MASK 0xff
> +
> +#define MTK_DP_ENC0_P0_30A8 (ENC0_OFFSET + 0x0A8)
> +
> +#define MTK_DP_ENC0_P0_30AC (ENC0_OFFSET + 0x0AC)
> +
> +#define MTK_DP_ENC0_P0_30B0 (ENC0_OFFSET + 0x0B0)
> +
> +#define MTK_DP_ENC0_P0_30B4 (ENC0_OFFSET + 0x0B4)
> +#define ISRC_CFG_DP_ENC0_P0_MASK 0xff00
> +#define ISRC_CFG_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC0_P0_30B8 (ENC0_OFFSET + 0x0B8)
> +
> +#define MTK_DP_ENC0_P0_30BC (ENC0_OFFSET + 0x0BC)
> +#define ISRC_CONT_DP_ENC0_P0_MASK 0x1
> +#define ISRC_CONT_DP_ENC0_P0_SHIFT 0
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK 0x700
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT 8
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_2                             \
> +	(1 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_4                             \
> +	(2 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_8                             \
> +	(3 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2                             \
> +	(5 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_4                             \
> +	(6 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_8                             \
> +	(7 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +
> +#define MTK_DP_ENC0_P0_30D8 (ENC0_OFFSET + 0x0D8)
> +
> +#define MTK_DP_ENC0_P0_312C (ENC0_OFFSET + 0x12C)
> +#define ASP_HB2_DP_ENC0_P0_MASK 0xff
> +#define ASP_HB3_DP_ENC0_P0_MASK 0xff00
> +#define ASP_HB3_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC0_P0_3130 (ENC0_OFFSET + 0x130)
> +
> +#define MTK_DP_ENC0_P0_3138 (ENC0_OFFSET + 0x138)
> +
> +#define MTK_DP_ENC0_P0_3154 (ENC0_OFFSET + 0x154)
> +#define PGEN_HTOTAL_DP_ENC0_P0_MASK 0x3fff
> +
> +#define MTK_DP_ENC0_P0_3158 (ENC0_OFFSET + 0x158)
> +#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK 0x3fff
> +
> +#define MTK_DP_ENC0_P0_315C (ENC0_OFFSET + 0x15C)
> +#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x3fff
> +
> +#define MTK_DP_ENC0_P0_3160 (ENC0_OFFSET + 0x160)
> +#define PGEN_HFDE_START_DP_ENC0_P0_MASK 0x3fff
> +
> +#define MTK_DP_ENC0_P0_3164 (ENC0_OFFSET + 0x164)
> +#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x3fff
> +
> +#define MTK_DP_ENC0_P0_3168 (ENC0_OFFSET + 0x168)
> +#define PGEN_VTOTAL_DP_ENC0_P0_MASK 0x1fff
> +
> +#define MTK_DP_ENC0_P0_316C (ENC0_OFFSET + 0x16C)
> +#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK 0x1fff
> +
> +#define MTK_DP_ENC0_P0_3170 (ENC0_OFFSET + 0x170)
> +#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x1fff
> +
> +#define MTK_DP_ENC0_P0_3174 (ENC0_OFFSET + 0x174)
> +#define PGEN_VFDE_START_DP_ENC0_P0_MASK 0x1fff
> +
> +#define MTK_DP_ENC0_P0_3178 (ENC0_OFFSET + 0x178)
> +#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x1fff
> +
> +#define MTK_DP_ENC0_P0_31B0 (ENC0_OFFSET + 0x1B0)
> +#define PGEN_PATTERN_SEL_SHIFT 4
> +#define PGEN_PATTERN_SEL_MASK 0x0070
> +
> +#define MTK_DP_ENC0_P0_31C8 (ENC0_OFFSET + 0x1C8)
> +#define VSC_EXT_VESA_HB0_DP_ENC0_P0_MASK 0xff
> +#define VSC_EXT_VESA_HB1_DP_ENC0_P0_MASK 0xff00
> +#define VSC_EXT_VESA_HB1_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC0_P0_31CC (ENC0_OFFSET + 0x1CC)
> +#define VSC_EXT_VESA_HB2_DP_ENC0_P0_MASK 0xff
> +#define VSC_EXT_VESA_HB2_DP_ENC0_P0_SHIFT 0
> +#define VSC_EXT_VESA_HB3_DP_ENC0_P0_MASK 0xff00
> +
> +#define MTK_DP_ENC0_P0_31D0 (ENC0_OFFSET + 0x1D0)
> +#define VSC_EXT_CEA_HB0_DP_ENC0_P0_MASK 0xff
> +#define VSC_EXT_CEA_HB1_DP_ENC0_P0_MASK 0xff00
> +#define VSC_EXT_CEA_HB1_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC0_P0_31D4 (ENC0_OFFSET + 0x1D4)
> +#define VSC_EXT_CEA_HB2_DP_ENC0_P0_MASK 0xff
> +#define VSC_EXT_CEA_HB2_DP_ENC0_P0_SHIFT 0
> +#define VSC_EXT_CEA_HB3_DP_ENC0_P0_MASK 0xff00
> +
> +#define MTK_DP_ENC0_P0_31D8 (ENC0_OFFSET + 0x1D8)
> +#define VSC_EXT_VESA_NUM_DP_ENC0_P0_MASK 0x3f
> +#define VSC_EXT_VESA_NUM_DP_ENC0_P0_SHIFT 0
> +#define VSC_EXT_CEA_NUM_DP_ENC0_P0_MASK 0x3f00
> +#define VSC_EXT_CEA_NUM_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC0_P0_31DC (ENC0_OFFSET + 0x1DC)
> +#define HDR0_CFG_DP_ENC0_P0_MASK 0xff
> +#define HDR0_CFG_DP_ENC0_P0_SHIFT 0
> +
> +#define MTK_DP_ENC0_P0_31E8 (ENC0_OFFSET + 0x1E8)
> +
> +#define MTK_DP_ENC0_P0_31EC (ENC0_OFFSET + 0x1EC)
> +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK 0x10
> +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT 4
> +#define ISRC1_HB3_DP_ENC0_P0_MASK 0xff00
> +#define ISRC1_HB3_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC1_P0_3200 (ENC1_OFFSET + 0x000)
> +
> +#define MTK_DP_ENC1_P0_3280 (ENC1_OFFSET + 0x080)
> +#define SDP_PACKET_TYPE_DP_ENC1_P0_MASK 0x1f
> +#define SDP_PACKET_W_DP_ENC1_P0 0x20
> +#define SDP_PACKET_W_DP_ENC1_P0_MASK 0x20
> +#define SDP_PACKET_W_DP_ENC1_P0_SHIFT 5
> +
> +#define MTK_DP_ENC1_P0_328C (ENC1_OFFSET + 0x08C)
> +
> +#define MTK_DP_ENC1_P0_3290 (ENC1_OFFSET + 0x090)
> +
> +#define MTK_DP_ENC1_P0_32A0 (ENC1_OFFSET + 0x0A0)
> +
> +#define MTK_DP_ENC1_P0_32A4 (ENC1_OFFSET + 0x0A4)
> +
> +#define MTK_DP_ENC1_P0_3300 (ENC1_OFFSET + 0x100)
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK 0x300
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT 8
> +
> +#define MTK_DP_ENC1_P0_3304 (ENC1_OFFSET + 0x104)
> +#define AU_PRTY_REGEN_DP_ENC1_P0_MASK 0x100
> +#define AU_CH_STS_REGEN_DP_ENC1_P0_MASK 0x200
> +#define AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK 0x1000
> +
> +#define MTK_DP_ENC1_P0_3324 (ENC1_OFFSET + 0x124)
> +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK 0x300
> +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT 8
> +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX                                       \
> +	(0 << AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT)
> +
> +#define MTK_DP_ENC1_P0_3364 (ENC1_OFFSET + 0x164)
> +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK 0xfff
> +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT 0
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK 0xf000
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT 12
> +
> +#define MTK_DP_ENC1_P0_3368 (ENC1_OFFSET + 0x168)
> +#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT 0
> +#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT 4
> +#define SDP_DP13_EN_DP_ENC1_P0_SHIFT 8
> +#define BS2BS_MODE_DP_ENC1_P0_MASK 0x3000
> +#define BS2BS_MODE_DP_ENC1_P0_SHIFT 12
> +
> +#define MTK_DP_ENC1_P0_33F4 (ENC1_OFFSET + 0x1F4)
> +
> +#define MTK_DP_TRANS_P0_3400 (TRANS_OFFSET + 0x000)
> +#define PATTERN1_EN_DP_TRANS_P0_MASK 0x1000
> +#define PATTERN1_EN_DP_TRANS_P0_SHIFT 12
> +#define PATTERN2_EN_DP_TRANS_P0_MASK 0x2000
> +#define PATTERN3_EN_DP_TRANS_P0_MASK 0x4000
> +#define PATTERN4_EN_DP_TRANS_P0_MASK 0x8000
> +
> +#define MTK_DP_TRANS_P0_3404 (TRANS_OFFSET + 0x004)
> +#define DP_SCR_EN_DP_TRANS_P0_MASK 0x1
> +
> +#define MTK_DP_TRANS_P0_340C (TRANS_OFFSET + 0x00C)
> +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK 0x2000
> +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT 13
> +
> +#define MTK_DP_TRANS_P0_3410 (TRANS_OFFSET + 0x010)
> +#define HPD_DEB_THD_DP_TRANS_P0_MASK 0xf
> +#define HPD_DEB_THD_DP_TRANS_P0_SHIFT 0
> +#define HPD_INT_THD_DP_TRANS_P0_MASK 0xf0
> +#define HPD_INT_THD_DP_TRANS_P0_SHIFT 4
> +#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US (2 << HPD_INT_THD_DP_TRANS_P0_SHIFT)
> +#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US                                   \
> +	(2 << (HPD_INT_THD_DP_TRANS_P0_SHIFT + 2))
> +#define HPD_DISC_THD_DP_TRANS_P0_MASK 0xf00
> +#define HPD_DISC_THD_DP_TRANS_P0_SHIFT 8
> +#define HPD_CONN_THD_DP_TRANS_P0_MASK 0xf000
> +#define HPD_CONN_THD_DP_TRANS_P0_SHIFT 12
> +
> +#define MTK_DP_TRANS_P0_3414 (TRANS_OFFSET + 0x014)
> +#define HPD_DB_DP_TRANS_P0_MASK 0x4
> +
> +#define MTK_DP_TRANS_P0_3418 (TRANS_OFFSET + 0x018)
> +#define IRQ_CLR_DP_TRANS_P0_MASK 0xf
> +#define IRQ_MASK_DP_TRANS_P0_MASK 0xf0
> +#define IRQ_MASK_DP_TRANS_P0_SHIFT 4
> +#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ (BIT(1) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> +#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ (BIT(2) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> +#define IRQ_MASK_DP_TRANS_P0_INT_IRQ (BIT(3) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> +#define IRQ_STATUS_DP_TRANS_P0_MASK 0xf000
> +#define IRQ_STATUS_DP_TRANS_P0_SHIFT 12
> +
> +#define MTK_DP_TRANS_P0_342C (TRANS_OFFSET + 0x02C)
> +#define XTAL_FREQ_DP_TRANS_P0_DEFAULT 0x69
> +#define XTAL_FREQ_DP_TRANS_P0_MASK 0xff
> +
> +#define MTK_DP_TRANS_P0_3430 (TRANS_OFFSET + 0x030)
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK 0x3
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT BIT(1)
> +
> +#define MTK_DP_TRANS_P0_34A4 (TRANS_OFFSET + 0x0A4)
> +#define LANE_NUM_DP_TRANS_P0_MASK 0xc
> +#define LANE_NUM_DP_TRANS_P0_SHIFT 2
> +
> +#define MTK_DP_TRANS_P0_3540 (TRANS_OFFSET + 0x140)
> +#define FEC_EN_DP_TRANS_P0_MASK 0x1
> +#define FEC_EN_DP_TRANS_P0_SHIFT 0
> +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK 0x8
> +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT 3
> +
> +#define MTK_DP_TRANS_P0_3580 (TRANS_OFFSET + 0x180)
> +#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK 0x100
> +#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK 0x200
> +#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK 0x400
> +#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK 0x800
> +
> +#define MTK_DP_TRANS_P0_35C4 (TRANS_OFFSET + 0x1C4)
> +#define SW_IRQ_MASK_DP_TRANS_P0_MASK 0xffff
> +
> +#define MTK_DP_TRANS_P0_35C8 (TRANS_OFFSET + 0x1C8)
> +#define SW_IRQ_CLR_DP_TRANS_P0_MASK 0xffff
> +
> +#define SW_IRQ_STATUS_DP_TRANS_P0_MASK 0xffff
> +#define SW_IRQ_STATUS_DP_TRANS_P0_SHIFT 0
> +
> +#define MTK_DP_TRANS_P0_35D0 (TRANS_OFFSET + 0x1D0)
> +#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK 0xffff
> +
> +#define MTK_DP_TRANS_P0_35F0 (TRANS_OFFSET + 0x1F0)
> +
> +#define MTK_DP_AUX_P0_360C (AUX_OFFSET + 0x00C)
> +#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK 0x1fff
> +
> +#define MTK_DP_AUX_P0_3614 (AUX_OFFSET + 0x014)
> +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK 0x7f
> +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT 0
> +
> +#define MTK_DP_AUX_P0_3618 (AUX_OFFSET + 0x018)
> +#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK 0x200
> +#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK 0xf
> +
> +#define MTK_DP_AUX_P0_3620 (AUX_OFFSET + 0x020)
> +#define AUX_RD_MODE_AUX_TX_P0_MASK 0x200
> +#define AUX_RX_FIFO_READ_PULSE_TX_P0_MASK 0x100
> +#define AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT 8
> +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK 0xff
> +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT 0
> +
> +#define MTK_DP_AUX_P0_3624 (AUX_OFFSET + 0x024)
> +#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK 0xf
> +
> +#define MTK_DP_AUX_P0_3628 (AUX_OFFSET + 0x028)
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK 0x3ff
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT 0
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE                                     \
> +	(BIT(0) << AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT)
> +
> +#define MTK_DP_AUX_P0_362C (AUX_OFFSET + 0x02C)
> +#define AUX_NO_LENGTH_AUX_TX_P0_MASK 0x1
> +#define AUX_NO_LENGTH_AUX_TX_P0_SHIFT 0
> +#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK 0x2
> +#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK 0xfffc
> +
> +#define MTK_DP_AUX_P0_3630 (AUX_OFFSET + 0x030)
> +#define AUX_TX_REQUEST_READY_AUX_TX_P0_MASK 0x8
> +#define AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT 3
> +
> +#define MTK_DP_AUX_P0_3634 (AUX_OFFSET + 0x034)
> +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK 0xff00
> +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT 8
> +
> +#define MTK_DP_AUX_P0_3640 (AUX_OFFSET + 0x040)
> +#define AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK 0x40
> +#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 6
> +#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 5
> +#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 4
> +#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT 3
> +#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT 2
> +#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT 1
> +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK 0x1
> +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT 0
> +
> +#define MTK_DP_AUX_P0_3644 (AUX_OFFSET + 0x044)
> +#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK 0xf
> +
> +#define MTK_DP_AUX_P0_3648 (AUX_OFFSET + 0x048)
> +#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK 0xffff
> +
> +#define MTK_DP_AUX_P0_364C (AUX_OFFSET + 0x04C)
> +#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK 0xf
> +
> +#define MTK_DP_AUX_P0_3650 (AUX_OFFSET + 0x050)
> +#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK 0xf000
> +#define MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT 12
> +#define PHY_FIFO_RST_AUX_TX_P0_MASK 0x200
> +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK 0x100
> +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT 8
> +
> +#define MTK_DP_AUX_P0_3658 (AUX_OFFSET + 0x058)
> +#define AUX_TX_OV_EN_AUX_TX_P0_MASK 0x1
> +
> +#define MTK_DP_AUX_P0_3690 (AUX_OFFSET + 0x090)
> +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK 0x100
> +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT 8
> +
> +#define MTK_DP_AUX_P0_3704 (AUX_OFFSET + 0x104)
> +#define AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK 0x2
> +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK 0x4
> +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT 2
> +
> +#define MTK_DP_AUX_P0_3708 (AUX_OFFSET + 0x108)
> +
> +#define MTK_DP_AUX_P0_37C8 (AUX_OFFSET + 0x1C8)
> +#define MTK_ATOP_EN_AUX_TX_P0_MASK 0x1
> +#define MTK_ATOP_EN_AUX_TX_P0_SHIFT 0
> +
> +#define MTK_DP_TOP_PWR_STATE (TOP_OFFSET + 0x00)
> +#define DP_PWR_STATE_MASK 0x3
> +#define DP_PWR_STATE_BANDGAP 1
> +#define DP_PWR_STATE_BANDGAP_TPLL 2
> +#define DP_PWR_STATE_BANDGAP_TPLL_LANE 3
> +
> +#define MTK_DP_TOP_SWING_EMP (TOP_OFFSET + 0x04)
> +#define DP_TX0_VOLT_SWING_MASK 0x3
> +#define DP_TX0_VOLT_SWING_SHIFT 0
> +#define DP_TX0_PRE_EMPH_MASK 0xc
> +#define DP_TX0_PRE_EMPH_SHIFT 2
> +#define DP_TX1_VOLT_SWING_MASK 0x300
> +#define DP_TX1_VOLT_SWING_SHIFT 8
> +#define DP_TX1_PRE_EMPH_MASK 0xc00
> +#define DP_TX2_VOLT_SWING_MASK 0x30000
> +#define DP_TX2_PRE_EMPH_MASK 0xc0000
> +#define DP_TX3_VOLT_SWING_MASK 0x3000000
> +#define DP_TX3_PRE_EMPH_MASK 0xc000000
> +
> +#define MTK_DP_TOP_RESET_AND_PROBE (TOP_OFFSET + 0x20)
> +#define SW_RST_B_SHIFT 0
> +#define SW_RST_B_PHYD (BIT(4) << SW_RST_B_SHIFT)
> +
> +#define MTK_DP_TOP_IRQ_STATUS (TOP_OFFSET + 0x28)
> +#define RGS_IRQ_STATUS_SHIFT 0
> +#define RGS_IRQ_STATUS_TRANSMITTER (BIT(1) << RGS_IRQ_STATUS_SHIFT)
> +
> +#define MTK_DP_TOP_IRQ_MASK (TOP_OFFSET + 0x2C)
> +#define IRQ_MASK_AUX_TOP_IRQ BIT(2)
> +
> +#define MTK_DP_TOP_MEM_PD (TOP_OFFSET + 0x38)
> +#define MEM_ISO_EN_SHIFT 0
> +#define FUSE_SEL_SHIFT 2
> +
> +#endif /*_MTK_DP_REG_H_*/
> diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> index 384074f69111b..e6e88e3cd811d 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
>  	MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
>  };
>  
> +enum TVDPLL_CLK {
> +	TVDPLL_PLL = 0,
> +	TVDPLL_D2 = 2,
> +	TVDPLL_D4 = 4,
> +	TVDPLL_D8 = 8,
> +	TVDPLL_D16 = 16,
> +};
> +
>  struct mtk_dpi {
>  	struct drm_encoder encoder;
>  	struct drm_bridge bridge;
> @@ -71,8 +79,10 @@ struct mtk_dpi {
>  	void __iomem *regs;
>  	struct device *dev;
>  	struct clk *engine_clk;
> +	struct clk *dpi_ck_cg;
>  	struct clk *pixel_clk;
>  	struct clk *tvd_clk;
> +	struct clk *pclk_src[5];
>  	int irq;
>  	struct drm_display_mode mode;
>  	const struct mtk_dpi_conf *conf;
> @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
>  	u32 hvsize_mask;
>  	u32 channel_swap_shift;
>  	u32 yuv422_en_bit;
> +	u32 csc_enable_bit;
>  	const struct mtk_dpi_yc_limit *limit;
>  };
>  
> @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
>  
>  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
>  {
> -	mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> +	mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
>  }
>  
>  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
>  		mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
>  }
>  
> +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> +{
> +	u32 matrix_sel = 0;
> +
> +	switch (format) {
> +	case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> +	case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> +	case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> +	case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> +	case MTK_DPI_COLOR_FORMAT_XV_YCC:
> +		if (dpi->mode.hdisplay <= 720)
> +			matrix_sel = 0x2;
> +		break;
> +	default:
> +		break;
> +	}
> +	mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> +}
> +
>  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
>  					enum mtk_dpi_out_color_format format)
>  {
>  	if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
>  	    (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
>  		mtk_dpi_config_yuv422_enable(dpi, false);
> -		if (dpi->conf->csc_support)
> +		if (dpi->conf->csc_support) {
>  			mtk_dpi_config_csc_enable(dpi, true);
> +			mtk_dpi_matrix_sel(dpi, format);
> +		}
>  		if (dpi->conf->swap_input_support)
>  			mtk_dpi_config_swap_input(dpi, false);
>  		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
>  	} else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
>  		   (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
>  		mtk_dpi_config_yuv422_enable(dpi, true);
> -		if (dpi->conf->csc_support)
> +		if (dpi->conf->csc_support) {
>  			mtk_dpi_config_csc_enable(dpi, true);
> +			mtk_dpi_matrix_sel(dpi, format);
> +		}
>  		if (dpi->conf->swap_input_support)
>  			mtk_dpi_config_swap_input(dpi, true);
>  		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
>  	mtk_dpi_disable(dpi);
>  	clk_disable_unprepare(dpi->pixel_clk);
>  	clk_disable_unprepare(dpi->engine_clk);
> +	clk_disable_unprepare(dpi->dpi_ck_cg);
> +	clk_disable_unprepare(dpi->tvd_clk);
>  }
>  
>  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
>  	if (++dpi->refcount != 1)
>  		return 0;
>  
> +	ret = clk_prepare_enable(dpi->tvd_clk);
> +	if (ret) {
> +		dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> +		goto err_pixel;
> +	}
> +
>  	ret = clk_prepare_enable(dpi->engine_clk);
>  	if (ret) {
>  		dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
>  		goto err_refcount;
>  	}
>  
> +	ret = clk_prepare_enable(dpi->dpi_ck_cg);
> +	if (ret) {
> +		dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> +		goto err_ck_cg;
> +	}
> +
>  	ret = clk_prepare_enable(dpi->pixel_clk);
>  	if (ret) {
>  		dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
>  	if (dpi->pinctrl && dpi->pins_dpi)
>  		pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
>  
> -	mtk_dpi_enable(dpi);
>  	return 0;
>  
>  err_pixel:
> +	clk_disable_unprepare(dpi->dpi_ck_cg);
> +err_ck_cg:
>  	clk_disable_unprepare(dpi->engine_clk);
>  err_refcount:
>  	dpi->refcount--;
> @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
>  	pll_rate = clk_get_rate(dpi->tvd_clk);
>  
>  	vm.pixelclock = pll_rate / factor;
> -	if (dpi->conf->is_dpintf)
> -		clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> -	else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> +	if (dpi->conf->is_dpintf) {
> +		if (factor == 1)
> +			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> +		else if (factor == 2)
> +			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> +		else if (factor == 4)
> +			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> +		else
> +			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> +	} else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
>  		 (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
>  		clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
>  	else
> @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
>  	}
>  	mtk_dpi_sw_reset(dpi, false);
>  
> +	mtk_dpi_enable(dpi);
> +
>  	return 0;
>  }
>  
> @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
>  	u32 *input_fmts;
>  
>  	*num_input_fmts = 0;
> -
>  	input_fmts = kcalloc(1, sizeof(*input_fmts),
>  			     GFP_KERNEL);
>  	if (!input_fmts)
> @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
>  		if (dpi->conf->num_output_fmts)
>  			out_bus_format = dpi->conf->output_fmts[0];
>  
> -	dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> +	dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
>  		bridge_state->input_bus_cfg.format,
>  		bridge_state->output_bus_cfg.format);
>  
> @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
>  	dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
>  	dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
>  	dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> -	dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> +	if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> +		dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> +	else
> +		dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
>  
>  	return 0;
>  }
> @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
>  	MEDIA_BUS_FMT_RGB888_2X12_BE,
>  };
>  
> +static const u32 mt8195_output_fmts[] = {
> +	MEDIA_BUS_FMT_RGB888_1X24,
> +	MEDIA_BUS_FMT_YUV8_1X24,
> +	MEDIA_BUS_FMT_YUYV8_1X16,
> +};
> +
>  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
>  	.c_bottom = 0x0010,
>  	.c_top = 0x0FE0,
> @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
>  	.hvsize_mask = HSIZE_MASK,
>  	.channel_swap_shift = CH_SWAP,
>  	.yuv422_en_bit = YUV422_EN,
> +	.csc_enable_bit = CSC_ENABLE,
>  	.limit = &mtk_dpi_limit,
>  };
>  
> @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
>  	.hvsize_mask = HSIZE_MASK,
>  	.channel_swap_shift = CH_SWAP,
>  	.yuv422_en_bit = YUV422_EN,
> +	.csc_enable_bit = CSC_ENABLE,
>  	.limit = &mtk_dpi_limit,
>  };
>  
> @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
>  	.hvsize_mask = HSIZE_MASK,
>  	.channel_swap_shift = CH_SWAP,
>  	.yuv422_en_bit = YUV422_EN,
> +	.csc_enable_bit = CSC_ENABLE,
>  	.limit = &mtk_dpi_limit,
>  };
>  
> @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
>  	.hvsize_mask = HSIZE_MASK,
>  	.channel_swap_shift = CH_SWAP,
>  	.yuv422_en_bit = YUV422_EN,
> +	.csc_enable_bit = CSC_ENABLE,
>  	.limit = &mtk_dpi_limit,
>  };
>  
>  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
>  	.cal_factor = mt8195_dpintf_calculate_factor,
> -	.output_fmts = mt8173_output_fmts,
> -	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> +	.output_fmts = mt8195_output_fmts,
> +	.num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
>  	.is_dpintf = true,
> +	.csc_support = true,
>  	.dimension_mask = DPINTF_HPW_MASK,
>  	.hvsize_mask = DPINTF_HSIZE_MASK,
>  	.channel_swap_shift = DPINTF_CH_SWAP,
>  	.yuv422_en_bit = DPINTF_YUV422_EN,
> +	.csc_enable_bit = DPINTF_CSC_ENABLE,
>  	.limit = &mtk_dpintf_limit,
>  };
>  
> @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
>  		return ret;
>  	}
>  
> +	dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> +	if (IS_ERR(dpi->dpi_ck_cg)) {
> +		ret = PTR_ERR(dpi->dpi_ck_cg);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> +				ret);
> +
> +		return ret;
> +	}
> +
>  	dpi->pixel_clk = devm_clk_get(dev, "pixel");
>  	if (IS_ERR(dpi->pixel_clk)) {
>  		ret = PTR_ERR(dpi->pixel_clk);
> @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
>  		return ret;
>  	}
>  
> +	dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> +	dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> +	dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> +	dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> +
>  	dpi->irq = platform_get_irq(pdev, 0);
>  	if (dpi->irq <= 0)
>  		return -EINVAL;

All those changes look unrelated as well?

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-15 10:11     ` Maxime Ripard
  0 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-11-15 10:11 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 134325 bytes --]

Hi,

On Wed, Nov 10, 2021 at 02:06:23PM +0100, Guillaume Ranquet wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> according phy driver mediatek-dp-phy.
> 
> It supports both functional units on the mt8195, the embedded
> DisplayPort as well as the external DisplayPort units. It offers
> hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> to 4 lanes.

There's a number of checkpatch --strict errors in there, make sure to fix them.

> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.

If the PHY is in the same register space than the DP controller, why do
you need a separate PHY driver in the first place?

> This driver is based on an initial version by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reported-by: kernel test robot <lkp@intel.com>

> ---
>  drivers/gpu/drm/drm_edid.c              |    2 +-
>  drivers/gpu/drm/mediatek/Kconfig        |    7 +
>  drivers/gpu/drm/mediatek/Makefile       |    2 +
>  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
>  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
>  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
>  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
>  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
>  9 files changed, 3799 insertions(+), 13 deletions(-)
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> 
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 500279a82167a..bfd98b50ceb5b 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
>  	 * modes and forbids YCRCB422 support for all video modes per
>  	 * HDMI 1.3 spec.
>  	 */
> -	info->color_formats = DRM_COLOR_FORMAT_RGB444;
> +	info->color_formats |= DRM_COLOR_FORMAT_RGB444;
>  
>  	/* YCRCB444 is optional according to spec. */
>  	if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {

This looks unrelated?

> diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> index 2976d21e9a34a..029b94c716131 100644
> --- a/drivers/gpu/drm/mediatek/Kconfig
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
>  	select PHY_MTK_HDMI
>  	help
>  	  DRM/KMS HDMI driver for Mediatek SoCs
> +
> +config MTK_DPTX_SUPPORT
> +	tristate "DRM DPTX Support for Mediatek SoCs"
> +	depends on DRM_MEDIATEK
> +	select PHY_MTK_DP
> +	help
> +	  DRM/KMS Display Port driver for Mediatek SoCs.
> diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> index 29098d7c8307c..d86a6406055e6 100644
> --- a/drivers/gpu/drm/mediatek/Makefile
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
>  			  mtk_hdmi_ddc.o
>  
>  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> +
> +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> new file mode 100644
> index 0000000000000..83087219d5a5e
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -0,0 +1,3094 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019 MediaTek Inc.
> + * Copyright (c) 2021 BayLibre
> + */
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_dp_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <linux/arm-smccc.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <sound/hdmi-codec.h>
> +#include <video/videomode.h>
> +
> +#include "mtk_dp_reg.h"
> +
> +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> +
> +#define MTK_DP_MAX_LANES 4
> +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> +
> +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> +
> +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> +
> +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> +
> +#define MTK_DP_DP_VERSION_11 0x11
> +
> +enum mtk_dp_state {
> +	MTK_DP_STATE_INITIAL,
> +	MTK_DP_STATE_IDLE,
> +	MTK_DP_STATE_PREPARE,
> +	MTK_DP_STATE_NORMAL,
> +};
> +
> +enum mtk_dp_train_state {
> +	MTK_DP_TRAIN_STATE_STARTUP = 0,
> +	MTK_DP_TRAIN_STATE_CHECKCAP,
> +	MTK_DP_TRAIN_STATE_CHECKEDID,
> +	MTK_DP_TRAIN_STATE_TRAINING_PRE,
> +	MTK_DP_TRAIN_STATE_TRAINING,
> +	MTK_DP_TRAIN_STATE_CHECKTIMING,
> +	MTK_DP_TRAIN_STATE_NORMAL,
> +	MTK_DP_TRAIN_STATE_POWERSAVE,
> +	MTK_DP_TRAIN_STATE_DPIDLE,
> +};
> +
> +struct mtk_dp_timings {
> +	struct videomode vm;
> +
> +	u16 htotal;
> +	u16 vtotal;
> +	u8 frame_rate;
> +	u32 pix_rate_khz;
> +};
> +
> +struct mtk_dp_train_info {
> +	bool tps3;
> +	bool tps4;
> +	bool sink_ssc;
> +	bool cable_plugged_in;
> +	bool cable_state_change;
> +	bool cr_done;
> +	bool eq_done;
> +
> +	// link_rate is in multiple of 0.27Gbps
> +	int link_rate;
> +	int lane_count;
> +
> +	int irq_status;
> +	int check_cap_count;
> +};
> +
> +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> +enum mtk_dp_color_format {
> +	MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> +	MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> +	MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> +	MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> +	MTK_DP_COLOR_FORMAT_YONLY = 4,
> +	MTK_DP_COLOR_FORMAT_RAW = 5,
> +	MTK_DP_COLOR_FORMAT_RESERVED = 6,
> +	MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> +	MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> +};
> +
> +// Multiple of 0.27Gbps
> +enum mtk_dp_linkrate {
> +	MTK_DP_LINKRATE_RBR = 0x6,
> +	MTK_DP_LINKRATE_HBR = 0xA,
> +	MTK_DP_LINKRATE_HBR2 = 0x14,
> +	MTK_DP_LINKRATE_HBR25 = 0x19,
> +	MTK_DP_LINKRATE_HBR3 = 0x1E,
> +};
> +
> +// Same values as used for DP Spec MISC0 bits 5,6,7
> +enum mtk_dp_color_depth {
> +	MTK_DP_COLOR_DEPTH_6BIT = 0,
> +	MTK_DP_COLOR_DEPTH_8BIT = 1,
> +	MTK_DP_COLOR_DEPTH_10BIT = 2,
> +	MTK_DP_COLOR_DEPTH_12BIT = 3,
> +	MTK_DP_COLOR_DEPTH_16BIT = 4,
> +	MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> +};
> +
> +struct mtk_dp_audio_cfg {
> +	int sample_rate;
> +	int word_length_bits;
> +	int channels;
> +};
> +
> +struct mtk_dp_info {
> +	enum mtk_dp_color_depth depth;
> +	enum mtk_dp_color_format format;
> +	struct mtk_dp_audio_cfg audio_caps;
> +	struct mtk_dp_timings timings;
> +};
> +
> +struct dp_cal_data {
> +	unsigned int glb_bias_trim;
> +	unsigned int clktx_impse;
> +
> +	unsigned int ln0_tx_impsel_pmos;
> +	unsigned int ln0_tx_impsel_nmos;
> +	unsigned int ln1_tx_impsel_pmos;
> +	unsigned int ln1_tx_impsel_nmos;
> +	unsigned int ln2_tx_impsel_pmos;
> +	unsigned int ln2_tx_impsel_nmos;
> +	unsigned int ln3_tx_impsel_pmos;
> +	unsigned int ln3_tx_impsel_nmos;
> +};
> +
> +struct mtk_dp {
> +	struct device *dev;
> +	struct platform_device *phy_dev;
> +	struct phy *phy;
> +	struct dp_cal_data cal_data;
> +
> +	struct drm_device *drm_dev;
> +	struct drm_bridge bridge;
> +	struct drm_bridge *next_bridge;
> +	struct drm_dp_aux aux;
> +
> +	struct mutex edid_lock;
> +	struct edid *edid;
> +
> +	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> +
> +	struct mtk_dp_info info;
> +	enum mtk_dp_state state;
> +
> +	struct mtk_dp_train_info train_info;
> +	enum mtk_dp_train_state train_state;
> +	unsigned int input_fmt;
> +
> +	struct regmap *regs;
> +	struct clk *dp_tx_clk;
> +
> +	bool enabled;
> +	bool audio_enable;
> +
> +	bool has_fec;
> +	struct mutex dp_lock;
> +
> +	struct mutex update_plugged_status_lock;
> +
> +	hdmi_codec_plugged_cb plugged_cb;
> +	struct device *codec_dev;
> +	u8 connector_eld[MAX_ELD_BYTES];
> +};
> +
> +enum mtk_dp_sdp_type {
> +	MTK_DP_SDP_NONE = 0x00,
> +	MTK_DP_SDP_ACM = 0x01,
> +	MTK_DP_SDP_ISRC = 0x02,
> +	MTK_DP_SDP_AVI = 0x03,
> +	MTK_DP_SDP_AUI = 0x04,
> +	MTK_DP_SDP_SPD = 0x05,
> +	MTK_DP_SDP_MPEG = 0x06,
> +	MTK_DP_SDP_NTSC = 0x07,
> +	MTK_DP_SDP_VSP = 0x08,
> +	MTK_DP_SDP_VSC = 0x09,
> +	MTK_DP_SDP_EXT = 0x0A,
> +	MTK_DP_SDP_PPS0 = 0x0B,
> +	MTK_DP_SDP_PPS1 = 0x0C,
> +	MTK_DP_SDP_PPS2 = 0x0D,
> +	MTK_DP_SDP_PPS3 = 0x0E,
> +	MTK_DP_SDP_DRM = 0x10,
> +	MTK_DP_SDP_MAX_NUM
> +};
> +
> +struct mtk_dp_sdp_packet {
> +	enum mtk_dp_sdp_type type;
> +	struct dp_sdp sdp;
> +};
> +
> +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> +union mtk_dp_audio_channel_status {
> +	struct {
> +		u8 rev : 1;
> +		u8 is_lpcm : 1;
> +		u8 copy_right : 1;
> +		u8 additional_format_info : 3;
> +		u8 channel_status_mode : 2;
> +		u8 category_code;
> +		u8 src_num : 4;
> +		u8 channel_num : 4;
> +		u8 sampling_freq : 4;
> +		u8 clk_accuracy : 2;
> +		u8 rev2 : 2;
> +		u8 word_len : 4;
> +		u8 original_sampling_freq : 4;
> +	} iec;
> +
> +	u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> +};

Why don't you leverage hdmi_codec_params and snd_aes_iec958?

> +static struct regmap_config mtk_dp_regmap_config = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +	.max_register = SEC_OFFSET + 0x90,
> +	.name = "mtk-dp-registers",
> +};
> +
> +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
> +{
> +	return mtk_dp->next_bridge != NULL;
> +}
> +
> +static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
> +{
> +	return container_of(b, struct mtk_dp, bridge);
> +}
> +
> +static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
> +{
> +	u32 read_val;
> +	int ret;
> +
> +	ret = regmap_read(mtk_dp->regs, offset, &read_val);
> +	if (ret) {
> +		dev_err(mtk_dp->dev, "Failed to read register 0x%x: %d\n",
> +			offset, ret);
> +		return 0;
> +	}
> +
> +	return read_val;
> +}
> +
> +static void mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
> +{
> +	int ret;
> +
> +	ret = regmap_write(mtk_dp->regs, offset, val);
> +	if (ret)
> +		dev_err(mtk_dp->dev,
> +			"Failed to write register 0x%x with value 0x%x: %d\n",
> +			offset, val, ret);
> +}
> +
> +static void mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, u32 val,
> +			       u32 mask)
> +{
> +	int ret;
> +
> +	ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
> +	if (ret)
> +		dev_err(mtk_dp->dev,
> +			"Failed to update register 0x%x with value 0x%x, mask 0x%x: %d\n",
> +			offset, val, mask, ret);
> +}
> +
> +static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32 offset, u8 *buf,
> +				    size_t length)
> +{
> +	int i;
> +	int num_regs = (length + 1) / 2;
> +
> +	// 2 bytes per register
> +	for (i = 0; i < num_regs; i++) {
> +		u32 val = buf[i * 2] |
> +			  (i * 2 + 1 < length ? buf[i * 2 + 1] << 8 : 0);
> +
> +		mtk_dp_write(mtk_dp, offset + i * 4, val);
> +	}
> +}
> +
> +static unsigned long mtk_dp_sip_atf_call(unsigned int cmd, unsigned int para)
> +{
> +	struct arm_smccc_res res;
> +
> +	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32, cmd, para, 0, 0, 0, 0, 0,
> +		      &res);
> +
> +	pr_debug("[DPTX]%s cmd 0x%x, p1 0x%x, ret 0x%lx-0x%lx", __func__, cmd,
> +		 para, res.a0, res.a1);
> +	return res.a1;
> +}
> +
> +static void mtk_dp_msa_bypass_disable(struct mtk_dp *mtk_dp)
> +{
> +	const u16 bits_to_set =
> +		BIT(HTOTAL_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(VTOTAL_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(HSTART_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(VSTART_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(HWIDTH_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(VHEIGHT_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(HSP_SEL_DP_ENC0_P0_SHIFT) | BIT(HSW_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(VSP_SEL_DP_ENC0_P0_SHIFT) | BIT(VSW_SEL_DP_ENC0_P0_SHIFT);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, bits_to_set,
> +			   bits_to_set);
> +}
> +
> +static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
> +{
> +	struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010, timings->htotal,
> +			   HTOTAL_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
> +			   timings->vm.hsync_len + timings->vm.hback_porch,
> +			   HSTART_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> +			   timings->vm.hsync_len << HSW_SW_DP_ENC0_P0_SHIFT,
> +			   HSW_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028, 0,
> +			   HSP_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020, timings->vm.hactive,
> +			   HWIDTH_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014, timings->vtotal,
> +			   VTOTAL_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
> +			   timings->vm.vsync_len + timings->vm.vback_porch,
> +			   VSTART_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> +			   timings->vm.vsync_len << VSW_SW_DP_ENC0_P0_SHIFT,
> +			   VSW_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C, 0,
> +			   VSP_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024, timings->vm.vactive,
> +			   VHEIGHT_SW_DP_ENC0_P0_MASK);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064, timings->vm.hactive,
> +			   HDE_NUM_LAST_DP_ENC0_P0_MASK);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154, timings->htotal,
> +			   PGEN_HTOTAL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
> +			   timings->vm.hfront_porch,
> +			   PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C, timings->vm.hsync_len,
> +			   PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
> +			   timings->vm.hback_porch + timings->vm.hsync_len,
> +			   PGEN_HFDE_START_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164, timings->vm.hactive,
> +			   PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168, timings->vtotal,
> +			   PGEN_VTOTAL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
> +			   timings->vm.vfront_porch,
> +			   PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170, timings->vm.vsync_len,
> +			   PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
> +			   timings->vm.vback_porch + timings->vm.vsync_len,
> +			   PGEN_VFDE_START_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178, timings->vm.vactive,
> +			   PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
> +				    enum mtk_dp_color_format color_format)
> +{
> +	u32 val;
> +
> +	mtk_dp->info.format = color_format;
> +
> +	// Update MISC0
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +			   color_format << DP_TEST_COLOR_FORMAT_SHIFT,
> +			   DP_TEST_COLOR_FORMAT_MASK);
> +
> +	switch (color_format) {
> +	case MTK_DP_COLOR_FORMAT_YUV_422:
> +		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
> +		break;
> +	case MTK_DP_COLOR_FORMAT_YUV_420:
> +		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420;
> +		break;
> +	case MTK_DP_COLOR_FORMAT_YONLY:
> +	case MTK_DP_COLOR_FORMAT_RAW:
> +	case MTK_DP_COLOR_FORMAT_RESERVED:
> +	case MTK_DP_COLOR_FORMAT_UNKNOWN:
> +		drm_warn(mtk_dp->drm_dev, "Unsupported color format: %d\n",
> +			 color_format);
> +		fallthrough;
> +	case MTK_DP_COLOR_FORMAT_RGB_444:
> +	case MTK_DP_COLOR_FORMAT_YUV_444:
> +		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
> +		break;
> +	}
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
> +			   PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp,
> +				   enum mtk_dp_color_depth color_depth)
> +{
> +	u32 val;
> +
> +	mtk_dp->info.depth = color_depth;
> +
> +	// Update MISC0
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +			   color_depth << DP_TEST_BIT_DEPTH_SHIFT,
> +			   DP_TEST_BIT_DEPTH_MASK);
> +
> +	switch (color_depth) {
> +	case MTK_DP_COLOR_DEPTH_6BIT:
> +		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT;
> +		break;
> +	case MTK_DP_COLOR_DEPTH_8BIT:
> +		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT;
> +		break;
> +	case MTK_DP_COLOR_DEPTH_10BIT:
> +		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT;
> +		break;
> +	case MTK_DP_COLOR_DEPTH_12BIT:
> +		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT;
> +		break;
> +	case MTK_DP_COLOR_DEPTH_16BIT:
> +		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT;
> +		break;
> +	case MTK_DP_COLOR_DEPTH_UNKNOWN:
> +		drm_warn(mtk_dp->drm_dev, "Unsupported color depth %d\n",
> +			 color_depth);
> +		return;
> +	}
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
> +			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_mn_overwrite_disable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
> +			   VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32 val)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   val << SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT,
> +			   SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   BIT(VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT),
> +			   VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> +			   0x20 << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
> +			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +			   0x20 << SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT,
> +			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
> +			   2 << VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT,
> +			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +			   4 << FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT,
> +			   FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
> +	mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368,
> +		     1 << VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT |
> +			     1 << VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT |
> +			     BIT(SDP_DP13_EN_DP_ENC1_P0_SHIFT) |
> +			     1 << BS2BS_MODE_DP_ENC1_P0_SHIFT);
> +}
> +
> +static void mtk_dp_pg_disable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038, 0,
> +			   VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
> +			   4 << PGEN_PATTERN_SEL_SHIFT, PGEN_PATTERN_SEL_MASK);
> +}
> +
> +static void mtk_dp_audio_setup_channels(struct mtk_dp *mtk_dp,
> +					struct mtk_dp_audio_cfg *cfg)
> +{
> +	u32 channel_enable_bits;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3324,
> +			   AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX,
> +			   AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK);
> +
> +	//audio channel count change reset
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(9), BIT(9));
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> +			   AU_PRTY_REGEN_DP_ENC1_P0_MASK,
> +			   AU_PRTY_REGEN_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> +			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK,
> +			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> +			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK,
> +			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK);
> +
> +	switch (cfg->channels) {
> +	case 2:
> +		channel_enable_bits = AUDIO_2CH_SEL_DP_ENC0_P0_MASK |
> +				      AUDIO_2CH_EN_DP_ENC0_P0_MASK;
> +		break;
> +	case 8:
> +	default:
> +		channel_enable_bits = AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
> +				      AUDIO_8CH_EN_DP_ENC0_P0_MASK;
> +		break;
> +	}
> +	mtk_dp_update_bits(
> +		mtk_dp, MTK_DP_ENC0_P0_3088,
> +		channel_enable_bits | AU_EN_DP_ENC0_P0_MASK,
> +		AUDIO_2CH_SEL_DP_ENC0_P0_MASK | AUDIO_2CH_EN_DP_ENC0_P0_MASK |
> +			AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
> +			AUDIO_8CH_EN_DP_ENC0_P0_MASK | AU_EN_DP_ENC0_P0_MASK);
> +
> +	//audio channel count change reset
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, 0, BIT(9));
> +
> +	//enable audio reset
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(0), BIT(0));
> +}
> +
> +static void mtk_dp_audio_channel_status_set(struct mtk_dp *mtk_dp,
> +					    struct mtk_dp_audio_cfg *cfg)
> +{
> +	union mtk_dp_audio_channel_status channel_status;
> +
> +	memset(&channel_status, 0, sizeof(channel_status));
> +
> +	switch (cfg->sample_rate) {
> +	case 32000:
> +		channel_status.iec.sampling_freq = 3;
> +		break;
> +	case 44100:
> +		channel_status.iec.sampling_freq = 0;
> +		break;
> +	case 48000:
> +		channel_status.iec.sampling_freq = 2;
> +		break;
> +	case 88200:
> +		channel_status.iec.sampling_freq = 8;
> +		break;
> +	case 96000:
> +		channel_status.iec.sampling_freq = 0xA;
> +		break;
> +	case 192000:
> +		channel_status.iec.sampling_freq = 0xE;
> +		break;
> +	default:
> +		channel_status.iec.sampling_freq = 0x1;
> +		break;
> +	}
> +
> +	switch (cfg->word_length_bits) {
> +	case 16:
> +		channel_status.iec.word_len = 0x02;
> +		break;
> +	case 20:
> +		channel_status.iec.word_len = 0x03;
> +		break;
> +	case 24:
> +		channel_status.iec.word_len = 0x0B;
> +		break;
> +	}

Yeah, this is provided by hdmi-codec through hdmi_codec_params in
prepare.

> +	// IEC 60958 consumer channel status bits
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_308C,
> +			   channel_status.buf[1] << 8 | channel_status.buf[0],
> +			   CH_STATUS_0_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3090,
> +			   channel_status.buf[3] << 8 | channel_status.buf[2],
> +			   CH_STATUS_1_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3094, channel_status.buf[4],
> +			   CH_STATUS_2_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_audio_sdp_asp_set_channels(struct mtk_dp *mtk_dp,
> +					      int channels)
> +{
> +	if (channels != 2 && channels != 8)
> +		channels = 8;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_312C,
> +			   (channels - 1) << ASP_HB3_DP_ENC0_P0_SHIFT,
> +			   ASP_HB2_DP_ENC0_P0_MASK | ASP_HB3_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_audio_set_divider(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
> +			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2,
> +			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK);
> +}
> +
> +static bool mtk_dp_plug_state(struct mtk_dp *mtk_dp)
> +{
> +	return !!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
> +		  HPD_DB_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_sdp_trigger_packet(struct mtk_dp *mtk_dp,
> +				      enum mtk_dp_sdp_type type)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, type,
> +			   SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, SDP_PACKET_W_DP_ENC1_P0,
> +			   SDP_PACKET_W_DP_ENC1_P0);
> +}
> +
> +static void mtk_dp_sdp_set_data(struct mtk_dp *mtk_dp, u8 *data_bytes)
> +{
> +	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_ENC1_P0_3200, data_bytes, 0x10);
> +}
> +
> +static void mtk_dp_sdp_set_header(struct mtk_dp *mtk_dp,
> +				  enum mtk_dp_sdp_type type,
> +				  struct dp_sdp_header *header)
> +{
> +	u32 db_addr;
> +
> +	switch (type) {
> +	case MTK_DP_SDP_DRM:
> +		db_addr = MTK_DP_ENC0_P0_3138;
> +		break;
> +	case MTK_DP_SDP_PPS0:
> +	case MTK_DP_SDP_PPS1:
> +	case MTK_DP_SDP_PPS2:
> +	case MTK_DP_SDP_PPS3:
> +		db_addr = MTK_DP_ENC0_P0_3130;
> +		break;
> +	default:
> +		db_addr = MTK_DP_ENC0_P0_30D8 + (type - MTK_DP_SDP_ACM) * 8;
> +	}
> +
> +	mtk_dp_bulk_16bit_write(mtk_dp, db_addr, (u8 *)header, 4);
> +}
> +
> +static const u32 mtk_dp_sdp_type_to_reg[MTK_DP_SDP_MAX_NUM] = {
> +	/* MTK_DP_SDP_NONE => */ 0x0,
> +	/* MTK_DP_SDP_ACM  => */ MTK_DP_ENC0_P0_30B4,
> +	/* MTK_DP_SDP_ISRC => */ MTK_DP_ENC0_P0_30B4 + 1,
> +	/* MTK_DP_SDP_AVI  => */ MTK_DP_ENC0_P0_30A4 + 1,
> +	/* MTK_DP_SDP_AUI  => */ MTK_DP_ENC0_P0_30A8,
> +	/* MTK_DP_SDP_SPD  => */ MTK_DP_ENC0_P0_30A8 + 1,
> +	/* MTK_DP_SDP_MPEG => */ MTK_DP_ENC0_P0_30AC,
> +	/* MTK_DP_SDP_NTSC => */ MTK_DP_ENC0_P0_30AC + 1,
> +	/* MTK_DP_SDP_VSP  => */ MTK_DP_ENC0_P0_30B0,
> +	/* MTK_DP_SDP_VSC  => */ MTK_DP_ENC0_P0_30B8,
> +	/* MTK_DP_SDP_EXT  => */ MTK_DP_ENC0_P0_30B0 + 1,
> +	/* MTK_DP_SDP_PPS0 => */ MTK_DP_ENC0_P0_31E8,
> +	/* MTK_DP_SDP_PPS1 => */ MTK_DP_ENC0_P0_31E8,
> +	/* MTK_DP_SDP_PPS2 => */ MTK_DP_ENC0_P0_31E8,
> +	/* MTK_DP_SDP_PPS3 => */ MTK_DP_ENC0_P0_31E8,
> +	/* MTK_DP_SDP_DRM  => */ MTK_DP_ENC0_P0_31DC,
> +};
> +
> +static void mtk_dp_disable_sdp(struct mtk_dp *mtk_dp, enum mtk_dp_sdp_type type)
> +{
> +	if (type == MTK_DP_SDP_NONE)
> +		return;
> +
> +	// Disable periodic send
> +	mtk_dp_update_bits(mtk_dp, mtk_dp_sdp_type_to_reg[type] & 0xfffc, 0,
> +			   0xFF << ((mtk_dp_sdp_type_to_reg[type] & 3) * 8));
> +}
> +
> +static void mtk_dp_setup_sdp(struct mtk_dp *mtk_dp,
> +			     struct mtk_dp_sdp_packet *packet)
> +{
> +	mtk_dp_sdp_set_data(mtk_dp, packet->sdp.db);
> +	mtk_dp_sdp_set_header(mtk_dp, packet->type, &packet->sdp.sdp_header);
> +
> +	mtk_dp_disable_sdp(mtk_dp, packet->type);
> +
> +	switch (packet->type) {
> +	case MTK_DP_SDP_NONE:
> +		break;
> +	case MTK_DP_SDP_ISRC:
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> +				   0x1C << ISRC1_HB3_DP_ENC0_P0_SHIFT,
> +				   ISRC1_HB3_DP_ENC0_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, MTK_DP_SDP_ISRC,
> +				   SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
> +
> +		if (packet->sdp.sdp_header.HB3 & BIT(2))
> +			mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
> +					   BIT(ISRC_CONT_DP_ENC0_P0_SHIFT),
> +					   ISRC_CONT_DP_ENC0_P0_MASK);
> +		else
> +			mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC, 0,
> +					   ISRC_CONT_DP_ENC0_P0_MASK);
> +
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280,
> +				   SDP_PACKET_W_DP_ENC1_P0,
> +				   SDP_PACKET_W_DP_ENC1_P0);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30B4,
> +				   5 << ISRC_CFG_DP_ENC0_P0_SHIFT,
> +				   ISRC_CFG_DP_ENC0_P0_MASK);
> +		break;
> +	case MTK_DP_SDP_DRM:
> +		mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31DC,
> +				   5 << HDR0_CFG_DP_ENC0_P0_SHIFT,
> +				   HDR0_CFG_DP_ENC0_P0_MASK);
> +		break;
> +	case MTK_DP_SDP_ACM:
> +	case MTK_DP_SDP_AVI:
> +	case MTK_DP_SDP_AUI:
> +	case MTK_DP_SDP_SPD:
> +	case MTK_DP_SDP_MPEG:
> +	case MTK_DP_SDP_NTSC:
> +	case MTK_DP_SDP_VSP:
> +	case MTK_DP_SDP_VSC:
> +	case MTK_DP_SDP_EXT:
> +	case MTK_DP_SDP_PPS0:
> +	case MTK_DP_SDP_PPS1:
> +	case MTK_DP_SDP_PPS2:
> +	case MTK_DP_SDP_PPS3:
> +		mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
> +		// Enable periodic sending
> +		mtk_dp_update_bits(
> +			mtk_dp, mtk_dp_sdp_type_to_reg[packet->type] & 0xfffc,
> +			0x05 << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
> +				 8),
> +			0xff << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
> +				 8));
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static void mtk_dp_sdp_vsc_ext_disable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A0, 0,
> +			   BIT(7) | BIT(8) | BIT(12));
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_328C, 0, BIT(7));
> +}
> +
> +static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_write(
> +		mtk_dp, MTK_DP_AUX_P0_3640,
> +		BIT(AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT));
> +}
> +
> +static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32 addr)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644, cmd,
> +			   MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648, addr,
> +			   MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C, addr >> 16,
> +			   MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_cmd_complete(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +			   BIT(MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT),
> +			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK |
> +				   PHY_FIFO_RST_AUX_TX_P0_MASK |
> +				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
> +			   BIT(AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT),
> +			   AUX_TX_REQUEST_READY_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> +				       size_t length)
> +{
> +	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf, length);
> +}
> +
> +static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> +				    size_t length, int read_delay)
> +{
> +	int read_pos;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620, 0,
> +			   AUX_RD_MODE_AUX_TX_P0_MASK);
> +
> +	for (read_pos = 0; read_pos < length; read_pos++) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> +				   BIT(AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT),
> +				   AUX_RX_FIFO_READ_PULSE_TX_P0_MASK);
> +		usleep_range(read_delay, read_delay * 2);
> +		buf[read_pos] =
> +			(u8)(mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3620) &
> +			     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK >>
> +				     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT);
> +	}
> +}
> +
> +static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t length)
> +{
> +	if (length > 0) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +				   (length - 1)
> +					   << MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT,
> +				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
> +				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
> +					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	} else {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +				   BIT(AUX_NO_LENGTH_AUX_TX_P0_SHIFT),
> +				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
> +					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	}
> +}
> +
> +static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp, bool is_read)
> +{
> +	int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
> +
> +	while (--wait_reply) {
> +		u32 aux_irq_status;
> +
> +		if (is_read) {
> +			u32 fifo_status =
> +				mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3618);
> +
> +			if (fifo_status &
> +			    (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
> +			     AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
> +				return 0;
> +			}
> +		}
> +
> +		aux_irq_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3640);
> +		if (aux_irq_status & AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK)
> +			return 0;
> +
> +		if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK)
> +			return -ETIMEDOUT;
> +
> +		usleep_range(100, 500);
> +	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool is_read, u8 cmd,
> +				  u32 addr, u8 *buf, size_t length)
> +{
> +	int ret;
> +	u32 reply_cmd;
> +
> +	if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
> +			(cmd == DP_AUX_NATIVE_READ && !length)))
> +		return -EINVAL;
> +
> +	if (!is_read)
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> +				   BIT(AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT),
> +				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK);
> +
> +	mtk_dp_aux_cmd_complete(mtk_dp);
> +	mtk_dp_aux_irq_clear(mtk_dp);
> +	usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> +		     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> +
> +	mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
> +	mtk_dp_aux_set_length(mtk_dp, length);
> +
> +	if (!is_read) {
> +		if (length)
> +			mtk_dp_aux_fill_write_fifo(mtk_dp, buf, length);
> +
> +		mtk_dp_update_bits(
> +			mtk_dp, MTK_DP_AUX_P0_3704,
> +			AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK,
> +			AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK);
> +	}
> +
> +	mtk_dp_aux_request_ready(mtk_dp);
> +
> +	ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
> +
> +	reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
> +		    AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
> +
> +	if (ret || reply_cmd) {
> +		u32 phy_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3628) &
> +				 AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
> +		if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
> +			drm_err(mtk_dp->drm_dev,
> +				"AUX Rx Aux hang, need SW reset\n");
> +			ret = -EIO;
> +		}
> +
> +		mtk_dp_aux_cmd_complete(mtk_dp);
> +		mtk_dp_aux_irq_clear(mtk_dp);
> +
> +		usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> +			     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> +		return -ETIMEDOUT;
> +	}
> +
> +	if (!length) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
> +				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
> +					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	} else if (is_read) {
> +		int read_delay;
> +
> +		if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
> +		    cmd == DP_AUX_I2C_READ)
> +			read_delay = 500;
> +		else
> +			read_delay = 100;
> +
> +		mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length, read_delay);
> +	}
> +
> +	mtk_dp_aux_cmd_complete(mtk_dp);
> +	mtk_dp_aux_irq_clear(mtk_dp);
> +	usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> +		     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> +
> +	return 0;
> +}
> +
> +static bool mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int lane_num,
> +					  int swing_val, int preemphasis)
> +{
> +	u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
> +
> +	if (lane_num < 0 || lane_num > 3)
> +		return false;
> +
> +	dev_dbg(mtk_dp->dev,
> +		"link training swing_val= 0x%x, preemphasis = 0x%x\n",
> +		swing_val, preemphasis);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +			   swing_val << (DP_TX0_VOLT_SWING_SHIFT + lane_shift),
> +			   DP_TX0_VOLT_SWING_MASK << lane_shift);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +			   preemphasis << (DP_TX0_PRE_EMPH_SHIFT + lane_shift),
> +			   DP_TX0_PRE_EMPH_MASK << lane_shift);
> +
> +	return true;
> +}
> +
> +static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP, 0,
> +			   DP_TX0_VOLT_SWING_MASK | DP_TX1_VOLT_SWING_MASK |
> +				   DP_TX2_VOLT_SWING_MASK |
> +				   DP_TX3_VOLT_SWING_MASK |
> +				   DP_TX0_PRE_EMPH_MASK | DP_TX1_PRE_EMPH_MASK |
> +				   DP_TX2_PRE_EMPH_MASK | DP_TX3_PRE_EMPH_MASK);
> +}
> +
> +static void mtk_dp_fec_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> +			   enable ? BIT(FEC_EN_DP_TRANS_P0_SHIFT) : 0,
> +			   FEC_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +	u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
> +			 SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
> +
> +	if (irq_status) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, irq_status,
> +				   SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, 0,
> +				   SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +	}
> +
> +	return irq_status;
> +}
> +
> +static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +	u8 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
> +			 IRQ_STATUS_DP_TRANS_P0_MASK) >>
> +			IRQ_STATUS_DP_TRANS_P0_SHIFT;
> +
> +	if (irq_status) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, irq_status,
> +				   IRQ_CLR_DP_TRANS_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, 0,
> +				   IRQ_CLR_DP_TRANS_P0_MASK);
> +	}
> +
> +	return irq_status;
> +}
> +
> +static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	u32 val = 0;
> +
> +	if (!enable)
> +		val = IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
> +		      IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
> +		      IRQ_MASK_DP_TRANS_P0_INT_IRQ;
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, val,
> +			   IRQ_MASK_DP_TRANS_P0_MASK);
> +}
> +
> +void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
> +			   XTAL_FREQ_DP_TRANS_P0_DEFAULT,
> +			   XTAL_FREQ_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> +			   BIT(FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT),
> +			   FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> +			   BIT(AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT),
> +			   AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
> +			   SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK, IRQ_MASK_AUX_TOP_IRQ,
> +			   IRQ_MASK_AUX_TOP_IRQ);
> +}
> +
> +static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp *mtk_dp)
> +{
> +	// Debounce threshold
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   8 << HPD_DEB_THD_DP_TRANS_P0_SHIFT,
> +			   HPD_DEB_THD_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
> +			    HPD_INT_THD_DP_TRANS_P0_UPPER_1100US)
> +				   << HPD_INT_THD_DP_TRANS_P0_SHIFT,
> +			   HPD_INT_THD_DP_TRANS_P0_MASK);
> +
> +	// Connect threshold 1.5ms + 5 x 0.1ms = 2ms
> +	// Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   (5 << HPD_DISC_THD_DP_TRANS_P0_SHIFT) |
> +				   (5 << HPD_CONN_THD_DP_TRANS_P0_SHIFT),
> +			   HPD_DISC_THD_DP_TRANS_P0_MASK |
> +				   HPD_CONN_THD_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
> +			   HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
> +			   HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
> +{
> +	// modify timeout threshold = 1595 [12 : 8]
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C, 0x1595,
> +			   AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658, 0,
> +			   AUX_TX_OV_EN_AUX_TX_P0_MASK);
> +	// 25 for 26M
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
> +			   25 << AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT,
> +			   AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
> +	// 13 for 26M
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
> +			   13 << AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT,
> +			   AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
> +			   BIT(MTK_ATOP_EN_AUX_TX_P0_SHIFT),
> +			   MTK_ATOP_EN_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_digital_settings(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
> +			   VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
> +	mtk_dp_set_color_format(mtk_dp, MTK_DP_COLOR_FORMAT_RGB_444);
> +	mtk_dp_set_color_depth(mtk_dp, MTK_DP_COLOR_DEPTH_8BIT);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
> +			   1 << BS2BS_MODE_DP_ENC1_P0_SHIFT,
> +			   BS2BS_MODE_DP_ENC1_P0_MASK);
> +
> +	// dp tx encoder reset all sw
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +			   BIT(DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT),
> +			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
> +	usleep_range(1000, 5000);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
> +			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> +			   BIT(DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT),
> +			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
> +	usleep_range(1000, 5000);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C, 0,
> +			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
> +			   lanes == 0 ? 0 : BIT(3), BIT(3) | BIT(2));
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, lanes,
> +			   LANE_NUM_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
> +			   lanes << LANE_NUM_DP_TRANS_P0_SHIFT,
> +			   LANE_NUM_DP_TRANS_P0_MASK);
> +}
> +
> +static int link_rate_to_mb_per_s(struct mtk_dp *mtk_dp,
> +				 enum mtk_dp_linkrate linkrate)
> +{
> +	switch (linkrate) {
> +	default:
> +		drm_err(mtk_dp->drm_dev,
> +			"Implementation error, unknown linkrate %d\n",
> +			linkrate);
> +		fallthrough;
> +	case MTK_DP_LINKRATE_RBR:
> +		return 1620;
> +	case MTK_DP_LINKRATE_HBR:
> +		return 2700;
> +	case MTK_DP_LINKRATE_HBR2:
> +		return 5400;
> +	case MTK_DP_LINKRATE_HBR3:
> +		return 8100;
> +	}
> +}
> +
> +static u32 check_cal_data_valid(u32 min, u32 max, u32 val, u32 default_val)
> +{
> +	if (val < min || val > max)
> +		return default_val;
> +
> +	return val;
> +}
> +
> +static int mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
> +{
> +	struct dp_cal_data *cal_data = &mtk_dp->cal_data;
> +	struct device *dev = mtk_dp->dev;
> +	struct nvmem_cell *cell;
> +	u32 *buf;
> +	size_t len;
> +
> +	cell = nvmem_cell_get(dev, "dp_calibration_data");
> +	if (IS_ERR(cell)) {
> +		dev_err(dev,
> +			"Error: Failed to get nvmem cell dp_calibration_data\n");
> +		return PTR_ERR(cell);
> +	}
> +
> +	buf = (u32 *)nvmem_cell_read(cell, &len);
> +	nvmem_cell_put(cell);
> +
> +	if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
> +		dev_err(dev,
> +			"Error: Failed to read nvmem_cell_read fail len %ld\n",
> +			(len / sizeof(u32)));
> +		return PTR_ERR(buf);
> +	}
> +
> +	if (mtk_dp_is_edp(mtk_dp)) {
> +		cal_data->glb_bias_trim = check_cal_data_valid(
> +			1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf);
> +		cal_data->clktx_impse =
> +			check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8);
> +		cal_data->ln0_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8);
> +		cal_data->ln0_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8);
> +		cal_data->ln1_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8);
> +		cal_data->ln1_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8);
> +		cal_data->ln2_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8);
> +		cal_data->ln2_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8);
> +		cal_data->ln3_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8);
> +		cal_data->ln3_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8);
> +	} else {
> +		cal_data->glb_bias_trim = check_cal_data_valid(
> +			1, 0x1e, (buf[0] >> 27) & 0x1f, 0xf);
> +		cal_data->clktx_impse =
> +			check_cal_data_valid(1, 0xe, (buf[0] >> 13) & 0xf, 0x8);
> +		cal_data->ln0_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 28) & 0xf, 0x8);
> +		cal_data->ln0_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 24) & 0xf, 0x8);
> +		cal_data->ln1_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 20) & 0xf, 0x8);
> +		cal_data->ln1_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 16) & 0xf, 0x8);
> +		cal_data->ln2_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 12) & 0xf, 0x8);
> +		cal_data->ln2_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 8) & 0xf, 0x8);
> +		cal_data->ln3_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 4) & 0xf, 0x8);
> +		cal_data->ln3_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, buf[1] & 0xf, 0x8);
> +	}
> +
> +	kfree(buf);
> +
> +	return 0;
> +}
> +
> +void mtk_dp_set_cal_data(struct mtk_dp *mtk_dp)
> +{
> +	struct dp_cal_data *cal_data = &mtk_dp->cal_data;
> +
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
> +			   cal_data->clktx_impse << 20, RG_CKM_PT0_CKTX_IMPSEL);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
> +			   cal_data->glb_bias_trim << 16,
> +			   RG_XTP_GLB_BIAS_INTR_CTRL);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +			   cal_data->ln0_tx_impsel_pmos << 12,
> +			   RG_XTP_LN0_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +			   cal_data->ln0_tx_impsel_nmos << 16,
> +			   RG_XTP_LN0_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +			   cal_data->ln1_tx_impsel_pmos << 12,
> +			   RG_XTP_LN1_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +			   cal_data->ln1_tx_impsel_nmos << 16,
> +			   RG_XTP_LN1_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +			   cal_data->ln2_tx_impsel_pmos << 12,
> +			   RG_XTP_LN2_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +			   cal_data->ln2_tx_impsel_nmos << 16,
> +			   RG_XTP_LN2_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +			   cal_data->ln3_tx_impsel_pmos << 12,
> +			   RG_XTP_LN3_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +			   cal_data->ln3_tx_impsel_nmos << 16,
> +			   RG_XTP_LN3_TX_IMPSEL_NMOS);
> +}
> +
> +static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
> +				enum mtk_dp_linkrate link_rate, int lane_count)
> +{
> +	int ret;
> +	union phy_configure_opts
> +		phy_opts = { .dp = {
> +				     .link_rate = link_rate_to_mb_per_s(
> +					     mtk_dp, link_rate),
> +				     .set_rate = 1,
> +				     .lanes = lane_count,
> +				     .set_lanes = 1,
> +				     .ssc = mtk_dp->train_info.sink_ssc,
> +			     } };
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP,
> +			   DP_PWR_STATE_MASK);
> +
> +	ret = phy_configure(mtk_dp->phy, &phy_opts);
> +
> +	mtk_dp_set_cal_data(mtk_dp);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL_LANE, DP_PWR_STATE_MASK);
> +	return ret;
> +}
> +
> +static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	const u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
> +			POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
> +			POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
> +			POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580, enable ? val : 0, val);
> +}
> +
> +static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int pattern)
> +{
> +	if (pattern < 0 || pattern > 4) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Implementation error, no such pattern %d\n", pattern);
> +		return;
> +	}
> +
> +	if (pattern == 1) // TPS1
> +		mtk_dp_set_idle_pattern(mtk_dp, false);
> +
> +	mtk_dp_update_bits(
> +		mtk_dp, MTK_DP_TRANS_P0_3400,
> +		pattern ? BIT(pattern - 1) << PATTERN1_EN_DP_TRANS_P0_SHIFT : 0,
> +		PATTERN1_EN_DP_TRANS_P0_MASK | PATTERN2_EN_DP_TRANS_P0_MASK |
> +			PATTERN3_EN_DP_TRANS_P0_MASK |
> +			PATTERN4_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +			   enable ? BIT(ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT) : 0,
> +			   ENHANCED_FRAME_EN_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
> +			   enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
> +			   DP_SCR_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	u32 val = BIT(VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT);
> +
> +	if (enable)
> +		val |= BIT(VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, val,
> +			   VIDEO_MUTE_SEL_DP_ENC0_P0_MASK |
> +				   VIDEO_MUTE_SW_DP_ENC0_P0_MASK);
> +
> +	if (mtk_dp_is_edp(mtk_dp))
> +		mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable);
> +	else
> +		mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable);
> +}
> +
> +static void mtk_dp_audio_mute(struct mtk_dp *mtk_dp, bool mute)
> +{
> +	if (mute) {
> +		mtk_dp_update_bits(
> +			mtk_dp, MTK_DP_ENC0_P0_3030,
> +			BIT(VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT) |
> +				BIT(VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT),
> +			VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
> +				VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
> +
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088, 0,
> +				   AU_EN_DP_ENC0_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0,
> +				   AU_TS_CFG_DP_ENC0_P0_MASK);
> +
> +	} else {
> +		mtk_dp_update_bits(
> +			mtk_dp, MTK_DP_ENC0_P0_3030, 0,
> +			VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
> +				VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
> +
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088,
> +				   BIT(AU_EN_DP_ENC0_P0_SHIFT),
> +				   AU_EN_DP_ENC0_P0_MASK);
> +		// Send one every two frames
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0x0F,
> +				   AU_TS_CFG_DP_ENC0_P0_MASK);
> +	}
> +}
> +
> +static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, 0,
> +			   SW_RST_B_PHYD);
> +	usleep_range(10, 200);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, SW_RST_B_PHYD,
> +			   SW_RST_B_PHYD);
> +}
> +
> +static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
> +	usleep_range(10, 200);
> +	mtk_dp_write(mtk_dp, MTK_DP_0034,
> +		     DA_CKM_CKTX0_EN_FORCE_EN | DA_CKM_BIAS_LPF_EN_FORCE_VAL |
> +			     DA_CKM_BIAS_EN_FORCE_VAL |
> +			     DA_XTP_GLB_LDO_EN_FORCE_VAL |
> +			     DA_XTP_GLB_AVD10_ON_FORCE_VAL);
> +	// Disable RX
> +	mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
> +	mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
> +		     0x550 | BIT(FUSE_SEL_SHIFT) | BIT(MEM_ISO_EN_SHIFT));
> +}
> +
> +static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR2;
> +	mtk_dp->train_info.lane_count = MTK_DP_MAX_LANES;
> +	mtk_dp->train_info.irq_status = 0;
> +	mtk_dp->train_info.cable_plugged_in = false;
> +	mtk_dp->train_info.cable_state_change = false;
> +	mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> +	mtk_dp->state = MTK_DP_STATE_INITIAL;
> +
> +	mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
> +	mtk_dp->info.depth = MTK_DP_COLOR_DEPTH_8BIT;
> +	memset(&mtk_dp->info.timings, 0, sizeof(struct mtk_dp_timings));
> +	mtk_dp->info.timings.frame_rate = 60;
> +
> +	mtk_dp->has_fec = false;
> +	mtk_dp->audio_enable = false;
> +}
> +
> +static void mtk_dp_sdp_set_down_cnt_init(struct mtk_dp *mtk_dp,
> +					 u32 sram_read_start)
> +{
> +	u32 sdp_down_cnt_init = 0;
> +	u32 dc_offset;
> +
> +	if (mtk_dp->info.timings.pix_rate_khz > 0)
> +		sdp_down_cnt_init = sram_read_start *
> +				    mtk_dp->train_info.link_rate * 2700 * 8 /
> +				    (mtk_dp->info.timings.pix_rate_khz * 4);
> +
> +	switch (mtk_dp->train_info.lane_count) {
> +	case 1:
> +		sdp_down_cnt_init = sdp_down_cnt_init > 0x1A ?
> +						  sdp_down_cnt_init :
> +						  0x1A; //26
> +		break;
> +	case 2:
> +		// case for LowResolution && High Audio Sample Rate
> +		dc_offset = mtk_dp->info.timings.vtotal <= 525 ? 0x04 : 0x00;
> +		sdp_down_cnt_init = sdp_down_cnt_init > 0x10 ?
> +						  sdp_down_cnt_init :
> +						  0x10 + dc_offset; //20 or 16
> +		break;
> +	case 4:
> +	default:
> +		sdp_down_cnt_init =
> +			sdp_down_cnt_init > 0x06 ? sdp_down_cnt_init : 0x06; //6
> +		break;
> +	}
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> +			   sdp_down_cnt_init
> +				   << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
> +			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_sdp_set_down_cnt_init_in_hblank(struct mtk_dp *mtk_dp)
> +{
> +	int pix_clk_mhz;
> +	u32 dc_offset;
> +	u32 spd_down_cnt_init = 0;
> +
> +	pix_clk_mhz = mtk_dp->info.format == MTK_DP_COLOR_FORMAT_YUV_420 ?
> +				    mtk_dp->info.timings.pix_rate_khz / 2000 :
> +				    mtk_dp->info.timings.pix_rate_khz / 1000;
> +
> +	switch (mtk_dp->train_info.lane_count) {
> +	case 1:
> +		spd_down_cnt_init = 0x20;
> +		break;
> +	case 2:
> +		dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x14 : 0x00;
> +		spd_down_cnt_init = 0x18 + dc_offset;
> +		break;
> +	case 4:
> +	default:
> +		dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x08 : 0x00;
> +		if (pix_clk_mhz > mtk_dp->train_info.link_rate * 27)
> +			spd_down_cnt_init = 0x8;
> +		else
> +			spd_down_cnt_init = 0x10 + dc_offset;
> +		break;
> +	}
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364, spd_down_cnt_init,
> +			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
> +}
> +
> +static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
> +{
> +	u32 sram_read_start = MTK_DP_TBC_BUF_READ_START_ADDR;
> +
> +	if (mtk_dp->train_info.lane_count > 0) {
> +		sram_read_start = min_t(
> +			u32, MTK_DP_TBC_BUF_READ_START_ADDR,
> +			mtk_dp->info.timings.vm.hactive /
> +				(mtk_dp->train_info.lane_count * 4 * 2 * 2));
> +		mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
> +	}
> +
> +	mtk_dp_setup_encoder(mtk_dp);
> +	mtk_dp_sdp_set_down_cnt_init_in_hblank(mtk_dp);
> +	mtk_dp_sdp_set_down_cnt_init(mtk_dp, sram_read_start);
> +}
> +
> +static void mtk_dp_calculate_pixrate(struct mtk_dp *mtk_dp)
> +{
> +	int target_frame_rate = 60;
> +	int target_pixel_clk;
> +
> +	if (mtk_dp->info.timings.frame_rate > 0) {
> +		target_frame_rate = mtk_dp->info.timings.frame_rate;
> +		target_pixel_clk = (int)mtk_dp->info.timings.htotal *
> +				   (int)mtk_dp->info.timings.vtotal *
> +				   target_frame_rate;
> +	} else if (mtk_dp->info.timings.pix_rate_khz > 0) {
> +		target_pixel_clk = mtk_dp->info.timings.pix_rate_khz * 1000;
> +	} else {
> +		target_pixel_clk = (int)mtk_dp->info.timings.htotal *
> +				   (int)mtk_dp->info.timings.vtotal *
> +				   target_frame_rate;
> +	}
> +
> +	if (target_pixel_clk > 0)
> +		mtk_dp->info.timings.pix_rate_khz = target_pixel_clk / 1000;
> +}
> +
> +static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_msa_bypass_disable(mtk_dp);
> +	mtk_dp_calculate_pixrate(mtk_dp);
> +	mtk_dp_pg_disable(mtk_dp);
> +	mtk_dp_setup_tu(mtk_dp);
> +}
> +
> +static void mtk_dp_edid_free(struct mtk_dp *mtk_dp)
> +{
> +	mutex_lock(&mtk_dp->edid_lock);
> +	kfree(mtk_dp->edid);
> +	mtk_dp->edid = NULL;
> +	mutex_unlock(&mtk_dp->edid_lock);
> +}
> +
> +static void mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp)
> +{
> +	ssize_t ret;
> +	u8 sink_count;
> +	u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +	u32 sink_count_reg;
> +	u32 link_status_reg;
> +	bool locked;
> +
> +	sink_count_reg = DP_SINK_COUNT_ESI;
> +	link_status_reg = DP_LANE0_1_STATUS;
> +
> +	ret = drm_dp_dpcd_readb(&mtk_dp->aux, sink_count_reg, &sink_count);
> +	if (ret < 0) {
> +		drm_info(mtk_dp->drm_dev, "Read sink count failed: %ld\n", ret);
> +		return;
> +	}
> +
> +	ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
> +	if (ret < 0) {
> +		drm_info(mtk_dp->drm_dev,
> +			 "Read DP_SINK_COUNT_ESI failed: %ld\n", ret);
> +		return;
> +	}
> +
> +	ret = drm_dp_dpcd_read(&mtk_dp->aux, link_status_reg, link_status,
> +			       sizeof(link_status));
> +	if (!ret) {
> +		drm_info(mtk_dp->drm_dev, "Read link status failed: %ld\n",
> +			 ret);
> +		return;
> +	}
> +
> +	locked = drm_dp_channel_eq_ok(link_status,
> +				      mtk_dp->train_info.lane_count);
> +	if (!locked && mtk_dp->train_state > MTK_DP_TRAIN_STATE_TRAINING_PRE)
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
> +
> +	if (link_status[1] & DP_REMOTE_CONTROL_COMMAND_PENDING)
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR,
> +				   DP_REMOTE_CONTROL_COMMAND_PENDING);
> +
> +	if (DP_GET_SINK_COUNT(sink_count) &&
> +	    (link_status[2] & DP_DOWNSTREAM_PORT_STATUS_CHANGED)) {
> +		mtk_dp_edid_free(mtk_dp);
> +		mtk_dp->train_info.check_cap_count = 0;
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
> +		msleep(20);
> +	}
> +}
> +
> +static void mtk_dp_sdp_stop_sending(struct mtk_dp *mtk_dp)
> +{
> +	u8 packet_type;
> +
> +	for (packet_type = MTK_DP_SDP_ACM; packet_type < MTK_DP_SDP_MAX_NUM;
> +	     packet_type++)
> +		mtk_dp_disable_sdp(mtk_dp, packet_type);
> +
> +	mtk_dp_sdp_vsc_ext_disable(mtk_dp);
> +}
> +
> +static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes,
> +					  u8 dpcd_adjust_req[2])
> +{
> +	int lane;
> +
> +	for (lane = 0; lane < lanes; ++lane) {
> +		u8 val;
> +		u8 swing;
> +		u8 preemphasis;
> +		int index = lane / 2;
> +		int shift = lane % 2 ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
> +
> +		swing = (dpcd_adjust_req[index] >> shift) &
> +			DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
> +		preemphasis = ((dpcd_adjust_req[index] >> shift) &
> +			       DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
> +			      DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
> +		val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
> +		      preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +
> +		if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
> +			val |= DP_TRAIN_MAX_SWING_REACHED;
> +		if (preemphasis == 3)
> +			val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> +
> +		mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing, preemphasis);
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET + lane,
> +				   val);
> +	}
> +
> +	// Wait for the signal to be stable enough
> +	usleep_range(2000, 5000);
> +}
> +
> +static void mtk_dp_read_link_status(struct mtk_dp *mtk_dp,
> +				    u8 link_status[DP_LINK_STATUS_SIZE])
> +{
> +	drm_dp_dpcd_read(&mtk_dp->aux, DP_LANE0_1_STATUS, link_status,
> +			 DP_LINK_STATUS_SIZE);
> +}
> +
> +static int mtk_dp_train_flow(struct mtk_dp *mtk_dp, int target_link_rate,
> +			     int target_lane_count)
> +{
> +	u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +	u8 lane_adjust[2] = {};
> +	bool pass_tps1 = false;
> +	bool pass_tps2_3 = false;
> +	int train_retries;
> +	int status_control;
> +	int iteration_count;
> +	u8 prev_lane_adjust;
> +	u8 val;
> +
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET, target_link_rate);
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> +			   target_lane_count | DP_LANE_COUNT_ENHANCED_FRAME_EN);
> +
> +	if (mtk_dp->train_info.sink_ssc)
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
> +				   DP_SPREAD_AMP_0_5);
> +
> +	train_retries = 0;
> +	status_control = 0;
> +	iteration_count = 1;
> +	prev_lane_adjust = 0xFF;
> +
> +	mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
> +	mtk_dp_phy_configure(mtk_dp, target_link_rate, target_lane_count);
> +
> +	dev_dbg(mtk_dp->dev,
> +		"Link train target_link_rate = 0x%x, target_lane_count = 0x%x\n",
> +		target_link_rate, target_lane_count);
> +
> +	do {
> +		train_retries++;
> +		if (!mtk_dp->train_info.cable_plugged_in ||
> +		    ((mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) !=
> +		     0x0)) {
> +			return -ENODEV;
> +		}
> +
> +		if (mtk_dp->train_state < MTK_DP_TRAIN_STATE_TRAINING)
> +			return -EAGAIN;
> +
> +		if (!pass_tps1) {
> +			mtk_dp_training_set_scramble(mtk_dp, false);
> +
> +			if (status_control == 0) {
> +				status_control = 1;
> +				mtk_dp_train_set_pattern(mtk_dp, 1);
> +				val = DP_LINK_SCRAMBLING_DISABLE |
> +				      DP_TRAINING_PATTERN_1;
> +				drm_dp_dpcd_writeb(
> +					&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +					DP_LINK_SCRAMBLING_DISABLE |
> +						DP_TRAINING_PATTERN_1);
> +				drm_dp_dpcd_read(&mtk_dp->aux,
> +						 DP_ADJUST_REQUEST_LANE0_1,
> +						 lane_adjust,
> +						 sizeof(lane_adjust));
> +				iteration_count++;
> +
> +				mtk_dp_train_update_swing_pre(
> +					mtk_dp, target_lane_count, lane_adjust);
> +			}
> +
> +			drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
> +							       mtk_dp->rx_cap);
> +			mtk_dp_read_link_status(mtk_dp, link_status);
> +
> +			if (drm_dp_clock_recovery_ok(link_status,
> +						     target_lane_count)) {
> +				mtk_dp->train_info.cr_done = true;
> +				pass_tps1 = true;
> +				train_retries = 0;
> +				iteration_count = 1;
> +				dev_dbg(mtk_dp->dev, "Link train CR pass\n");
> +			} else if (prev_lane_adjust == link_status[4]) {
> +				iteration_count++;
> +				if (prev_lane_adjust &
> +				    DP_ADJUST_VOLTAGE_SWING_LANE0_MASK)
> +					break;
> +			} else {
> +				prev_lane_adjust = link_status[4];
> +			}
> +			dev_dbg(mtk_dp->dev, "Link train CQ fail\n");
> +		} else if (pass_tps1 && !pass_tps2_3) {
> +			if (status_control == 1) {
> +				status_control = 2;
> +				if (mtk_dp->train_info.tps4) {
> +					mtk_dp_train_set_pattern(mtk_dp, 4);
> +					val = DP_TRAINING_PATTERN_4;
> +				} else if (mtk_dp->train_info.tps3) {
> +					mtk_dp_train_set_pattern(mtk_dp, 3);
> +					val = DP_LINK_SCRAMBLING_DISABLE |
> +					      DP_TRAINING_PATTERN_3;
> +				} else {
> +					mtk_dp_train_set_pattern(mtk_dp, 2);
> +					val = DP_LINK_SCRAMBLING_DISABLE |
> +					      DP_TRAINING_PATTERN_2;
> +				}
> +				drm_dp_dpcd_writeb(&mtk_dp->aux,
> +						   DP_TRAINING_PATTERN_SET,
> +						   val);
> +
> +				drm_dp_dpcd_read(&mtk_dp->aux,
> +						 DP_ADJUST_REQUEST_LANE0_1,
> +						 lane_adjust,
> +						 sizeof(lane_adjust));
> +
> +				iteration_count++;
> +				mtk_dp_train_update_swing_pre(
> +					mtk_dp, target_lane_count, lane_adjust);
> +			}
> +
> +			drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
> +							   mtk_dp->rx_cap);
> +
> +			mtk_dp_read_link_status(mtk_dp, link_status);
> +
> +			if (!drm_dp_clock_recovery_ok(link_status,
> +						      target_lane_count)) {
> +				mtk_dp->train_info.cr_done = false;
> +				mtk_dp->train_info.eq_done = false;
> +				dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
> +				break;
> +			}
> +
> +			if (drm_dp_channel_eq_ok(link_status,
> +						 target_lane_count)) {
> +				mtk_dp->train_info.eq_done = true;
> +				pass_tps2_3 = true;
> +				dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
> +				break;
> +			}
> +
> +			if (prev_lane_adjust == link_status[4])
> +				iteration_count++;
> +			else
> +				prev_lane_adjust = link_status[4];
> +		}
> +
> +		drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
> +				 lane_adjust, sizeof(lane_adjust));
> +		mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
> +					      lane_adjust);
> +	} while (train_retries < MTK_DP_TRAIN_RETRY_LIMIT &&
> +		 iteration_count < MTK_DP_TRAIN_MAX_ITERATIONS);
> +
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +			   DP_TRAINING_PATTERN_DISABLE);
> +	mtk_dp_train_set_pattern(mtk_dp, 0);
> +
> +	if (pass_tps2_3) {
> +		mtk_dp->train_info.link_rate = target_link_rate;
> +		mtk_dp->train_info.lane_count = target_lane_count;
> +
> +		mtk_dp_training_set_scramble(mtk_dp, true);
> +
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> +				   target_lane_count |
> +					   DP_LANE_COUNT_ENHANCED_FRAME_EN);
> +		mtk_dp_set_enhanced_frame_mode(mtk_dp, true);
> +
> +		return 0;
> +	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static bool mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
> +{
> +	u8 buf[DP_RECEIVER_CAP_SIZE] = {};
> +	u8 val;
> +	struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
> +
> +	if (!mtk_dp_plug_state(mtk_dp))
> +		return false;
> +
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> +	usleep_range(2000, 5000);
> +
> +	drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
> +
> +	memcpy(mtk_dp->rx_cap, buf, min(sizeof(mtk_dp->rx_cap), sizeof(buf)));
> +	mtk_dp->rx_cap[DP_TRAINING_AUX_RD_INTERVAL] &= DP_TRAINING_AUX_RD_MASK;
> +
> +	train_info->link_rate =
> +		min_t(int, MTK_DP_MAX_LINK_RATE, buf[DP_MAX_LINK_RATE]);
> +	train_info->lane_count =
> +		min_t(int, MTK_DP_MAX_LANES, drm_dp_max_lane_count(buf));
> +
> +	train_info->tps3 = drm_dp_tps3_supported(buf);
> +	train_info->tps4 = drm_dp_tps4_supported(buf);
> +
> +	train_info->sink_ssc =
> +		!!(buf[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5);
> +
> +	train_info->sink_ssc = false;
> +
> +	drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
> +	if (val & DP_MST_CAP) {
> +		// Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0
> +		drm_dp_dpcd_readb(&mtk_dp->aux,
> +				  DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0, &val);
> +		if (val)
> +			drm_dp_dpcd_writeb(&mtk_dp->aux,
> +					   DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
> +					   val);
> +	}
> +
> +	return true;
> +}
> +
> +static int mtk_dp_edid_parse_audio_capabilities(struct mtk_dp *mtk_dp,
> +						struct mtk_dp_audio_cfg *cfg)
> +{
> +	struct cea_sad *sads;
> +	int sad_count;
> +	int i;
> +	int ret = 0;
> +
> +	mutex_lock(&mtk_dp->edid_lock);
> +	if (!mtk_dp->edid) {
> +		mutex_unlock(&mtk_dp->edid_lock);
> +		dev_err(mtk_dp->dev, "EDID not found!\n");
> +		return -EINVAL;
> +	}
> +
> +	sad_count = drm_edid_to_sad(mtk_dp->edid, &sads);
> +	mutex_unlock(&mtk_dp->edid_lock);
> +	if (sad_count <= 0) {
> +		drm_info(mtk_dp->drm_dev, "The SADs is NULL\n");
> +		return 0;
> +	}
> +
> +	for (i = 0; i < sad_count; i++) {
> +		int sample_rate;
> +		int word_length;
> +		// Only PCM supported at the moment
> +		if (sads[i].format != HDMI_AUDIO_CODING_TYPE_PCM)
> +			continue;
> +
> +		sample_rate = drm_cea_sad_get_sample_rate(&sads[i]);
> +		word_length =
> +			drm_cea_sad_get_uncompressed_word_length(&sads[i]);
> +		if (sample_rate <= 0 || word_length <= 0)
> +			continue;
> +
> +		cfg->channels = sads[i].channels;
> +		cfg->word_length_bits = word_length;
> +		cfg->sample_rate = sample_rate;
> +		ret = 1;
> +		break;
> +	}
> +	kfree(sads);
> +
> +	return ret;
> +}
> +
> +static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
> +{
> +	phy_reset(mtk_dp->phy);
> +	mtk_dp_reset_swing_pre_emphasis(mtk_dp);
> +
> +	usleep_range(2000, 5000);
> +}
> +
> +static int mtk_dp_train_start(struct mtk_dp *mtk_dp)
> +{
> +	int ret = 0;
> +	int lane_count;
> +	int link_rate;
> +	int train_limit;
> +	int max_link_rate;
> +	int plug_wait;
> +
> +	for (plug_wait = 7; !mtk_dp_plug_state(mtk_dp) && plug_wait > 0;
> +	     --plug_wait)
> +		usleep_range(1000, 5000);
> +	if (plug_wait == 0) {
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> +		return -ENODEV;
> +	}
> +
> +	link_rate = mtk_dp->rx_cap[1];
> +	lane_count = mtk_dp->rx_cap[2] & 0x1F;
> +
> +	mtk_dp->train_info.link_rate = min(MTK_DP_MAX_LINK_RATE, link_rate);
> +	mtk_dp->train_info.lane_count = min(MTK_DP_MAX_LANES, lane_count);
> +	link_rate = mtk_dp->train_info.link_rate;
> +	lane_count = mtk_dp->train_info.lane_count;
> +
> +	switch (link_rate) {
> +	case MTK_DP_LINKRATE_RBR:
> +	case MTK_DP_LINKRATE_HBR:
> +	case MTK_DP_LINKRATE_HBR2:
> +	case MTK_DP_LINKRATE_HBR25:
> +	case MTK_DP_LINKRATE_HBR3:
> +		break;
> +	default:
> +		mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR3;
> +		break;
> +	};
> +
> +	max_link_rate = link_rate;
> +	for (train_limit = 0; train_limit <= 6; ++train_limit) {
> +		mtk_dp->train_info.cr_done = false;
> +		mtk_dp->train_info.eq_done = false;
> +
> +		mtk_dp_train_change_mode(mtk_dp);
> +		ret = mtk_dp_train_flow(mtk_dp, link_rate, lane_count);
> +		if (ret == -ENODEV || ret == -EAGAIN)
> +			return ret;
> +
> +		if (!mtk_dp->train_info.cr_done) {
> +			switch (link_rate) {
> +			case MTK_DP_LINKRATE_RBR:
> +				lane_count = lane_count / 2;
> +				link_rate = max_link_rate;
> +				if (lane_count == 0x0) {
> +					mtk_dp->train_state =
> +						MTK_DP_TRAIN_STATE_DPIDLE;
> +					return -EIO;
> +				}
> +				break;
> +			case MTK_DP_LINKRATE_HBR:
> +				link_rate = MTK_DP_LINKRATE_RBR;
> +				break;
> +			case MTK_DP_LINKRATE_HBR2:
> +				link_rate = MTK_DP_LINKRATE_HBR;
> +				break;
> +			case MTK_DP_LINKRATE_HBR3:
> +				link_rate = MTK_DP_LINKRATE_HBR2;
> +				break;
> +			default:
> +				return -EINVAL;
> +			};
> +		} else if (!mtk_dp->train_info.eq_done) {
> +			lane_count /= 2;
> +			if (lane_count == 0)
> +				return -EIO;
> +		} else {
> +			return 0;
> +		}
> +	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_train_handler(struct mtk_dp *mtk_dp)
> +{
> +	int ret = 0;
> +
> +	if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
> +		return ret;
> +
> +	switch (mtk_dp->train_state) {
> +	case MTK_DP_TRAIN_STATE_STARTUP:
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> +		break;
> +
> +	case MTK_DP_TRAIN_STATE_CHECKCAP:
> +		if (mtk_dp_parse_capabilities(mtk_dp)) {
> +			mtk_dp->train_info.check_cap_count = 0;
> +			mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
> +		} else {
> +			mtk_dp->train_info.check_cap_count++;
> +
> +			if (mtk_dp->train_info.check_cap_count >
> +			    MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT) {
> +				mtk_dp->train_info.check_cap_count = 0;
> +				mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> +				ret = -ETIMEDOUT;
> +			}
> +		}
> +		break;
> +
> +	case MTK_DP_TRAIN_STATE_CHECKEDID: {
> +		int caps_found = mtk_dp_edid_parse_audio_capabilities(
> +			mtk_dp, &mtk_dp->info.audio_caps);
> +		mtk_dp->audio_enable = caps_found > 0;
> +		if (!mtk_dp->audio_enable)
> +			memset(&mtk_dp->info.audio_caps, 0,
> +			       sizeof(mtk_dp->info.audio_caps));
> +	}
> +
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
> +		break;
> +
> +	case MTK_DP_TRAIN_STATE_TRAINING_PRE:
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING;
> +		break;
> +
> +	case MTK_DP_TRAIN_STATE_TRAINING:
> +		ret = mtk_dp_train_start(mtk_dp);
> +		if (!ret) {
> +			mtk_dp_video_mute(mtk_dp, true);
> +			mtk_dp_audio_mute(mtk_dp, true);
> +			mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKTIMING;
> +			mtk_dp_fec_enable(mtk_dp, mtk_dp->has_fec);
> +		} else if (ret != -EAGAIN) {
> +			mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> +		}
> +
> +		ret = 0;
> +		break;
> +
> +	case MTK_DP_TRAIN_STATE_CHECKTIMING:
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_NORMAL;
> +		break;
> +	case MTK_DP_TRAIN_STATE_NORMAL:
> +		break;
> +	case MTK_DP_TRAIN_STATE_POWERSAVE:
> +		break;
> +	case MTK_DP_TRAIN_STATE_DPIDLE:
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	if (enable) {
> +		mtk_dp_set_tx_out(mtk_dp);
> +		mtk_dp_video_mute(mtk_dp, false);
> +	} else {
> +		mtk_dp_video_mute(mtk_dp, true);
> +	}
> +}
> +
> +static void mtk_dp_audio_sdp_setup(struct mtk_dp *mtk_dp,
> +				   struct mtk_dp_audio_cfg *cfg)
> +{
> +	struct mtk_dp_sdp_packet packet;
> +	struct hdmi_audio_infoframe frame;
> +
> +	hdmi_audio_infoframe_init(&frame);
> +	frame.coding_type = HDMI_AUDIO_CODING_TYPE_PCM;
> +	frame.channels = cfg->channels;
> +	frame.sample_frequency = cfg->sample_rate;
> +
> +	switch (cfg->word_length_bits) {
> +	case 16:
> +		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
> +		break;
> +	case 20:
> +		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_20;
> +		break;
> +	case 24:
> +	default:
> +		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_24;
> +		break;
> +	}
> +
> +	packet.type = MTK_DP_SDP_AUI;
> +	hdmi_audio_infoframe_pack_for_dp(&frame, &packet.sdp,
> +					 MTK_DP_DP_VERSION_11);
> +
> +	mtk_dp_audio_sdp_asp_set_channels(mtk_dp, cfg->channels);
> +	mtk_dp_setup_sdp(mtk_dp, &packet);
> +}
> +
> +static void mtk_dp_audio_setup(struct mtk_dp *mtk_dp,
> +			       struct mtk_dp_audio_cfg *cfg)
> +{
> +	mtk_dp_audio_sdp_setup(mtk_dp, cfg);
> +	mtk_dp_audio_channel_status_set(mtk_dp, cfg);
> +
> +	mtk_dp_audio_setup_channels(mtk_dp, cfg);
> +	mtk_dp_audio_set_divider(mtk_dp);
> +}
> +
> +static void mtk_dp_video_config(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_mn_overwrite_disable(mtk_dp);
> +
> +	mtk_dp_set_msa(mtk_dp);
> +
> +	mtk_dp_set_color_depth(mtk_dp, mtk_dp->info.depth);
> +	mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
> +}
> +
> +static int mtk_dp_state_handler(struct mtk_dp *mtk_dp)
> +{
> +	int ret = 0;
> +
> +	switch (mtk_dp->state) {
> +	case MTK_DP_STATE_INITIAL:
> +		mtk_dp_video_mute(mtk_dp, true);
> +		mtk_dp_audio_mute(mtk_dp, true);
> +		mtk_dp->state = MTK_DP_STATE_IDLE;
> +		break;
> +
> +	case MTK_DP_STATE_IDLE:
> +		if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
> +			mtk_dp->state = MTK_DP_STATE_PREPARE;
> +		break;
> +
> +	case MTK_DP_STATE_PREPARE:
> +		mtk_dp_video_config(mtk_dp);
> +		mtk_dp_video_enable(mtk_dp, true);
> +
> +		if (mtk_dp->audio_enable) {
> +			mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_caps);
> +			mtk_dp_audio_mute(mtk_dp, false);
> +		}
> +
> +		mtk_dp->state = MTK_DP_STATE_NORMAL;
> +		break;
> +
> +	case MTK_DP_STATE_NORMAL:
> +		if (mtk_dp->train_state != MTK_DP_TRAIN_STATE_NORMAL) {
> +			mtk_dp_video_mute(mtk_dp, true);
> +			mtk_dp_audio_mute(mtk_dp, true);
> +			mtk_dp_sdp_stop_sending(mtk_dp);
> +			mtk_dp->state = MTK_DP_STATE_IDLE;
> +		}
> +		break;
> +
> +	default:
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_set_idle_pattern(mtk_dp, true);
> +	mtk_dp_initialize_priv_data(mtk_dp);
> +
> +	mtk_dp_initialize_settings(mtk_dp);
> +	mtk_dp_initialize_aux_settings(mtk_dp);
> +	mtk_dp_initialize_digital_settings(mtk_dp);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
> +			   BIT(RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT),
> +			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK);
> +	mtk_dp_initialize_hpd_detect_settings(mtk_dp);
> +
> +	mtk_dp_digital_sw_reset(mtk_dp);
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev;
> +	int event;
> +	u8 buf[DP_RECEIVER_CAP_SIZE] = {};
> +
> +	event = mtk_dp_plug_state(mtk_dp) ? connector_status_connected :
> +						  connector_status_disconnected;
> +
> +	if (event < 0)
> +		return IRQ_HANDLED;
> +
> +	if (mtk_dp->drm_dev) {
> +		dev_info(mtk_dp->dev, "drm_helper_hpd_irq_event\n");
> +		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
> +	}
> +
> +	if (mtk_dp->train_info.cable_state_change) {
> +		mtk_dp->train_info.cable_state_change = false;
> +
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> +
> +		if (!mtk_dp->train_info.cable_plugged_in ||
> +		    !mtk_dp_plug_state(mtk_dp)) {
> +			mtk_dp_video_mute(mtk_dp, true);
> +			mtk_dp_audio_mute(mtk_dp, true);
> +
> +			mtk_dp_initialize_priv_data(mtk_dp);
> +			mtk_dp_set_idle_pattern(mtk_dp, true);
> +			if (mtk_dp->has_fec)
> +				mtk_dp_fec_enable(mtk_dp, false);
> +			mtk_dp_sdp_stop_sending(mtk_dp);
> +
> +			mtk_dp_edid_free(mtk_dp);
> +			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +					   DP_PWR_STATE_BANDGAP_TPLL,
> +					   DP_PWR_STATE_MASK);
> +		} else {
> +			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +					   DP_PWR_STATE_BANDGAP_TPLL_LANE,
> +					   DP_PWR_STATE_MASK);
> +			drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
> +			mtk_dp->train_info.link_rate =
> +				min_t(int, MTK_DP_MAX_LINK_RATE,
> +				      buf[DP_MAX_LINK_RATE]);
> +			mtk_dp->train_info.lane_count =
> +				min_t(int, MTK_DP_MAX_LANES,
> +				      drm_dp_max_lane_count(buf));
> +		}
> +	}
> +
> +	if (mtk_dp->train_info.irq_status & MTK_DP_HPD_INTERRUPT) {
> +		dev_info(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n");
> +		mtk_dp->train_info.irq_status &= ~MTK_DP_HPD_INTERRUPT;
> +		mtk_dp_hpd_sink_event(mtk_dp);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mtk_dp_hpd_isr_handler(struct mtk_dp *mtk_dp)
> +{
> +	bool connected;
> +	u16 swirq_status = mtk_dp_swirq_get_clear(mtk_dp);
> +	u8 hwirq_status = mtk_dp_hwirq_get_clear(mtk_dp);
> +	struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
> +
> +	train_info->irq_status |= hwirq_status | swirq_status;
> +
> +	if (!train_info->irq_status)
> +		return IRQ_HANDLED;
> +
> +	connected = mtk_dp_plug_state(mtk_dp);
> +	if (connected || !train_info->cable_plugged_in)
> +		train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
> +	else if (!connected || train_info->cable_plugged_in)
> +		train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
> +
> +	if (!(train_info->irq_status &
> +	      (MTK_DP_HPD_CONNECT | MTK_DP_HPD_DISCONNECT)))
> +		return IRQ_HANDLED;
> +
> +	if (train_info->irq_status & MTK_DP_HPD_CONNECT) {
> +		train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
> +		train_info->cable_plugged_in = true;
> +	} else {
> +		train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
> +		train_info->cable_plugged_in = false;
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> +	}
> +	train_info->cable_state_change = true;
> +	return IRQ_WAKE_THREAD;
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev;
> +	u32 irq_status;
> +
> +	irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS);
> +
> +	if (!irq_status)
> +		return IRQ_HANDLED;
> +
> +	if (irq_status & RGS_IRQ_STATUS_TRANSMITTER)
> +		return mtk_dp_hpd_isr_handler(mtk_dp);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int mtk_dp_dt_parse_pdata(struct mtk_dp *mtk_dp,
> +				 struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	int ret = 0;
> +	void __iomem *base;
> +
> +	base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(base))
> +		return PTR_ERR(base);
> +
> +	mtk_dp->regs = devm_regmap_init_mmio(dev, base, &mtk_dp_regmap_config);
> +	if (IS_ERR(mtk_dp->regs))
> +		return PTR_ERR(mtk_dp->regs);
> +
> +	mtk_dp->dp_tx_clk = devm_clk_get(dev, "faxi");
> +	if (IS_ERR(mtk_dp->dp_tx_clk)) {
> +		ret = PTR_ERR(mtk_dp->dp_tx_clk);
> +		dev_err(dev, "Failed to get dptx clock: %d\n", ret);
> +		mtk_dp->dp_tx_clk = NULL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_update_plugged_status(struct mtk_dp *mtk_dp)
> +{
> +	bool connected, has_audio;
> +
> +	mutex_lock(&mtk_dp->update_plugged_status_lock);
> +	connected = mtk_dp_plug_state(mtk_dp);
> +	has_audio = drm_detect_monitor_audio(mtk_dp->edid);
> +	if (mtk_dp->plugged_cb && mtk_dp->codec_dev)
> +		mtk_dp->plugged_cb(mtk_dp->codec_dev, connected & has_audio);
> +	mutex_unlock(&mtk_dp->update_plugged_status_lock);
> +}
> +
> +static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	enum drm_connector_status ret = connector_status_disconnected;
> +	u8 sink_count = 0;
> +
> +	if (mtk_dp_is_edp(mtk_dp))
> +		return connector_status_connected;
> +
> +	if (mtk_dp_plug_state(mtk_dp)) {
> +		drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
> +		if (DP_GET_SINK_COUNT(sink_count))
> +			ret = connector_status_connected;
> +	}
> +
> +	mtk_dp_update_plugged_status(mtk_dp);
> +	return ret;
> +}
> +
> +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> +				    struct drm_connector *connector)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	bool enabled = mtk_dp->enabled;
> +	struct edid *new_edid = NULL;
> +
> +	if (!enabled)
> +		drm_bridge_chain_pre_enable(bridge);
> +
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> +	usleep_range(2000, 5000);
> +
> +	if (mtk_dp_plug_state(mtk_dp))
> +		new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> +
> +	if (!enabled)
> +		drm_bridge_chain_post_disable(bridge);
> +
> +	mutex_lock(&mtk_dp->edid_lock);
> +	kfree(mtk_dp->edid);
> +	mtk_dp->edid = NULL;
> +
> +	if (!new_edid) {
> +		mutex_unlock(&mtk_dp->edid_lock);
> +		return NULL;
> +	}
> +
> +	mtk_dp->edid = drm_edid_duplicate(new_edid);
> +	mutex_unlock(&mtk_dp->edid_lock);
> +
> +	return new_edid;
> +}
> +
> +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> +				   struct drm_dp_aux_msg *msg)
> +{
> +	ssize_t err = -EAGAIN;
> +	struct mtk_dp *mtk_dp;
> +	bool is_read;
> +	u8 request;
> +	size_t accessed_bytes = 0;
> +	int retry = 3, ret = 0;
> +
> +	mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> +
> +	if (!mtk_dp->train_info.cable_plugged_in ||
> +	    mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> +		err = -EAGAIN;
> +		goto err;
> +	}
> +
> +	switch (msg->request) {
> +	case DP_AUX_I2C_MOT:
> +	case DP_AUX_I2C_WRITE:
> +	case DP_AUX_NATIVE_WRITE:
> +	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> +	case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> +		request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> +		is_read = false;
> +		break;
> +	case DP_AUX_I2C_READ:
> +	case DP_AUX_NATIVE_READ:
> +	case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> +		request = msg->request;
> +		is_read = true;
> +		break;
> +	default:
> +		drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> +			msg->request);
> +		err = -EINVAL;
> +		goto err;
> +	}
> +
> +	if (msg->size == 0) {
> +		mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> +				       msg->address + accessed_bytes,
> +				       msg->buffer + accessed_bytes, 0);
> +	} else {
> +		while (accessed_bytes < msg->size) {
> +			size_t to_access =
> +				min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> +				      msg->size - accessed_bytes);
> +			retry = 3;
> +			while (retry--) {
> +				ret = mtk_dp_aux_do_transfer(
> +					mtk_dp, is_read, request,
> +					msg->address + accessed_bytes,
> +					msg->buffer + accessed_bytes,
> +					to_access);
> +				if (ret == 0)
> +					break;
> +				udelay(50);
> +			}
> +			if (!retry && ret) {
> +				drm_info(mtk_dp->drm_dev,
> +					 "Failed to do AUX transfer: %d\n",
> +					 ret);
> +				break;
> +			}
> +			accessed_bytes += to_access;
> +		}
> +	}
> +err:
> +	if (!ret) {
> +		msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +		ret = msg->size;
> +	} else {
> +		msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> +		return err;
> +	}
> +
> +	msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +	return msg->size;
> +}
> +
> +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> +{
> +	drm_dp_aux_init(&mtk_dp->aux);
> +	mtk_dp->aux.name = "aux_mtk_dp";
> +	mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> +}
> +
> +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> +{
> +	mutex_lock(&mtk_dp->dp_lock);
> +
> +	mtk_dp_hwirq_enable(mtk_dp, false);
> +	mtk_dp_power_disable(mtk_dp);
> +	phy_exit(mtk_dp->phy);
> +	clk_disable_unprepare(mtk_dp->dp_tx_clk);
> +
> +	mutex_unlock(&mtk_dp->dp_lock);
> +}
> +
> +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> +{
> +	int ret = 0;
> +
> +	mutex_lock(&mtk_dp->dp_lock);
> +
> +	ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> +	if (ret < 0) {
> +		dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> +		goto err;
> +	}
> +	ret = phy_init(mtk_dp->phy);
> +	if (ret) {
> +		dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> +		goto err_phy_init;
> +	}
> +	ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> +	if (ret) {
> +		dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> +		goto err_phy_config;
> +	}
> +
> +	mtk_dp_init_port(mtk_dp);
> +	mtk_dp_power_enable(mtk_dp);
> +	mtk_dp_hwirq_enable(mtk_dp, true);
> +
> +err_phy_config:
> +	phy_exit(mtk_dp->phy);
> +err_phy_init:
> +	clk_disable_unprepare(mtk_dp->dp_tx_clk);
> +err:
> +	mutex_unlock(&mtk_dp->dp_lock);
> +	return ret;
> +}
> +
> +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> +				enum drm_bridge_attach_flags flags)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	int ret;
> +
> +	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> +		dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> +		return -EINVAL;
> +	}
> +
> +	ret = mtk_dp_poweron(mtk_dp);
> +	if (ret)
> +		return ret;
> +
> +	if (mtk_dp->next_bridge) {
> +		ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> +					&mtk_dp->bridge, flags);
> +		if (ret) {
> +			drm_warn(mtk_dp->drm_dev,
> +				 "Failed to attach external bridge: %d\n", ret);
> +			goto err_bridge_attach;
> +		}
> +	}
> +
> +	mtk_dp->drm_dev = bridge->dev;
> +
> +	return 0;
> +
> +err_bridge_attach:
> +	mtk_dp_poweroff(mtk_dp);
> +	return ret;
> +}
> +
> +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +	mtk_dp->drm_dev = NULL;
> +
> +	mtk_dp_poweroff(mtk_dp);
> +}
> +
> +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> +					 struct drm_bridge_state *old_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +	mtk_dp_video_mute(mtk_dp, true);
> +	mtk_dp_audio_mute(mtk_dp, true);
> +	mtk_dp->state = MTK_DP_STATE_IDLE;
> +	mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> +
> +	mtk_dp->enabled = false;
> +	msleep(100);
> +}
> +
> +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> +					  struct drm_display_mode *mode)
> +{
> +	struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> +
> +	drm_display_mode_to_videomode(mode, &timings->vm);
> +	timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> +	timings->htotal = mode->htotal;
> +	timings->vtotal = mode->vtotal;
> +}
> +
> +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> +					struct drm_bridge_state *old_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	struct drm_connector *conn;
> +	struct drm_connector_state *conn_state;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *crtc_state;
> +	int ret = 0;
> +	int i;
> +
> +	conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> +							bridge->encoder);
> +	if (!conn) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as connector is missing\n");
> +		return;
> +	}
> +
> +	memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);

This should be protected by a mutex (just like any resource shared
between KMS and ALSA)

> +	conn_state =
> +		drm_atomic_get_new_connector_state(old_state->base.state, conn);
> +	if (!conn_state) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as connector state is missing\n");
> +		return;
> +	}
> +
> +	crtc = conn_state->crtc;
> +	if (!crtc) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as connector state doesn't have a crtc\n");
> +		return;
> +	}
> +
> +	crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> +	if (!crtc_state) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as crtc state is missing\n");
> +		return;
> +	}
> +
> +	mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);
> +	if (!mtk_dp_parse_capabilities(mtk_dp)) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as nothing is plugged in\n");
> +		return;
> +	}
> +
> +	//training
> +	for (i = 0; i < 50; i++) {
> +		ret = mtk_dp_train_handler(mtk_dp);
> +		if (ret) {
> +			drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> +				ret);
> +			return;
> +		}
> +
> +		ret = mtk_dp_state_handler(mtk_dp);
> +		if (ret) {
> +			drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> +				ret);
> +			return;
> +		}
> +	}
> +
> +	mtk_dp->enabled = true;
> +	mtk_dp_update_plugged_status(mtk_dp);
> +}
> +
> +static enum drm_mode_status
> +mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
> +			 const struct drm_display_info *info,
> +			 const struct drm_display_mode *mode)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	u32 rx_linkrate;
> +	u32 bpp;
> +
> +	bpp = info->color_formats & DRM_COLOR_FORMAT_YCRCB422 ? 16 : 24;
> +	rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
> +	if (rx_linkrate * mtk_dp->train_info.lane_count < mode->clock * bpp / 8)
> +		return MODE_CLOCK_HIGH;
> +
> +	if (mode->clock > 600000)
> +		return MODE_CLOCK_HIGH;
> +
> +	if ((mode->clock * 1000) / (mode->htotal * mode->vtotal) > 62)
> +		return MODE_CLOCK_HIGH;
> +
> +	return MODE_OK;
> +}
> +
> +static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(
> +	struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
> +	struct drm_crtc_state *crtc_state,
> +	struct drm_connector_state *conn_state, unsigned int *num_output_fmts)
> +{
> +	u32 *output_fmts;
> +
> +	*num_output_fmts = 0;
> +	output_fmts = kcalloc(1, sizeof(*output_fmts), GFP_KERNEL);
> +	if (!output_fmts)
> +		return NULL;
> +	*num_output_fmts = 1;
> +	output_fmts[0] = MEDIA_BUS_FMT_FIXED;
> +	return output_fmts;
> +}
> +
> +static const u32 mt8195_input_fmts[] = {
> +	MEDIA_BUS_FMT_RGB888_1X24,
> +	MEDIA_BUS_FMT_YUV8_1X24,
> +	MEDIA_BUS_FMT_YUYV8_1X16,
> +};
> +
> +static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(
> +	struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
> +	struct drm_crtc_state *crtc_state,
> +	struct drm_connector_state *conn_state, u32 output_fmt,
> +	unsigned int *num_input_fmts)
> +{
> +	u32 *input_fmts;
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> +	struct drm_display_info *display_info =
> +		&conn_state->connector->display_info;
> +	u32 rx_linkrate;
> +	u32 bpp;
> +
> +	bpp = (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422) ? 16 :
> +										24;
> +	rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
> +	*num_input_fmts = 0;
> +	input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts), sizeof(*input_fmts),
> +			     GFP_KERNEL);
> +	if (!input_fmts)
> +		return NULL;
> +
> +	*num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
> +
> +	memcpy(input_fmts, mt8195_input_fmts,
> +	       sizeof(*input_fmts) * ARRAY_SIZE(mt8195_input_fmts));
> +
> +	if (((rx_linkrate * mtk_dp->train_info.lane_count) <
> +	     (mode->clock * 24 / 8)) &&
> +	    ((rx_linkrate * mtk_dp->train_info.lane_count) >
> +	     (mode->clock * 16 / 8)) &&
> +	    (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422)) {
> +		kfree(input_fmts);
> +		input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL);
> +		*num_input_fmts = 1;
> +		input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
> +	}
> +
> +	return input_fmts;
> +}
> +
> +static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
> +				      struct drm_bridge_state *bridge_state,
> +				      struct drm_crtc_state *crtc_state,
> +				      struct drm_connector_state *conn_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	unsigned int input_bus_format;
> +
> +	input_bus_format = bridge_state->input_bus_cfg.format;
> +
> +	dev_info(mtk_dp->dev, "input format 0x%04x, output format 0x%04x\n",
> +		 bridge_state->input_bus_cfg.format,
> +		 bridge_state->output_bus_cfg.format);
> +
> +	mtk_dp->input_fmt = input_bus_format;
> +	if (mtk_dp->input_fmt == MEDIA_BUS_FMT_YUYV8_1X16)
> +		mtk_dp->info.format = MTK_DP_COLOR_FORMAT_YUV_422;
> +	else
> +		mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
> +
> +	return 0;
> +}
> +
> +static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
> +	.atomic_check = mtk_dp_bridge_atomic_check,
> +	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> +	.atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts,
> +	.atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts,
> +	.atomic_reset = drm_atomic_helper_bridge_reset,
> +	.attach = mtk_dp_bridge_attach,
> +	.detach = mtk_dp_bridge_detach,
> +	.atomic_enable = mtk_dp_bridge_atomic_enable,
> +	.atomic_disable = mtk_dp_bridge_atomic_disable,
> +	.mode_valid = mtk_dp_bridge_mode_valid,
> +	.get_edid = mtk_dp_get_edid,
> +	.detect = mtk_dp_bdg_detect,
> +};
> +
> +/*
> + * HDMI audio codec callbacks
> + */
> +static int mtk_dp_audio_hw_params(struct device *dev, void *data,
> +				  struct hdmi_codec_daifmt *daifmt,
> +				  struct hdmi_codec_params *params)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +	struct mtk_dp_audio_cfg cfg;
> +
> +	if (!mtk_dp->enabled) {
> +		pr_err("%s, DP is not ready!\n", __func__);
> +		return -ENODEV;
> +	}
> +
> +	cfg.channels = params->cea.channels;
> +	cfg.sample_rate = params->sample_rate;
> +	cfg.word_length_bits = 24;
> +
> +	mtk_dp_audio_setup(mtk_dp, &cfg);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_audio_startup(struct device *dev, void *data)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	mtk_dp_audio_mute(mtk_dp, false);
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_audio_shutdown(struct device *dev, void *data)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	mtk_dp_audio_mute(mtk_dp, true);
> +}
> +
> +static int mtk_dp_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
> +				size_t len)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	if (mtk_dp->enabled)
> +		memcpy(buf, mtk_dp->connector_eld, len);
> +	else
> +		memset(buf, 0, len);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_audio_hook_plugged_cb(struct device *dev, void *data,
> +					hdmi_codec_plugged_cb fn,
> +					struct device *codec_dev)
> +{
> +	struct mtk_dp *mtk_dp = data;
> +
> +	mutex_lock(&mtk_dp->update_plugged_status_lock);
> +	mtk_dp->plugged_cb = fn;
> +	mtk_dp->codec_dev = codec_dev;
> +	mutex_unlock(&mtk_dp->update_plugged_status_lock);
> +
> +	mtk_dp_update_plugged_status(mtk_dp);
> +
> +	return 0;
> +}
> +
> +static const struct hdmi_codec_ops mtk_dp_audio_codec_ops = {
> +	.hw_params = mtk_dp_audio_hw_params,
> +	.audio_startup = mtk_dp_audio_startup,
> +	.audio_shutdown = mtk_dp_audio_shutdown,
> +	.get_eld = mtk_dp_audio_get_eld,
> +	.hook_plugged_cb = mtk_dp_audio_hook_plugged_cb,
> +	.no_capture_mute = 1,
> +};
> +
> +static int mtk_dp_register_audio_driver(struct device *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +	struct hdmi_codec_pdata codec_data = {
> +		.ops = &mtk_dp_audio_codec_ops,
> +		.max_i2s_channels = 8,
> +		.i2s = 1,
> +		.data = mtk_dp,
> +	};
> +	struct platform_device *pdev;
> +
> +	pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
> +					     PLATFORM_DEVID_AUTO, &codec_data,
> +					     sizeof(codec_data));
> +	if (IS_ERR(pdev))
> +		return PTR_ERR(pdev);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_probe(struct platform_device *pdev)
> +{
> +	struct mtk_dp *mtk_dp;
> +	struct device *dev = &pdev->dev;
> +	int ret;
> +	int irq_num = 0;
> +	struct drm_panel *panel = NULL;
> +
> +	mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> +	if (!mtk_dp)
> +		return -ENOMEM;
> +
> +	mtk_dp->dev = dev;
> +
> +	irq_num = platform_get_irq(pdev, 0);
> +	if (irq_num < 0) {
> +		dev_err(dev, "failed to request dp irq resource\n");
> +		return -EPROBE_DEFER;
> +	}
> +
> +	ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel,
> +					  &mtk_dp->next_bridge);
> +	if (ret == -ENODEV) {
> +		dev_info(
> +			dev,
> +			"No panel connected in devicetree, continuing as external DP\n");
> +		mtk_dp->next_bridge = NULL;
> +	} else if (ret) {
> +		dev_err(dev, "Failed to find panel or bridge: %d\n", ret);
> +		return ret;
> +	}
> +
> +	if (panel) {
> +		mtk_dp->next_bridge = devm_drm_panel_bridge_add(dev, panel);
> +		if (IS_ERR(mtk_dp->next_bridge)) {
> +			ret = PTR_ERR(mtk_dp->next_bridge);
> +			dev_err(dev, "Failed to create bridge: %d\n", ret);
> +			return -EPROBE_DEFER;
> +		}
> +	}

You should be using devm_drm_of_get_bridge here

> +	ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> +	if (ret)
> +		return ret;

pdata?

> +
> +	mtk_dp_aux_init(mtk_dp);
> +
> +	ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
> +					mtk_dp_hpd_event_thread,
> +					IRQ_TYPE_LEVEL_HIGH, dev_name(dev),
> +					mtk_dp);
> +	if (ret) {
> +		dev_err(dev, "failed to request mediatek dptx irq\n");
> +		return -EPROBE_DEFER;
> +	}
> +
> +	mutex_init(&mtk_dp->dp_lock);
> +	mutex_init(&mtk_dp->edid_lock);
> +
> +	platform_set_drvdata(pdev, mtk_dp);
> +
> +	if (!mtk_dp_is_edp(mtk_dp)) {
> +		mutex_init(&mtk_dp->update_plugged_status_lock);
> +		ret = mtk_dp_register_audio_driver(dev);
> +		if (ret) {
> +			dev_err(dev, "Failed to register audio driver: %d\n",
> +				ret);
> +			return ret;
> +		}
> +	}
> +
> +	mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
> +							PLATFORM_DEVID_AUTO,
> +							&mtk_dp->regs,
> +							sizeof(&mtk_dp->regs));
> +	if (IS_ERR(mtk_dp->phy_dev)) {
> +		dev_err(dev, "Failed to create device mediatek-dp-phy: %ld\n",
> +			PTR_ERR(mtk_dp->phy_dev));
> +		return PTR_ERR(mtk_dp->phy_dev);
> +	}
> +
> +	mtk_dp_get_calibration_data(mtk_dp);
> +
> +	mtk_dp->phy = dev_get_drvdata(&mtk_dp->phy_dev->dev);
> +	if (IS_ERR(mtk_dp->phy)) {
> +		dev_err(dev, "Failed to get phy: %ld\n", PTR_ERR(mtk_dp->phy));
> +		platform_device_unregister(mtk_dp->phy_dev);
> +		return PTR_ERR(mtk_dp->phy);
> +	}
> +
> +	mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
> +	mtk_dp->bridge.of_node = dev->of_node;
> +	if (mtk_dp_is_edp(mtk_dp))
> +		mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> +	else
> +		mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
> +
> +	mtk_dp->bridge.ops =
> +		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
> +	drm_bridge_add(&mtk_dp->bridge);
> +
> +	pm_runtime_enable(dev);
> +	pm_runtime_get_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_remove(struct platform_device *pdev)
> +{
> +	struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
> +
> +	platform_device_unregister(mtk_dp->phy_dev);
> +
> +	mtk_dp_video_mute(mtk_dp, true);
> +	mtk_dp_audio_mute(mtk_dp, true);
> +
> +	pm_runtime_disable(&pdev->dev);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int mtk_dp_suspend(struct device *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	mtk_dp_power_disable(mtk_dp);
> +	mtk_dp_hwirq_enable(mtk_dp, false);
> +
> +	pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_resume(struct device *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	pm_runtime_get_sync(dev);
> +
> +	mtk_dp_init_port(mtk_dp);
> +	mtk_dp_power_enable(mtk_dp);
> +	mtk_dp_hwirq_enable(mtk_dp, true);
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
> +
> +static const struct of_device_id mtk_dp_of_match[] = {
> +	{
> +		.compatible = "mediatek,mt8195-dp-tx",
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
> +
> +struct platform_driver mtk_dp_driver = {
> +	.probe = mtk_dp_probe,
> +	.remove = mtk_dp_remove,
> +	.driver = {
> +		.name = "mediatek-drm-dp",
> +		.of_match_table = mtk_dp_of_match,
> +		.pm = &mtk_dp_pm_ops,
> +	},
> +};
> +
> +MODULE_AUTHOR("Jason-JH.Lin <jason-jh.lin@mediatek.com>");
> +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> +MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> new file mode 100644
> index 0000000000000..79952ac30e9e6
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> @@ -0,0 +1,568 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019 MediaTek Inc.
> + * Copyright (c) 2021 BayLibre
> + */
> +#ifndef _MTK_DP_REG_H_
> +#define _MTK_DP_REG_H_
> +
> +#define MTK_DP_SIP_CONTROL_AARCH32 0x82000523
> +#define MTK_DP_SIP_ATF_VIDEO_UNMUTE 0x20
> +#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE 0x21
> +
> +#define DP_PHY_GLB_BIAS_GEN_00 0x0000
> +#define RG_XTP_GLB_BIAS_INTR_CTRL (0x1f << 16)
> +
> +#define DP_PHY_GLB_DPAUX_TX 0x0008
> +#define RG_CKM_PT0_CKTX_IMPSEL (0xf << 20)
> +
> +#define DP_PHY_LANE_TX_0 0x104
> +#define RG_XTP_LN0_TX_IMPSEL_PMOS (0xf << 12)
> +#define RG_XTP_LN0_TX_IMPSEL_NMOS (0xf << 16)
> +
> +#define DP_PHY_LANE_TX_1 0x204
> +#define RG_XTP_LN1_TX_IMPSEL_PMOS (0xf << 12)
> +#define RG_XTP_LN1_TX_IMPSEL_NMOS (0xf << 16)
> +
> +#define DP_PHY_LANE_TX_2 0x304
> +#define RG_XTP_LN2_TX_IMPSEL_PMOS (0xf << 12)
> +#define RG_XTP_LN2_TX_IMPSEL_NMOS (0xf << 16)
> +
> +#define DP_PHY_LANE_TX_3 0x404
> +#define RG_XTP_LN3_TX_IMPSEL_PMOS (0xf << 12)
> +#define RG_XTP_LN3_TX_IMPSEL_NMOS (0xf << 16)
> +
> +#define TOP_OFFSET 0x2000
> +#define ENC0_OFFSET 0x3000
> +#define ENC1_OFFSET 0x3200
> +#define TRANS_OFFSET 0x3400
> +#define AUX_OFFSET 0x3600
> +#define SEC_OFFSET 0x4000
> +
> +#define MTK_DP_HPD_DISCONNECT BIT(1)
> +#define MTK_DP_HPD_CONNECT BIT(2)
> +#define MTK_DP_HPD_INTERRUPT BIT(3)
> +
> +#define MTK_DP_0034 0x034
> +#define DA_XTP_GLB_CKDET_EN_FORCE_VAL BIT(15)
> +#define DA_XTP_GLB_CKDET_EN_FORCE_EN BIT(14)
> +#define DA_CKM_INTCKTX_EN_FORCE_VAL BIT(13)
> +#define DA_CKM_INTCKTX_EN_FORCE_EN BIT(12)
> +#define DA_CKM_CKTX0_EN_FORCE_VAL BIT(11)
> +#define DA_CKM_CKTX0_EN_FORCE_EN BIT(10)
> +#define DA_CKM_XTAL_CK_FORCE_VAL BIT(9)
> +#define DA_CKM_XTAL_CK_FORCE_EN BIT(8)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_VAL BIT(7)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_EN BIT(6)
> +#define DA_CKM_BIAS_EN_FORCE_VAL BIT(5)
> +#define DA_CKM_BIAS_EN_FORCE_EN BIT(4)
> +#define DA_XTP_GLB_AVD10_ON_FORCE_VAL BIT(3)
> +#define DA_XTP_GLB_AVD10_ON_FORCE BIT(2)
> +#define DA_XTP_GLB_LDO_EN_FORCE_VAL BIT(1)
> +#define DA_XTP_GLB_LDO_EN_FORCE_EN BIT(0)
> +
> +#define MTK_DP_1040 0x1040
> +#define RG_DPAUX_RX_VALID_DEGLITCH_EN BIT(2)
> +#define RG_XTP_GLB_CKDET_EN BIT(1)
> +#define RG_DPAUX_RX_EN BIT(0)
> +
> +#define MTK_DP_ENC0_P0_3000 (ENC0_OFFSET + 0x000)
> +#define LANE_NUM_DP_ENC0_P0_MASK 0x3
> +#define VIDEO_MUTE_SW_DP_ENC0_P0_MASK 0x4
> +#define VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT 2
> +#define VIDEO_MUTE_SEL_DP_ENC0_P0_MASK 0x8
> +#define VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT 3
> +#define ENHANCED_FRAME_EN_DP_ENC0_P0_MASK 0x10
> +#define ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT 4
> +
> +#define MTK_DP_ENC0_P0_3004 (ENC0_OFFSET + 0x004)
> +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK 0x100
> +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_SHIFT 8
> +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK 0x200
> +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT 9
> +
> +#define MTK_DP_ENC0_P0_3008 (ENC0_OFFSET + 0x008)
> +#define VIDEO_M_CODE_SW_0_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_300C (ENC0_OFFSET + 0x00C)
> +#define VIDEO_M_CODE_SW_1_DP_ENC0_P0_MASK 0xff
> +
> +#define MTK_DP_ENC0_P0_3010 (ENC0_OFFSET + 0x010)
> +#define HTOTAL_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3014 (ENC0_OFFSET + 0x014)
> +#define VTOTAL_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3018 (ENC0_OFFSET + 0x018)
> +#define HSTART_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_301C (ENC0_OFFSET + 0x01C)
> +#define VSTART_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3020 (ENC0_OFFSET + 0x020)
> +#define HWIDTH_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3024 (ENC0_OFFSET + 0x024)
> +#define VHEIGHT_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3028 (ENC0_OFFSET + 0x028)
> +#define HSW_SW_DP_ENC0_P0_MASK 0x7fff
> +#define HSW_SW_DP_ENC0_P0_SHIFT 0
> +#define HSP_SW_DP_ENC0_P0_MASK 0x8000
> +
> +#define MTK_DP_ENC0_P0_302C (ENC0_OFFSET + 0x02C)
> +#define VSW_SW_DP_ENC0_P0_MASK 0x7fff
> +#define VSW_SW_DP_ENC0_P0_SHIFT 0
> +#define VSP_SW_DP_ENC0_P0_MASK 0x8000
> +
> +#define MTK_DP_ENC0_P0_3030 (ENC0_OFFSET + 0x030)
> +#define HTOTAL_SEL_DP_ENC0_P0_SHIFT 0
> +#define VTOTAL_SEL_DP_ENC0_P0_SHIFT 1
> +#define HSTART_SEL_DP_ENC0_P0_SHIFT 2
> +#define VSTART_SEL_DP_ENC0_P0_SHIFT 3
> +#define HWIDTH_SEL_DP_ENC0_P0_SHIFT 4
> +#define VHEIGHT_SEL_DP_ENC0_P0_SHIFT 5
> +#define HSP_SEL_DP_ENC0_P0_SHIFT 6
> +#define HSW_SEL_DP_ENC0_P0_SHIFT 7
> +#define VSP_SEL_DP_ENC0_P0_SHIFT 8
> +#define VSW_SEL_DP_ENC0_P0_SHIFT 9
> +#define VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK 0x800
> +#define VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT 11
> +#define VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK 0x1000
> +#define VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT 12
> +
> +#define MTK_DP_ENC0_P0_3034 (ENC0_OFFSET + 0x034)
> +
> +#define MTK_DP_ENC0_P0_3038 (ENC0_OFFSET + 0x038)
> +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK 0x800
> +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_SHIFT 11
> +
> +#define MTK_DP_ENC0_P0_303C (ENC0_OFFSET + 0x03C)
> +#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK 0x3f
> +#define SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT 0
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK 0x700
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT 8
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT                                     \
> +	(0 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT                                     \
> +	(1 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT                                     \
> +	(2 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT                                      \
> +	(3 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT                                      \
> +	(4 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK 0x7000
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT 12
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB                                     \
> +	(0 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422                                \
> +	(1 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420                                \
> +	(2 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> +#define VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK 0x8000
> +#define VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT 15
> +
> +#define MTK_DP_ENC0_P0_3040 (ENC0_OFFSET + 0x040)
> +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK 0xfff
> +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT 0
> +
> +#define MTK_DP_ENC0_P0_3044 (ENC0_OFFSET + 0x044)
> +#define VIDEO_N_CODE_0_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3048 (ENC0_OFFSET + 0x048)
> +#define VIDEO_N_CODE_1_DP_ENC0_P0_MASK 0xff
> +
> +#define MTK_DP_ENC0_P0_304C (ENC0_OFFSET + 0x04C)
> +#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK 0x4
> +#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK 0x100
> +
> +#define MTK_DP_ENC0_P0_3050 (ENC0_OFFSET + 0x050)
> +#define VIDEO_N_CODE_MN_GEN_0_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3054 (ENC0_OFFSET + 0x054)
> +#define VIDEO_N_CODE_MN_GEN_1_DP_ENC0_P0_MASK 0xff
> +
> +#define MTK_DP_ENC0_P0_3064 (ENC0_OFFSET + 0x064)
> +#define HDE_NUM_LAST_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3088 (ENC0_OFFSET + 0x088)
> +#define AU_EN_DP_ENC0_P0_MASK 0x40
> +#define AU_EN_DP_ENC0_P0_SHIFT 6
> +#define AUDIO_8CH_EN_DP_ENC0_P0_MASK 0x80
> +#define AUDIO_8CH_SEL_DP_ENC0_P0_MASK 0x100
> +#define AUDIO_2CH_EN_DP_ENC0_P0_MASK 0x4000
> +#define AUDIO_2CH_SEL_DP_ENC0_P0_MASK 0x8000
> +
> +#define MTK_DP_ENC0_P0_308C (ENC0_OFFSET + 0x08C)
> +#define CH_STATUS_0_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3090 (ENC0_OFFSET + 0x090)
> +#define CH_STATUS_1_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3094 (ENC0_OFFSET + 0x094)
> +#define CH_STATUS_2_DP_ENC0_P0_MASK 0xff
> +
> +#define MTK_DP_ENC0_P0_30A0 (ENC0_OFFSET + 0x0A0)
> +
> +#define MTK_DP_ENC0_P0_30A4 (ENC0_OFFSET + 0x0A4)
> +#define AU_TS_CFG_DP_ENC0_P0_MASK 0xff
> +
> +#define MTK_DP_ENC0_P0_30A8 (ENC0_OFFSET + 0x0A8)
> +
> +#define MTK_DP_ENC0_P0_30AC (ENC0_OFFSET + 0x0AC)
> +
> +#define MTK_DP_ENC0_P0_30B0 (ENC0_OFFSET + 0x0B0)
> +
> +#define MTK_DP_ENC0_P0_30B4 (ENC0_OFFSET + 0x0B4)
> +#define ISRC_CFG_DP_ENC0_P0_MASK 0xff00
> +#define ISRC_CFG_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC0_P0_30B8 (ENC0_OFFSET + 0x0B8)
> +
> +#define MTK_DP_ENC0_P0_30BC (ENC0_OFFSET + 0x0BC)
> +#define ISRC_CONT_DP_ENC0_P0_MASK 0x1
> +#define ISRC_CONT_DP_ENC0_P0_SHIFT 0
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK 0x700
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT 8
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_2                             \
> +	(1 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_4                             \
> +	(2 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_8                             \
> +	(3 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2                             \
> +	(5 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_4                             \
> +	(6 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_8                             \
> +	(7 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +
> +#define MTK_DP_ENC0_P0_30D8 (ENC0_OFFSET + 0x0D8)
> +
> +#define MTK_DP_ENC0_P0_312C (ENC0_OFFSET + 0x12C)
> +#define ASP_HB2_DP_ENC0_P0_MASK 0xff
> +#define ASP_HB3_DP_ENC0_P0_MASK 0xff00
> +#define ASP_HB3_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC0_P0_3130 (ENC0_OFFSET + 0x130)
> +
> +#define MTK_DP_ENC0_P0_3138 (ENC0_OFFSET + 0x138)
> +
> +#define MTK_DP_ENC0_P0_3154 (ENC0_OFFSET + 0x154)
> +#define PGEN_HTOTAL_DP_ENC0_P0_MASK 0x3fff
> +
> +#define MTK_DP_ENC0_P0_3158 (ENC0_OFFSET + 0x158)
> +#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK 0x3fff
> +
> +#define MTK_DP_ENC0_P0_315C (ENC0_OFFSET + 0x15C)
> +#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x3fff
> +
> +#define MTK_DP_ENC0_P0_3160 (ENC0_OFFSET + 0x160)
> +#define PGEN_HFDE_START_DP_ENC0_P0_MASK 0x3fff
> +
> +#define MTK_DP_ENC0_P0_3164 (ENC0_OFFSET + 0x164)
> +#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x3fff
> +
> +#define MTK_DP_ENC0_P0_3168 (ENC0_OFFSET + 0x168)
> +#define PGEN_VTOTAL_DP_ENC0_P0_MASK 0x1fff
> +
> +#define MTK_DP_ENC0_P0_316C (ENC0_OFFSET + 0x16C)
> +#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK 0x1fff
> +
> +#define MTK_DP_ENC0_P0_3170 (ENC0_OFFSET + 0x170)
> +#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x1fff
> +
> +#define MTK_DP_ENC0_P0_3174 (ENC0_OFFSET + 0x174)
> +#define PGEN_VFDE_START_DP_ENC0_P0_MASK 0x1fff
> +
> +#define MTK_DP_ENC0_P0_3178 (ENC0_OFFSET + 0x178)
> +#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x1fff
> +
> +#define MTK_DP_ENC0_P0_31B0 (ENC0_OFFSET + 0x1B0)
> +#define PGEN_PATTERN_SEL_SHIFT 4
> +#define PGEN_PATTERN_SEL_MASK 0x0070
> +
> +#define MTK_DP_ENC0_P0_31C8 (ENC0_OFFSET + 0x1C8)
> +#define VSC_EXT_VESA_HB0_DP_ENC0_P0_MASK 0xff
> +#define VSC_EXT_VESA_HB1_DP_ENC0_P0_MASK 0xff00
> +#define VSC_EXT_VESA_HB1_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC0_P0_31CC (ENC0_OFFSET + 0x1CC)
> +#define VSC_EXT_VESA_HB2_DP_ENC0_P0_MASK 0xff
> +#define VSC_EXT_VESA_HB2_DP_ENC0_P0_SHIFT 0
> +#define VSC_EXT_VESA_HB3_DP_ENC0_P0_MASK 0xff00
> +
> +#define MTK_DP_ENC0_P0_31D0 (ENC0_OFFSET + 0x1D0)
> +#define VSC_EXT_CEA_HB0_DP_ENC0_P0_MASK 0xff
> +#define VSC_EXT_CEA_HB1_DP_ENC0_P0_MASK 0xff00
> +#define VSC_EXT_CEA_HB1_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC0_P0_31D4 (ENC0_OFFSET + 0x1D4)
> +#define VSC_EXT_CEA_HB2_DP_ENC0_P0_MASK 0xff
> +#define VSC_EXT_CEA_HB2_DP_ENC0_P0_SHIFT 0
> +#define VSC_EXT_CEA_HB3_DP_ENC0_P0_MASK 0xff00
> +
> +#define MTK_DP_ENC0_P0_31D8 (ENC0_OFFSET + 0x1D8)
> +#define VSC_EXT_VESA_NUM_DP_ENC0_P0_MASK 0x3f
> +#define VSC_EXT_VESA_NUM_DP_ENC0_P0_SHIFT 0
> +#define VSC_EXT_CEA_NUM_DP_ENC0_P0_MASK 0x3f00
> +#define VSC_EXT_CEA_NUM_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC0_P0_31DC (ENC0_OFFSET + 0x1DC)
> +#define HDR0_CFG_DP_ENC0_P0_MASK 0xff
> +#define HDR0_CFG_DP_ENC0_P0_SHIFT 0
> +
> +#define MTK_DP_ENC0_P0_31E8 (ENC0_OFFSET + 0x1E8)
> +
> +#define MTK_DP_ENC0_P0_31EC (ENC0_OFFSET + 0x1EC)
> +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK 0x10
> +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT 4
> +#define ISRC1_HB3_DP_ENC0_P0_MASK 0xff00
> +#define ISRC1_HB3_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC1_P0_3200 (ENC1_OFFSET + 0x000)
> +
> +#define MTK_DP_ENC1_P0_3280 (ENC1_OFFSET + 0x080)
> +#define SDP_PACKET_TYPE_DP_ENC1_P0_MASK 0x1f
> +#define SDP_PACKET_W_DP_ENC1_P0 0x20
> +#define SDP_PACKET_W_DP_ENC1_P0_MASK 0x20
> +#define SDP_PACKET_W_DP_ENC1_P0_SHIFT 5
> +
> +#define MTK_DP_ENC1_P0_328C (ENC1_OFFSET + 0x08C)
> +
> +#define MTK_DP_ENC1_P0_3290 (ENC1_OFFSET + 0x090)
> +
> +#define MTK_DP_ENC1_P0_32A0 (ENC1_OFFSET + 0x0A0)
> +
> +#define MTK_DP_ENC1_P0_32A4 (ENC1_OFFSET + 0x0A4)
> +
> +#define MTK_DP_ENC1_P0_3300 (ENC1_OFFSET + 0x100)
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK 0x300
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT 8
> +
> +#define MTK_DP_ENC1_P0_3304 (ENC1_OFFSET + 0x104)
> +#define AU_PRTY_REGEN_DP_ENC1_P0_MASK 0x100
> +#define AU_CH_STS_REGEN_DP_ENC1_P0_MASK 0x200
> +#define AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK 0x1000
> +
> +#define MTK_DP_ENC1_P0_3324 (ENC1_OFFSET + 0x124)
> +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK 0x300
> +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT 8
> +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX                                       \
> +	(0 << AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT)
> +
> +#define MTK_DP_ENC1_P0_3364 (ENC1_OFFSET + 0x164)
> +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK 0xfff
> +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT 0
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK 0xf000
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT 12
> +
> +#define MTK_DP_ENC1_P0_3368 (ENC1_OFFSET + 0x168)
> +#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT 0
> +#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT 4
> +#define SDP_DP13_EN_DP_ENC1_P0_SHIFT 8
> +#define BS2BS_MODE_DP_ENC1_P0_MASK 0x3000
> +#define BS2BS_MODE_DP_ENC1_P0_SHIFT 12
> +
> +#define MTK_DP_ENC1_P0_33F4 (ENC1_OFFSET + 0x1F4)
> +
> +#define MTK_DP_TRANS_P0_3400 (TRANS_OFFSET + 0x000)
> +#define PATTERN1_EN_DP_TRANS_P0_MASK 0x1000
> +#define PATTERN1_EN_DP_TRANS_P0_SHIFT 12
> +#define PATTERN2_EN_DP_TRANS_P0_MASK 0x2000
> +#define PATTERN3_EN_DP_TRANS_P0_MASK 0x4000
> +#define PATTERN4_EN_DP_TRANS_P0_MASK 0x8000
> +
> +#define MTK_DP_TRANS_P0_3404 (TRANS_OFFSET + 0x004)
> +#define DP_SCR_EN_DP_TRANS_P0_MASK 0x1
> +
> +#define MTK_DP_TRANS_P0_340C (TRANS_OFFSET + 0x00C)
> +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK 0x2000
> +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT 13
> +
> +#define MTK_DP_TRANS_P0_3410 (TRANS_OFFSET + 0x010)
> +#define HPD_DEB_THD_DP_TRANS_P0_MASK 0xf
> +#define HPD_DEB_THD_DP_TRANS_P0_SHIFT 0
> +#define HPD_INT_THD_DP_TRANS_P0_MASK 0xf0
> +#define HPD_INT_THD_DP_TRANS_P0_SHIFT 4
> +#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US (2 << HPD_INT_THD_DP_TRANS_P0_SHIFT)
> +#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US                                   \
> +	(2 << (HPD_INT_THD_DP_TRANS_P0_SHIFT + 2))
> +#define HPD_DISC_THD_DP_TRANS_P0_MASK 0xf00
> +#define HPD_DISC_THD_DP_TRANS_P0_SHIFT 8
> +#define HPD_CONN_THD_DP_TRANS_P0_MASK 0xf000
> +#define HPD_CONN_THD_DP_TRANS_P0_SHIFT 12
> +
> +#define MTK_DP_TRANS_P0_3414 (TRANS_OFFSET + 0x014)
> +#define HPD_DB_DP_TRANS_P0_MASK 0x4
> +
> +#define MTK_DP_TRANS_P0_3418 (TRANS_OFFSET + 0x018)
> +#define IRQ_CLR_DP_TRANS_P0_MASK 0xf
> +#define IRQ_MASK_DP_TRANS_P0_MASK 0xf0
> +#define IRQ_MASK_DP_TRANS_P0_SHIFT 4
> +#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ (BIT(1) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> +#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ (BIT(2) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> +#define IRQ_MASK_DP_TRANS_P0_INT_IRQ (BIT(3) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> +#define IRQ_STATUS_DP_TRANS_P0_MASK 0xf000
> +#define IRQ_STATUS_DP_TRANS_P0_SHIFT 12
> +
> +#define MTK_DP_TRANS_P0_342C (TRANS_OFFSET + 0x02C)
> +#define XTAL_FREQ_DP_TRANS_P0_DEFAULT 0x69
> +#define XTAL_FREQ_DP_TRANS_P0_MASK 0xff
> +
> +#define MTK_DP_TRANS_P0_3430 (TRANS_OFFSET + 0x030)
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK 0x3
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT BIT(1)
> +
> +#define MTK_DP_TRANS_P0_34A4 (TRANS_OFFSET + 0x0A4)
> +#define LANE_NUM_DP_TRANS_P0_MASK 0xc
> +#define LANE_NUM_DP_TRANS_P0_SHIFT 2
> +
> +#define MTK_DP_TRANS_P0_3540 (TRANS_OFFSET + 0x140)
> +#define FEC_EN_DP_TRANS_P0_MASK 0x1
> +#define FEC_EN_DP_TRANS_P0_SHIFT 0
> +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK 0x8
> +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT 3
> +
> +#define MTK_DP_TRANS_P0_3580 (TRANS_OFFSET + 0x180)
> +#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK 0x100
> +#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK 0x200
> +#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK 0x400
> +#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK 0x800
> +
> +#define MTK_DP_TRANS_P0_35C4 (TRANS_OFFSET + 0x1C4)
> +#define SW_IRQ_MASK_DP_TRANS_P0_MASK 0xffff
> +
> +#define MTK_DP_TRANS_P0_35C8 (TRANS_OFFSET + 0x1C8)
> +#define SW_IRQ_CLR_DP_TRANS_P0_MASK 0xffff
> +
> +#define SW_IRQ_STATUS_DP_TRANS_P0_MASK 0xffff
> +#define SW_IRQ_STATUS_DP_TRANS_P0_SHIFT 0
> +
> +#define MTK_DP_TRANS_P0_35D0 (TRANS_OFFSET + 0x1D0)
> +#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK 0xffff
> +
> +#define MTK_DP_TRANS_P0_35F0 (TRANS_OFFSET + 0x1F0)
> +
> +#define MTK_DP_AUX_P0_360C (AUX_OFFSET + 0x00C)
> +#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK 0x1fff
> +
> +#define MTK_DP_AUX_P0_3614 (AUX_OFFSET + 0x014)
> +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK 0x7f
> +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT 0
> +
> +#define MTK_DP_AUX_P0_3618 (AUX_OFFSET + 0x018)
> +#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK 0x200
> +#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK 0xf
> +
> +#define MTK_DP_AUX_P0_3620 (AUX_OFFSET + 0x020)
> +#define AUX_RD_MODE_AUX_TX_P0_MASK 0x200
> +#define AUX_RX_FIFO_READ_PULSE_TX_P0_MASK 0x100
> +#define AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT 8
> +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK 0xff
> +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT 0
> +
> +#define MTK_DP_AUX_P0_3624 (AUX_OFFSET + 0x024)
> +#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK 0xf
> +
> +#define MTK_DP_AUX_P0_3628 (AUX_OFFSET + 0x028)
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK 0x3ff
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT 0
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE                                     \
> +	(BIT(0) << AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT)
> +
> +#define MTK_DP_AUX_P0_362C (AUX_OFFSET + 0x02C)
> +#define AUX_NO_LENGTH_AUX_TX_P0_MASK 0x1
> +#define AUX_NO_LENGTH_AUX_TX_P0_SHIFT 0
> +#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK 0x2
> +#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK 0xfffc
> +
> +#define MTK_DP_AUX_P0_3630 (AUX_OFFSET + 0x030)
> +#define AUX_TX_REQUEST_READY_AUX_TX_P0_MASK 0x8
> +#define AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT 3
> +
> +#define MTK_DP_AUX_P0_3634 (AUX_OFFSET + 0x034)
> +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK 0xff00
> +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT 8
> +
> +#define MTK_DP_AUX_P0_3640 (AUX_OFFSET + 0x040)
> +#define AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK 0x40
> +#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 6
> +#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 5
> +#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 4
> +#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT 3
> +#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT 2
> +#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT 1
> +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK 0x1
> +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT 0
> +
> +#define MTK_DP_AUX_P0_3644 (AUX_OFFSET + 0x044)
> +#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK 0xf
> +
> +#define MTK_DP_AUX_P0_3648 (AUX_OFFSET + 0x048)
> +#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK 0xffff
> +
> +#define MTK_DP_AUX_P0_364C (AUX_OFFSET + 0x04C)
> +#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK 0xf
> +
> +#define MTK_DP_AUX_P0_3650 (AUX_OFFSET + 0x050)
> +#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK 0xf000
> +#define MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT 12
> +#define PHY_FIFO_RST_AUX_TX_P0_MASK 0x200
> +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK 0x100
> +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT 8
> +
> +#define MTK_DP_AUX_P0_3658 (AUX_OFFSET + 0x058)
> +#define AUX_TX_OV_EN_AUX_TX_P0_MASK 0x1
> +
> +#define MTK_DP_AUX_P0_3690 (AUX_OFFSET + 0x090)
> +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK 0x100
> +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT 8
> +
> +#define MTK_DP_AUX_P0_3704 (AUX_OFFSET + 0x104)
> +#define AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK 0x2
> +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK 0x4
> +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT 2
> +
> +#define MTK_DP_AUX_P0_3708 (AUX_OFFSET + 0x108)
> +
> +#define MTK_DP_AUX_P0_37C8 (AUX_OFFSET + 0x1C8)
> +#define MTK_ATOP_EN_AUX_TX_P0_MASK 0x1
> +#define MTK_ATOP_EN_AUX_TX_P0_SHIFT 0
> +
> +#define MTK_DP_TOP_PWR_STATE (TOP_OFFSET + 0x00)
> +#define DP_PWR_STATE_MASK 0x3
> +#define DP_PWR_STATE_BANDGAP 1
> +#define DP_PWR_STATE_BANDGAP_TPLL 2
> +#define DP_PWR_STATE_BANDGAP_TPLL_LANE 3
> +
> +#define MTK_DP_TOP_SWING_EMP (TOP_OFFSET + 0x04)
> +#define DP_TX0_VOLT_SWING_MASK 0x3
> +#define DP_TX0_VOLT_SWING_SHIFT 0
> +#define DP_TX0_PRE_EMPH_MASK 0xc
> +#define DP_TX0_PRE_EMPH_SHIFT 2
> +#define DP_TX1_VOLT_SWING_MASK 0x300
> +#define DP_TX1_VOLT_SWING_SHIFT 8
> +#define DP_TX1_PRE_EMPH_MASK 0xc00
> +#define DP_TX2_VOLT_SWING_MASK 0x30000
> +#define DP_TX2_PRE_EMPH_MASK 0xc0000
> +#define DP_TX3_VOLT_SWING_MASK 0x3000000
> +#define DP_TX3_PRE_EMPH_MASK 0xc000000
> +
> +#define MTK_DP_TOP_RESET_AND_PROBE (TOP_OFFSET + 0x20)
> +#define SW_RST_B_SHIFT 0
> +#define SW_RST_B_PHYD (BIT(4) << SW_RST_B_SHIFT)
> +
> +#define MTK_DP_TOP_IRQ_STATUS (TOP_OFFSET + 0x28)
> +#define RGS_IRQ_STATUS_SHIFT 0
> +#define RGS_IRQ_STATUS_TRANSMITTER (BIT(1) << RGS_IRQ_STATUS_SHIFT)
> +
> +#define MTK_DP_TOP_IRQ_MASK (TOP_OFFSET + 0x2C)
> +#define IRQ_MASK_AUX_TOP_IRQ BIT(2)
> +
> +#define MTK_DP_TOP_MEM_PD (TOP_OFFSET + 0x38)
> +#define MEM_ISO_EN_SHIFT 0
> +#define FUSE_SEL_SHIFT 2
> +
> +#endif /*_MTK_DP_REG_H_*/
> diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> index 384074f69111b..e6e88e3cd811d 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
>  	MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
>  };
>  
> +enum TVDPLL_CLK {
> +	TVDPLL_PLL = 0,
> +	TVDPLL_D2 = 2,
> +	TVDPLL_D4 = 4,
> +	TVDPLL_D8 = 8,
> +	TVDPLL_D16 = 16,
> +};
> +
>  struct mtk_dpi {
>  	struct drm_encoder encoder;
>  	struct drm_bridge bridge;
> @@ -71,8 +79,10 @@ struct mtk_dpi {
>  	void __iomem *regs;
>  	struct device *dev;
>  	struct clk *engine_clk;
> +	struct clk *dpi_ck_cg;
>  	struct clk *pixel_clk;
>  	struct clk *tvd_clk;
> +	struct clk *pclk_src[5];
>  	int irq;
>  	struct drm_display_mode mode;
>  	const struct mtk_dpi_conf *conf;
> @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
>  	u32 hvsize_mask;
>  	u32 channel_swap_shift;
>  	u32 yuv422_en_bit;
> +	u32 csc_enable_bit;
>  	const struct mtk_dpi_yc_limit *limit;
>  };
>  
> @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
>  
>  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
>  {
> -	mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> +	mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
>  }
>  
>  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
>  		mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
>  }
>  
> +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> +{
> +	u32 matrix_sel = 0;
> +
> +	switch (format) {
> +	case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> +	case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> +	case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> +	case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> +	case MTK_DPI_COLOR_FORMAT_XV_YCC:
> +		if (dpi->mode.hdisplay <= 720)
> +			matrix_sel = 0x2;
> +		break;
> +	default:
> +		break;
> +	}
> +	mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> +}
> +
>  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
>  					enum mtk_dpi_out_color_format format)
>  {
>  	if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
>  	    (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
>  		mtk_dpi_config_yuv422_enable(dpi, false);
> -		if (dpi->conf->csc_support)
> +		if (dpi->conf->csc_support) {
>  			mtk_dpi_config_csc_enable(dpi, true);
> +			mtk_dpi_matrix_sel(dpi, format);
> +		}
>  		if (dpi->conf->swap_input_support)
>  			mtk_dpi_config_swap_input(dpi, false);
>  		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
>  	} else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
>  		   (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
>  		mtk_dpi_config_yuv422_enable(dpi, true);
> -		if (dpi->conf->csc_support)
> +		if (dpi->conf->csc_support) {
>  			mtk_dpi_config_csc_enable(dpi, true);
> +			mtk_dpi_matrix_sel(dpi, format);
> +		}
>  		if (dpi->conf->swap_input_support)
>  			mtk_dpi_config_swap_input(dpi, true);
>  		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
>  	mtk_dpi_disable(dpi);
>  	clk_disable_unprepare(dpi->pixel_clk);
>  	clk_disable_unprepare(dpi->engine_clk);
> +	clk_disable_unprepare(dpi->dpi_ck_cg);
> +	clk_disable_unprepare(dpi->tvd_clk);
>  }
>  
>  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
>  	if (++dpi->refcount != 1)
>  		return 0;
>  
> +	ret = clk_prepare_enable(dpi->tvd_clk);
> +	if (ret) {
> +		dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> +		goto err_pixel;
> +	}
> +
>  	ret = clk_prepare_enable(dpi->engine_clk);
>  	if (ret) {
>  		dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
>  		goto err_refcount;
>  	}
>  
> +	ret = clk_prepare_enable(dpi->dpi_ck_cg);
> +	if (ret) {
> +		dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> +		goto err_ck_cg;
> +	}
> +
>  	ret = clk_prepare_enable(dpi->pixel_clk);
>  	if (ret) {
>  		dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
>  	if (dpi->pinctrl && dpi->pins_dpi)
>  		pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
>  
> -	mtk_dpi_enable(dpi);
>  	return 0;
>  
>  err_pixel:
> +	clk_disable_unprepare(dpi->dpi_ck_cg);
> +err_ck_cg:
>  	clk_disable_unprepare(dpi->engine_clk);
>  err_refcount:
>  	dpi->refcount--;
> @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
>  	pll_rate = clk_get_rate(dpi->tvd_clk);
>  
>  	vm.pixelclock = pll_rate / factor;
> -	if (dpi->conf->is_dpintf)
> -		clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> -	else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> +	if (dpi->conf->is_dpintf) {
> +		if (factor == 1)
> +			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> +		else if (factor == 2)
> +			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> +		else if (factor == 4)
> +			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> +		else
> +			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> +	} else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
>  		 (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
>  		clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
>  	else
> @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
>  	}
>  	mtk_dpi_sw_reset(dpi, false);
>  
> +	mtk_dpi_enable(dpi);
> +
>  	return 0;
>  }
>  
> @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
>  	u32 *input_fmts;
>  
>  	*num_input_fmts = 0;
> -
>  	input_fmts = kcalloc(1, sizeof(*input_fmts),
>  			     GFP_KERNEL);
>  	if (!input_fmts)
> @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
>  		if (dpi->conf->num_output_fmts)
>  			out_bus_format = dpi->conf->output_fmts[0];
>  
> -	dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> +	dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
>  		bridge_state->input_bus_cfg.format,
>  		bridge_state->output_bus_cfg.format);
>  
> @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
>  	dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
>  	dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
>  	dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> -	dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> +	if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> +		dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> +	else
> +		dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
>  
>  	return 0;
>  }
> @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
>  	MEDIA_BUS_FMT_RGB888_2X12_BE,
>  };
>  
> +static const u32 mt8195_output_fmts[] = {
> +	MEDIA_BUS_FMT_RGB888_1X24,
> +	MEDIA_BUS_FMT_YUV8_1X24,
> +	MEDIA_BUS_FMT_YUYV8_1X16,
> +};
> +
>  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
>  	.c_bottom = 0x0010,
>  	.c_top = 0x0FE0,
> @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
>  	.hvsize_mask = HSIZE_MASK,
>  	.channel_swap_shift = CH_SWAP,
>  	.yuv422_en_bit = YUV422_EN,
> +	.csc_enable_bit = CSC_ENABLE,
>  	.limit = &mtk_dpi_limit,
>  };
>  
> @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
>  	.hvsize_mask = HSIZE_MASK,
>  	.channel_swap_shift = CH_SWAP,
>  	.yuv422_en_bit = YUV422_EN,
> +	.csc_enable_bit = CSC_ENABLE,
>  	.limit = &mtk_dpi_limit,
>  };
>  
> @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
>  	.hvsize_mask = HSIZE_MASK,
>  	.channel_swap_shift = CH_SWAP,
>  	.yuv422_en_bit = YUV422_EN,
> +	.csc_enable_bit = CSC_ENABLE,
>  	.limit = &mtk_dpi_limit,
>  };
>  
> @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
>  	.hvsize_mask = HSIZE_MASK,
>  	.channel_swap_shift = CH_SWAP,
>  	.yuv422_en_bit = YUV422_EN,
> +	.csc_enable_bit = CSC_ENABLE,
>  	.limit = &mtk_dpi_limit,
>  };
>  
>  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
>  	.cal_factor = mt8195_dpintf_calculate_factor,
> -	.output_fmts = mt8173_output_fmts,
> -	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> +	.output_fmts = mt8195_output_fmts,
> +	.num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
>  	.is_dpintf = true,
> +	.csc_support = true,
>  	.dimension_mask = DPINTF_HPW_MASK,
>  	.hvsize_mask = DPINTF_HSIZE_MASK,
>  	.channel_swap_shift = DPINTF_CH_SWAP,
>  	.yuv422_en_bit = DPINTF_YUV422_EN,
> +	.csc_enable_bit = DPINTF_CSC_ENABLE,
>  	.limit = &mtk_dpintf_limit,
>  };
>  
> @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
>  		return ret;
>  	}
>  
> +	dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> +	if (IS_ERR(dpi->dpi_ck_cg)) {
> +		ret = PTR_ERR(dpi->dpi_ck_cg);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> +				ret);
> +
> +		return ret;
> +	}
> +
>  	dpi->pixel_clk = devm_clk_get(dev, "pixel");
>  	if (IS_ERR(dpi->pixel_clk)) {
>  		ret = PTR_ERR(dpi->pixel_clk);
> @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
>  		return ret;
>  	}
>  
> +	dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> +	dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> +	dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> +	dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> +
>  	dpi->irq = platform_get_irq(pdev, 0);
>  	if (dpi->irq <= 0)
>  		return -EINVAL;

All those changes look unrelated as well?

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-15 10:11     ` Maxime Ripard
  0 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-11-15 10:11 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 134325 bytes --]

Hi,

On Wed, Nov 10, 2021 at 02:06:23PM +0100, Guillaume Ranquet wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> according phy driver mediatek-dp-phy.
> 
> It supports both functional units on the mt8195, the embedded
> DisplayPort as well as the external DisplayPort units. It offers
> hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> to 4 lanes.

There's a number of checkpatch --strict errors in there, make sure to fix them.

> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.

If the PHY is in the same register space than the DP controller, why do
you need a separate PHY driver in the first place?

> This driver is based on an initial version by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reported-by: kernel test robot <lkp@intel.com>

> ---
>  drivers/gpu/drm/drm_edid.c              |    2 +-
>  drivers/gpu/drm/mediatek/Kconfig        |    7 +
>  drivers/gpu/drm/mediatek/Makefile       |    2 +
>  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
>  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
>  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
>  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
>  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
>  9 files changed, 3799 insertions(+), 13 deletions(-)
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> 
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 500279a82167a..bfd98b50ceb5b 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
>  	 * modes and forbids YCRCB422 support for all video modes per
>  	 * HDMI 1.3 spec.
>  	 */
> -	info->color_formats = DRM_COLOR_FORMAT_RGB444;
> +	info->color_formats |= DRM_COLOR_FORMAT_RGB444;
>  
>  	/* YCRCB444 is optional according to spec. */
>  	if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {

This looks unrelated?

> diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> index 2976d21e9a34a..029b94c716131 100644
> --- a/drivers/gpu/drm/mediatek/Kconfig
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
>  	select PHY_MTK_HDMI
>  	help
>  	  DRM/KMS HDMI driver for Mediatek SoCs
> +
> +config MTK_DPTX_SUPPORT
> +	tristate "DRM DPTX Support for Mediatek SoCs"
> +	depends on DRM_MEDIATEK
> +	select PHY_MTK_DP
> +	help
> +	  DRM/KMS Display Port driver for Mediatek SoCs.
> diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> index 29098d7c8307c..d86a6406055e6 100644
> --- a/drivers/gpu/drm/mediatek/Makefile
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
>  			  mtk_hdmi_ddc.o
>  
>  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> +
> +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> new file mode 100644
> index 0000000000000..83087219d5a5e
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -0,0 +1,3094 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019 MediaTek Inc.
> + * Copyright (c) 2021 BayLibre
> + */
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_dp_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <linux/arm-smccc.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <sound/hdmi-codec.h>
> +#include <video/videomode.h>
> +
> +#include "mtk_dp_reg.h"
> +
> +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> +
> +#define MTK_DP_MAX_LANES 4
> +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> +
> +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> +
> +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> +
> +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> +
> +#define MTK_DP_DP_VERSION_11 0x11
> +
> +enum mtk_dp_state {
> +	MTK_DP_STATE_INITIAL,
> +	MTK_DP_STATE_IDLE,
> +	MTK_DP_STATE_PREPARE,
> +	MTK_DP_STATE_NORMAL,
> +};
> +
> +enum mtk_dp_train_state {
> +	MTK_DP_TRAIN_STATE_STARTUP = 0,
> +	MTK_DP_TRAIN_STATE_CHECKCAP,
> +	MTK_DP_TRAIN_STATE_CHECKEDID,
> +	MTK_DP_TRAIN_STATE_TRAINING_PRE,
> +	MTK_DP_TRAIN_STATE_TRAINING,
> +	MTK_DP_TRAIN_STATE_CHECKTIMING,
> +	MTK_DP_TRAIN_STATE_NORMAL,
> +	MTK_DP_TRAIN_STATE_POWERSAVE,
> +	MTK_DP_TRAIN_STATE_DPIDLE,
> +};
> +
> +struct mtk_dp_timings {
> +	struct videomode vm;
> +
> +	u16 htotal;
> +	u16 vtotal;
> +	u8 frame_rate;
> +	u32 pix_rate_khz;
> +};
> +
> +struct mtk_dp_train_info {
> +	bool tps3;
> +	bool tps4;
> +	bool sink_ssc;
> +	bool cable_plugged_in;
> +	bool cable_state_change;
> +	bool cr_done;
> +	bool eq_done;
> +
> +	// link_rate is in multiple of 0.27Gbps
> +	int link_rate;
> +	int lane_count;
> +
> +	int irq_status;
> +	int check_cap_count;
> +};
> +
> +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> +enum mtk_dp_color_format {
> +	MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> +	MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> +	MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> +	MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> +	MTK_DP_COLOR_FORMAT_YONLY = 4,
> +	MTK_DP_COLOR_FORMAT_RAW = 5,
> +	MTK_DP_COLOR_FORMAT_RESERVED = 6,
> +	MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> +	MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> +};
> +
> +// Multiple of 0.27Gbps
> +enum mtk_dp_linkrate {
> +	MTK_DP_LINKRATE_RBR = 0x6,
> +	MTK_DP_LINKRATE_HBR = 0xA,
> +	MTK_DP_LINKRATE_HBR2 = 0x14,
> +	MTK_DP_LINKRATE_HBR25 = 0x19,
> +	MTK_DP_LINKRATE_HBR3 = 0x1E,
> +};
> +
> +// Same values as used for DP Spec MISC0 bits 5,6,7
> +enum mtk_dp_color_depth {
> +	MTK_DP_COLOR_DEPTH_6BIT = 0,
> +	MTK_DP_COLOR_DEPTH_8BIT = 1,
> +	MTK_DP_COLOR_DEPTH_10BIT = 2,
> +	MTK_DP_COLOR_DEPTH_12BIT = 3,
> +	MTK_DP_COLOR_DEPTH_16BIT = 4,
> +	MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> +};
> +
> +struct mtk_dp_audio_cfg {
> +	int sample_rate;
> +	int word_length_bits;
> +	int channels;
> +};
> +
> +struct mtk_dp_info {
> +	enum mtk_dp_color_depth depth;
> +	enum mtk_dp_color_format format;
> +	struct mtk_dp_audio_cfg audio_caps;
> +	struct mtk_dp_timings timings;
> +};
> +
> +struct dp_cal_data {
> +	unsigned int glb_bias_trim;
> +	unsigned int clktx_impse;
> +
> +	unsigned int ln0_tx_impsel_pmos;
> +	unsigned int ln0_tx_impsel_nmos;
> +	unsigned int ln1_tx_impsel_pmos;
> +	unsigned int ln1_tx_impsel_nmos;
> +	unsigned int ln2_tx_impsel_pmos;
> +	unsigned int ln2_tx_impsel_nmos;
> +	unsigned int ln3_tx_impsel_pmos;
> +	unsigned int ln3_tx_impsel_nmos;
> +};
> +
> +struct mtk_dp {
> +	struct device *dev;
> +	struct platform_device *phy_dev;
> +	struct phy *phy;
> +	struct dp_cal_data cal_data;
> +
> +	struct drm_device *drm_dev;
> +	struct drm_bridge bridge;
> +	struct drm_bridge *next_bridge;
> +	struct drm_dp_aux aux;
> +
> +	struct mutex edid_lock;
> +	struct edid *edid;
> +
> +	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> +
> +	struct mtk_dp_info info;
> +	enum mtk_dp_state state;
> +
> +	struct mtk_dp_train_info train_info;
> +	enum mtk_dp_train_state train_state;
> +	unsigned int input_fmt;
> +
> +	struct regmap *regs;
> +	struct clk *dp_tx_clk;
> +
> +	bool enabled;
> +	bool audio_enable;
> +
> +	bool has_fec;
> +	struct mutex dp_lock;
> +
> +	struct mutex update_plugged_status_lock;
> +
> +	hdmi_codec_plugged_cb plugged_cb;
> +	struct device *codec_dev;
> +	u8 connector_eld[MAX_ELD_BYTES];
> +};
> +
> +enum mtk_dp_sdp_type {
> +	MTK_DP_SDP_NONE = 0x00,
> +	MTK_DP_SDP_ACM = 0x01,
> +	MTK_DP_SDP_ISRC = 0x02,
> +	MTK_DP_SDP_AVI = 0x03,
> +	MTK_DP_SDP_AUI = 0x04,
> +	MTK_DP_SDP_SPD = 0x05,
> +	MTK_DP_SDP_MPEG = 0x06,
> +	MTK_DP_SDP_NTSC = 0x07,
> +	MTK_DP_SDP_VSP = 0x08,
> +	MTK_DP_SDP_VSC = 0x09,
> +	MTK_DP_SDP_EXT = 0x0A,
> +	MTK_DP_SDP_PPS0 = 0x0B,
> +	MTK_DP_SDP_PPS1 = 0x0C,
> +	MTK_DP_SDP_PPS2 = 0x0D,
> +	MTK_DP_SDP_PPS3 = 0x0E,
> +	MTK_DP_SDP_DRM = 0x10,
> +	MTK_DP_SDP_MAX_NUM
> +};
> +
> +struct mtk_dp_sdp_packet {
> +	enum mtk_dp_sdp_type type;
> +	struct dp_sdp sdp;
> +};
> +
> +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> +union mtk_dp_audio_channel_status {
> +	struct {
> +		u8 rev : 1;
> +		u8 is_lpcm : 1;
> +		u8 copy_right : 1;
> +		u8 additional_format_info : 3;
> +		u8 channel_status_mode : 2;
> +		u8 category_code;
> +		u8 src_num : 4;
> +		u8 channel_num : 4;
> +		u8 sampling_freq : 4;
> +		u8 clk_accuracy : 2;
> +		u8 rev2 : 2;
> +		u8 word_len : 4;
> +		u8 original_sampling_freq : 4;
> +	} iec;
> +
> +	u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> +};

Why don't you leverage hdmi_codec_params and snd_aes_iec958?

> +static struct regmap_config mtk_dp_regmap_config = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +	.max_register = SEC_OFFSET + 0x90,
> +	.name = "mtk-dp-registers",
> +};
> +
> +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
> +{
> +	return mtk_dp->next_bridge != NULL;
> +}
> +
> +static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
> +{
> +	return container_of(b, struct mtk_dp, bridge);
> +}
> +
> +static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
> +{
> +	u32 read_val;
> +	int ret;
> +
> +	ret = regmap_read(mtk_dp->regs, offset, &read_val);
> +	if (ret) {
> +		dev_err(mtk_dp->dev, "Failed to read register 0x%x: %d\n",
> +			offset, ret);
> +		return 0;
> +	}
> +
> +	return read_val;
> +}
> +
> +static void mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
> +{
> +	int ret;
> +
> +	ret = regmap_write(mtk_dp->regs, offset, val);
> +	if (ret)
> +		dev_err(mtk_dp->dev,
> +			"Failed to write register 0x%x with value 0x%x: %d\n",
> +			offset, val, ret);
> +}
> +
> +static void mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, u32 val,
> +			       u32 mask)
> +{
> +	int ret;
> +
> +	ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
> +	if (ret)
> +		dev_err(mtk_dp->dev,
> +			"Failed to update register 0x%x with value 0x%x, mask 0x%x: %d\n",
> +			offset, val, mask, ret);
> +}
> +
> +static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32 offset, u8 *buf,
> +				    size_t length)
> +{
> +	int i;
> +	int num_regs = (length + 1) / 2;
> +
> +	// 2 bytes per register
> +	for (i = 0; i < num_regs; i++) {
> +		u32 val = buf[i * 2] |
> +			  (i * 2 + 1 < length ? buf[i * 2 + 1] << 8 : 0);
> +
> +		mtk_dp_write(mtk_dp, offset + i * 4, val);
> +	}
> +}
> +
> +static unsigned long mtk_dp_sip_atf_call(unsigned int cmd, unsigned int para)
> +{
> +	struct arm_smccc_res res;
> +
> +	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32, cmd, para, 0, 0, 0, 0, 0,
> +		      &res);
> +
> +	pr_debug("[DPTX]%s cmd 0x%x, p1 0x%x, ret 0x%lx-0x%lx", __func__, cmd,
> +		 para, res.a0, res.a1);
> +	return res.a1;
> +}
> +
> +static void mtk_dp_msa_bypass_disable(struct mtk_dp *mtk_dp)
> +{
> +	const u16 bits_to_set =
> +		BIT(HTOTAL_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(VTOTAL_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(HSTART_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(VSTART_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(HWIDTH_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(VHEIGHT_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(HSP_SEL_DP_ENC0_P0_SHIFT) | BIT(HSW_SEL_DP_ENC0_P0_SHIFT) |
> +		BIT(VSP_SEL_DP_ENC0_P0_SHIFT) | BIT(VSW_SEL_DP_ENC0_P0_SHIFT);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, bits_to_set,
> +			   bits_to_set);
> +}
> +
> +static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
> +{
> +	struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010, timings->htotal,
> +			   HTOTAL_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
> +			   timings->vm.hsync_len + timings->vm.hback_porch,
> +			   HSTART_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> +			   timings->vm.hsync_len << HSW_SW_DP_ENC0_P0_SHIFT,
> +			   HSW_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028, 0,
> +			   HSP_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020, timings->vm.hactive,
> +			   HWIDTH_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014, timings->vtotal,
> +			   VTOTAL_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
> +			   timings->vm.vsync_len + timings->vm.vback_porch,
> +			   VSTART_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> +			   timings->vm.vsync_len << VSW_SW_DP_ENC0_P0_SHIFT,
> +			   VSW_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C, 0,
> +			   VSP_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024, timings->vm.vactive,
> +			   VHEIGHT_SW_DP_ENC0_P0_MASK);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064, timings->vm.hactive,
> +			   HDE_NUM_LAST_DP_ENC0_P0_MASK);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154, timings->htotal,
> +			   PGEN_HTOTAL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
> +			   timings->vm.hfront_porch,
> +			   PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C, timings->vm.hsync_len,
> +			   PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
> +			   timings->vm.hback_porch + timings->vm.hsync_len,
> +			   PGEN_HFDE_START_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164, timings->vm.hactive,
> +			   PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168, timings->vtotal,
> +			   PGEN_VTOTAL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
> +			   timings->vm.vfront_porch,
> +			   PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170, timings->vm.vsync_len,
> +			   PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
> +			   timings->vm.vback_porch + timings->vm.vsync_len,
> +			   PGEN_VFDE_START_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178, timings->vm.vactive,
> +			   PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
> +				    enum mtk_dp_color_format color_format)
> +{
> +	u32 val;
> +
> +	mtk_dp->info.format = color_format;
> +
> +	// Update MISC0
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +			   color_format << DP_TEST_COLOR_FORMAT_SHIFT,
> +			   DP_TEST_COLOR_FORMAT_MASK);
> +
> +	switch (color_format) {
> +	case MTK_DP_COLOR_FORMAT_YUV_422:
> +		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
> +		break;
> +	case MTK_DP_COLOR_FORMAT_YUV_420:
> +		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420;
> +		break;
> +	case MTK_DP_COLOR_FORMAT_YONLY:
> +	case MTK_DP_COLOR_FORMAT_RAW:
> +	case MTK_DP_COLOR_FORMAT_RESERVED:
> +	case MTK_DP_COLOR_FORMAT_UNKNOWN:
> +		drm_warn(mtk_dp->drm_dev, "Unsupported color format: %d\n",
> +			 color_format);
> +		fallthrough;
> +	case MTK_DP_COLOR_FORMAT_RGB_444:
> +	case MTK_DP_COLOR_FORMAT_YUV_444:
> +		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
> +		break;
> +	}
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
> +			   PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp,
> +				   enum mtk_dp_color_depth color_depth)
> +{
> +	u32 val;
> +
> +	mtk_dp->info.depth = color_depth;
> +
> +	// Update MISC0
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +			   color_depth << DP_TEST_BIT_DEPTH_SHIFT,
> +			   DP_TEST_BIT_DEPTH_MASK);
> +
> +	switch (color_depth) {
> +	case MTK_DP_COLOR_DEPTH_6BIT:
> +		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT;
> +		break;
> +	case MTK_DP_COLOR_DEPTH_8BIT:
> +		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT;
> +		break;
> +	case MTK_DP_COLOR_DEPTH_10BIT:
> +		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT;
> +		break;
> +	case MTK_DP_COLOR_DEPTH_12BIT:
> +		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT;
> +		break;
> +	case MTK_DP_COLOR_DEPTH_16BIT:
> +		val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT;
> +		break;
> +	case MTK_DP_COLOR_DEPTH_UNKNOWN:
> +		drm_warn(mtk_dp->drm_dev, "Unsupported color depth %d\n",
> +			 color_depth);
> +		return;
> +	}
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
> +			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_mn_overwrite_disable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
> +			   VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32 val)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   val << SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT,
> +			   SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   BIT(VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT),
> +			   VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> +			   0x20 << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
> +			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +			   0x20 << SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT,
> +			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
> +			   2 << VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT,
> +			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +			   4 << FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT,
> +			   FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
> +	mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368,
> +		     1 << VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT |
> +			     1 << VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT |
> +			     BIT(SDP_DP13_EN_DP_ENC1_P0_SHIFT) |
> +			     1 << BS2BS_MODE_DP_ENC1_P0_SHIFT);
> +}
> +
> +static void mtk_dp_pg_disable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038, 0,
> +			   VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
> +			   4 << PGEN_PATTERN_SEL_SHIFT, PGEN_PATTERN_SEL_MASK);
> +}
> +
> +static void mtk_dp_audio_setup_channels(struct mtk_dp *mtk_dp,
> +					struct mtk_dp_audio_cfg *cfg)
> +{
> +	u32 channel_enable_bits;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3324,
> +			   AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX,
> +			   AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK);
> +
> +	//audio channel count change reset
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(9), BIT(9));
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> +			   AU_PRTY_REGEN_DP_ENC1_P0_MASK,
> +			   AU_PRTY_REGEN_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> +			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK,
> +			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> +			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK,
> +			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK);
> +
> +	switch (cfg->channels) {
> +	case 2:
> +		channel_enable_bits = AUDIO_2CH_SEL_DP_ENC0_P0_MASK |
> +				      AUDIO_2CH_EN_DP_ENC0_P0_MASK;
> +		break;
> +	case 8:
> +	default:
> +		channel_enable_bits = AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
> +				      AUDIO_8CH_EN_DP_ENC0_P0_MASK;
> +		break;
> +	}
> +	mtk_dp_update_bits(
> +		mtk_dp, MTK_DP_ENC0_P0_3088,
> +		channel_enable_bits | AU_EN_DP_ENC0_P0_MASK,
> +		AUDIO_2CH_SEL_DP_ENC0_P0_MASK | AUDIO_2CH_EN_DP_ENC0_P0_MASK |
> +			AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
> +			AUDIO_8CH_EN_DP_ENC0_P0_MASK | AU_EN_DP_ENC0_P0_MASK);
> +
> +	//audio channel count change reset
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, 0, BIT(9));
> +
> +	//enable audio reset
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(0), BIT(0));
> +}
> +
> +static void mtk_dp_audio_channel_status_set(struct mtk_dp *mtk_dp,
> +					    struct mtk_dp_audio_cfg *cfg)
> +{
> +	union mtk_dp_audio_channel_status channel_status;
> +
> +	memset(&channel_status, 0, sizeof(channel_status));
> +
> +	switch (cfg->sample_rate) {
> +	case 32000:
> +		channel_status.iec.sampling_freq = 3;
> +		break;
> +	case 44100:
> +		channel_status.iec.sampling_freq = 0;
> +		break;
> +	case 48000:
> +		channel_status.iec.sampling_freq = 2;
> +		break;
> +	case 88200:
> +		channel_status.iec.sampling_freq = 8;
> +		break;
> +	case 96000:
> +		channel_status.iec.sampling_freq = 0xA;
> +		break;
> +	case 192000:
> +		channel_status.iec.sampling_freq = 0xE;
> +		break;
> +	default:
> +		channel_status.iec.sampling_freq = 0x1;
> +		break;
> +	}
> +
> +	switch (cfg->word_length_bits) {
> +	case 16:
> +		channel_status.iec.word_len = 0x02;
> +		break;
> +	case 20:
> +		channel_status.iec.word_len = 0x03;
> +		break;
> +	case 24:
> +		channel_status.iec.word_len = 0x0B;
> +		break;
> +	}

Yeah, this is provided by hdmi-codec through hdmi_codec_params in
prepare.

> +	// IEC 60958 consumer channel status bits
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_308C,
> +			   channel_status.buf[1] << 8 | channel_status.buf[0],
> +			   CH_STATUS_0_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3090,
> +			   channel_status.buf[3] << 8 | channel_status.buf[2],
> +			   CH_STATUS_1_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3094, channel_status.buf[4],
> +			   CH_STATUS_2_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_audio_sdp_asp_set_channels(struct mtk_dp *mtk_dp,
> +					      int channels)
> +{
> +	if (channels != 2 && channels != 8)
> +		channels = 8;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_312C,
> +			   (channels - 1) << ASP_HB3_DP_ENC0_P0_SHIFT,
> +			   ASP_HB2_DP_ENC0_P0_MASK | ASP_HB3_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_audio_set_divider(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
> +			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2,
> +			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK);
> +}
> +
> +static bool mtk_dp_plug_state(struct mtk_dp *mtk_dp)
> +{
> +	return !!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
> +		  HPD_DB_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_sdp_trigger_packet(struct mtk_dp *mtk_dp,
> +				      enum mtk_dp_sdp_type type)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, type,
> +			   SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, SDP_PACKET_W_DP_ENC1_P0,
> +			   SDP_PACKET_W_DP_ENC1_P0);
> +}
> +
> +static void mtk_dp_sdp_set_data(struct mtk_dp *mtk_dp, u8 *data_bytes)
> +{
> +	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_ENC1_P0_3200, data_bytes, 0x10);
> +}
> +
> +static void mtk_dp_sdp_set_header(struct mtk_dp *mtk_dp,
> +				  enum mtk_dp_sdp_type type,
> +				  struct dp_sdp_header *header)
> +{
> +	u32 db_addr;
> +
> +	switch (type) {
> +	case MTK_DP_SDP_DRM:
> +		db_addr = MTK_DP_ENC0_P0_3138;
> +		break;
> +	case MTK_DP_SDP_PPS0:
> +	case MTK_DP_SDP_PPS1:
> +	case MTK_DP_SDP_PPS2:
> +	case MTK_DP_SDP_PPS3:
> +		db_addr = MTK_DP_ENC0_P0_3130;
> +		break;
> +	default:
> +		db_addr = MTK_DP_ENC0_P0_30D8 + (type - MTK_DP_SDP_ACM) * 8;
> +	}
> +
> +	mtk_dp_bulk_16bit_write(mtk_dp, db_addr, (u8 *)header, 4);
> +}
> +
> +static const u32 mtk_dp_sdp_type_to_reg[MTK_DP_SDP_MAX_NUM] = {
> +	/* MTK_DP_SDP_NONE => */ 0x0,
> +	/* MTK_DP_SDP_ACM  => */ MTK_DP_ENC0_P0_30B4,
> +	/* MTK_DP_SDP_ISRC => */ MTK_DP_ENC0_P0_30B4 + 1,
> +	/* MTK_DP_SDP_AVI  => */ MTK_DP_ENC0_P0_30A4 + 1,
> +	/* MTK_DP_SDP_AUI  => */ MTK_DP_ENC0_P0_30A8,
> +	/* MTK_DP_SDP_SPD  => */ MTK_DP_ENC0_P0_30A8 + 1,
> +	/* MTK_DP_SDP_MPEG => */ MTK_DP_ENC0_P0_30AC,
> +	/* MTK_DP_SDP_NTSC => */ MTK_DP_ENC0_P0_30AC + 1,
> +	/* MTK_DP_SDP_VSP  => */ MTK_DP_ENC0_P0_30B0,
> +	/* MTK_DP_SDP_VSC  => */ MTK_DP_ENC0_P0_30B8,
> +	/* MTK_DP_SDP_EXT  => */ MTK_DP_ENC0_P0_30B0 + 1,
> +	/* MTK_DP_SDP_PPS0 => */ MTK_DP_ENC0_P0_31E8,
> +	/* MTK_DP_SDP_PPS1 => */ MTK_DP_ENC0_P0_31E8,
> +	/* MTK_DP_SDP_PPS2 => */ MTK_DP_ENC0_P0_31E8,
> +	/* MTK_DP_SDP_PPS3 => */ MTK_DP_ENC0_P0_31E8,
> +	/* MTK_DP_SDP_DRM  => */ MTK_DP_ENC0_P0_31DC,
> +};
> +
> +static void mtk_dp_disable_sdp(struct mtk_dp *mtk_dp, enum mtk_dp_sdp_type type)
> +{
> +	if (type == MTK_DP_SDP_NONE)
> +		return;
> +
> +	// Disable periodic send
> +	mtk_dp_update_bits(mtk_dp, mtk_dp_sdp_type_to_reg[type] & 0xfffc, 0,
> +			   0xFF << ((mtk_dp_sdp_type_to_reg[type] & 3) * 8));
> +}
> +
> +static void mtk_dp_setup_sdp(struct mtk_dp *mtk_dp,
> +			     struct mtk_dp_sdp_packet *packet)
> +{
> +	mtk_dp_sdp_set_data(mtk_dp, packet->sdp.db);
> +	mtk_dp_sdp_set_header(mtk_dp, packet->type, &packet->sdp.sdp_header);
> +
> +	mtk_dp_disable_sdp(mtk_dp, packet->type);
> +
> +	switch (packet->type) {
> +	case MTK_DP_SDP_NONE:
> +		break;
> +	case MTK_DP_SDP_ISRC:
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> +				   0x1C << ISRC1_HB3_DP_ENC0_P0_SHIFT,
> +				   ISRC1_HB3_DP_ENC0_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, MTK_DP_SDP_ISRC,
> +				   SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
> +
> +		if (packet->sdp.sdp_header.HB3 & BIT(2))
> +			mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
> +					   BIT(ISRC_CONT_DP_ENC0_P0_SHIFT),
> +					   ISRC_CONT_DP_ENC0_P0_MASK);
> +		else
> +			mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC, 0,
> +					   ISRC_CONT_DP_ENC0_P0_MASK);
> +
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280,
> +				   SDP_PACKET_W_DP_ENC1_P0,
> +				   SDP_PACKET_W_DP_ENC1_P0);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30B4,
> +				   5 << ISRC_CFG_DP_ENC0_P0_SHIFT,
> +				   ISRC_CFG_DP_ENC0_P0_MASK);
> +		break;
> +	case MTK_DP_SDP_DRM:
> +		mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31DC,
> +				   5 << HDR0_CFG_DP_ENC0_P0_SHIFT,
> +				   HDR0_CFG_DP_ENC0_P0_MASK);
> +		break;
> +	case MTK_DP_SDP_ACM:
> +	case MTK_DP_SDP_AVI:
> +	case MTK_DP_SDP_AUI:
> +	case MTK_DP_SDP_SPD:
> +	case MTK_DP_SDP_MPEG:
> +	case MTK_DP_SDP_NTSC:
> +	case MTK_DP_SDP_VSP:
> +	case MTK_DP_SDP_VSC:
> +	case MTK_DP_SDP_EXT:
> +	case MTK_DP_SDP_PPS0:
> +	case MTK_DP_SDP_PPS1:
> +	case MTK_DP_SDP_PPS2:
> +	case MTK_DP_SDP_PPS3:
> +		mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
> +		// Enable periodic sending
> +		mtk_dp_update_bits(
> +			mtk_dp, mtk_dp_sdp_type_to_reg[packet->type] & 0xfffc,
> +			0x05 << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
> +				 8),
> +			0xff << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
> +				 8));
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static void mtk_dp_sdp_vsc_ext_disable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A0, 0,
> +			   BIT(7) | BIT(8) | BIT(12));
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_328C, 0, BIT(7));
> +}
> +
> +static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_write(
> +		mtk_dp, MTK_DP_AUX_P0_3640,
> +		BIT(AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
> +			BIT(AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT));
> +}
> +
> +static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32 addr)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644, cmd,
> +			   MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648, addr,
> +			   MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C, addr >> 16,
> +			   MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_cmd_complete(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +			   BIT(MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT),
> +			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK |
> +				   PHY_FIFO_RST_AUX_TX_P0_MASK |
> +				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
> +			   BIT(AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT),
> +			   AUX_TX_REQUEST_READY_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> +				       size_t length)
> +{
> +	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf, length);
> +}
> +
> +static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> +				    size_t length, int read_delay)
> +{
> +	int read_pos;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620, 0,
> +			   AUX_RD_MODE_AUX_TX_P0_MASK);
> +
> +	for (read_pos = 0; read_pos < length; read_pos++) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> +				   BIT(AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT),
> +				   AUX_RX_FIFO_READ_PULSE_TX_P0_MASK);
> +		usleep_range(read_delay, read_delay * 2);
> +		buf[read_pos] =
> +			(u8)(mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3620) &
> +			     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK >>
> +				     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT);
> +	}
> +}
> +
> +static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t length)
> +{
> +	if (length > 0) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +				   (length - 1)
> +					   << MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT,
> +				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
> +				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
> +					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	} else {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +				   BIT(AUX_NO_LENGTH_AUX_TX_P0_SHIFT),
> +				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
> +					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	}
> +}
> +
> +static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp, bool is_read)
> +{
> +	int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
> +
> +	while (--wait_reply) {
> +		u32 aux_irq_status;
> +
> +		if (is_read) {
> +			u32 fifo_status =
> +				mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3618);
> +
> +			if (fifo_status &
> +			    (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
> +			     AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
> +				return 0;
> +			}
> +		}
> +
> +		aux_irq_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3640);
> +		if (aux_irq_status & AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK)
> +			return 0;
> +
> +		if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK)
> +			return -ETIMEDOUT;
> +
> +		usleep_range(100, 500);
> +	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool is_read, u8 cmd,
> +				  u32 addr, u8 *buf, size_t length)
> +{
> +	int ret;
> +	u32 reply_cmd;
> +
> +	if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
> +			(cmd == DP_AUX_NATIVE_READ && !length)))
> +		return -EINVAL;
> +
> +	if (!is_read)
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> +				   BIT(AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT),
> +				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK);
> +
> +	mtk_dp_aux_cmd_complete(mtk_dp);
> +	mtk_dp_aux_irq_clear(mtk_dp);
> +	usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> +		     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> +
> +	mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
> +	mtk_dp_aux_set_length(mtk_dp, length);
> +
> +	if (!is_read) {
> +		if (length)
> +			mtk_dp_aux_fill_write_fifo(mtk_dp, buf, length);
> +
> +		mtk_dp_update_bits(
> +			mtk_dp, MTK_DP_AUX_P0_3704,
> +			AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK,
> +			AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK);
> +	}
> +
> +	mtk_dp_aux_request_ready(mtk_dp);
> +
> +	ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
> +
> +	reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
> +		    AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
> +
> +	if (ret || reply_cmd) {
> +		u32 phy_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3628) &
> +				 AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
> +		if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
> +			drm_err(mtk_dp->drm_dev,
> +				"AUX Rx Aux hang, need SW reset\n");
> +			ret = -EIO;
> +		}
> +
> +		mtk_dp_aux_cmd_complete(mtk_dp);
> +		mtk_dp_aux_irq_clear(mtk_dp);
> +
> +		usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> +			     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> +		return -ETIMEDOUT;
> +	}
> +
> +	if (!length) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
> +				   AUX_NO_LENGTH_AUX_TX_P0_MASK |
> +					   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +					   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	} else if (is_read) {
> +		int read_delay;
> +
> +		if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
> +		    cmd == DP_AUX_I2C_READ)
> +			read_delay = 500;
> +		else
> +			read_delay = 100;
> +
> +		mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length, read_delay);
> +	}
> +
> +	mtk_dp_aux_cmd_complete(mtk_dp);
> +	mtk_dp_aux_irq_clear(mtk_dp);
> +	usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> +		     MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> +
> +	return 0;
> +}
> +
> +static bool mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int lane_num,
> +					  int swing_val, int preemphasis)
> +{
> +	u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
> +
> +	if (lane_num < 0 || lane_num > 3)
> +		return false;
> +
> +	dev_dbg(mtk_dp->dev,
> +		"link training swing_val= 0x%x, preemphasis = 0x%x\n",
> +		swing_val, preemphasis);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +			   swing_val << (DP_TX0_VOLT_SWING_SHIFT + lane_shift),
> +			   DP_TX0_VOLT_SWING_MASK << lane_shift);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +			   preemphasis << (DP_TX0_PRE_EMPH_SHIFT + lane_shift),
> +			   DP_TX0_PRE_EMPH_MASK << lane_shift);
> +
> +	return true;
> +}
> +
> +static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP, 0,
> +			   DP_TX0_VOLT_SWING_MASK | DP_TX1_VOLT_SWING_MASK |
> +				   DP_TX2_VOLT_SWING_MASK |
> +				   DP_TX3_VOLT_SWING_MASK |
> +				   DP_TX0_PRE_EMPH_MASK | DP_TX1_PRE_EMPH_MASK |
> +				   DP_TX2_PRE_EMPH_MASK | DP_TX3_PRE_EMPH_MASK);
> +}
> +
> +static void mtk_dp_fec_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> +			   enable ? BIT(FEC_EN_DP_TRANS_P0_SHIFT) : 0,
> +			   FEC_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +	u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
> +			 SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
> +
> +	if (irq_status) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, irq_status,
> +				   SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, 0,
> +				   SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +	}
> +
> +	return irq_status;
> +}
> +
> +static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +	u8 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
> +			 IRQ_STATUS_DP_TRANS_P0_MASK) >>
> +			IRQ_STATUS_DP_TRANS_P0_SHIFT;
> +
> +	if (irq_status) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, irq_status,
> +				   IRQ_CLR_DP_TRANS_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, 0,
> +				   IRQ_CLR_DP_TRANS_P0_MASK);
> +	}
> +
> +	return irq_status;
> +}
> +
> +static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	u32 val = 0;
> +
> +	if (!enable)
> +		val = IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
> +		      IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
> +		      IRQ_MASK_DP_TRANS_P0_INT_IRQ;
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, val,
> +			   IRQ_MASK_DP_TRANS_P0_MASK);
> +}
> +
> +void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
> +			   XTAL_FREQ_DP_TRANS_P0_DEFAULT,
> +			   XTAL_FREQ_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> +			   BIT(FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT),
> +			   FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> +			   BIT(AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT),
> +			   AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
> +			   SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK, IRQ_MASK_AUX_TOP_IRQ,
> +			   IRQ_MASK_AUX_TOP_IRQ);
> +}
> +
> +static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp *mtk_dp)
> +{
> +	// Debounce threshold
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   8 << HPD_DEB_THD_DP_TRANS_P0_SHIFT,
> +			   HPD_DEB_THD_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
> +			    HPD_INT_THD_DP_TRANS_P0_UPPER_1100US)
> +				   << HPD_INT_THD_DP_TRANS_P0_SHIFT,
> +			   HPD_INT_THD_DP_TRANS_P0_MASK);
> +
> +	// Connect threshold 1.5ms + 5 x 0.1ms = 2ms
> +	// Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   (5 << HPD_DISC_THD_DP_TRANS_P0_SHIFT) |
> +				   (5 << HPD_CONN_THD_DP_TRANS_P0_SHIFT),
> +			   HPD_DISC_THD_DP_TRANS_P0_MASK |
> +				   HPD_CONN_THD_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
> +			   HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
> +			   HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
> +{
> +	// modify timeout threshold = 1595 [12 : 8]
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C, 0x1595,
> +			   AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658, 0,
> +			   AUX_TX_OV_EN_AUX_TX_P0_MASK);
> +	// 25 for 26M
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
> +			   25 << AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT,
> +			   AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
> +	// 13 for 26M
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
> +			   13 << AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT,
> +			   AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
> +			   BIT(MTK_ATOP_EN_AUX_TX_P0_SHIFT),
> +			   MTK_ATOP_EN_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_digital_settings(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
> +			   VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
> +	mtk_dp_set_color_format(mtk_dp, MTK_DP_COLOR_FORMAT_RGB_444);
> +	mtk_dp_set_color_depth(mtk_dp, MTK_DP_COLOR_DEPTH_8BIT);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
> +			   1 << BS2BS_MODE_DP_ENC1_P0_SHIFT,
> +			   BS2BS_MODE_DP_ENC1_P0_MASK);
> +
> +	// dp tx encoder reset all sw
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +			   BIT(DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT),
> +			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
> +	usleep_range(1000, 5000);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
> +			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> +			   BIT(DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT),
> +			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
> +	usleep_range(1000, 5000);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C, 0,
> +			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
> +			   lanes == 0 ? 0 : BIT(3), BIT(3) | BIT(2));
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, lanes,
> +			   LANE_NUM_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
> +			   lanes << LANE_NUM_DP_TRANS_P0_SHIFT,
> +			   LANE_NUM_DP_TRANS_P0_MASK);
> +}
> +
> +static int link_rate_to_mb_per_s(struct mtk_dp *mtk_dp,
> +				 enum mtk_dp_linkrate linkrate)
> +{
> +	switch (linkrate) {
> +	default:
> +		drm_err(mtk_dp->drm_dev,
> +			"Implementation error, unknown linkrate %d\n",
> +			linkrate);
> +		fallthrough;
> +	case MTK_DP_LINKRATE_RBR:
> +		return 1620;
> +	case MTK_DP_LINKRATE_HBR:
> +		return 2700;
> +	case MTK_DP_LINKRATE_HBR2:
> +		return 5400;
> +	case MTK_DP_LINKRATE_HBR3:
> +		return 8100;
> +	}
> +}
> +
> +static u32 check_cal_data_valid(u32 min, u32 max, u32 val, u32 default_val)
> +{
> +	if (val < min || val > max)
> +		return default_val;
> +
> +	return val;
> +}
> +
> +static int mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
> +{
> +	struct dp_cal_data *cal_data = &mtk_dp->cal_data;
> +	struct device *dev = mtk_dp->dev;
> +	struct nvmem_cell *cell;
> +	u32 *buf;
> +	size_t len;
> +
> +	cell = nvmem_cell_get(dev, "dp_calibration_data");
> +	if (IS_ERR(cell)) {
> +		dev_err(dev,
> +			"Error: Failed to get nvmem cell dp_calibration_data\n");
> +		return PTR_ERR(cell);
> +	}
> +
> +	buf = (u32 *)nvmem_cell_read(cell, &len);
> +	nvmem_cell_put(cell);
> +
> +	if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
> +		dev_err(dev,
> +			"Error: Failed to read nvmem_cell_read fail len %ld\n",
> +			(len / sizeof(u32)));
> +		return PTR_ERR(buf);
> +	}
> +
> +	if (mtk_dp_is_edp(mtk_dp)) {
> +		cal_data->glb_bias_trim = check_cal_data_valid(
> +			1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf);
> +		cal_data->clktx_impse =
> +			check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8);
> +		cal_data->ln0_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8);
> +		cal_data->ln0_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8);
> +		cal_data->ln1_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8);
> +		cal_data->ln1_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8);
> +		cal_data->ln2_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8);
> +		cal_data->ln2_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8);
> +		cal_data->ln3_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8);
> +		cal_data->ln3_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8);
> +	} else {
> +		cal_data->glb_bias_trim = check_cal_data_valid(
> +			1, 0x1e, (buf[0] >> 27) & 0x1f, 0xf);
> +		cal_data->clktx_impse =
> +			check_cal_data_valid(1, 0xe, (buf[0] >> 13) & 0xf, 0x8);
> +		cal_data->ln0_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 28) & 0xf, 0x8);
> +		cal_data->ln0_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 24) & 0xf, 0x8);
> +		cal_data->ln1_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 20) & 0xf, 0x8);
> +		cal_data->ln1_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 16) & 0xf, 0x8);
> +		cal_data->ln2_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 12) & 0xf, 0x8);
> +		cal_data->ln2_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 8) & 0xf, 0x8);
> +		cal_data->ln3_tx_impsel_pmos =
> +			check_cal_data_valid(1, 0xe, (buf[1] >> 4) & 0xf, 0x8);
> +		cal_data->ln3_tx_impsel_nmos =
> +			check_cal_data_valid(1, 0xe, buf[1] & 0xf, 0x8);
> +	}
> +
> +	kfree(buf);
> +
> +	return 0;
> +}
> +
> +void mtk_dp_set_cal_data(struct mtk_dp *mtk_dp)
> +{
> +	struct dp_cal_data *cal_data = &mtk_dp->cal_data;
> +
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
> +			   cal_data->clktx_impse << 20, RG_CKM_PT0_CKTX_IMPSEL);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
> +			   cal_data->glb_bias_trim << 16,
> +			   RG_XTP_GLB_BIAS_INTR_CTRL);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +			   cal_data->ln0_tx_impsel_pmos << 12,
> +			   RG_XTP_LN0_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +			   cal_data->ln0_tx_impsel_nmos << 16,
> +			   RG_XTP_LN0_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +			   cal_data->ln1_tx_impsel_pmos << 12,
> +			   RG_XTP_LN1_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +			   cal_data->ln1_tx_impsel_nmos << 16,
> +			   RG_XTP_LN1_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +			   cal_data->ln2_tx_impsel_pmos << 12,
> +			   RG_XTP_LN2_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +			   cal_data->ln2_tx_impsel_nmos << 16,
> +			   RG_XTP_LN2_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +			   cal_data->ln3_tx_impsel_pmos << 12,
> +			   RG_XTP_LN3_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +			   cal_data->ln3_tx_impsel_nmos << 16,
> +			   RG_XTP_LN3_TX_IMPSEL_NMOS);
> +}
> +
> +static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
> +				enum mtk_dp_linkrate link_rate, int lane_count)
> +{
> +	int ret;
> +	union phy_configure_opts
> +		phy_opts = { .dp = {
> +				     .link_rate = link_rate_to_mb_per_s(
> +					     mtk_dp, link_rate),
> +				     .set_rate = 1,
> +				     .lanes = lane_count,
> +				     .set_lanes = 1,
> +				     .ssc = mtk_dp->train_info.sink_ssc,
> +			     } };
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP,
> +			   DP_PWR_STATE_MASK);
> +
> +	ret = phy_configure(mtk_dp->phy, &phy_opts);
> +
> +	mtk_dp_set_cal_data(mtk_dp);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL_LANE, DP_PWR_STATE_MASK);
> +	return ret;
> +}
> +
> +static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	const u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
> +			POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
> +			POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
> +			POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580, enable ? val : 0, val);
> +}
> +
> +static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int pattern)
> +{
> +	if (pattern < 0 || pattern > 4) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Implementation error, no such pattern %d\n", pattern);
> +		return;
> +	}
> +
> +	if (pattern == 1) // TPS1
> +		mtk_dp_set_idle_pattern(mtk_dp, false);
> +
> +	mtk_dp_update_bits(
> +		mtk_dp, MTK_DP_TRANS_P0_3400,
> +		pattern ? BIT(pattern - 1) << PATTERN1_EN_DP_TRANS_P0_SHIFT : 0,
> +		PATTERN1_EN_DP_TRANS_P0_MASK | PATTERN2_EN_DP_TRANS_P0_MASK |
> +			PATTERN3_EN_DP_TRANS_P0_MASK |
> +			PATTERN4_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +			   enable ? BIT(ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT) : 0,
> +			   ENHANCED_FRAME_EN_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
> +			   enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
> +			   DP_SCR_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	u32 val = BIT(VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT);
> +
> +	if (enable)
> +		val |= BIT(VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, val,
> +			   VIDEO_MUTE_SEL_DP_ENC0_P0_MASK |
> +				   VIDEO_MUTE_SW_DP_ENC0_P0_MASK);
> +
> +	if (mtk_dp_is_edp(mtk_dp))
> +		mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable);
> +	else
> +		mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable);
> +}
> +
> +static void mtk_dp_audio_mute(struct mtk_dp *mtk_dp, bool mute)
> +{
> +	if (mute) {
> +		mtk_dp_update_bits(
> +			mtk_dp, MTK_DP_ENC0_P0_3030,
> +			BIT(VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT) |
> +				BIT(VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT),
> +			VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
> +				VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
> +
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088, 0,
> +				   AU_EN_DP_ENC0_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0,
> +				   AU_TS_CFG_DP_ENC0_P0_MASK);
> +
> +	} else {
> +		mtk_dp_update_bits(
> +			mtk_dp, MTK_DP_ENC0_P0_3030, 0,
> +			VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
> +				VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
> +
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088,
> +				   BIT(AU_EN_DP_ENC0_P0_SHIFT),
> +				   AU_EN_DP_ENC0_P0_MASK);
> +		// Send one every two frames
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0x0F,
> +				   AU_TS_CFG_DP_ENC0_P0_MASK);
> +	}
> +}
> +
> +static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, 0,
> +			   SW_RST_B_PHYD);
> +	usleep_range(10, 200);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, SW_RST_B_PHYD,
> +			   SW_RST_B_PHYD);
> +}
> +
> +static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
> +	usleep_range(10, 200);
> +	mtk_dp_write(mtk_dp, MTK_DP_0034,
> +		     DA_CKM_CKTX0_EN_FORCE_EN | DA_CKM_BIAS_LPF_EN_FORCE_VAL |
> +			     DA_CKM_BIAS_EN_FORCE_VAL |
> +			     DA_XTP_GLB_LDO_EN_FORCE_VAL |
> +			     DA_XTP_GLB_AVD10_ON_FORCE_VAL);
> +	// Disable RX
> +	mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
> +	mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
> +		     0x550 | BIT(FUSE_SEL_SHIFT) | BIT(MEM_ISO_EN_SHIFT));
> +}
> +
> +static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR2;
> +	mtk_dp->train_info.lane_count = MTK_DP_MAX_LANES;
> +	mtk_dp->train_info.irq_status = 0;
> +	mtk_dp->train_info.cable_plugged_in = false;
> +	mtk_dp->train_info.cable_state_change = false;
> +	mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> +	mtk_dp->state = MTK_DP_STATE_INITIAL;
> +
> +	mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
> +	mtk_dp->info.depth = MTK_DP_COLOR_DEPTH_8BIT;
> +	memset(&mtk_dp->info.timings, 0, sizeof(struct mtk_dp_timings));
> +	mtk_dp->info.timings.frame_rate = 60;
> +
> +	mtk_dp->has_fec = false;
> +	mtk_dp->audio_enable = false;
> +}
> +
> +static void mtk_dp_sdp_set_down_cnt_init(struct mtk_dp *mtk_dp,
> +					 u32 sram_read_start)
> +{
> +	u32 sdp_down_cnt_init = 0;
> +	u32 dc_offset;
> +
> +	if (mtk_dp->info.timings.pix_rate_khz > 0)
> +		sdp_down_cnt_init = sram_read_start *
> +				    mtk_dp->train_info.link_rate * 2700 * 8 /
> +				    (mtk_dp->info.timings.pix_rate_khz * 4);
> +
> +	switch (mtk_dp->train_info.lane_count) {
> +	case 1:
> +		sdp_down_cnt_init = sdp_down_cnt_init > 0x1A ?
> +						  sdp_down_cnt_init :
> +						  0x1A; //26
> +		break;
> +	case 2:
> +		// case for LowResolution && High Audio Sample Rate
> +		dc_offset = mtk_dp->info.timings.vtotal <= 525 ? 0x04 : 0x00;
> +		sdp_down_cnt_init = sdp_down_cnt_init > 0x10 ?
> +						  sdp_down_cnt_init :
> +						  0x10 + dc_offset; //20 or 16
> +		break;
> +	case 4:
> +	default:
> +		sdp_down_cnt_init =
> +			sdp_down_cnt_init > 0x06 ? sdp_down_cnt_init : 0x06; //6
> +		break;
> +	}
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> +			   sdp_down_cnt_init
> +				   << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
> +			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_sdp_set_down_cnt_init_in_hblank(struct mtk_dp *mtk_dp)
> +{
> +	int pix_clk_mhz;
> +	u32 dc_offset;
> +	u32 spd_down_cnt_init = 0;
> +
> +	pix_clk_mhz = mtk_dp->info.format == MTK_DP_COLOR_FORMAT_YUV_420 ?
> +				    mtk_dp->info.timings.pix_rate_khz / 2000 :
> +				    mtk_dp->info.timings.pix_rate_khz / 1000;
> +
> +	switch (mtk_dp->train_info.lane_count) {
> +	case 1:
> +		spd_down_cnt_init = 0x20;
> +		break;
> +	case 2:
> +		dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x14 : 0x00;
> +		spd_down_cnt_init = 0x18 + dc_offset;
> +		break;
> +	case 4:
> +	default:
> +		dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x08 : 0x00;
> +		if (pix_clk_mhz > mtk_dp->train_info.link_rate * 27)
> +			spd_down_cnt_init = 0x8;
> +		else
> +			spd_down_cnt_init = 0x10 + dc_offset;
> +		break;
> +	}
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364, spd_down_cnt_init,
> +			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
> +}
> +
> +static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
> +{
> +	u32 sram_read_start = MTK_DP_TBC_BUF_READ_START_ADDR;
> +
> +	if (mtk_dp->train_info.lane_count > 0) {
> +		sram_read_start = min_t(
> +			u32, MTK_DP_TBC_BUF_READ_START_ADDR,
> +			mtk_dp->info.timings.vm.hactive /
> +				(mtk_dp->train_info.lane_count * 4 * 2 * 2));
> +		mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
> +	}
> +
> +	mtk_dp_setup_encoder(mtk_dp);
> +	mtk_dp_sdp_set_down_cnt_init_in_hblank(mtk_dp);
> +	mtk_dp_sdp_set_down_cnt_init(mtk_dp, sram_read_start);
> +}
> +
> +static void mtk_dp_calculate_pixrate(struct mtk_dp *mtk_dp)
> +{
> +	int target_frame_rate = 60;
> +	int target_pixel_clk;
> +
> +	if (mtk_dp->info.timings.frame_rate > 0) {
> +		target_frame_rate = mtk_dp->info.timings.frame_rate;
> +		target_pixel_clk = (int)mtk_dp->info.timings.htotal *
> +				   (int)mtk_dp->info.timings.vtotal *
> +				   target_frame_rate;
> +	} else if (mtk_dp->info.timings.pix_rate_khz > 0) {
> +		target_pixel_clk = mtk_dp->info.timings.pix_rate_khz * 1000;
> +	} else {
> +		target_pixel_clk = (int)mtk_dp->info.timings.htotal *
> +				   (int)mtk_dp->info.timings.vtotal *
> +				   target_frame_rate;
> +	}
> +
> +	if (target_pixel_clk > 0)
> +		mtk_dp->info.timings.pix_rate_khz = target_pixel_clk / 1000;
> +}
> +
> +static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_msa_bypass_disable(mtk_dp);
> +	mtk_dp_calculate_pixrate(mtk_dp);
> +	mtk_dp_pg_disable(mtk_dp);
> +	mtk_dp_setup_tu(mtk_dp);
> +}
> +
> +static void mtk_dp_edid_free(struct mtk_dp *mtk_dp)
> +{
> +	mutex_lock(&mtk_dp->edid_lock);
> +	kfree(mtk_dp->edid);
> +	mtk_dp->edid = NULL;
> +	mutex_unlock(&mtk_dp->edid_lock);
> +}
> +
> +static void mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp)
> +{
> +	ssize_t ret;
> +	u8 sink_count;
> +	u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +	u32 sink_count_reg;
> +	u32 link_status_reg;
> +	bool locked;
> +
> +	sink_count_reg = DP_SINK_COUNT_ESI;
> +	link_status_reg = DP_LANE0_1_STATUS;
> +
> +	ret = drm_dp_dpcd_readb(&mtk_dp->aux, sink_count_reg, &sink_count);
> +	if (ret < 0) {
> +		drm_info(mtk_dp->drm_dev, "Read sink count failed: %ld\n", ret);
> +		return;
> +	}
> +
> +	ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
> +	if (ret < 0) {
> +		drm_info(mtk_dp->drm_dev,
> +			 "Read DP_SINK_COUNT_ESI failed: %ld\n", ret);
> +		return;
> +	}
> +
> +	ret = drm_dp_dpcd_read(&mtk_dp->aux, link_status_reg, link_status,
> +			       sizeof(link_status));
> +	if (!ret) {
> +		drm_info(mtk_dp->drm_dev, "Read link status failed: %ld\n",
> +			 ret);
> +		return;
> +	}
> +
> +	locked = drm_dp_channel_eq_ok(link_status,
> +				      mtk_dp->train_info.lane_count);
> +	if (!locked && mtk_dp->train_state > MTK_DP_TRAIN_STATE_TRAINING_PRE)
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
> +
> +	if (link_status[1] & DP_REMOTE_CONTROL_COMMAND_PENDING)
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR,
> +				   DP_REMOTE_CONTROL_COMMAND_PENDING);
> +
> +	if (DP_GET_SINK_COUNT(sink_count) &&
> +	    (link_status[2] & DP_DOWNSTREAM_PORT_STATUS_CHANGED)) {
> +		mtk_dp_edid_free(mtk_dp);
> +		mtk_dp->train_info.check_cap_count = 0;
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
> +		msleep(20);
> +	}
> +}
> +
> +static void mtk_dp_sdp_stop_sending(struct mtk_dp *mtk_dp)
> +{
> +	u8 packet_type;
> +
> +	for (packet_type = MTK_DP_SDP_ACM; packet_type < MTK_DP_SDP_MAX_NUM;
> +	     packet_type++)
> +		mtk_dp_disable_sdp(mtk_dp, packet_type);
> +
> +	mtk_dp_sdp_vsc_ext_disable(mtk_dp);
> +}
> +
> +static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes,
> +					  u8 dpcd_adjust_req[2])
> +{
> +	int lane;
> +
> +	for (lane = 0; lane < lanes; ++lane) {
> +		u8 val;
> +		u8 swing;
> +		u8 preemphasis;
> +		int index = lane / 2;
> +		int shift = lane % 2 ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
> +
> +		swing = (dpcd_adjust_req[index] >> shift) &
> +			DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
> +		preemphasis = ((dpcd_adjust_req[index] >> shift) &
> +			       DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
> +			      DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
> +		val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
> +		      preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +
> +		if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
> +			val |= DP_TRAIN_MAX_SWING_REACHED;
> +		if (preemphasis == 3)
> +			val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> +
> +		mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing, preemphasis);
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET + lane,
> +				   val);
> +	}
> +
> +	// Wait for the signal to be stable enough
> +	usleep_range(2000, 5000);
> +}
> +
> +static void mtk_dp_read_link_status(struct mtk_dp *mtk_dp,
> +				    u8 link_status[DP_LINK_STATUS_SIZE])
> +{
> +	drm_dp_dpcd_read(&mtk_dp->aux, DP_LANE0_1_STATUS, link_status,
> +			 DP_LINK_STATUS_SIZE);
> +}
> +
> +static int mtk_dp_train_flow(struct mtk_dp *mtk_dp, int target_link_rate,
> +			     int target_lane_count)
> +{
> +	u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +	u8 lane_adjust[2] = {};
> +	bool pass_tps1 = false;
> +	bool pass_tps2_3 = false;
> +	int train_retries;
> +	int status_control;
> +	int iteration_count;
> +	u8 prev_lane_adjust;
> +	u8 val;
> +
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET, target_link_rate);
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> +			   target_lane_count | DP_LANE_COUNT_ENHANCED_FRAME_EN);
> +
> +	if (mtk_dp->train_info.sink_ssc)
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
> +				   DP_SPREAD_AMP_0_5);
> +
> +	train_retries = 0;
> +	status_control = 0;
> +	iteration_count = 1;
> +	prev_lane_adjust = 0xFF;
> +
> +	mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
> +	mtk_dp_phy_configure(mtk_dp, target_link_rate, target_lane_count);
> +
> +	dev_dbg(mtk_dp->dev,
> +		"Link train target_link_rate = 0x%x, target_lane_count = 0x%x\n",
> +		target_link_rate, target_lane_count);
> +
> +	do {
> +		train_retries++;
> +		if (!mtk_dp->train_info.cable_plugged_in ||
> +		    ((mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) !=
> +		     0x0)) {
> +			return -ENODEV;
> +		}
> +
> +		if (mtk_dp->train_state < MTK_DP_TRAIN_STATE_TRAINING)
> +			return -EAGAIN;
> +
> +		if (!pass_tps1) {
> +			mtk_dp_training_set_scramble(mtk_dp, false);
> +
> +			if (status_control == 0) {
> +				status_control = 1;
> +				mtk_dp_train_set_pattern(mtk_dp, 1);
> +				val = DP_LINK_SCRAMBLING_DISABLE |
> +				      DP_TRAINING_PATTERN_1;
> +				drm_dp_dpcd_writeb(
> +					&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +					DP_LINK_SCRAMBLING_DISABLE |
> +						DP_TRAINING_PATTERN_1);
> +				drm_dp_dpcd_read(&mtk_dp->aux,
> +						 DP_ADJUST_REQUEST_LANE0_1,
> +						 lane_adjust,
> +						 sizeof(lane_adjust));
> +				iteration_count++;
> +
> +				mtk_dp_train_update_swing_pre(
> +					mtk_dp, target_lane_count, lane_adjust);
> +			}
> +
> +			drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
> +							       mtk_dp->rx_cap);
> +			mtk_dp_read_link_status(mtk_dp, link_status);
> +
> +			if (drm_dp_clock_recovery_ok(link_status,
> +						     target_lane_count)) {
> +				mtk_dp->train_info.cr_done = true;
> +				pass_tps1 = true;
> +				train_retries = 0;
> +				iteration_count = 1;
> +				dev_dbg(mtk_dp->dev, "Link train CR pass\n");
> +			} else if (prev_lane_adjust == link_status[4]) {
> +				iteration_count++;
> +				if (prev_lane_adjust &
> +				    DP_ADJUST_VOLTAGE_SWING_LANE0_MASK)
> +					break;
> +			} else {
> +				prev_lane_adjust = link_status[4];
> +			}
> +			dev_dbg(mtk_dp->dev, "Link train CQ fail\n");
> +		} else if (pass_tps1 && !pass_tps2_3) {
> +			if (status_control == 1) {
> +				status_control = 2;
> +				if (mtk_dp->train_info.tps4) {
> +					mtk_dp_train_set_pattern(mtk_dp, 4);
> +					val = DP_TRAINING_PATTERN_4;
> +				} else if (mtk_dp->train_info.tps3) {
> +					mtk_dp_train_set_pattern(mtk_dp, 3);
> +					val = DP_LINK_SCRAMBLING_DISABLE |
> +					      DP_TRAINING_PATTERN_3;
> +				} else {
> +					mtk_dp_train_set_pattern(mtk_dp, 2);
> +					val = DP_LINK_SCRAMBLING_DISABLE |
> +					      DP_TRAINING_PATTERN_2;
> +				}
> +				drm_dp_dpcd_writeb(&mtk_dp->aux,
> +						   DP_TRAINING_PATTERN_SET,
> +						   val);
> +
> +				drm_dp_dpcd_read(&mtk_dp->aux,
> +						 DP_ADJUST_REQUEST_LANE0_1,
> +						 lane_adjust,
> +						 sizeof(lane_adjust));
> +
> +				iteration_count++;
> +				mtk_dp_train_update_swing_pre(
> +					mtk_dp, target_lane_count, lane_adjust);
> +			}
> +
> +			drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
> +							   mtk_dp->rx_cap);
> +
> +			mtk_dp_read_link_status(mtk_dp, link_status);
> +
> +			if (!drm_dp_clock_recovery_ok(link_status,
> +						      target_lane_count)) {
> +				mtk_dp->train_info.cr_done = false;
> +				mtk_dp->train_info.eq_done = false;
> +				dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
> +				break;
> +			}
> +
> +			if (drm_dp_channel_eq_ok(link_status,
> +						 target_lane_count)) {
> +				mtk_dp->train_info.eq_done = true;
> +				pass_tps2_3 = true;
> +				dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
> +				break;
> +			}
> +
> +			if (prev_lane_adjust == link_status[4])
> +				iteration_count++;
> +			else
> +				prev_lane_adjust = link_status[4];
> +		}
> +
> +		drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
> +				 lane_adjust, sizeof(lane_adjust));
> +		mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
> +					      lane_adjust);
> +	} while (train_retries < MTK_DP_TRAIN_RETRY_LIMIT &&
> +		 iteration_count < MTK_DP_TRAIN_MAX_ITERATIONS);
> +
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +			   DP_TRAINING_PATTERN_DISABLE);
> +	mtk_dp_train_set_pattern(mtk_dp, 0);
> +
> +	if (pass_tps2_3) {
> +		mtk_dp->train_info.link_rate = target_link_rate;
> +		mtk_dp->train_info.lane_count = target_lane_count;
> +
> +		mtk_dp_training_set_scramble(mtk_dp, true);
> +
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> +				   target_lane_count |
> +					   DP_LANE_COUNT_ENHANCED_FRAME_EN);
> +		mtk_dp_set_enhanced_frame_mode(mtk_dp, true);
> +
> +		return 0;
> +	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static bool mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
> +{
> +	u8 buf[DP_RECEIVER_CAP_SIZE] = {};
> +	u8 val;
> +	struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
> +
> +	if (!mtk_dp_plug_state(mtk_dp))
> +		return false;
> +
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> +	usleep_range(2000, 5000);
> +
> +	drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
> +
> +	memcpy(mtk_dp->rx_cap, buf, min(sizeof(mtk_dp->rx_cap), sizeof(buf)));
> +	mtk_dp->rx_cap[DP_TRAINING_AUX_RD_INTERVAL] &= DP_TRAINING_AUX_RD_MASK;
> +
> +	train_info->link_rate =
> +		min_t(int, MTK_DP_MAX_LINK_RATE, buf[DP_MAX_LINK_RATE]);
> +	train_info->lane_count =
> +		min_t(int, MTK_DP_MAX_LANES, drm_dp_max_lane_count(buf));
> +
> +	train_info->tps3 = drm_dp_tps3_supported(buf);
> +	train_info->tps4 = drm_dp_tps4_supported(buf);
> +
> +	train_info->sink_ssc =
> +		!!(buf[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5);
> +
> +	train_info->sink_ssc = false;
> +
> +	drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
> +	if (val & DP_MST_CAP) {
> +		// Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0
> +		drm_dp_dpcd_readb(&mtk_dp->aux,
> +				  DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0, &val);
> +		if (val)
> +			drm_dp_dpcd_writeb(&mtk_dp->aux,
> +					   DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
> +					   val);
> +	}
> +
> +	return true;
> +}
> +
> +static int mtk_dp_edid_parse_audio_capabilities(struct mtk_dp *mtk_dp,
> +						struct mtk_dp_audio_cfg *cfg)
> +{
> +	struct cea_sad *sads;
> +	int sad_count;
> +	int i;
> +	int ret = 0;
> +
> +	mutex_lock(&mtk_dp->edid_lock);
> +	if (!mtk_dp->edid) {
> +		mutex_unlock(&mtk_dp->edid_lock);
> +		dev_err(mtk_dp->dev, "EDID not found!\n");
> +		return -EINVAL;
> +	}
> +
> +	sad_count = drm_edid_to_sad(mtk_dp->edid, &sads);
> +	mutex_unlock(&mtk_dp->edid_lock);
> +	if (sad_count <= 0) {
> +		drm_info(mtk_dp->drm_dev, "The SADs is NULL\n");
> +		return 0;
> +	}
> +
> +	for (i = 0; i < sad_count; i++) {
> +		int sample_rate;
> +		int word_length;
> +		// Only PCM supported at the moment
> +		if (sads[i].format != HDMI_AUDIO_CODING_TYPE_PCM)
> +			continue;
> +
> +		sample_rate = drm_cea_sad_get_sample_rate(&sads[i]);
> +		word_length =
> +			drm_cea_sad_get_uncompressed_word_length(&sads[i]);
> +		if (sample_rate <= 0 || word_length <= 0)
> +			continue;
> +
> +		cfg->channels = sads[i].channels;
> +		cfg->word_length_bits = word_length;
> +		cfg->sample_rate = sample_rate;
> +		ret = 1;
> +		break;
> +	}
> +	kfree(sads);
> +
> +	return ret;
> +}
> +
> +static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
> +{
> +	phy_reset(mtk_dp->phy);
> +	mtk_dp_reset_swing_pre_emphasis(mtk_dp);
> +
> +	usleep_range(2000, 5000);
> +}
> +
> +static int mtk_dp_train_start(struct mtk_dp *mtk_dp)
> +{
> +	int ret = 0;
> +	int lane_count;
> +	int link_rate;
> +	int train_limit;
> +	int max_link_rate;
> +	int plug_wait;
> +
> +	for (plug_wait = 7; !mtk_dp_plug_state(mtk_dp) && plug_wait > 0;
> +	     --plug_wait)
> +		usleep_range(1000, 5000);
> +	if (plug_wait == 0) {
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> +		return -ENODEV;
> +	}
> +
> +	link_rate = mtk_dp->rx_cap[1];
> +	lane_count = mtk_dp->rx_cap[2] & 0x1F;
> +
> +	mtk_dp->train_info.link_rate = min(MTK_DP_MAX_LINK_RATE, link_rate);
> +	mtk_dp->train_info.lane_count = min(MTK_DP_MAX_LANES, lane_count);
> +	link_rate = mtk_dp->train_info.link_rate;
> +	lane_count = mtk_dp->train_info.lane_count;
> +
> +	switch (link_rate) {
> +	case MTK_DP_LINKRATE_RBR:
> +	case MTK_DP_LINKRATE_HBR:
> +	case MTK_DP_LINKRATE_HBR2:
> +	case MTK_DP_LINKRATE_HBR25:
> +	case MTK_DP_LINKRATE_HBR3:
> +		break;
> +	default:
> +		mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR3;
> +		break;
> +	};
> +
> +	max_link_rate = link_rate;
> +	for (train_limit = 0; train_limit <= 6; ++train_limit) {
> +		mtk_dp->train_info.cr_done = false;
> +		mtk_dp->train_info.eq_done = false;
> +
> +		mtk_dp_train_change_mode(mtk_dp);
> +		ret = mtk_dp_train_flow(mtk_dp, link_rate, lane_count);
> +		if (ret == -ENODEV || ret == -EAGAIN)
> +			return ret;
> +
> +		if (!mtk_dp->train_info.cr_done) {
> +			switch (link_rate) {
> +			case MTK_DP_LINKRATE_RBR:
> +				lane_count = lane_count / 2;
> +				link_rate = max_link_rate;
> +				if (lane_count == 0x0) {
> +					mtk_dp->train_state =
> +						MTK_DP_TRAIN_STATE_DPIDLE;
> +					return -EIO;
> +				}
> +				break;
> +			case MTK_DP_LINKRATE_HBR:
> +				link_rate = MTK_DP_LINKRATE_RBR;
> +				break;
> +			case MTK_DP_LINKRATE_HBR2:
> +				link_rate = MTK_DP_LINKRATE_HBR;
> +				break;
> +			case MTK_DP_LINKRATE_HBR3:
> +				link_rate = MTK_DP_LINKRATE_HBR2;
> +				break;
> +			default:
> +				return -EINVAL;
> +			};
> +		} else if (!mtk_dp->train_info.eq_done) {
> +			lane_count /= 2;
> +			if (lane_count == 0)
> +				return -EIO;
> +		} else {
> +			return 0;
> +		}
> +	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_train_handler(struct mtk_dp *mtk_dp)
> +{
> +	int ret = 0;
> +
> +	if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
> +		return ret;
> +
> +	switch (mtk_dp->train_state) {
> +	case MTK_DP_TRAIN_STATE_STARTUP:
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> +		break;
> +
> +	case MTK_DP_TRAIN_STATE_CHECKCAP:
> +		if (mtk_dp_parse_capabilities(mtk_dp)) {
> +			mtk_dp->train_info.check_cap_count = 0;
> +			mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
> +		} else {
> +			mtk_dp->train_info.check_cap_count++;
> +
> +			if (mtk_dp->train_info.check_cap_count >
> +			    MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT) {
> +				mtk_dp->train_info.check_cap_count = 0;
> +				mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> +				ret = -ETIMEDOUT;
> +			}
> +		}
> +		break;
> +
> +	case MTK_DP_TRAIN_STATE_CHECKEDID: {
> +		int caps_found = mtk_dp_edid_parse_audio_capabilities(
> +			mtk_dp, &mtk_dp->info.audio_caps);
> +		mtk_dp->audio_enable = caps_found > 0;
> +		if (!mtk_dp->audio_enable)
> +			memset(&mtk_dp->info.audio_caps, 0,
> +			       sizeof(mtk_dp->info.audio_caps));
> +	}
> +
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
> +		break;
> +
> +	case MTK_DP_TRAIN_STATE_TRAINING_PRE:
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING;
> +		break;
> +
> +	case MTK_DP_TRAIN_STATE_TRAINING:
> +		ret = mtk_dp_train_start(mtk_dp);
> +		if (!ret) {
> +			mtk_dp_video_mute(mtk_dp, true);
> +			mtk_dp_audio_mute(mtk_dp, true);
> +			mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKTIMING;
> +			mtk_dp_fec_enable(mtk_dp, mtk_dp->has_fec);
> +		} else if (ret != -EAGAIN) {
> +			mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> +		}
> +
> +		ret = 0;
> +		break;
> +
> +	case MTK_DP_TRAIN_STATE_CHECKTIMING:
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_NORMAL;
> +		break;
> +	case MTK_DP_TRAIN_STATE_NORMAL:
> +		break;
> +	case MTK_DP_TRAIN_STATE_POWERSAVE:
> +		break;
> +	case MTK_DP_TRAIN_STATE_DPIDLE:
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	if (enable) {
> +		mtk_dp_set_tx_out(mtk_dp);
> +		mtk_dp_video_mute(mtk_dp, false);
> +	} else {
> +		mtk_dp_video_mute(mtk_dp, true);
> +	}
> +}
> +
> +static void mtk_dp_audio_sdp_setup(struct mtk_dp *mtk_dp,
> +				   struct mtk_dp_audio_cfg *cfg)
> +{
> +	struct mtk_dp_sdp_packet packet;
> +	struct hdmi_audio_infoframe frame;
> +
> +	hdmi_audio_infoframe_init(&frame);
> +	frame.coding_type = HDMI_AUDIO_CODING_TYPE_PCM;
> +	frame.channels = cfg->channels;
> +	frame.sample_frequency = cfg->sample_rate;
> +
> +	switch (cfg->word_length_bits) {
> +	case 16:
> +		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
> +		break;
> +	case 20:
> +		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_20;
> +		break;
> +	case 24:
> +	default:
> +		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_24;
> +		break;
> +	}
> +
> +	packet.type = MTK_DP_SDP_AUI;
> +	hdmi_audio_infoframe_pack_for_dp(&frame, &packet.sdp,
> +					 MTK_DP_DP_VERSION_11);
> +
> +	mtk_dp_audio_sdp_asp_set_channels(mtk_dp, cfg->channels);
> +	mtk_dp_setup_sdp(mtk_dp, &packet);
> +}
> +
> +static void mtk_dp_audio_setup(struct mtk_dp *mtk_dp,
> +			       struct mtk_dp_audio_cfg *cfg)
> +{
> +	mtk_dp_audio_sdp_setup(mtk_dp, cfg);
> +	mtk_dp_audio_channel_status_set(mtk_dp, cfg);
> +
> +	mtk_dp_audio_setup_channels(mtk_dp, cfg);
> +	mtk_dp_audio_set_divider(mtk_dp);
> +}
> +
> +static void mtk_dp_video_config(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_mn_overwrite_disable(mtk_dp);
> +
> +	mtk_dp_set_msa(mtk_dp);
> +
> +	mtk_dp_set_color_depth(mtk_dp, mtk_dp->info.depth);
> +	mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
> +}
> +
> +static int mtk_dp_state_handler(struct mtk_dp *mtk_dp)
> +{
> +	int ret = 0;
> +
> +	switch (mtk_dp->state) {
> +	case MTK_DP_STATE_INITIAL:
> +		mtk_dp_video_mute(mtk_dp, true);
> +		mtk_dp_audio_mute(mtk_dp, true);
> +		mtk_dp->state = MTK_DP_STATE_IDLE;
> +		break;
> +
> +	case MTK_DP_STATE_IDLE:
> +		if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
> +			mtk_dp->state = MTK_DP_STATE_PREPARE;
> +		break;
> +
> +	case MTK_DP_STATE_PREPARE:
> +		mtk_dp_video_config(mtk_dp);
> +		mtk_dp_video_enable(mtk_dp, true);
> +
> +		if (mtk_dp->audio_enable) {
> +			mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_caps);
> +			mtk_dp_audio_mute(mtk_dp, false);
> +		}
> +
> +		mtk_dp->state = MTK_DP_STATE_NORMAL;
> +		break;
> +
> +	case MTK_DP_STATE_NORMAL:
> +		if (mtk_dp->train_state != MTK_DP_TRAIN_STATE_NORMAL) {
> +			mtk_dp_video_mute(mtk_dp, true);
> +			mtk_dp_audio_mute(mtk_dp, true);
> +			mtk_dp_sdp_stop_sending(mtk_dp);
> +			mtk_dp->state = MTK_DP_STATE_IDLE;
> +		}
> +		break;
> +
> +	default:
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_set_idle_pattern(mtk_dp, true);
> +	mtk_dp_initialize_priv_data(mtk_dp);
> +
> +	mtk_dp_initialize_settings(mtk_dp);
> +	mtk_dp_initialize_aux_settings(mtk_dp);
> +	mtk_dp_initialize_digital_settings(mtk_dp);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
> +			   BIT(RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT),
> +			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK);
> +	mtk_dp_initialize_hpd_detect_settings(mtk_dp);
> +
> +	mtk_dp_digital_sw_reset(mtk_dp);
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev;
> +	int event;
> +	u8 buf[DP_RECEIVER_CAP_SIZE] = {};
> +
> +	event = mtk_dp_plug_state(mtk_dp) ? connector_status_connected :
> +						  connector_status_disconnected;
> +
> +	if (event < 0)
> +		return IRQ_HANDLED;
> +
> +	if (mtk_dp->drm_dev) {
> +		dev_info(mtk_dp->dev, "drm_helper_hpd_irq_event\n");
> +		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
> +	}
> +
> +	if (mtk_dp->train_info.cable_state_change) {
> +		mtk_dp->train_info.cable_state_change = false;
> +
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> +
> +		if (!mtk_dp->train_info.cable_plugged_in ||
> +		    !mtk_dp_plug_state(mtk_dp)) {
> +			mtk_dp_video_mute(mtk_dp, true);
> +			mtk_dp_audio_mute(mtk_dp, true);
> +
> +			mtk_dp_initialize_priv_data(mtk_dp);
> +			mtk_dp_set_idle_pattern(mtk_dp, true);
> +			if (mtk_dp->has_fec)
> +				mtk_dp_fec_enable(mtk_dp, false);
> +			mtk_dp_sdp_stop_sending(mtk_dp);
> +
> +			mtk_dp_edid_free(mtk_dp);
> +			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +					   DP_PWR_STATE_BANDGAP_TPLL,
> +					   DP_PWR_STATE_MASK);
> +		} else {
> +			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +					   DP_PWR_STATE_BANDGAP_TPLL_LANE,
> +					   DP_PWR_STATE_MASK);
> +			drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
> +			mtk_dp->train_info.link_rate =
> +				min_t(int, MTK_DP_MAX_LINK_RATE,
> +				      buf[DP_MAX_LINK_RATE]);
> +			mtk_dp->train_info.lane_count =
> +				min_t(int, MTK_DP_MAX_LANES,
> +				      drm_dp_max_lane_count(buf));
> +		}
> +	}
> +
> +	if (mtk_dp->train_info.irq_status & MTK_DP_HPD_INTERRUPT) {
> +		dev_info(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n");
> +		mtk_dp->train_info.irq_status &= ~MTK_DP_HPD_INTERRUPT;
> +		mtk_dp_hpd_sink_event(mtk_dp);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mtk_dp_hpd_isr_handler(struct mtk_dp *mtk_dp)
> +{
> +	bool connected;
> +	u16 swirq_status = mtk_dp_swirq_get_clear(mtk_dp);
> +	u8 hwirq_status = mtk_dp_hwirq_get_clear(mtk_dp);
> +	struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
> +
> +	train_info->irq_status |= hwirq_status | swirq_status;
> +
> +	if (!train_info->irq_status)
> +		return IRQ_HANDLED;
> +
> +	connected = mtk_dp_plug_state(mtk_dp);
> +	if (connected || !train_info->cable_plugged_in)
> +		train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
> +	else if (!connected || train_info->cable_plugged_in)
> +		train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
> +
> +	if (!(train_info->irq_status &
> +	      (MTK_DP_HPD_CONNECT | MTK_DP_HPD_DISCONNECT)))
> +		return IRQ_HANDLED;
> +
> +	if (train_info->irq_status & MTK_DP_HPD_CONNECT) {
> +		train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
> +		train_info->cable_plugged_in = true;
> +	} else {
> +		train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
> +		train_info->cable_plugged_in = false;
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> +	}
> +	train_info->cable_state_change = true;
> +	return IRQ_WAKE_THREAD;
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev;
> +	u32 irq_status;
> +
> +	irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS);
> +
> +	if (!irq_status)
> +		return IRQ_HANDLED;
> +
> +	if (irq_status & RGS_IRQ_STATUS_TRANSMITTER)
> +		return mtk_dp_hpd_isr_handler(mtk_dp);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int mtk_dp_dt_parse_pdata(struct mtk_dp *mtk_dp,
> +				 struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	int ret = 0;
> +	void __iomem *base;
> +
> +	base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(base))
> +		return PTR_ERR(base);
> +
> +	mtk_dp->regs = devm_regmap_init_mmio(dev, base, &mtk_dp_regmap_config);
> +	if (IS_ERR(mtk_dp->regs))
> +		return PTR_ERR(mtk_dp->regs);
> +
> +	mtk_dp->dp_tx_clk = devm_clk_get(dev, "faxi");
> +	if (IS_ERR(mtk_dp->dp_tx_clk)) {
> +		ret = PTR_ERR(mtk_dp->dp_tx_clk);
> +		dev_err(dev, "Failed to get dptx clock: %d\n", ret);
> +		mtk_dp->dp_tx_clk = NULL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_update_plugged_status(struct mtk_dp *mtk_dp)
> +{
> +	bool connected, has_audio;
> +
> +	mutex_lock(&mtk_dp->update_plugged_status_lock);
> +	connected = mtk_dp_plug_state(mtk_dp);
> +	has_audio = drm_detect_monitor_audio(mtk_dp->edid);
> +	if (mtk_dp->plugged_cb && mtk_dp->codec_dev)
> +		mtk_dp->plugged_cb(mtk_dp->codec_dev, connected & has_audio);
> +	mutex_unlock(&mtk_dp->update_plugged_status_lock);
> +}
> +
> +static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	enum drm_connector_status ret = connector_status_disconnected;
> +	u8 sink_count = 0;
> +
> +	if (mtk_dp_is_edp(mtk_dp))
> +		return connector_status_connected;
> +
> +	if (mtk_dp_plug_state(mtk_dp)) {
> +		drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
> +		if (DP_GET_SINK_COUNT(sink_count))
> +			ret = connector_status_connected;
> +	}
> +
> +	mtk_dp_update_plugged_status(mtk_dp);
> +	return ret;
> +}
> +
> +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> +				    struct drm_connector *connector)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	bool enabled = mtk_dp->enabled;
> +	struct edid *new_edid = NULL;
> +
> +	if (!enabled)
> +		drm_bridge_chain_pre_enable(bridge);
> +
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> +	usleep_range(2000, 5000);
> +
> +	if (mtk_dp_plug_state(mtk_dp))
> +		new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> +
> +	if (!enabled)
> +		drm_bridge_chain_post_disable(bridge);
> +
> +	mutex_lock(&mtk_dp->edid_lock);
> +	kfree(mtk_dp->edid);
> +	mtk_dp->edid = NULL;
> +
> +	if (!new_edid) {
> +		mutex_unlock(&mtk_dp->edid_lock);
> +		return NULL;
> +	}
> +
> +	mtk_dp->edid = drm_edid_duplicate(new_edid);
> +	mutex_unlock(&mtk_dp->edid_lock);
> +
> +	return new_edid;
> +}
> +
> +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> +				   struct drm_dp_aux_msg *msg)
> +{
> +	ssize_t err = -EAGAIN;
> +	struct mtk_dp *mtk_dp;
> +	bool is_read;
> +	u8 request;
> +	size_t accessed_bytes = 0;
> +	int retry = 3, ret = 0;
> +
> +	mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> +
> +	if (!mtk_dp->train_info.cable_plugged_in ||
> +	    mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> +		mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> +		err = -EAGAIN;
> +		goto err;
> +	}
> +
> +	switch (msg->request) {
> +	case DP_AUX_I2C_MOT:
> +	case DP_AUX_I2C_WRITE:
> +	case DP_AUX_NATIVE_WRITE:
> +	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> +	case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> +		request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> +		is_read = false;
> +		break;
> +	case DP_AUX_I2C_READ:
> +	case DP_AUX_NATIVE_READ:
> +	case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> +		request = msg->request;
> +		is_read = true;
> +		break;
> +	default:
> +		drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> +			msg->request);
> +		err = -EINVAL;
> +		goto err;
> +	}
> +
> +	if (msg->size == 0) {
> +		mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> +				       msg->address + accessed_bytes,
> +				       msg->buffer + accessed_bytes, 0);
> +	} else {
> +		while (accessed_bytes < msg->size) {
> +			size_t to_access =
> +				min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> +				      msg->size - accessed_bytes);
> +			retry = 3;
> +			while (retry--) {
> +				ret = mtk_dp_aux_do_transfer(
> +					mtk_dp, is_read, request,
> +					msg->address + accessed_bytes,
> +					msg->buffer + accessed_bytes,
> +					to_access);
> +				if (ret == 0)
> +					break;
> +				udelay(50);
> +			}
> +			if (!retry && ret) {
> +				drm_info(mtk_dp->drm_dev,
> +					 "Failed to do AUX transfer: %d\n",
> +					 ret);
> +				break;
> +			}
> +			accessed_bytes += to_access;
> +		}
> +	}
> +err:
> +	if (!ret) {
> +		msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +		ret = msg->size;
> +	} else {
> +		msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> +		return err;
> +	}
> +
> +	msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +	return msg->size;
> +}
> +
> +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> +{
> +	drm_dp_aux_init(&mtk_dp->aux);
> +	mtk_dp->aux.name = "aux_mtk_dp";
> +	mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> +}
> +
> +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> +{
> +	mutex_lock(&mtk_dp->dp_lock);
> +
> +	mtk_dp_hwirq_enable(mtk_dp, false);
> +	mtk_dp_power_disable(mtk_dp);
> +	phy_exit(mtk_dp->phy);
> +	clk_disable_unprepare(mtk_dp->dp_tx_clk);
> +
> +	mutex_unlock(&mtk_dp->dp_lock);
> +}
> +
> +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> +{
> +	int ret = 0;
> +
> +	mutex_lock(&mtk_dp->dp_lock);
> +
> +	ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> +	if (ret < 0) {
> +		dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> +		goto err;
> +	}
> +	ret = phy_init(mtk_dp->phy);
> +	if (ret) {
> +		dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> +		goto err_phy_init;
> +	}
> +	ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> +	if (ret) {
> +		dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> +		goto err_phy_config;
> +	}
> +
> +	mtk_dp_init_port(mtk_dp);
> +	mtk_dp_power_enable(mtk_dp);
> +	mtk_dp_hwirq_enable(mtk_dp, true);
> +
> +err_phy_config:
> +	phy_exit(mtk_dp->phy);
> +err_phy_init:
> +	clk_disable_unprepare(mtk_dp->dp_tx_clk);
> +err:
> +	mutex_unlock(&mtk_dp->dp_lock);
> +	return ret;
> +}
> +
> +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> +				enum drm_bridge_attach_flags flags)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	int ret;
> +
> +	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> +		dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> +		return -EINVAL;
> +	}
> +
> +	ret = mtk_dp_poweron(mtk_dp);
> +	if (ret)
> +		return ret;
> +
> +	if (mtk_dp->next_bridge) {
> +		ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> +					&mtk_dp->bridge, flags);
> +		if (ret) {
> +			drm_warn(mtk_dp->drm_dev,
> +				 "Failed to attach external bridge: %d\n", ret);
> +			goto err_bridge_attach;
> +		}
> +	}
> +
> +	mtk_dp->drm_dev = bridge->dev;
> +
> +	return 0;
> +
> +err_bridge_attach:
> +	mtk_dp_poweroff(mtk_dp);
> +	return ret;
> +}
> +
> +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +	mtk_dp->drm_dev = NULL;
> +
> +	mtk_dp_poweroff(mtk_dp);
> +}
> +
> +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> +					 struct drm_bridge_state *old_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +	mtk_dp_video_mute(mtk_dp, true);
> +	mtk_dp_audio_mute(mtk_dp, true);
> +	mtk_dp->state = MTK_DP_STATE_IDLE;
> +	mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> +
> +	mtk_dp->enabled = false;
> +	msleep(100);
> +}
> +
> +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> +					  struct drm_display_mode *mode)
> +{
> +	struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> +
> +	drm_display_mode_to_videomode(mode, &timings->vm);
> +	timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> +	timings->htotal = mode->htotal;
> +	timings->vtotal = mode->vtotal;
> +}
> +
> +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> +					struct drm_bridge_state *old_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	struct drm_connector *conn;
> +	struct drm_connector_state *conn_state;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *crtc_state;
> +	int ret = 0;
> +	int i;
> +
> +	conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> +							bridge->encoder);
> +	if (!conn) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as connector is missing\n");
> +		return;
> +	}
> +
> +	memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);

This should be protected by a mutex (just like any resource shared
between KMS and ALSA)

> +	conn_state =
> +		drm_atomic_get_new_connector_state(old_state->base.state, conn);
> +	if (!conn_state) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as connector state is missing\n");
> +		return;
> +	}
> +
> +	crtc = conn_state->crtc;
> +	if (!crtc) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as connector state doesn't have a crtc\n");
> +		return;
> +	}
> +
> +	crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> +	if (!crtc_state) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as crtc state is missing\n");
> +		return;
> +	}
> +
> +	mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);
> +	if (!mtk_dp_parse_capabilities(mtk_dp)) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as nothing is plugged in\n");
> +		return;
> +	}
> +
> +	//training
> +	for (i = 0; i < 50; i++) {
> +		ret = mtk_dp_train_handler(mtk_dp);
> +		if (ret) {
> +			drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> +				ret);
> +			return;
> +		}
> +
> +		ret = mtk_dp_state_handler(mtk_dp);
> +		if (ret) {
> +			drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> +				ret);
> +			return;
> +		}
> +	}
> +
> +	mtk_dp->enabled = true;
> +	mtk_dp_update_plugged_status(mtk_dp);
> +}
> +
> +static enum drm_mode_status
> +mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
> +			 const struct drm_display_info *info,
> +			 const struct drm_display_mode *mode)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	u32 rx_linkrate;
> +	u32 bpp;
> +
> +	bpp = info->color_formats & DRM_COLOR_FORMAT_YCRCB422 ? 16 : 24;
> +	rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
> +	if (rx_linkrate * mtk_dp->train_info.lane_count < mode->clock * bpp / 8)
> +		return MODE_CLOCK_HIGH;
> +
> +	if (mode->clock > 600000)
> +		return MODE_CLOCK_HIGH;
> +
> +	if ((mode->clock * 1000) / (mode->htotal * mode->vtotal) > 62)
> +		return MODE_CLOCK_HIGH;
> +
> +	return MODE_OK;
> +}
> +
> +static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(
> +	struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
> +	struct drm_crtc_state *crtc_state,
> +	struct drm_connector_state *conn_state, unsigned int *num_output_fmts)
> +{
> +	u32 *output_fmts;
> +
> +	*num_output_fmts = 0;
> +	output_fmts = kcalloc(1, sizeof(*output_fmts), GFP_KERNEL);
> +	if (!output_fmts)
> +		return NULL;
> +	*num_output_fmts = 1;
> +	output_fmts[0] = MEDIA_BUS_FMT_FIXED;
> +	return output_fmts;
> +}
> +
> +static const u32 mt8195_input_fmts[] = {
> +	MEDIA_BUS_FMT_RGB888_1X24,
> +	MEDIA_BUS_FMT_YUV8_1X24,
> +	MEDIA_BUS_FMT_YUYV8_1X16,
> +};
> +
> +static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(
> +	struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
> +	struct drm_crtc_state *crtc_state,
> +	struct drm_connector_state *conn_state, u32 output_fmt,
> +	unsigned int *num_input_fmts)
> +{
> +	u32 *input_fmts;
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> +	struct drm_display_info *display_info =
> +		&conn_state->connector->display_info;
> +	u32 rx_linkrate;
> +	u32 bpp;
> +
> +	bpp = (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422) ? 16 :
> +										24;
> +	rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
> +	*num_input_fmts = 0;
> +	input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts), sizeof(*input_fmts),
> +			     GFP_KERNEL);
> +	if (!input_fmts)
> +		return NULL;
> +
> +	*num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
> +
> +	memcpy(input_fmts, mt8195_input_fmts,
> +	       sizeof(*input_fmts) * ARRAY_SIZE(mt8195_input_fmts));
> +
> +	if (((rx_linkrate * mtk_dp->train_info.lane_count) <
> +	     (mode->clock * 24 / 8)) &&
> +	    ((rx_linkrate * mtk_dp->train_info.lane_count) >
> +	     (mode->clock * 16 / 8)) &&
> +	    (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422)) {
> +		kfree(input_fmts);
> +		input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL);
> +		*num_input_fmts = 1;
> +		input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
> +	}
> +
> +	return input_fmts;
> +}
> +
> +static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
> +				      struct drm_bridge_state *bridge_state,
> +				      struct drm_crtc_state *crtc_state,
> +				      struct drm_connector_state *conn_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	unsigned int input_bus_format;
> +
> +	input_bus_format = bridge_state->input_bus_cfg.format;
> +
> +	dev_info(mtk_dp->dev, "input format 0x%04x, output format 0x%04x\n",
> +		 bridge_state->input_bus_cfg.format,
> +		 bridge_state->output_bus_cfg.format);
> +
> +	mtk_dp->input_fmt = input_bus_format;
> +	if (mtk_dp->input_fmt == MEDIA_BUS_FMT_YUYV8_1X16)
> +		mtk_dp->info.format = MTK_DP_COLOR_FORMAT_YUV_422;
> +	else
> +		mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
> +
> +	return 0;
> +}
> +
> +static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
> +	.atomic_check = mtk_dp_bridge_atomic_check,
> +	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> +	.atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts,
> +	.atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts,
> +	.atomic_reset = drm_atomic_helper_bridge_reset,
> +	.attach = mtk_dp_bridge_attach,
> +	.detach = mtk_dp_bridge_detach,
> +	.atomic_enable = mtk_dp_bridge_atomic_enable,
> +	.atomic_disable = mtk_dp_bridge_atomic_disable,
> +	.mode_valid = mtk_dp_bridge_mode_valid,
> +	.get_edid = mtk_dp_get_edid,
> +	.detect = mtk_dp_bdg_detect,
> +};
> +
> +/*
> + * HDMI audio codec callbacks
> + */
> +static int mtk_dp_audio_hw_params(struct device *dev, void *data,
> +				  struct hdmi_codec_daifmt *daifmt,
> +				  struct hdmi_codec_params *params)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +	struct mtk_dp_audio_cfg cfg;
> +
> +	if (!mtk_dp->enabled) {
> +		pr_err("%s, DP is not ready!\n", __func__);
> +		return -ENODEV;
> +	}
> +
> +	cfg.channels = params->cea.channels;
> +	cfg.sample_rate = params->sample_rate;
> +	cfg.word_length_bits = 24;
> +
> +	mtk_dp_audio_setup(mtk_dp, &cfg);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_audio_startup(struct device *dev, void *data)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	mtk_dp_audio_mute(mtk_dp, false);
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_audio_shutdown(struct device *dev, void *data)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	mtk_dp_audio_mute(mtk_dp, true);
> +}
> +
> +static int mtk_dp_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
> +				size_t len)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	if (mtk_dp->enabled)
> +		memcpy(buf, mtk_dp->connector_eld, len);
> +	else
> +		memset(buf, 0, len);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_audio_hook_plugged_cb(struct device *dev, void *data,
> +					hdmi_codec_plugged_cb fn,
> +					struct device *codec_dev)
> +{
> +	struct mtk_dp *mtk_dp = data;
> +
> +	mutex_lock(&mtk_dp->update_plugged_status_lock);
> +	mtk_dp->plugged_cb = fn;
> +	mtk_dp->codec_dev = codec_dev;
> +	mutex_unlock(&mtk_dp->update_plugged_status_lock);
> +
> +	mtk_dp_update_plugged_status(mtk_dp);
> +
> +	return 0;
> +}
> +
> +static const struct hdmi_codec_ops mtk_dp_audio_codec_ops = {
> +	.hw_params = mtk_dp_audio_hw_params,
> +	.audio_startup = mtk_dp_audio_startup,
> +	.audio_shutdown = mtk_dp_audio_shutdown,
> +	.get_eld = mtk_dp_audio_get_eld,
> +	.hook_plugged_cb = mtk_dp_audio_hook_plugged_cb,
> +	.no_capture_mute = 1,
> +};
> +
> +static int mtk_dp_register_audio_driver(struct device *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +	struct hdmi_codec_pdata codec_data = {
> +		.ops = &mtk_dp_audio_codec_ops,
> +		.max_i2s_channels = 8,
> +		.i2s = 1,
> +		.data = mtk_dp,
> +	};
> +	struct platform_device *pdev;
> +
> +	pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
> +					     PLATFORM_DEVID_AUTO, &codec_data,
> +					     sizeof(codec_data));
> +	if (IS_ERR(pdev))
> +		return PTR_ERR(pdev);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_probe(struct platform_device *pdev)
> +{
> +	struct mtk_dp *mtk_dp;
> +	struct device *dev = &pdev->dev;
> +	int ret;
> +	int irq_num = 0;
> +	struct drm_panel *panel = NULL;
> +
> +	mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> +	if (!mtk_dp)
> +		return -ENOMEM;
> +
> +	mtk_dp->dev = dev;
> +
> +	irq_num = platform_get_irq(pdev, 0);
> +	if (irq_num < 0) {
> +		dev_err(dev, "failed to request dp irq resource\n");
> +		return -EPROBE_DEFER;
> +	}
> +
> +	ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel,
> +					  &mtk_dp->next_bridge);
> +	if (ret == -ENODEV) {
> +		dev_info(
> +			dev,
> +			"No panel connected in devicetree, continuing as external DP\n");
> +		mtk_dp->next_bridge = NULL;
> +	} else if (ret) {
> +		dev_err(dev, "Failed to find panel or bridge: %d\n", ret);
> +		return ret;
> +	}
> +
> +	if (panel) {
> +		mtk_dp->next_bridge = devm_drm_panel_bridge_add(dev, panel);
> +		if (IS_ERR(mtk_dp->next_bridge)) {
> +			ret = PTR_ERR(mtk_dp->next_bridge);
> +			dev_err(dev, "Failed to create bridge: %d\n", ret);
> +			return -EPROBE_DEFER;
> +		}
> +	}

You should be using devm_drm_of_get_bridge here

> +	ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> +	if (ret)
> +		return ret;

pdata?

> +
> +	mtk_dp_aux_init(mtk_dp);
> +
> +	ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
> +					mtk_dp_hpd_event_thread,
> +					IRQ_TYPE_LEVEL_HIGH, dev_name(dev),
> +					mtk_dp);
> +	if (ret) {
> +		dev_err(dev, "failed to request mediatek dptx irq\n");
> +		return -EPROBE_DEFER;
> +	}
> +
> +	mutex_init(&mtk_dp->dp_lock);
> +	mutex_init(&mtk_dp->edid_lock);
> +
> +	platform_set_drvdata(pdev, mtk_dp);
> +
> +	if (!mtk_dp_is_edp(mtk_dp)) {
> +		mutex_init(&mtk_dp->update_plugged_status_lock);
> +		ret = mtk_dp_register_audio_driver(dev);
> +		if (ret) {
> +			dev_err(dev, "Failed to register audio driver: %d\n",
> +				ret);
> +			return ret;
> +		}
> +	}
> +
> +	mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
> +							PLATFORM_DEVID_AUTO,
> +							&mtk_dp->regs,
> +							sizeof(&mtk_dp->regs));
> +	if (IS_ERR(mtk_dp->phy_dev)) {
> +		dev_err(dev, "Failed to create device mediatek-dp-phy: %ld\n",
> +			PTR_ERR(mtk_dp->phy_dev));
> +		return PTR_ERR(mtk_dp->phy_dev);
> +	}
> +
> +	mtk_dp_get_calibration_data(mtk_dp);
> +
> +	mtk_dp->phy = dev_get_drvdata(&mtk_dp->phy_dev->dev);
> +	if (IS_ERR(mtk_dp->phy)) {
> +		dev_err(dev, "Failed to get phy: %ld\n", PTR_ERR(mtk_dp->phy));
> +		platform_device_unregister(mtk_dp->phy_dev);
> +		return PTR_ERR(mtk_dp->phy);
> +	}
> +
> +	mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
> +	mtk_dp->bridge.of_node = dev->of_node;
> +	if (mtk_dp_is_edp(mtk_dp))
> +		mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> +	else
> +		mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
> +
> +	mtk_dp->bridge.ops =
> +		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
> +	drm_bridge_add(&mtk_dp->bridge);
> +
> +	pm_runtime_enable(dev);
> +	pm_runtime_get_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_remove(struct platform_device *pdev)
> +{
> +	struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
> +
> +	platform_device_unregister(mtk_dp->phy_dev);
> +
> +	mtk_dp_video_mute(mtk_dp, true);
> +	mtk_dp_audio_mute(mtk_dp, true);
> +
> +	pm_runtime_disable(&pdev->dev);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int mtk_dp_suspend(struct device *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	mtk_dp_power_disable(mtk_dp);
> +	mtk_dp_hwirq_enable(mtk_dp, false);
> +
> +	pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_resume(struct device *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	pm_runtime_get_sync(dev);
> +
> +	mtk_dp_init_port(mtk_dp);
> +	mtk_dp_power_enable(mtk_dp);
> +	mtk_dp_hwirq_enable(mtk_dp, true);
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
> +
> +static const struct of_device_id mtk_dp_of_match[] = {
> +	{
> +		.compatible = "mediatek,mt8195-dp-tx",
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
> +
> +struct platform_driver mtk_dp_driver = {
> +	.probe = mtk_dp_probe,
> +	.remove = mtk_dp_remove,
> +	.driver = {
> +		.name = "mediatek-drm-dp",
> +		.of_match_table = mtk_dp_of_match,
> +		.pm = &mtk_dp_pm_ops,
> +	},
> +};
> +
> +MODULE_AUTHOR("Jason-JH.Lin <jason-jh.lin@mediatek.com>");
> +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> +MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> new file mode 100644
> index 0000000000000..79952ac30e9e6
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> @@ -0,0 +1,568 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019 MediaTek Inc.
> + * Copyright (c) 2021 BayLibre
> + */
> +#ifndef _MTK_DP_REG_H_
> +#define _MTK_DP_REG_H_
> +
> +#define MTK_DP_SIP_CONTROL_AARCH32 0x82000523
> +#define MTK_DP_SIP_ATF_VIDEO_UNMUTE 0x20
> +#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE 0x21
> +
> +#define DP_PHY_GLB_BIAS_GEN_00 0x0000
> +#define RG_XTP_GLB_BIAS_INTR_CTRL (0x1f << 16)
> +
> +#define DP_PHY_GLB_DPAUX_TX 0x0008
> +#define RG_CKM_PT0_CKTX_IMPSEL (0xf << 20)
> +
> +#define DP_PHY_LANE_TX_0 0x104
> +#define RG_XTP_LN0_TX_IMPSEL_PMOS (0xf << 12)
> +#define RG_XTP_LN0_TX_IMPSEL_NMOS (0xf << 16)
> +
> +#define DP_PHY_LANE_TX_1 0x204
> +#define RG_XTP_LN1_TX_IMPSEL_PMOS (0xf << 12)
> +#define RG_XTP_LN1_TX_IMPSEL_NMOS (0xf << 16)
> +
> +#define DP_PHY_LANE_TX_2 0x304
> +#define RG_XTP_LN2_TX_IMPSEL_PMOS (0xf << 12)
> +#define RG_XTP_LN2_TX_IMPSEL_NMOS (0xf << 16)
> +
> +#define DP_PHY_LANE_TX_3 0x404
> +#define RG_XTP_LN3_TX_IMPSEL_PMOS (0xf << 12)
> +#define RG_XTP_LN3_TX_IMPSEL_NMOS (0xf << 16)
> +
> +#define TOP_OFFSET 0x2000
> +#define ENC0_OFFSET 0x3000
> +#define ENC1_OFFSET 0x3200
> +#define TRANS_OFFSET 0x3400
> +#define AUX_OFFSET 0x3600
> +#define SEC_OFFSET 0x4000
> +
> +#define MTK_DP_HPD_DISCONNECT BIT(1)
> +#define MTK_DP_HPD_CONNECT BIT(2)
> +#define MTK_DP_HPD_INTERRUPT BIT(3)
> +
> +#define MTK_DP_0034 0x034
> +#define DA_XTP_GLB_CKDET_EN_FORCE_VAL BIT(15)
> +#define DA_XTP_GLB_CKDET_EN_FORCE_EN BIT(14)
> +#define DA_CKM_INTCKTX_EN_FORCE_VAL BIT(13)
> +#define DA_CKM_INTCKTX_EN_FORCE_EN BIT(12)
> +#define DA_CKM_CKTX0_EN_FORCE_VAL BIT(11)
> +#define DA_CKM_CKTX0_EN_FORCE_EN BIT(10)
> +#define DA_CKM_XTAL_CK_FORCE_VAL BIT(9)
> +#define DA_CKM_XTAL_CK_FORCE_EN BIT(8)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_VAL BIT(7)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_EN BIT(6)
> +#define DA_CKM_BIAS_EN_FORCE_VAL BIT(5)
> +#define DA_CKM_BIAS_EN_FORCE_EN BIT(4)
> +#define DA_XTP_GLB_AVD10_ON_FORCE_VAL BIT(3)
> +#define DA_XTP_GLB_AVD10_ON_FORCE BIT(2)
> +#define DA_XTP_GLB_LDO_EN_FORCE_VAL BIT(1)
> +#define DA_XTP_GLB_LDO_EN_FORCE_EN BIT(0)
> +
> +#define MTK_DP_1040 0x1040
> +#define RG_DPAUX_RX_VALID_DEGLITCH_EN BIT(2)
> +#define RG_XTP_GLB_CKDET_EN BIT(1)
> +#define RG_DPAUX_RX_EN BIT(0)
> +
> +#define MTK_DP_ENC0_P0_3000 (ENC0_OFFSET + 0x000)
> +#define LANE_NUM_DP_ENC0_P0_MASK 0x3
> +#define VIDEO_MUTE_SW_DP_ENC0_P0_MASK 0x4
> +#define VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT 2
> +#define VIDEO_MUTE_SEL_DP_ENC0_P0_MASK 0x8
> +#define VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT 3
> +#define ENHANCED_FRAME_EN_DP_ENC0_P0_MASK 0x10
> +#define ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT 4
> +
> +#define MTK_DP_ENC0_P0_3004 (ENC0_OFFSET + 0x004)
> +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK 0x100
> +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_SHIFT 8
> +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK 0x200
> +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT 9
> +
> +#define MTK_DP_ENC0_P0_3008 (ENC0_OFFSET + 0x008)
> +#define VIDEO_M_CODE_SW_0_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_300C (ENC0_OFFSET + 0x00C)
> +#define VIDEO_M_CODE_SW_1_DP_ENC0_P0_MASK 0xff
> +
> +#define MTK_DP_ENC0_P0_3010 (ENC0_OFFSET + 0x010)
> +#define HTOTAL_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3014 (ENC0_OFFSET + 0x014)
> +#define VTOTAL_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3018 (ENC0_OFFSET + 0x018)
> +#define HSTART_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_301C (ENC0_OFFSET + 0x01C)
> +#define VSTART_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3020 (ENC0_OFFSET + 0x020)
> +#define HWIDTH_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3024 (ENC0_OFFSET + 0x024)
> +#define VHEIGHT_SW_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3028 (ENC0_OFFSET + 0x028)
> +#define HSW_SW_DP_ENC0_P0_MASK 0x7fff
> +#define HSW_SW_DP_ENC0_P0_SHIFT 0
> +#define HSP_SW_DP_ENC0_P0_MASK 0x8000
> +
> +#define MTK_DP_ENC0_P0_302C (ENC0_OFFSET + 0x02C)
> +#define VSW_SW_DP_ENC0_P0_MASK 0x7fff
> +#define VSW_SW_DP_ENC0_P0_SHIFT 0
> +#define VSP_SW_DP_ENC0_P0_MASK 0x8000
> +
> +#define MTK_DP_ENC0_P0_3030 (ENC0_OFFSET + 0x030)
> +#define HTOTAL_SEL_DP_ENC0_P0_SHIFT 0
> +#define VTOTAL_SEL_DP_ENC0_P0_SHIFT 1
> +#define HSTART_SEL_DP_ENC0_P0_SHIFT 2
> +#define VSTART_SEL_DP_ENC0_P0_SHIFT 3
> +#define HWIDTH_SEL_DP_ENC0_P0_SHIFT 4
> +#define VHEIGHT_SEL_DP_ENC0_P0_SHIFT 5
> +#define HSP_SEL_DP_ENC0_P0_SHIFT 6
> +#define HSW_SEL_DP_ENC0_P0_SHIFT 7
> +#define VSP_SEL_DP_ENC0_P0_SHIFT 8
> +#define VSW_SEL_DP_ENC0_P0_SHIFT 9
> +#define VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK 0x800
> +#define VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT 11
> +#define VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK 0x1000
> +#define VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT 12
> +
> +#define MTK_DP_ENC0_P0_3034 (ENC0_OFFSET + 0x034)
> +
> +#define MTK_DP_ENC0_P0_3038 (ENC0_OFFSET + 0x038)
> +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK 0x800
> +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_SHIFT 11
> +
> +#define MTK_DP_ENC0_P0_303C (ENC0_OFFSET + 0x03C)
> +#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK 0x3f
> +#define SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT 0
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK 0x700
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT 8
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT                                     \
> +	(0 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT                                     \
> +	(1 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT                                     \
> +	(2 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT                                      \
> +	(3 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT                                      \
> +	(4 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK 0x7000
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT 12
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB                                     \
> +	(0 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422                                \
> +	(1 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420                                \
> +	(2 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> +#define VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK 0x8000
> +#define VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT 15
> +
> +#define MTK_DP_ENC0_P0_3040 (ENC0_OFFSET + 0x040)
> +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK 0xfff
> +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT 0
> +
> +#define MTK_DP_ENC0_P0_3044 (ENC0_OFFSET + 0x044)
> +#define VIDEO_N_CODE_0_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3048 (ENC0_OFFSET + 0x048)
> +#define VIDEO_N_CODE_1_DP_ENC0_P0_MASK 0xff
> +
> +#define MTK_DP_ENC0_P0_304C (ENC0_OFFSET + 0x04C)
> +#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK 0x4
> +#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK 0x100
> +
> +#define MTK_DP_ENC0_P0_3050 (ENC0_OFFSET + 0x050)
> +#define VIDEO_N_CODE_MN_GEN_0_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3054 (ENC0_OFFSET + 0x054)
> +#define VIDEO_N_CODE_MN_GEN_1_DP_ENC0_P0_MASK 0xff
> +
> +#define MTK_DP_ENC0_P0_3064 (ENC0_OFFSET + 0x064)
> +#define HDE_NUM_LAST_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3088 (ENC0_OFFSET + 0x088)
> +#define AU_EN_DP_ENC0_P0_MASK 0x40
> +#define AU_EN_DP_ENC0_P0_SHIFT 6
> +#define AUDIO_8CH_EN_DP_ENC0_P0_MASK 0x80
> +#define AUDIO_8CH_SEL_DP_ENC0_P0_MASK 0x100
> +#define AUDIO_2CH_EN_DP_ENC0_P0_MASK 0x4000
> +#define AUDIO_2CH_SEL_DP_ENC0_P0_MASK 0x8000
> +
> +#define MTK_DP_ENC0_P0_308C (ENC0_OFFSET + 0x08C)
> +#define CH_STATUS_0_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3090 (ENC0_OFFSET + 0x090)
> +#define CH_STATUS_1_DP_ENC0_P0_MASK 0xffff
> +
> +#define MTK_DP_ENC0_P0_3094 (ENC0_OFFSET + 0x094)
> +#define CH_STATUS_2_DP_ENC0_P0_MASK 0xff
> +
> +#define MTK_DP_ENC0_P0_30A0 (ENC0_OFFSET + 0x0A0)
> +
> +#define MTK_DP_ENC0_P0_30A4 (ENC0_OFFSET + 0x0A4)
> +#define AU_TS_CFG_DP_ENC0_P0_MASK 0xff
> +
> +#define MTK_DP_ENC0_P0_30A8 (ENC0_OFFSET + 0x0A8)
> +
> +#define MTK_DP_ENC0_P0_30AC (ENC0_OFFSET + 0x0AC)
> +
> +#define MTK_DP_ENC0_P0_30B0 (ENC0_OFFSET + 0x0B0)
> +
> +#define MTK_DP_ENC0_P0_30B4 (ENC0_OFFSET + 0x0B4)
> +#define ISRC_CFG_DP_ENC0_P0_MASK 0xff00
> +#define ISRC_CFG_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC0_P0_30B8 (ENC0_OFFSET + 0x0B8)
> +
> +#define MTK_DP_ENC0_P0_30BC (ENC0_OFFSET + 0x0BC)
> +#define ISRC_CONT_DP_ENC0_P0_MASK 0x1
> +#define ISRC_CONT_DP_ENC0_P0_SHIFT 0
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK 0x700
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT 8
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_2                             \
> +	(1 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_4                             \
> +	(2 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_8                             \
> +	(3 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2                             \
> +	(5 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_4                             \
> +	(6 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_8                             \
> +	(7 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> +
> +#define MTK_DP_ENC0_P0_30D8 (ENC0_OFFSET + 0x0D8)
> +
> +#define MTK_DP_ENC0_P0_312C (ENC0_OFFSET + 0x12C)
> +#define ASP_HB2_DP_ENC0_P0_MASK 0xff
> +#define ASP_HB3_DP_ENC0_P0_MASK 0xff00
> +#define ASP_HB3_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC0_P0_3130 (ENC0_OFFSET + 0x130)
> +
> +#define MTK_DP_ENC0_P0_3138 (ENC0_OFFSET + 0x138)
> +
> +#define MTK_DP_ENC0_P0_3154 (ENC0_OFFSET + 0x154)
> +#define PGEN_HTOTAL_DP_ENC0_P0_MASK 0x3fff
> +
> +#define MTK_DP_ENC0_P0_3158 (ENC0_OFFSET + 0x158)
> +#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK 0x3fff
> +
> +#define MTK_DP_ENC0_P0_315C (ENC0_OFFSET + 0x15C)
> +#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x3fff
> +
> +#define MTK_DP_ENC0_P0_3160 (ENC0_OFFSET + 0x160)
> +#define PGEN_HFDE_START_DP_ENC0_P0_MASK 0x3fff
> +
> +#define MTK_DP_ENC0_P0_3164 (ENC0_OFFSET + 0x164)
> +#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x3fff
> +
> +#define MTK_DP_ENC0_P0_3168 (ENC0_OFFSET + 0x168)
> +#define PGEN_VTOTAL_DP_ENC0_P0_MASK 0x1fff
> +
> +#define MTK_DP_ENC0_P0_316C (ENC0_OFFSET + 0x16C)
> +#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK 0x1fff
> +
> +#define MTK_DP_ENC0_P0_3170 (ENC0_OFFSET + 0x170)
> +#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x1fff
> +
> +#define MTK_DP_ENC0_P0_3174 (ENC0_OFFSET + 0x174)
> +#define PGEN_VFDE_START_DP_ENC0_P0_MASK 0x1fff
> +
> +#define MTK_DP_ENC0_P0_3178 (ENC0_OFFSET + 0x178)
> +#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x1fff
> +
> +#define MTK_DP_ENC0_P0_31B0 (ENC0_OFFSET + 0x1B0)
> +#define PGEN_PATTERN_SEL_SHIFT 4
> +#define PGEN_PATTERN_SEL_MASK 0x0070
> +
> +#define MTK_DP_ENC0_P0_31C8 (ENC0_OFFSET + 0x1C8)
> +#define VSC_EXT_VESA_HB0_DP_ENC0_P0_MASK 0xff
> +#define VSC_EXT_VESA_HB1_DP_ENC0_P0_MASK 0xff00
> +#define VSC_EXT_VESA_HB1_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC0_P0_31CC (ENC0_OFFSET + 0x1CC)
> +#define VSC_EXT_VESA_HB2_DP_ENC0_P0_MASK 0xff
> +#define VSC_EXT_VESA_HB2_DP_ENC0_P0_SHIFT 0
> +#define VSC_EXT_VESA_HB3_DP_ENC0_P0_MASK 0xff00
> +
> +#define MTK_DP_ENC0_P0_31D0 (ENC0_OFFSET + 0x1D0)
> +#define VSC_EXT_CEA_HB0_DP_ENC0_P0_MASK 0xff
> +#define VSC_EXT_CEA_HB1_DP_ENC0_P0_MASK 0xff00
> +#define VSC_EXT_CEA_HB1_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC0_P0_31D4 (ENC0_OFFSET + 0x1D4)
> +#define VSC_EXT_CEA_HB2_DP_ENC0_P0_MASK 0xff
> +#define VSC_EXT_CEA_HB2_DP_ENC0_P0_SHIFT 0
> +#define VSC_EXT_CEA_HB3_DP_ENC0_P0_MASK 0xff00
> +
> +#define MTK_DP_ENC0_P0_31D8 (ENC0_OFFSET + 0x1D8)
> +#define VSC_EXT_VESA_NUM_DP_ENC0_P0_MASK 0x3f
> +#define VSC_EXT_VESA_NUM_DP_ENC0_P0_SHIFT 0
> +#define VSC_EXT_CEA_NUM_DP_ENC0_P0_MASK 0x3f00
> +#define VSC_EXT_CEA_NUM_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC0_P0_31DC (ENC0_OFFSET + 0x1DC)
> +#define HDR0_CFG_DP_ENC0_P0_MASK 0xff
> +#define HDR0_CFG_DP_ENC0_P0_SHIFT 0
> +
> +#define MTK_DP_ENC0_P0_31E8 (ENC0_OFFSET + 0x1E8)
> +
> +#define MTK_DP_ENC0_P0_31EC (ENC0_OFFSET + 0x1EC)
> +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK 0x10
> +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT 4
> +#define ISRC1_HB3_DP_ENC0_P0_MASK 0xff00
> +#define ISRC1_HB3_DP_ENC0_P0_SHIFT 8
> +
> +#define MTK_DP_ENC1_P0_3200 (ENC1_OFFSET + 0x000)
> +
> +#define MTK_DP_ENC1_P0_3280 (ENC1_OFFSET + 0x080)
> +#define SDP_PACKET_TYPE_DP_ENC1_P0_MASK 0x1f
> +#define SDP_PACKET_W_DP_ENC1_P0 0x20
> +#define SDP_PACKET_W_DP_ENC1_P0_MASK 0x20
> +#define SDP_PACKET_W_DP_ENC1_P0_SHIFT 5
> +
> +#define MTK_DP_ENC1_P0_328C (ENC1_OFFSET + 0x08C)
> +
> +#define MTK_DP_ENC1_P0_3290 (ENC1_OFFSET + 0x090)
> +
> +#define MTK_DP_ENC1_P0_32A0 (ENC1_OFFSET + 0x0A0)
> +
> +#define MTK_DP_ENC1_P0_32A4 (ENC1_OFFSET + 0x0A4)
> +
> +#define MTK_DP_ENC1_P0_3300 (ENC1_OFFSET + 0x100)
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK 0x300
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT 8
> +
> +#define MTK_DP_ENC1_P0_3304 (ENC1_OFFSET + 0x104)
> +#define AU_PRTY_REGEN_DP_ENC1_P0_MASK 0x100
> +#define AU_CH_STS_REGEN_DP_ENC1_P0_MASK 0x200
> +#define AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK 0x1000
> +
> +#define MTK_DP_ENC1_P0_3324 (ENC1_OFFSET + 0x124)
> +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK 0x300
> +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT 8
> +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX                                       \
> +	(0 << AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT)
> +
> +#define MTK_DP_ENC1_P0_3364 (ENC1_OFFSET + 0x164)
> +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK 0xfff
> +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT 0
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK 0xf000
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT 12
> +
> +#define MTK_DP_ENC1_P0_3368 (ENC1_OFFSET + 0x168)
> +#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT 0
> +#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT 4
> +#define SDP_DP13_EN_DP_ENC1_P0_SHIFT 8
> +#define BS2BS_MODE_DP_ENC1_P0_MASK 0x3000
> +#define BS2BS_MODE_DP_ENC1_P0_SHIFT 12
> +
> +#define MTK_DP_ENC1_P0_33F4 (ENC1_OFFSET + 0x1F4)
> +
> +#define MTK_DP_TRANS_P0_3400 (TRANS_OFFSET + 0x000)
> +#define PATTERN1_EN_DP_TRANS_P0_MASK 0x1000
> +#define PATTERN1_EN_DP_TRANS_P0_SHIFT 12
> +#define PATTERN2_EN_DP_TRANS_P0_MASK 0x2000
> +#define PATTERN3_EN_DP_TRANS_P0_MASK 0x4000
> +#define PATTERN4_EN_DP_TRANS_P0_MASK 0x8000
> +
> +#define MTK_DP_TRANS_P0_3404 (TRANS_OFFSET + 0x004)
> +#define DP_SCR_EN_DP_TRANS_P0_MASK 0x1
> +
> +#define MTK_DP_TRANS_P0_340C (TRANS_OFFSET + 0x00C)
> +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK 0x2000
> +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT 13
> +
> +#define MTK_DP_TRANS_P0_3410 (TRANS_OFFSET + 0x010)
> +#define HPD_DEB_THD_DP_TRANS_P0_MASK 0xf
> +#define HPD_DEB_THD_DP_TRANS_P0_SHIFT 0
> +#define HPD_INT_THD_DP_TRANS_P0_MASK 0xf0
> +#define HPD_INT_THD_DP_TRANS_P0_SHIFT 4
> +#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US (2 << HPD_INT_THD_DP_TRANS_P0_SHIFT)
> +#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US                                   \
> +	(2 << (HPD_INT_THD_DP_TRANS_P0_SHIFT + 2))
> +#define HPD_DISC_THD_DP_TRANS_P0_MASK 0xf00
> +#define HPD_DISC_THD_DP_TRANS_P0_SHIFT 8
> +#define HPD_CONN_THD_DP_TRANS_P0_MASK 0xf000
> +#define HPD_CONN_THD_DP_TRANS_P0_SHIFT 12
> +
> +#define MTK_DP_TRANS_P0_3414 (TRANS_OFFSET + 0x014)
> +#define HPD_DB_DP_TRANS_P0_MASK 0x4
> +
> +#define MTK_DP_TRANS_P0_3418 (TRANS_OFFSET + 0x018)
> +#define IRQ_CLR_DP_TRANS_P0_MASK 0xf
> +#define IRQ_MASK_DP_TRANS_P0_MASK 0xf0
> +#define IRQ_MASK_DP_TRANS_P0_SHIFT 4
> +#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ (BIT(1) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> +#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ (BIT(2) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> +#define IRQ_MASK_DP_TRANS_P0_INT_IRQ (BIT(3) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> +#define IRQ_STATUS_DP_TRANS_P0_MASK 0xf000
> +#define IRQ_STATUS_DP_TRANS_P0_SHIFT 12
> +
> +#define MTK_DP_TRANS_P0_342C (TRANS_OFFSET + 0x02C)
> +#define XTAL_FREQ_DP_TRANS_P0_DEFAULT 0x69
> +#define XTAL_FREQ_DP_TRANS_P0_MASK 0xff
> +
> +#define MTK_DP_TRANS_P0_3430 (TRANS_OFFSET + 0x030)
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK 0x3
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT BIT(1)
> +
> +#define MTK_DP_TRANS_P0_34A4 (TRANS_OFFSET + 0x0A4)
> +#define LANE_NUM_DP_TRANS_P0_MASK 0xc
> +#define LANE_NUM_DP_TRANS_P0_SHIFT 2
> +
> +#define MTK_DP_TRANS_P0_3540 (TRANS_OFFSET + 0x140)
> +#define FEC_EN_DP_TRANS_P0_MASK 0x1
> +#define FEC_EN_DP_TRANS_P0_SHIFT 0
> +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK 0x8
> +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT 3
> +
> +#define MTK_DP_TRANS_P0_3580 (TRANS_OFFSET + 0x180)
> +#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK 0x100
> +#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK 0x200
> +#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK 0x400
> +#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK 0x800
> +
> +#define MTK_DP_TRANS_P0_35C4 (TRANS_OFFSET + 0x1C4)
> +#define SW_IRQ_MASK_DP_TRANS_P0_MASK 0xffff
> +
> +#define MTK_DP_TRANS_P0_35C8 (TRANS_OFFSET + 0x1C8)
> +#define SW_IRQ_CLR_DP_TRANS_P0_MASK 0xffff
> +
> +#define SW_IRQ_STATUS_DP_TRANS_P0_MASK 0xffff
> +#define SW_IRQ_STATUS_DP_TRANS_P0_SHIFT 0
> +
> +#define MTK_DP_TRANS_P0_35D0 (TRANS_OFFSET + 0x1D0)
> +#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK 0xffff
> +
> +#define MTK_DP_TRANS_P0_35F0 (TRANS_OFFSET + 0x1F0)
> +
> +#define MTK_DP_AUX_P0_360C (AUX_OFFSET + 0x00C)
> +#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK 0x1fff
> +
> +#define MTK_DP_AUX_P0_3614 (AUX_OFFSET + 0x014)
> +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK 0x7f
> +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT 0
> +
> +#define MTK_DP_AUX_P0_3618 (AUX_OFFSET + 0x018)
> +#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK 0x200
> +#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK 0xf
> +
> +#define MTK_DP_AUX_P0_3620 (AUX_OFFSET + 0x020)
> +#define AUX_RD_MODE_AUX_TX_P0_MASK 0x200
> +#define AUX_RX_FIFO_READ_PULSE_TX_P0_MASK 0x100
> +#define AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT 8
> +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK 0xff
> +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT 0
> +
> +#define MTK_DP_AUX_P0_3624 (AUX_OFFSET + 0x024)
> +#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK 0xf
> +
> +#define MTK_DP_AUX_P0_3628 (AUX_OFFSET + 0x028)
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK 0x3ff
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT 0
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE                                     \
> +	(BIT(0) << AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT)
> +
> +#define MTK_DP_AUX_P0_362C (AUX_OFFSET + 0x02C)
> +#define AUX_NO_LENGTH_AUX_TX_P0_MASK 0x1
> +#define AUX_NO_LENGTH_AUX_TX_P0_SHIFT 0
> +#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK 0x2
> +#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK 0xfffc
> +
> +#define MTK_DP_AUX_P0_3630 (AUX_OFFSET + 0x030)
> +#define AUX_TX_REQUEST_READY_AUX_TX_P0_MASK 0x8
> +#define AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT 3
> +
> +#define MTK_DP_AUX_P0_3634 (AUX_OFFSET + 0x034)
> +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK 0xff00
> +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT 8
> +
> +#define MTK_DP_AUX_P0_3640 (AUX_OFFSET + 0x040)
> +#define AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK 0x40
> +#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 6
> +#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 5
> +#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 4
> +#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT 3
> +#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT 2
> +#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT 1
> +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK 0x1
> +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT 0
> +
> +#define MTK_DP_AUX_P0_3644 (AUX_OFFSET + 0x044)
> +#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK 0xf
> +
> +#define MTK_DP_AUX_P0_3648 (AUX_OFFSET + 0x048)
> +#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK 0xffff
> +
> +#define MTK_DP_AUX_P0_364C (AUX_OFFSET + 0x04C)
> +#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK 0xf
> +
> +#define MTK_DP_AUX_P0_3650 (AUX_OFFSET + 0x050)
> +#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK 0xf000
> +#define MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT 12
> +#define PHY_FIFO_RST_AUX_TX_P0_MASK 0x200
> +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK 0x100
> +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT 8
> +
> +#define MTK_DP_AUX_P0_3658 (AUX_OFFSET + 0x058)
> +#define AUX_TX_OV_EN_AUX_TX_P0_MASK 0x1
> +
> +#define MTK_DP_AUX_P0_3690 (AUX_OFFSET + 0x090)
> +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK 0x100
> +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT 8
> +
> +#define MTK_DP_AUX_P0_3704 (AUX_OFFSET + 0x104)
> +#define AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK 0x2
> +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK 0x4
> +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT 2
> +
> +#define MTK_DP_AUX_P0_3708 (AUX_OFFSET + 0x108)
> +
> +#define MTK_DP_AUX_P0_37C8 (AUX_OFFSET + 0x1C8)
> +#define MTK_ATOP_EN_AUX_TX_P0_MASK 0x1
> +#define MTK_ATOP_EN_AUX_TX_P0_SHIFT 0
> +
> +#define MTK_DP_TOP_PWR_STATE (TOP_OFFSET + 0x00)
> +#define DP_PWR_STATE_MASK 0x3
> +#define DP_PWR_STATE_BANDGAP 1
> +#define DP_PWR_STATE_BANDGAP_TPLL 2
> +#define DP_PWR_STATE_BANDGAP_TPLL_LANE 3
> +
> +#define MTK_DP_TOP_SWING_EMP (TOP_OFFSET + 0x04)
> +#define DP_TX0_VOLT_SWING_MASK 0x3
> +#define DP_TX0_VOLT_SWING_SHIFT 0
> +#define DP_TX0_PRE_EMPH_MASK 0xc
> +#define DP_TX0_PRE_EMPH_SHIFT 2
> +#define DP_TX1_VOLT_SWING_MASK 0x300
> +#define DP_TX1_VOLT_SWING_SHIFT 8
> +#define DP_TX1_PRE_EMPH_MASK 0xc00
> +#define DP_TX2_VOLT_SWING_MASK 0x30000
> +#define DP_TX2_PRE_EMPH_MASK 0xc0000
> +#define DP_TX3_VOLT_SWING_MASK 0x3000000
> +#define DP_TX3_PRE_EMPH_MASK 0xc000000
> +
> +#define MTK_DP_TOP_RESET_AND_PROBE (TOP_OFFSET + 0x20)
> +#define SW_RST_B_SHIFT 0
> +#define SW_RST_B_PHYD (BIT(4) << SW_RST_B_SHIFT)
> +
> +#define MTK_DP_TOP_IRQ_STATUS (TOP_OFFSET + 0x28)
> +#define RGS_IRQ_STATUS_SHIFT 0
> +#define RGS_IRQ_STATUS_TRANSMITTER (BIT(1) << RGS_IRQ_STATUS_SHIFT)
> +
> +#define MTK_DP_TOP_IRQ_MASK (TOP_OFFSET + 0x2C)
> +#define IRQ_MASK_AUX_TOP_IRQ BIT(2)
> +
> +#define MTK_DP_TOP_MEM_PD (TOP_OFFSET + 0x38)
> +#define MEM_ISO_EN_SHIFT 0
> +#define FUSE_SEL_SHIFT 2
> +
> +#endif /*_MTK_DP_REG_H_*/
> diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> index 384074f69111b..e6e88e3cd811d 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
>  	MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
>  };
>  
> +enum TVDPLL_CLK {
> +	TVDPLL_PLL = 0,
> +	TVDPLL_D2 = 2,
> +	TVDPLL_D4 = 4,
> +	TVDPLL_D8 = 8,
> +	TVDPLL_D16 = 16,
> +};
> +
>  struct mtk_dpi {
>  	struct drm_encoder encoder;
>  	struct drm_bridge bridge;
> @@ -71,8 +79,10 @@ struct mtk_dpi {
>  	void __iomem *regs;
>  	struct device *dev;
>  	struct clk *engine_clk;
> +	struct clk *dpi_ck_cg;
>  	struct clk *pixel_clk;
>  	struct clk *tvd_clk;
> +	struct clk *pclk_src[5];
>  	int irq;
>  	struct drm_display_mode mode;
>  	const struct mtk_dpi_conf *conf;
> @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
>  	u32 hvsize_mask;
>  	u32 channel_swap_shift;
>  	u32 yuv422_en_bit;
> +	u32 csc_enable_bit;
>  	const struct mtk_dpi_yc_limit *limit;
>  };
>  
> @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
>  
>  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
>  {
> -	mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> +	mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
>  }
>  
>  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
>  		mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
>  }
>  
> +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> +{
> +	u32 matrix_sel = 0;
> +
> +	switch (format) {
> +	case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> +	case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> +	case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> +	case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> +	case MTK_DPI_COLOR_FORMAT_XV_YCC:
> +		if (dpi->mode.hdisplay <= 720)
> +			matrix_sel = 0x2;
> +		break;
> +	default:
> +		break;
> +	}
> +	mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> +}
> +
>  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
>  					enum mtk_dpi_out_color_format format)
>  {
>  	if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
>  	    (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
>  		mtk_dpi_config_yuv422_enable(dpi, false);
> -		if (dpi->conf->csc_support)
> +		if (dpi->conf->csc_support) {
>  			mtk_dpi_config_csc_enable(dpi, true);
> +			mtk_dpi_matrix_sel(dpi, format);
> +		}
>  		if (dpi->conf->swap_input_support)
>  			mtk_dpi_config_swap_input(dpi, false);
>  		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
>  	} else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
>  		   (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
>  		mtk_dpi_config_yuv422_enable(dpi, true);
> -		if (dpi->conf->csc_support)
> +		if (dpi->conf->csc_support) {
>  			mtk_dpi_config_csc_enable(dpi, true);
> +			mtk_dpi_matrix_sel(dpi, format);
> +		}
>  		if (dpi->conf->swap_input_support)
>  			mtk_dpi_config_swap_input(dpi, true);
>  		mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
>  	mtk_dpi_disable(dpi);
>  	clk_disable_unprepare(dpi->pixel_clk);
>  	clk_disable_unprepare(dpi->engine_clk);
> +	clk_disable_unprepare(dpi->dpi_ck_cg);
> +	clk_disable_unprepare(dpi->tvd_clk);
>  }
>  
>  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
>  	if (++dpi->refcount != 1)
>  		return 0;
>  
> +	ret = clk_prepare_enable(dpi->tvd_clk);
> +	if (ret) {
> +		dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> +		goto err_pixel;
> +	}
> +
>  	ret = clk_prepare_enable(dpi->engine_clk);
>  	if (ret) {
>  		dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
>  		goto err_refcount;
>  	}
>  
> +	ret = clk_prepare_enable(dpi->dpi_ck_cg);
> +	if (ret) {
> +		dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> +		goto err_ck_cg;
> +	}
> +
>  	ret = clk_prepare_enable(dpi->pixel_clk);
>  	if (ret) {
>  		dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
>  	if (dpi->pinctrl && dpi->pins_dpi)
>  		pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
>  
> -	mtk_dpi_enable(dpi);
>  	return 0;
>  
>  err_pixel:
> +	clk_disable_unprepare(dpi->dpi_ck_cg);
> +err_ck_cg:
>  	clk_disable_unprepare(dpi->engine_clk);
>  err_refcount:
>  	dpi->refcount--;
> @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
>  	pll_rate = clk_get_rate(dpi->tvd_clk);
>  
>  	vm.pixelclock = pll_rate / factor;
> -	if (dpi->conf->is_dpintf)
> -		clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> -	else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> +	if (dpi->conf->is_dpintf) {
> +		if (factor == 1)
> +			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> +		else if (factor == 2)
> +			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> +		else if (factor == 4)
> +			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> +		else
> +			clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> +	} else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
>  		 (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
>  		clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
>  	else
> @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
>  	}
>  	mtk_dpi_sw_reset(dpi, false);
>  
> +	mtk_dpi_enable(dpi);
> +
>  	return 0;
>  }
>  
> @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
>  	u32 *input_fmts;
>  
>  	*num_input_fmts = 0;
> -
>  	input_fmts = kcalloc(1, sizeof(*input_fmts),
>  			     GFP_KERNEL);
>  	if (!input_fmts)
> @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
>  		if (dpi->conf->num_output_fmts)
>  			out_bus_format = dpi->conf->output_fmts[0];
>  
> -	dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> +	dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
>  		bridge_state->input_bus_cfg.format,
>  		bridge_state->output_bus_cfg.format);
>  
> @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
>  	dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
>  	dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
>  	dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> -	dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> +	if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> +		dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> +	else
> +		dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
>  
>  	return 0;
>  }
> @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
>  	MEDIA_BUS_FMT_RGB888_2X12_BE,
>  };
>  
> +static const u32 mt8195_output_fmts[] = {
> +	MEDIA_BUS_FMT_RGB888_1X24,
> +	MEDIA_BUS_FMT_YUV8_1X24,
> +	MEDIA_BUS_FMT_YUYV8_1X16,
> +};
> +
>  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
>  	.c_bottom = 0x0010,
>  	.c_top = 0x0FE0,
> @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
>  	.hvsize_mask = HSIZE_MASK,
>  	.channel_swap_shift = CH_SWAP,
>  	.yuv422_en_bit = YUV422_EN,
> +	.csc_enable_bit = CSC_ENABLE,
>  	.limit = &mtk_dpi_limit,
>  };
>  
> @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
>  	.hvsize_mask = HSIZE_MASK,
>  	.channel_swap_shift = CH_SWAP,
>  	.yuv422_en_bit = YUV422_EN,
> +	.csc_enable_bit = CSC_ENABLE,
>  	.limit = &mtk_dpi_limit,
>  };
>  
> @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
>  	.hvsize_mask = HSIZE_MASK,
>  	.channel_swap_shift = CH_SWAP,
>  	.yuv422_en_bit = YUV422_EN,
> +	.csc_enable_bit = CSC_ENABLE,
>  	.limit = &mtk_dpi_limit,
>  };
>  
> @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
>  	.hvsize_mask = HSIZE_MASK,
>  	.channel_swap_shift = CH_SWAP,
>  	.yuv422_en_bit = YUV422_EN,
> +	.csc_enable_bit = CSC_ENABLE,
>  	.limit = &mtk_dpi_limit,
>  };
>  
>  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
>  	.cal_factor = mt8195_dpintf_calculate_factor,
> -	.output_fmts = mt8173_output_fmts,
> -	.num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> +	.output_fmts = mt8195_output_fmts,
> +	.num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
>  	.is_dpintf = true,
> +	.csc_support = true,
>  	.dimension_mask = DPINTF_HPW_MASK,
>  	.hvsize_mask = DPINTF_HSIZE_MASK,
>  	.channel_swap_shift = DPINTF_CH_SWAP,
>  	.yuv422_en_bit = DPINTF_YUV422_EN,
> +	.csc_enable_bit = DPINTF_CSC_ENABLE,
>  	.limit = &mtk_dpintf_limit,
>  };
>  
> @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
>  		return ret;
>  	}
>  
> +	dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> +	if (IS_ERR(dpi->dpi_ck_cg)) {
> +		ret = PTR_ERR(dpi->dpi_ck_cg);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> +				ret);
> +
> +		return ret;
> +	}
> +
>  	dpi->pixel_clk = devm_clk_get(dev, "pixel");
>  	if (IS_ERR(dpi->pixel_clk)) {
>  		ret = PTR_ERR(dpi->pixel_clk);
> @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
>  		return ret;
>  	}
>  
> +	dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> +	dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> +	dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> +	dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> +
>  	dpi->irq = platform_get_irq(pdev, 0);
>  	if (dpi->irq <= 0)
>  		return -EINVAL;

All those changes look unrelated as well?

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 170 bytes --]

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-11-15 10:11     ` Maxime Ripard
  (?)
@ 2021-11-15 14:33       ` Guillaume Ranquet
  -1 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-15 14:33 UTC (permalink / raw)
  To: Maxime Ripard, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Daniel Vetter, Chun-Kuang Hu,
	Philipp Zabel, Matthias Brugger
  Cc: Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Hi Maxime,

Thanks for your review.

Quoting Maxime Ripard (2021-11-15 11:11:29)
> Hi,
>
> On Wed, Nov 10, 2021 at 02:06:23PM +0100, Guillaume Ranquet wrote:
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > according phy driver mediatek-dp-phy.
> >
> > It supports both functional units on the mt8195, the embedded
> > DisplayPort as well as the external DisplayPort units. It offers
> > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > to 4 lanes.
>
> There's a number of checkpatch --strict errors in there, make sure to fix them.
This is a regression I've introduced, I think, when running
clang-format... I'll fix them.

>
> > The driver creates a child device for the phy. The child device will
> > never exist without the parent being active. As they are sharing a
> > register range, the parent passes a regmap pointer to the child so that
> > both can work with the same register range. The phy driver sets device
> > data that is read by the parent to get the phy device that can be used
> > to control the phy properties.
>
> If the PHY is in the same register space than the DP controller, why do
> you need a separate PHY driver in the first place?
>

This has been asked by Chun-Kuang Hu in a previous revision of the series:

https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/


> > This driver is based on an initial version by
> > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > Reported-by: kernel test robot <lkp@intel.com>
>
> > ---
> >  drivers/gpu/drm/drm_edid.c              |    2 +-
> >  drivers/gpu/drm/mediatek/Kconfig        |    7 +
> >  drivers/gpu/drm/mediatek/Makefile       |    2 +
> >  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
> >  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
> >  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
> >  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
> >  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
> >  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
> >  9 files changed, 3799 insertions(+), 13 deletions(-)
> >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
> >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> >
> > diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> > index 500279a82167a..bfd98b50ceb5b 100644
> > --- a/drivers/gpu/drm/drm_edid.c
> > +++ b/drivers/gpu/drm/drm_edid.c
> > @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
> >        * modes and forbids YCRCB422 support for all video modes per
> >        * HDMI 1.3 spec.
> >        */
> > -     info->color_formats = DRM_COLOR_FORMAT_RGB444;
> > +     info->color_formats |= DRM_COLOR_FORMAT_RGB444;
> >
> >       /* YCRCB444 is optional according to spec. */
> >       if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
>
> This looks unrelated?
>
Right, I should have kept it in a separate patch.

> > diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> > index 2976d21e9a34a..029b94c716131 100644
> > --- a/drivers/gpu/drm/mediatek/Kconfig
> > +++ b/drivers/gpu/drm/mediatek/Kconfig
> > @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
> >       select PHY_MTK_HDMI
> >       help
> >         DRM/KMS HDMI driver for Mediatek SoCs
> > +
> > +config MTK_DPTX_SUPPORT
> > +     tristate "DRM DPTX Support for Mediatek SoCs"
> > +     depends on DRM_MEDIATEK
> > +     select PHY_MTK_DP
> > +     help
> > +       DRM/KMS Display Port driver for Mediatek SoCs.
> > diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> > index 29098d7c8307c..d86a6406055e6 100644
> > --- a/drivers/gpu/drm/mediatek/Makefile
> > +++ b/drivers/gpu/drm/mediatek/Makefile
> > @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
> >                         mtk_hdmi_ddc.o
> >
> >  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> > +
> > +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> > new file mode 100644
> > index 0000000000000..83087219d5a5e
> > --- /dev/null
> > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> > @@ -0,0 +1,3094 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2019 MediaTek Inc.
> > + * Copyright (c) 2021 BayLibre
> > + */
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_bridge.h>
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_dp_helper.h>
> > +#include <drm/drm_edid.h>
> > +#include <drm/drm_of.h>
> > +#include <drm/drm_panel.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/drm_probe_helper.h>
> > +#include <linux/arm-smccc.h>
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/errno.h>
> > +#include <linux/kernel.h>
> > +#include <linux/nvmem-consumer.h>
> > +#include <linux/of.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <sound/hdmi-codec.h>
> > +#include <video/videomode.h>
> > +
> > +#include "mtk_dp_reg.h"
> > +
> > +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> > +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> > +
> > +#define MTK_DP_MAX_LANES 4
> > +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> > +
> > +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> > +
> > +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> > +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> > +
> > +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> > +
> > +#define MTK_DP_DP_VERSION_11 0x11
> > +
> > +enum mtk_dp_state {
> > +     MTK_DP_STATE_INITIAL,
> > +     MTK_DP_STATE_IDLE,
> > +     MTK_DP_STATE_PREPARE,
> > +     MTK_DP_STATE_NORMAL,
> > +};
> > +
> > +enum mtk_dp_train_state {
> > +     MTK_DP_TRAIN_STATE_STARTUP = 0,
> > +     MTK_DP_TRAIN_STATE_CHECKCAP,
> > +     MTK_DP_TRAIN_STATE_CHECKEDID,
> > +     MTK_DP_TRAIN_STATE_TRAINING_PRE,
> > +     MTK_DP_TRAIN_STATE_TRAINING,
> > +     MTK_DP_TRAIN_STATE_CHECKTIMING,
> > +     MTK_DP_TRAIN_STATE_NORMAL,
> > +     MTK_DP_TRAIN_STATE_POWERSAVE,
> > +     MTK_DP_TRAIN_STATE_DPIDLE,
> > +};
> > +
> > +struct mtk_dp_timings {
> > +     struct videomode vm;
> > +
> > +     u16 htotal;
> > +     u16 vtotal;
> > +     u8 frame_rate;
> > +     u32 pix_rate_khz;
> > +};
> > +
> > +struct mtk_dp_train_info {
> > +     bool tps3;
> > +     bool tps4;
> > +     bool sink_ssc;
> > +     bool cable_plugged_in;
> > +     bool cable_state_change;
> > +     bool cr_done;
> > +     bool eq_done;
> > +
> > +     // link_rate is in multiple of 0.27Gbps
> > +     int link_rate;
> > +     int lane_count;
> > +
> > +     int irq_status;
> > +     int check_cap_count;
> > +};
> > +
> > +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> > +enum mtk_dp_color_format {
> > +     MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> > +     MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> > +     MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> > +     MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> > +     MTK_DP_COLOR_FORMAT_YONLY = 4,
> > +     MTK_DP_COLOR_FORMAT_RAW = 5,
> > +     MTK_DP_COLOR_FORMAT_RESERVED = 6,
> > +     MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> > +     MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> > +};
> > +
> > +// Multiple of 0.27Gbps
> > +enum mtk_dp_linkrate {
> > +     MTK_DP_LINKRATE_RBR = 0x6,
> > +     MTK_DP_LINKRATE_HBR = 0xA,
> > +     MTK_DP_LINKRATE_HBR2 = 0x14,
> > +     MTK_DP_LINKRATE_HBR25 = 0x19,
> > +     MTK_DP_LINKRATE_HBR3 = 0x1E,
> > +};
> > +
> > +// Same values as used for DP Spec MISC0 bits 5,6,7
> > +enum mtk_dp_color_depth {
> > +     MTK_DP_COLOR_DEPTH_6BIT = 0,
> > +     MTK_DP_COLOR_DEPTH_8BIT = 1,
> > +     MTK_DP_COLOR_DEPTH_10BIT = 2,
> > +     MTK_DP_COLOR_DEPTH_12BIT = 3,
> > +     MTK_DP_COLOR_DEPTH_16BIT = 4,
> > +     MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> > +};
> > +
> > +struct mtk_dp_audio_cfg {
> > +     int sample_rate;
> > +     int word_length_bits;
> > +     int channels;
> > +};
> > +
> > +struct mtk_dp_info {
> > +     enum mtk_dp_color_depth depth;
> > +     enum mtk_dp_color_format format;
> > +     struct mtk_dp_audio_cfg audio_caps;
> > +     struct mtk_dp_timings timings;
> > +};
> > +
> > +struct dp_cal_data {
> > +     unsigned int glb_bias_trim;
> > +     unsigned int clktx_impse;
> > +
> > +     unsigned int ln0_tx_impsel_pmos;
> > +     unsigned int ln0_tx_impsel_nmos;
> > +     unsigned int ln1_tx_impsel_pmos;
> > +     unsigned int ln1_tx_impsel_nmos;
> > +     unsigned int ln2_tx_impsel_pmos;
> > +     unsigned int ln2_tx_impsel_nmos;
> > +     unsigned int ln3_tx_impsel_pmos;
> > +     unsigned int ln3_tx_impsel_nmos;
> > +};
> > +
> > +struct mtk_dp {
> > +     struct device *dev;
> > +     struct platform_device *phy_dev;
> > +     struct phy *phy;
> > +     struct dp_cal_data cal_data;
> > +
> > +     struct drm_device *drm_dev;
> > +     struct drm_bridge bridge;
> > +     struct drm_bridge *next_bridge;
> > +     struct drm_dp_aux aux;
> > +
> > +     struct mutex edid_lock;
> > +     struct edid *edid;
> > +
> > +     u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> > +
> > +     struct mtk_dp_info info;
> > +     enum mtk_dp_state state;
> > +
> > +     struct mtk_dp_train_info train_info;
> > +     enum mtk_dp_train_state train_state;
> > +     unsigned int input_fmt;
> > +
> > +     struct regmap *regs;
> > +     struct clk *dp_tx_clk;
> > +
> > +     bool enabled;
> > +     bool audio_enable;
> > +
> > +     bool has_fec;
> > +     struct mutex dp_lock;
> > +
> > +     struct mutex update_plugged_status_lock;
> > +
> > +     hdmi_codec_plugged_cb plugged_cb;
> > +     struct device *codec_dev;
> > +     u8 connector_eld[MAX_ELD_BYTES];
> > +};
> > +
> > +enum mtk_dp_sdp_type {
> > +     MTK_DP_SDP_NONE = 0x00,
> > +     MTK_DP_SDP_ACM = 0x01,
> > +     MTK_DP_SDP_ISRC = 0x02,
> > +     MTK_DP_SDP_AVI = 0x03,
> > +     MTK_DP_SDP_AUI = 0x04,
> > +     MTK_DP_SDP_SPD = 0x05,
> > +     MTK_DP_SDP_MPEG = 0x06,
> > +     MTK_DP_SDP_NTSC = 0x07,
> > +     MTK_DP_SDP_VSP = 0x08,
> > +     MTK_DP_SDP_VSC = 0x09,
> > +     MTK_DP_SDP_EXT = 0x0A,
> > +     MTK_DP_SDP_PPS0 = 0x0B,
> > +     MTK_DP_SDP_PPS1 = 0x0C,
> > +     MTK_DP_SDP_PPS2 = 0x0D,
> > +     MTK_DP_SDP_PPS3 = 0x0E,
> > +     MTK_DP_SDP_DRM = 0x10,
> > +     MTK_DP_SDP_MAX_NUM
> > +};
> > +
> > +struct mtk_dp_sdp_packet {
> > +     enum mtk_dp_sdp_type type;
> > +     struct dp_sdp sdp;
> > +};
> > +
> > +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> > +union mtk_dp_audio_channel_status {
> > +     struct {
> > +             u8 rev : 1;
> > +             u8 is_lpcm : 1;
> > +             u8 copy_right : 1;
> > +             u8 additional_format_info : 3;
> > +             u8 channel_status_mode : 2;
> > +             u8 category_code;
> > +             u8 src_num : 4;
> > +             u8 channel_num : 4;
> > +             u8 sampling_freq : 4;
> > +             u8 clk_accuracy : 2;
> > +             u8 rev2 : 2;
> > +             u8 word_len : 4;
> > +             u8 original_sampling_freq : 4;
> > +     } iec;
> > +
> > +     u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> > +};
>
> Why don't you leverage hdmi_codec_params and snd_aes_iec958?
>
I'll see if I can make it work with snd_aes_iec958 and get back to you.

> > +static struct regmap_config mtk_dp_regmap_config = {
> > +     .reg_bits = 32,
> > +     .val_bits = 32,
> > +     .reg_stride = 4,
> > +     .max_register = SEC_OFFSET + 0x90,
> > +     .name = "mtk-dp-registers",
> > +};
> > +
> > +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
> > +{
> > +     return mtk_dp->next_bridge != NULL;
> > +}
> > +
> > +static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
> > +{
> > +     return container_of(b, struct mtk_dp, bridge);
> > +}
> > +
> > +static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
> > +{
> > +     u32 read_val;
> > +     int ret;
> > +
> > +     ret = regmap_read(mtk_dp->regs, offset, &read_val);
> > +     if (ret) {
> > +             dev_err(mtk_dp->dev, "Failed to read register 0x%x: %d\n",
> > +                     offset, ret);
> > +             return 0;
> > +     }
> > +
> > +     return read_val;
> > +}
> > +
> > +static void mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
> > +{
> > +     int ret;
> > +
> > +     ret = regmap_write(mtk_dp->regs, offset, val);
> > +     if (ret)
> > +             dev_err(mtk_dp->dev,
> > +                     "Failed to write register 0x%x with value 0x%x: %d\n",
> > +                     offset, val, ret);
> > +}
> > +
> > +static void mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, u32 val,
> > +                            u32 mask)
> > +{
> > +     int ret;
> > +
> > +     ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
> > +     if (ret)
> > +             dev_err(mtk_dp->dev,
> > +                     "Failed to update register 0x%x with value 0x%x, mask 0x%x: %d\n",
> > +                     offset, val, mask, ret);
> > +}
> > +
> > +static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32 offset, u8 *buf,
> > +                                 size_t length)
> > +{
> > +     int i;
> > +     int num_regs = (length + 1) / 2;
> > +
> > +     // 2 bytes per register
> > +     for (i = 0; i < num_regs; i++) {
> > +             u32 val = buf[i * 2] |
> > +                       (i * 2 + 1 < length ? buf[i * 2 + 1] << 8 : 0);
> > +
> > +             mtk_dp_write(mtk_dp, offset + i * 4, val);
> > +     }
> > +}
> > +
> > +static unsigned long mtk_dp_sip_atf_call(unsigned int cmd, unsigned int para)
> > +{
> > +     struct arm_smccc_res res;
> > +
> > +     arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32, cmd, para, 0, 0, 0, 0, 0,
> > +                   &res);
> > +
> > +     pr_debug("[DPTX]%s cmd 0x%x, p1 0x%x, ret 0x%lx-0x%lx", __func__, cmd,
> > +              para, res.a0, res.a1);
> > +     return res.a1;
> > +}
> > +
> > +static void mtk_dp_msa_bypass_disable(struct mtk_dp *mtk_dp)
> > +{
> > +     const u16 bits_to_set =
> > +             BIT(HTOTAL_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(VTOTAL_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(HSTART_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(VSTART_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(HWIDTH_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(VHEIGHT_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(HSP_SEL_DP_ENC0_P0_SHIFT) | BIT(HSW_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(VSP_SEL_DP_ENC0_P0_SHIFT) | BIT(VSW_SEL_DP_ENC0_P0_SHIFT);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, bits_to_set,
> > +                        bits_to_set);
> > +}
> > +
> > +static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
> > +{
> > +     struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010, timings->htotal,
> > +                        HTOTAL_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
> > +                        timings->vm.hsync_len + timings->vm.hback_porch,
> > +                        HSTART_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> > +                        timings->vm.hsync_len << HSW_SW_DP_ENC0_P0_SHIFT,
> > +                        HSW_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028, 0,
> > +                        HSP_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020, timings->vm.hactive,
> > +                        HWIDTH_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014, timings->vtotal,
> > +                        VTOTAL_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
> > +                        timings->vm.vsync_len + timings->vm.vback_porch,
> > +                        VSTART_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> > +                        timings->vm.vsync_len << VSW_SW_DP_ENC0_P0_SHIFT,
> > +                        VSW_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C, 0,
> > +                        VSP_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024, timings->vm.vactive,
> > +                        VHEIGHT_SW_DP_ENC0_P0_MASK);
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064, timings->vm.hactive,
> > +                        HDE_NUM_LAST_DP_ENC0_P0_MASK);
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154, timings->htotal,
> > +                        PGEN_HTOTAL_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
> > +                        timings->vm.hfront_porch,
> > +                        PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C, timings->vm.hsync_len,
> > +                        PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
> > +                        timings->vm.hback_porch + timings->vm.hsync_len,
> > +                        PGEN_HFDE_START_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164, timings->vm.hactive,
> > +                        PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168, timings->vtotal,
> > +                        PGEN_VTOTAL_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
> > +                        timings->vm.vfront_porch,
> > +                        PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170, timings->vm.vsync_len,
> > +                        PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
> > +                        timings->vm.vback_porch + timings->vm.vsync_len,
> > +                        PGEN_VFDE_START_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178, timings->vm.vactive,
> > +                        PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
> > +                                 enum mtk_dp_color_format color_format)
> > +{
> > +     u32 val;
> > +
> > +     mtk_dp->info.format = color_format;
> > +
> > +     // Update MISC0
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> > +                        color_format << DP_TEST_COLOR_FORMAT_SHIFT,
> > +                        DP_TEST_COLOR_FORMAT_MASK);
> > +
> > +     switch (color_format) {
> > +     case MTK_DP_COLOR_FORMAT_YUV_422:
> > +             val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
> > +             break;
> > +     case MTK_DP_COLOR_FORMAT_YUV_420:
> > +             val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420;
> > +             break;
> > +     case MTK_DP_COLOR_FORMAT_YONLY:
> > +     case MTK_DP_COLOR_FORMAT_RAW:
> > +     case MTK_DP_COLOR_FORMAT_RESERVED:
> > +     case MTK_DP_COLOR_FORMAT_UNKNOWN:
> > +             drm_warn(mtk_dp->drm_dev, "Unsupported color format: %d\n",
> > +                      color_format);
> > +             fallthrough;
> > +     case MTK_DP_COLOR_FORMAT_RGB_444:
> > +     case MTK_DP_COLOR_FORMAT_YUV_444:
> > +             val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
> > +             break;
> > +     }
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
> > +                        PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp,
> > +                                enum mtk_dp_color_depth color_depth)
> > +{
> > +     u32 val;
> > +
> > +     mtk_dp->info.depth = color_depth;
> > +
> > +     // Update MISC0
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> > +                        color_depth << DP_TEST_BIT_DEPTH_SHIFT,
> > +                        DP_TEST_BIT_DEPTH_MASK);
> > +
> > +     switch (color_depth) {
> > +     case MTK_DP_COLOR_DEPTH_6BIT:
> > +             val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT;
> > +             break;
> > +     case MTK_DP_COLOR_DEPTH_8BIT:
> > +             val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT;
> > +             break;
> > +     case MTK_DP_COLOR_DEPTH_10BIT:
> > +             val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT;
> > +             break;
> > +     case MTK_DP_COLOR_DEPTH_12BIT:
> > +             val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT;
> > +             break;
> > +     case MTK_DP_COLOR_DEPTH_16BIT:
> > +             val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT;
> > +             break;
> > +     case MTK_DP_COLOR_DEPTH_UNKNOWN:
> > +             drm_warn(mtk_dp->drm_dev, "Unsupported color depth %d\n",
> > +                      color_depth);
> > +             return;
> > +     }
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
> > +                        VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_mn_overwrite_disable(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
> > +                        VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32 val)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> > +                        val << SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT,
> > +                        SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> > +                        BIT(VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT),
> > +                        VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> > +                        0x20 << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
> > +                        SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> > +                        0x20 << SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT,
> > +                        SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
> > +                        2 << VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT,
> > +                        VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> > +                        4 << FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT,
> > +                        FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
> > +     mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368,
> > +                  1 << VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT |
> > +                          1 << VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT |
> > +                          BIT(SDP_DP13_EN_DP_ENC1_P0_SHIFT) |
> > +                          1 << BS2BS_MODE_DP_ENC1_P0_SHIFT);
> > +}
> > +
> > +static void mtk_dp_pg_disable(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038, 0,
> > +                        VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
> > +                        4 << PGEN_PATTERN_SEL_SHIFT, PGEN_PATTERN_SEL_MASK);
> > +}
> > +
> > +static void mtk_dp_audio_setup_channels(struct mtk_dp *mtk_dp,
> > +                                     struct mtk_dp_audio_cfg *cfg)
> > +{
> > +     u32 channel_enable_bits;
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3324,
> > +                        AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX,
> > +                        AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK);
> > +
> > +     //audio channel count change reset
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(9), BIT(9));
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> > +                        AU_PRTY_REGEN_DP_ENC1_P0_MASK,
> > +                        AU_PRTY_REGEN_DP_ENC1_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> > +                        AU_CH_STS_REGEN_DP_ENC1_P0_MASK,
> > +                        AU_CH_STS_REGEN_DP_ENC1_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> > +                        AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK,
> > +                        AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK);
> > +
> > +     switch (cfg->channels) {
> > +     case 2:
> > +             channel_enable_bits = AUDIO_2CH_SEL_DP_ENC0_P0_MASK |
> > +                                   AUDIO_2CH_EN_DP_ENC0_P0_MASK;
> > +             break;
> > +     case 8:
> > +     default:
> > +             channel_enable_bits = AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
> > +                                   AUDIO_8CH_EN_DP_ENC0_P0_MASK;
> > +             break;
> > +     }
> > +     mtk_dp_update_bits(
> > +             mtk_dp, MTK_DP_ENC0_P0_3088,
> > +             channel_enable_bits | AU_EN_DP_ENC0_P0_MASK,
> > +             AUDIO_2CH_SEL_DP_ENC0_P0_MASK | AUDIO_2CH_EN_DP_ENC0_P0_MASK |
> > +                     AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
> > +                     AUDIO_8CH_EN_DP_ENC0_P0_MASK | AU_EN_DP_ENC0_P0_MASK);
> > +
> > +     //audio channel count change reset
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, 0, BIT(9));
> > +
> > +     //enable audio reset
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(0), BIT(0));
> > +}
> > +
> > +static void mtk_dp_audio_channel_status_set(struct mtk_dp *mtk_dp,
> > +                                         struct mtk_dp_audio_cfg *cfg)
> > +{
> > +     union mtk_dp_audio_channel_status channel_status;
> > +
> > +     memset(&channel_status, 0, sizeof(channel_status));
> > +
> > +     switch (cfg->sample_rate) {
> > +     case 32000:
> > +             channel_status.iec.sampling_freq = 3;
> > +             break;
> > +     case 44100:
> > +             channel_status.iec.sampling_freq = 0;
> > +             break;
> > +     case 48000:
> > +             channel_status.iec.sampling_freq = 2;
> > +             break;
> > +     case 88200:
> > +             channel_status.iec.sampling_freq = 8;
> > +             break;
> > +     case 96000:
> > +             channel_status.iec.sampling_freq = 0xA;
> > +             break;
> > +     case 192000:
> > +             channel_status.iec.sampling_freq = 0xE;
> > +             break;
> > +     default:
> > +             channel_status.iec.sampling_freq = 0x1;
> > +             break;
> > +     }
> > +
> > +     switch (cfg->word_length_bits) {
> > +     case 16:
> > +             channel_status.iec.word_len = 0x02;
> > +             break;
> > +     case 20:
> > +             channel_status.iec.word_len = 0x03;
> > +             break;
> > +     case 24:
> > +             channel_status.iec.word_len = 0x0B;
> > +             break;
> > +     }
>
> Yeah, this is provided by hdmi-codec through hdmi_codec_params in
> prepare.
>
Thx for the input, I'll use that instead.

> > +     // IEC 60958 consumer channel status bits
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_308C,
> > +                        channel_status.buf[1] << 8 | channel_status.buf[0],
> > +                        CH_STATUS_0_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3090,
> > +                        channel_status.buf[3] << 8 | channel_status.buf[2],
> > +                        CH_STATUS_1_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3094, channel_status.buf[4],
> > +                        CH_STATUS_2_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_audio_sdp_asp_set_channels(struct mtk_dp *mtk_dp,
> > +                                           int channels)
> > +{
> > +     if (channels != 2 && channels != 8)
> > +             channels = 8;
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_312C,
> > +                        (channels - 1) << ASP_HB3_DP_ENC0_P0_SHIFT,
> > +                        ASP_HB2_DP_ENC0_P0_MASK | ASP_HB3_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_audio_set_divider(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
> > +                        AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2,
> > +                        AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static bool mtk_dp_plug_state(struct mtk_dp *mtk_dp)
> > +{
> > +     return !!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
> > +               HPD_DB_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_sdp_trigger_packet(struct mtk_dp *mtk_dp,
> > +                                   enum mtk_dp_sdp_type type)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, type,
> > +                        SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, SDP_PACKET_W_DP_ENC1_P0,
> > +                        SDP_PACKET_W_DP_ENC1_P0);
> > +}
> > +
> > +static void mtk_dp_sdp_set_data(struct mtk_dp *mtk_dp, u8 *data_bytes)
> > +{
> > +     mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_ENC1_P0_3200, data_bytes, 0x10);
> > +}
> > +
> > +static void mtk_dp_sdp_set_header(struct mtk_dp *mtk_dp,
> > +                               enum mtk_dp_sdp_type type,
> > +                               struct dp_sdp_header *header)
> > +{
> > +     u32 db_addr;
> > +
> > +     switch (type) {
> > +     case MTK_DP_SDP_DRM:
> > +             db_addr = MTK_DP_ENC0_P0_3138;
> > +             break;
> > +     case MTK_DP_SDP_PPS0:
> > +     case MTK_DP_SDP_PPS1:
> > +     case MTK_DP_SDP_PPS2:
> > +     case MTK_DP_SDP_PPS3:
> > +             db_addr = MTK_DP_ENC0_P0_3130;
> > +             break;
> > +     default:
> > +             db_addr = MTK_DP_ENC0_P0_30D8 + (type - MTK_DP_SDP_ACM) * 8;
> > +     }
> > +
> > +     mtk_dp_bulk_16bit_write(mtk_dp, db_addr, (u8 *)header, 4);
> > +}
> > +
> > +static const u32 mtk_dp_sdp_type_to_reg[MTK_DP_SDP_MAX_NUM] = {
> > +     /* MTK_DP_SDP_NONE => */ 0x0,
> > +     /* MTK_DP_SDP_ACM  => */ MTK_DP_ENC0_P0_30B4,
> > +     /* MTK_DP_SDP_ISRC => */ MTK_DP_ENC0_P0_30B4 + 1,
> > +     /* MTK_DP_SDP_AVI  => */ MTK_DP_ENC0_P0_30A4 + 1,
> > +     /* MTK_DP_SDP_AUI  => */ MTK_DP_ENC0_P0_30A8,
> > +     /* MTK_DP_SDP_SPD  => */ MTK_DP_ENC0_P0_30A8 + 1,
> > +     /* MTK_DP_SDP_MPEG => */ MTK_DP_ENC0_P0_30AC,
> > +     /* MTK_DP_SDP_NTSC => */ MTK_DP_ENC0_P0_30AC + 1,
> > +     /* MTK_DP_SDP_VSP  => */ MTK_DP_ENC0_P0_30B0,
> > +     /* MTK_DP_SDP_VSC  => */ MTK_DP_ENC0_P0_30B8,
> > +     /* MTK_DP_SDP_EXT  => */ MTK_DP_ENC0_P0_30B0 + 1,
> > +     /* MTK_DP_SDP_PPS0 => */ MTK_DP_ENC0_P0_31E8,
> > +     /* MTK_DP_SDP_PPS1 => */ MTK_DP_ENC0_P0_31E8,
> > +     /* MTK_DP_SDP_PPS2 => */ MTK_DP_ENC0_P0_31E8,
> > +     /* MTK_DP_SDP_PPS3 => */ MTK_DP_ENC0_P0_31E8,
> > +     /* MTK_DP_SDP_DRM  => */ MTK_DP_ENC0_P0_31DC,
> > +};
> > +
> > +static void mtk_dp_disable_sdp(struct mtk_dp *mtk_dp, enum mtk_dp_sdp_type type)
> > +{
> > +     if (type == MTK_DP_SDP_NONE)
> > +             return;
> > +
> > +     // Disable periodic send
> > +     mtk_dp_update_bits(mtk_dp, mtk_dp_sdp_type_to_reg[type] & 0xfffc, 0,
> > +                        0xFF << ((mtk_dp_sdp_type_to_reg[type] & 3) * 8));
> > +}
> > +
> > +static void mtk_dp_setup_sdp(struct mtk_dp *mtk_dp,
> > +                          struct mtk_dp_sdp_packet *packet)
> > +{
> > +     mtk_dp_sdp_set_data(mtk_dp, packet->sdp.db);
> > +     mtk_dp_sdp_set_header(mtk_dp, packet->type, &packet->sdp.sdp_header);
> > +
> > +     mtk_dp_disable_sdp(mtk_dp, packet->type);
> > +
> > +     switch (packet->type) {
> > +     case MTK_DP_SDP_NONE:
> > +             break;
> > +     case MTK_DP_SDP_ISRC:
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> > +                                0x1C << ISRC1_HB3_DP_ENC0_P0_SHIFT,
> > +                                ISRC1_HB3_DP_ENC0_P0_MASK);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, MTK_DP_SDP_ISRC,
> > +                                SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
> > +
> > +             if (packet->sdp.sdp_header.HB3 & BIT(2))
> > +                     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
> > +                                        BIT(ISRC_CONT_DP_ENC0_P0_SHIFT),
> > +                                        ISRC_CONT_DP_ENC0_P0_MASK);
> > +             else
> > +                     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC, 0,
> > +                                        ISRC_CONT_DP_ENC0_P0_MASK);
> > +
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280,
> > +                                SDP_PACKET_W_DP_ENC1_P0,
> > +                                SDP_PACKET_W_DP_ENC1_P0);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30B4,
> > +                                5 << ISRC_CFG_DP_ENC0_P0_SHIFT,
> > +                                ISRC_CFG_DP_ENC0_P0_MASK);
> > +             break;
> > +     case MTK_DP_SDP_DRM:
> > +             mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31DC,
> > +                                5 << HDR0_CFG_DP_ENC0_P0_SHIFT,
> > +                                HDR0_CFG_DP_ENC0_P0_MASK);
> > +             break;
> > +     case MTK_DP_SDP_ACM:
> > +     case MTK_DP_SDP_AVI:
> > +     case MTK_DP_SDP_AUI:
> > +     case MTK_DP_SDP_SPD:
> > +     case MTK_DP_SDP_MPEG:
> > +     case MTK_DP_SDP_NTSC:
> > +     case MTK_DP_SDP_VSP:
> > +     case MTK_DP_SDP_VSC:
> > +     case MTK_DP_SDP_EXT:
> > +     case MTK_DP_SDP_PPS0:
> > +     case MTK_DP_SDP_PPS1:
> > +     case MTK_DP_SDP_PPS2:
> > +     case MTK_DP_SDP_PPS3:
> > +             mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
> > +             // Enable periodic sending
> > +             mtk_dp_update_bits(
> > +                     mtk_dp, mtk_dp_sdp_type_to_reg[packet->type] & 0xfffc,
> > +                     0x05 << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
> > +                              8),
> > +                     0xff << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
> > +                              8));
> > +             break;
> > +     default:
> > +             break;
> > +     }
> > +}
> > +
> > +static void mtk_dp_sdp_vsc_ext_disable(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A0, 0,
> > +                        BIT(7) | BIT(8) | BIT(12));
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_328C, 0, BIT(7));
> > +}
> > +
> > +static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_write(
> > +             mtk_dp, MTK_DP_AUX_P0_3640,
> > +             BIT(AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT));
> > +}
> > +
> > +static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32 addr)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644, cmd,
> > +                        MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648, addr,
> > +                        MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C, addr >> 16,
> > +                        MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_aux_cmd_complete(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> > +                        BIT(MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT),
> > +                        MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK |
> > +                                PHY_FIFO_RST_AUX_TX_P0_MASK |
> > +                                MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
> > +                        BIT(AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT),
> > +                        AUX_TX_REQUEST_READY_AUX_TX_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> > +                                    size_t length)
> > +{
> > +     mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf, length);
> > +}
> > +
> > +static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> > +                                 size_t length, int read_delay)
> > +{
> > +     int read_pos;
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620, 0,
> > +                        AUX_RD_MODE_AUX_TX_P0_MASK);
> > +
> > +     for (read_pos = 0; read_pos < length; read_pos++) {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> > +                                BIT(AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT),
> > +                                AUX_RX_FIFO_READ_PULSE_TX_P0_MASK);
> > +             usleep_range(read_delay, read_delay * 2);
> > +             buf[read_pos] =
> > +                     (u8)(mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3620) &
> > +                          AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK >>
> > +                                  AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT);
> > +     }
> > +}
> > +
> > +static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t length)
> > +{
> > +     if (length > 0) {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> > +                                (length - 1)
> > +                                        << MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT,
> > +                                MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
> > +                                AUX_NO_LENGTH_AUX_TX_P0_MASK |
> > +                                        AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> > +                                        AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> > +     } else {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> > +                                BIT(AUX_NO_LENGTH_AUX_TX_P0_SHIFT),
> > +                                AUX_NO_LENGTH_AUX_TX_P0_MASK |
> > +                                        AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> > +                                        AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> > +     }
> > +}
> > +
> > +static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp, bool is_read)
> > +{
> > +     int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
> > +
> > +     while (--wait_reply) {
> > +             u32 aux_irq_status;
> > +
> > +             if (is_read) {
> > +                     u32 fifo_status =
> > +                             mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3618);
> > +
> > +                     if (fifo_status &
> > +                         (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
> > +                          AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
> > +                             return 0;
> > +                     }
> > +             }
> > +
> > +             aux_irq_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3640);
> > +             if (aux_irq_status & AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK)
> > +                     return 0;
> > +
> > +             if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK)
> > +                     return -ETIMEDOUT;
> > +
> > +             usleep_range(100, 500);
> > +     }
> > +
> > +     return -ETIMEDOUT;
> > +}
> > +
> > +static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool is_read, u8 cmd,
> > +                               u32 addr, u8 *buf, size_t length)
> > +{
> > +     int ret;
> > +     u32 reply_cmd;
> > +
> > +     if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
> > +                     (cmd == DP_AUX_NATIVE_READ && !length)))
> > +             return -EINVAL;
> > +
> > +     if (!is_read)
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> > +                                BIT(AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT),
> > +                                AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK);
> > +
> > +     mtk_dp_aux_cmd_complete(mtk_dp);
> > +     mtk_dp_aux_irq_clear(mtk_dp);
> > +     usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> > +                  MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> > +
> > +     mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
> > +     mtk_dp_aux_set_length(mtk_dp, length);
> > +
> > +     if (!is_read) {
> > +             if (length)
> > +                     mtk_dp_aux_fill_write_fifo(mtk_dp, buf, length);
> > +
> > +             mtk_dp_update_bits(
> > +                     mtk_dp, MTK_DP_AUX_P0_3704,
> > +                     AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK,
> > +                     AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK);
> > +     }
> > +
> > +     mtk_dp_aux_request_ready(mtk_dp);
> > +
> > +     ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
> > +
> > +     reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
> > +                 AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
> > +
> > +     if (ret || reply_cmd) {
> > +             u32 phy_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3628) &
> > +                              AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
> > +             if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
> > +                     drm_err(mtk_dp->drm_dev,
> > +                             "AUX Rx Aux hang, need SW reset\n");
> > +                     ret = -EIO;
> > +             }
> > +
> > +             mtk_dp_aux_cmd_complete(mtk_dp);
> > +             mtk_dp_aux_irq_clear(mtk_dp);
> > +
> > +             usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> > +                          MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> > +             return -ETIMEDOUT;
> > +     }
> > +
> > +     if (!length) {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
> > +                                AUX_NO_LENGTH_AUX_TX_P0_MASK |
> > +                                        AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> > +                                        AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> > +     } else if (is_read) {
> > +             int read_delay;
> > +
> > +             if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
> > +                 cmd == DP_AUX_I2C_READ)
> > +                     read_delay = 500;
> > +             else
> > +                     read_delay = 100;
> > +
> > +             mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length, read_delay);
> > +     }
> > +
> > +     mtk_dp_aux_cmd_complete(mtk_dp);
> > +     mtk_dp_aux_irq_clear(mtk_dp);
> > +     usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> > +                  MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> > +
> > +     return 0;
> > +}
> > +
> > +static bool mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int lane_num,
> > +                                       int swing_val, int preemphasis)
> > +{
> > +     u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
> > +
> > +     if (lane_num < 0 || lane_num > 3)
> > +             return false;
> > +
> > +     dev_dbg(mtk_dp->dev,
> > +             "link training swing_val= 0x%x, preemphasis = 0x%x\n",
> > +             swing_val, preemphasis);
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> > +                        swing_val << (DP_TX0_VOLT_SWING_SHIFT + lane_shift),
> > +                        DP_TX0_VOLT_SWING_MASK << lane_shift);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> > +                        preemphasis << (DP_TX0_PRE_EMPH_SHIFT + lane_shift),
> > +                        DP_TX0_PRE_EMPH_MASK << lane_shift);
> > +
> > +     return true;
> > +}
> > +
> > +static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP, 0,
> > +                        DP_TX0_VOLT_SWING_MASK | DP_TX1_VOLT_SWING_MASK |
> > +                                DP_TX2_VOLT_SWING_MASK |
> > +                                DP_TX3_VOLT_SWING_MASK |
> > +                                DP_TX0_PRE_EMPH_MASK | DP_TX1_PRE_EMPH_MASK |
> > +                                DP_TX2_PRE_EMPH_MASK | DP_TX3_PRE_EMPH_MASK);
> > +}
> > +
> > +static void mtk_dp_fec_enable(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> > +                        enable ? BIT(FEC_EN_DP_TRANS_P0_SHIFT) : 0,
> > +                        FEC_EN_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
> > +{
> > +     u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
> > +                      SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
> > +
> > +     if (irq_status) {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, irq_status,
> > +                                SW_IRQ_CLR_DP_TRANS_P0_MASK);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, 0,
> > +                                SW_IRQ_CLR_DP_TRANS_P0_MASK);
> > +     }
> > +
> > +     return irq_status;
> > +}
> > +
> > +static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
> > +{
> > +     u8 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
> > +                      IRQ_STATUS_DP_TRANS_P0_MASK) >>
> > +                     IRQ_STATUS_DP_TRANS_P0_SHIFT;
> > +
> > +     if (irq_status) {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, irq_status,
> > +                                IRQ_CLR_DP_TRANS_P0_MASK);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, 0,
> > +                                IRQ_CLR_DP_TRANS_P0_MASK);
> > +     }
> > +
> > +     return irq_status;
> > +}
> > +
> > +static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     u32 val = 0;
> > +
> > +     if (!enable)
> > +             val = IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
> > +                   IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
> > +                   IRQ_MASK_DP_TRANS_P0_INT_IRQ;
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, val,
> > +                        IRQ_MASK_DP_TRANS_P0_MASK);
> > +}
> > +
> > +void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
> > +                        XTAL_FREQ_DP_TRANS_P0_DEFAULT,
> > +                        XTAL_FREQ_DP_TRANS_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> > +                        BIT(FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT),
> > +                        FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> > +                        BIT(AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT),
> > +                        AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
> > +                        SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK, IRQ_MASK_AUX_TOP_IRQ,
> > +                        IRQ_MASK_AUX_TOP_IRQ);
> > +}
> > +
> > +static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp *mtk_dp)
> > +{
> > +     // Debounce threshold
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> > +                        8 << HPD_DEB_THD_DP_TRANS_P0_SHIFT,
> > +                        HPD_DEB_THD_DP_TRANS_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> > +                        (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
> > +                         HPD_INT_THD_DP_TRANS_P0_UPPER_1100US)
> > +                                << HPD_INT_THD_DP_TRANS_P0_SHIFT,
> > +                        HPD_INT_THD_DP_TRANS_P0_MASK);
> > +
> > +     // Connect threshold 1.5ms + 5 x 0.1ms = 2ms
> > +     // Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> > +                        (5 << HPD_DISC_THD_DP_TRANS_P0_SHIFT) |
> > +                                (5 << HPD_CONN_THD_DP_TRANS_P0_SHIFT),
> > +                        HPD_DISC_THD_DP_TRANS_P0_MASK |
> > +                                HPD_CONN_THD_DP_TRANS_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
> > +                        HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
> > +                        HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
> > +{
> > +     // modify timeout threshold = 1595 [12 : 8]
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C, 0x1595,
> > +                        AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658, 0,
> > +                        AUX_TX_OV_EN_AUX_TX_P0_MASK);
> > +     // 25 for 26M
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
> > +                        25 << AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT,
> > +                        AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
> > +     // 13 for 26M
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
> > +                        13 << AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT,
> > +                        AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
> > +                        BIT(MTK_ATOP_EN_AUX_TX_P0_SHIFT),
> > +                        MTK_ATOP_EN_AUX_TX_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_initialize_digital_settings(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
> > +                        VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
> > +     mtk_dp_set_color_format(mtk_dp, MTK_DP_COLOR_FORMAT_RGB_444);
> > +     mtk_dp_set_color_depth(mtk_dp, MTK_DP_COLOR_DEPTH_8BIT);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
> > +                        1 << BS2BS_MODE_DP_ENC1_P0_SHIFT,
> > +                        BS2BS_MODE_DP_ENC1_P0_MASK);
> > +
> > +     // dp tx encoder reset all sw
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> > +                        BIT(DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT),
> > +                        DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
> > +     usleep_range(1000, 5000);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
> > +                        DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> > +                        BIT(DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT),
> > +                        DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
> > +     usleep_range(1000, 5000);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C, 0,
> > +                        DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
> > +                        lanes == 0 ? 0 : BIT(3), BIT(3) | BIT(2));
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, lanes,
> > +                        LANE_NUM_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
> > +                        lanes << LANE_NUM_DP_TRANS_P0_SHIFT,
> > +                        LANE_NUM_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static int link_rate_to_mb_per_s(struct mtk_dp *mtk_dp,
> > +                              enum mtk_dp_linkrate linkrate)
> > +{
> > +     switch (linkrate) {
> > +     default:
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Implementation error, unknown linkrate %d\n",
> > +                     linkrate);
> > +             fallthrough;
> > +     case MTK_DP_LINKRATE_RBR:
> > +             return 1620;
> > +     case MTK_DP_LINKRATE_HBR:
> > +             return 2700;
> > +     case MTK_DP_LINKRATE_HBR2:
> > +             return 5400;
> > +     case MTK_DP_LINKRATE_HBR3:
> > +             return 8100;
> > +     }
> > +}
> > +
> > +static u32 check_cal_data_valid(u32 min, u32 max, u32 val, u32 default_val)
> > +{
> > +     if (val < min || val > max)
> > +             return default_val;
> > +
> > +     return val;
> > +}
> > +
> > +static int mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
> > +{
> > +     struct dp_cal_data *cal_data = &mtk_dp->cal_data;
> > +     struct device *dev = mtk_dp->dev;
> > +     struct nvmem_cell *cell;
> > +     u32 *buf;
> > +     size_t len;
> > +
> > +     cell = nvmem_cell_get(dev, "dp_calibration_data");
> > +     if (IS_ERR(cell)) {
> > +             dev_err(dev,
> > +                     "Error: Failed to get nvmem cell dp_calibration_data\n");
> > +             return PTR_ERR(cell);
> > +     }
> > +
> > +     buf = (u32 *)nvmem_cell_read(cell, &len);
> > +     nvmem_cell_put(cell);
> > +
> > +     if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
> > +             dev_err(dev,
> > +                     "Error: Failed to read nvmem_cell_read fail len %ld\n",
> > +                     (len / sizeof(u32)));
> > +             return PTR_ERR(buf);
> > +     }
> > +
> > +     if (mtk_dp_is_edp(mtk_dp)) {
> > +             cal_data->glb_bias_trim = check_cal_data_valid(
> > +                     1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf);
> > +             cal_data->clktx_impse =
> > +                     check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8);
> > +             cal_data->ln0_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8);
> > +             cal_data->ln0_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8);
> > +             cal_data->ln1_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8);
> > +             cal_data->ln1_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8);
> > +             cal_data->ln2_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8);
> > +             cal_data->ln2_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8);
> > +             cal_data->ln3_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8);
> > +             cal_data->ln3_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8);
> > +     } else {
> > +             cal_data->glb_bias_trim = check_cal_data_valid(
> > +                     1, 0x1e, (buf[0] >> 27) & 0x1f, 0xf);
> > +             cal_data->clktx_impse =
> > +                     check_cal_data_valid(1, 0xe, (buf[0] >> 13) & 0xf, 0x8);
> > +             cal_data->ln0_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 28) & 0xf, 0x8);
> > +             cal_data->ln0_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 24) & 0xf, 0x8);
> > +             cal_data->ln1_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 20) & 0xf, 0x8);
> > +             cal_data->ln1_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 16) & 0xf, 0x8);
> > +             cal_data->ln2_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 12) & 0xf, 0x8);
> > +             cal_data->ln2_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 8) & 0xf, 0x8);
> > +             cal_data->ln3_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 4) & 0xf, 0x8);
> > +             cal_data->ln3_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, buf[1] & 0xf, 0x8);
> > +     }
> > +
> > +     kfree(buf);
> > +
> > +     return 0;
> > +}
> > +
> > +void mtk_dp_set_cal_data(struct mtk_dp *mtk_dp)
> > +{
> > +     struct dp_cal_data *cal_data = &mtk_dp->cal_data;
> > +
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
> > +                        cal_data->clktx_impse << 20, RG_CKM_PT0_CKTX_IMPSEL);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
> > +                        cal_data->glb_bias_trim << 16,
> > +                        RG_XTP_GLB_BIAS_INTR_CTRL);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> > +                        cal_data->ln0_tx_impsel_pmos << 12,
> > +                        RG_XTP_LN0_TX_IMPSEL_PMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> > +                        cal_data->ln0_tx_impsel_nmos << 16,
> > +                        RG_XTP_LN0_TX_IMPSEL_NMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> > +                        cal_data->ln1_tx_impsel_pmos << 12,
> > +                        RG_XTP_LN1_TX_IMPSEL_PMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> > +                        cal_data->ln1_tx_impsel_nmos << 16,
> > +                        RG_XTP_LN1_TX_IMPSEL_NMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> > +                        cal_data->ln2_tx_impsel_pmos << 12,
> > +                        RG_XTP_LN2_TX_IMPSEL_PMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> > +                        cal_data->ln2_tx_impsel_nmos << 16,
> > +                        RG_XTP_LN2_TX_IMPSEL_NMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> > +                        cal_data->ln3_tx_impsel_pmos << 12,
> > +                        RG_XTP_LN3_TX_IMPSEL_PMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> > +                        cal_data->ln3_tx_impsel_nmos << 16,
> > +                        RG_XTP_LN3_TX_IMPSEL_NMOS);
> > +}
> > +
> > +static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
> > +                             enum mtk_dp_linkrate link_rate, int lane_count)
> > +{
> > +     int ret;
> > +     union phy_configure_opts
> > +             phy_opts = { .dp = {
> > +                                  .link_rate = link_rate_to_mb_per_s(
> > +                                          mtk_dp, link_rate),
> > +                                  .set_rate = 1,
> > +                                  .lanes = lane_count,
> > +                                  .set_lanes = 1,
> > +                                  .ssc = mtk_dp->train_info.sink_ssc,
> > +                          } };
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP,
> > +                        DP_PWR_STATE_MASK);
> > +
> > +     ret = phy_configure(mtk_dp->phy, &phy_opts);
> > +
> > +     mtk_dp_set_cal_data(mtk_dp);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> > +                        DP_PWR_STATE_BANDGAP_TPLL_LANE, DP_PWR_STATE_MASK);
> > +     return ret;
> > +}
> > +
> > +static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     const u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
> > +                     POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
> > +                     POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
> > +                     POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580, enable ? val : 0, val);
> > +}
> > +
> > +static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int pattern)
> > +{
> > +     if (pattern < 0 || pattern > 4) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Implementation error, no such pattern %d\n", pattern);
> > +             return;
> > +     }
> > +
> > +     if (pattern == 1) // TPS1
> > +             mtk_dp_set_idle_pattern(mtk_dp, false);
> > +
> > +     mtk_dp_update_bits(
> > +             mtk_dp, MTK_DP_TRANS_P0_3400,
> > +             pattern ? BIT(pattern - 1) << PATTERN1_EN_DP_TRANS_P0_SHIFT : 0,
> > +             PATTERN1_EN_DP_TRANS_P0_MASK | PATTERN2_EN_DP_TRANS_P0_MASK |
> > +                     PATTERN3_EN_DP_TRANS_P0_MASK |
> > +                     PATTERN4_EN_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> > +                        enable ? BIT(ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT) : 0,
> > +                        ENHANCED_FRAME_EN_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
> > +                        enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
> > +                        DP_SCR_EN_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     u32 val = BIT(VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT);
> > +
> > +     if (enable)
> > +             val |= BIT(VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, val,
> > +                        VIDEO_MUTE_SEL_DP_ENC0_P0_MASK |
> > +                                VIDEO_MUTE_SW_DP_ENC0_P0_MASK);
> > +
> > +     if (mtk_dp_is_edp(mtk_dp))
> > +             mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable);
> > +     else
> > +             mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable);
> > +}
> > +
> > +static void mtk_dp_audio_mute(struct mtk_dp *mtk_dp, bool mute)
> > +{
> > +     if (mute) {
> > +             mtk_dp_update_bits(
> > +                     mtk_dp, MTK_DP_ENC0_P0_3030,
> > +                     BIT(VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT) |
> > +                             BIT(VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT),
> > +                     VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
> > +                             VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
> > +
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088, 0,
> > +                                AU_EN_DP_ENC0_P0_MASK);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0,
> > +                                AU_TS_CFG_DP_ENC0_P0_MASK);
> > +
> > +     } else {
> > +             mtk_dp_update_bits(
> > +                     mtk_dp, MTK_DP_ENC0_P0_3030, 0,
> > +                     VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
> > +                             VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
> > +
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088,
> > +                                BIT(AU_EN_DP_ENC0_P0_SHIFT),
> > +                                AU_EN_DP_ENC0_P0_MASK);
> > +             // Send one every two frames
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0x0F,
> > +                                AU_TS_CFG_DP_ENC0_P0_MASK);
> > +     }
> > +}
> > +
> > +static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, 0,
> > +                        SW_RST_B_PHYD);
> > +     usleep_range(10, 200);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, SW_RST_B_PHYD,
> > +                        SW_RST_B_PHYD);
> > +}
> > +
> > +static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
> > +     usleep_range(10, 200);
> > +     mtk_dp_write(mtk_dp, MTK_DP_0034,
> > +                  DA_CKM_CKTX0_EN_FORCE_EN | DA_CKM_BIAS_LPF_EN_FORCE_VAL |
> > +                          DA_CKM_BIAS_EN_FORCE_VAL |
> > +                          DA_XTP_GLB_LDO_EN_FORCE_VAL |
> > +                          DA_XTP_GLB_AVD10_ON_FORCE_VAL);
> > +     // Disable RX
> > +     mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
> > +     mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
> > +                  0x550 | BIT(FUSE_SEL_SHIFT) | BIT(MEM_ISO_EN_SHIFT));
> > +}
> > +
> > +static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR2;
> > +     mtk_dp->train_info.lane_count = MTK_DP_MAX_LANES;
> > +     mtk_dp->train_info.irq_status = 0;
> > +     mtk_dp->train_info.cable_plugged_in = false;
> > +     mtk_dp->train_info.cable_state_change = false;
> > +     mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > +     mtk_dp->state = MTK_DP_STATE_INITIAL;
> > +
> > +     mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
> > +     mtk_dp->info.depth = MTK_DP_COLOR_DEPTH_8BIT;
> > +     memset(&mtk_dp->info.timings, 0, sizeof(struct mtk_dp_timings));
> > +     mtk_dp->info.timings.frame_rate = 60;
> > +
> > +     mtk_dp->has_fec = false;
> > +     mtk_dp->audio_enable = false;
> > +}
> > +
> > +static void mtk_dp_sdp_set_down_cnt_init(struct mtk_dp *mtk_dp,
> > +                                      u32 sram_read_start)
> > +{
> > +     u32 sdp_down_cnt_init = 0;
> > +     u32 dc_offset;
> > +
> > +     if (mtk_dp->info.timings.pix_rate_khz > 0)
> > +             sdp_down_cnt_init = sram_read_start *
> > +                                 mtk_dp->train_info.link_rate * 2700 * 8 /
> > +                                 (mtk_dp->info.timings.pix_rate_khz * 4);
> > +
> > +     switch (mtk_dp->train_info.lane_count) {
> > +     case 1:
> > +             sdp_down_cnt_init = sdp_down_cnt_init > 0x1A ?
> > +                                               sdp_down_cnt_init :
> > +                                               0x1A; //26
> > +             break;
> > +     case 2:
> > +             // case for LowResolution && High Audio Sample Rate
> > +             dc_offset = mtk_dp->info.timings.vtotal <= 525 ? 0x04 : 0x00;
> > +             sdp_down_cnt_init = sdp_down_cnt_init > 0x10 ?
> > +                                               sdp_down_cnt_init :
> > +                                               0x10 + dc_offset; //20 or 16
> > +             break;
> > +     case 4:
> > +     default:
> > +             sdp_down_cnt_init =
> > +                     sdp_down_cnt_init > 0x06 ? sdp_down_cnt_init : 0x06; //6
> > +             break;
> > +     }
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> > +                        sdp_down_cnt_init
> > +                                << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
> > +                        SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_sdp_set_down_cnt_init_in_hblank(struct mtk_dp *mtk_dp)
> > +{
> > +     int pix_clk_mhz;
> > +     u32 dc_offset;
> > +     u32 spd_down_cnt_init = 0;
> > +
> > +     pix_clk_mhz = mtk_dp->info.format == MTK_DP_COLOR_FORMAT_YUV_420 ?
> > +                                 mtk_dp->info.timings.pix_rate_khz / 2000 :
> > +                                 mtk_dp->info.timings.pix_rate_khz / 1000;
> > +
> > +     switch (mtk_dp->train_info.lane_count) {
> > +     case 1:
> > +             spd_down_cnt_init = 0x20;
> > +             break;
> > +     case 2:
> > +             dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x14 : 0x00;
> > +             spd_down_cnt_init = 0x18 + dc_offset;
> > +             break;
> > +     case 4:
> > +     default:
> > +             dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x08 : 0x00;
> > +             if (pix_clk_mhz > mtk_dp->train_info.link_rate * 27)
> > +                     spd_down_cnt_init = 0x8;
> > +             else
> > +                     spd_down_cnt_init = 0x10 + dc_offset;
> > +             break;
> > +     }
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364, spd_down_cnt_init,
> > +                        SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
> > +{
> > +     u32 sram_read_start = MTK_DP_TBC_BUF_READ_START_ADDR;
> > +
> > +     if (mtk_dp->train_info.lane_count > 0) {
> > +             sram_read_start = min_t(
> > +                     u32, MTK_DP_TBC_BUF_READ_START_ADDR,
> > +                     mtk_dp->info.timings.vm.hactive /
> > +                             (mtk_dp->train_info.lane_count * 4 * 2 * 2));
> > +             mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
> > +     }
> > +
> > +     mtk_dp_setup_encoder(mtk_dp);
> > +     mtk_dp_sdp_set_down_cnt_init_in_hblank(mtk_dp);
> > +     mtk_dp_sdp_set_down_cnt_init(mtk_dp, sram_read_start);
> > +}
> > +
> > +static void mtk_dp_calculate_pixrate(struct mtk_dp *mtk_dp)
> > +{
> > +     int target_frame_rate = 60;
> > +     int target_pixel_clk;
> > +
> > +     if (mtk_dp->info.timings.frame_rate > 0) {
> > +             target_frame_rate = mtk_dp->info.timings.frame_rate;
> > +             target_pixel_clk = (int)mtk_dp->info.timings.htotal *
> > +                                (int)mtk_dp->info.timings.vtotal *
> > +                                target_frame_rate;
> > +     } else if (mtk_dp->info.timings.pix_rate_khz > 0) {
> > +             target_pixel_clk = mtk_dp->info.timings.pix_rate_khz * 1000;
> > +     } else {
> > +             target_pixel_clk = (int)mtk_dp->info.timings.htotal *
> > +                                (int)mtk_dp->info.timings.vtotal *
> > +                                target_frame_rate;
> > +     }
> > +
> > +     if (target_pixel_clk > 0)
> > +             mtk_dp->info.timings.pix_rate_khz = target_pixel_clk / 1000;
> > +}
> > +
> > +static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_msa_bypass_disable(mtk_dp);
> > +     mtk_dp_calculate_pixrate(mtk_dp);
> > +     mtk_dp_pg_disable(mtk_dp);
> > +     mtk_dp_setup_tu(mtk_dp);
> > +}
> > +
> > +static void mtk_dp_edid_free(struct mtk_dp *mtk_dp)
> > +{
> > +     mutex_lock(&mtk_dp->edid_lock);
> > +     kfree(mtk_dp->edid);
> > +     mtk_dp->edid = NULL;
> > +     mutex_unlock(&mtk_dp->edid_lock);
> > +}
> > +
> > +static void mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp)
> > +{
> > +     ssize_t ret;
> > +     u8 sink_count;
> > +     u8 link_status[DP_LINK_STATUS_SIZE] = {};
> > +     u32 sink_count_reg;
> > +     u32 link_status_reg;
> > +     bool locked;
> > +
> > +     sink_count_reg = DP_SINK_COUNT_ESI;
> > +     link_status_reg = DP_LANE0_1_STATUS;
> > +
> > +     ret = drm_dp_dpcd_readb(&mtk_dp->aux, sink_count_reg, &sink_count);
> > +     if (ret < 0) {
> > +             drm_info(mtk_dp->drm_dev, "Read sink count failed: %ld\n", ret);
> > +             return;
> > +     }
> > +
> > +     ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
> > +     if (ret < 0) {
> > +             drm_info(mtk_dp->drm_dev,
> > +                      "Read DP_SINK_COUNT_ESI failed: %ld\n", ret);
> > +             return;
> > +     }
> > +
> > +     ret = drm_dp_dpcd_read(&mtk_dp->aux, link_status_reg, link_status,
> > +                            sizeof(link_status));
> > +     if (!ret) {
> > +             drm_info(mtk_dp->drm_dev, "Read link status failed: %ld\n",
> > +                      ret);
> > +             return;
> > +     }
> > +
> > +     locked = drm_dp_channel_eq_ok(link_status,
> > +                                   mtk_dp->train_info.lane_count);
> > +     if (!locked && mtk_dp->train_state > MTK_DP_TRAIN_STATE_TRAINING_PRE)
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
> > +
> > +     if (link_status[1] & DP_REMOTE_CONTROL_COMMAND_PENDING)
> > +             drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR,
> > +                                DP_REMOTE_CONTROL_COMMAND_PENDING);
> > +
> > +     if (DP_GET_SINK_COUNT(sink_count) &&
> > +         (link_status[2] & DP_DOWNSTREAM_PORT_STATUS_CHANGED)) {
> > +             mtk_dp_edid_free(mtk_dp);
> > +             mtk_dp->train_info.check_cap_count = 0;
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
> > +             msleep(20);
> > +     }
> > +}
> > +
> > +static void mtk_dp_sdp_stop_sending(struct mtk_dp *mtk_dp)
> > +{
> > +     u8 packet_type;
> > +
> > +     for (packet_type = MTK_DP_SDP_ACM; packet_type < MTK_DP_SDP_MAX_NUM;
> > +          packet_type++)
> > +             mtk_dp_disable_sdp(mtk_dp, packet_type);
> > +
> > +     mtk_dp_sdp_vsc_ext_disable(mtk_dp);
> > +}
> > +
> > +static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes,
> > +                                       u8 dpcd_adjust_req[2])
> > +{
> > +     int lane;
> > +
> > +     for (lane = 0; lane < lanes; ++lane) {
> > +             u8 val;
> > +             u8 swing;
> > +             u8 preemphasis;
> > +             int index = lane / 2;
> > +             int shift = lane % 2 ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
> > +
> > +             swing = (dpcd_adjust_req[index] >> shift) &
> > +                     DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
> > +             preemphasis = ((dpcd_adjust_req[index] >> shift) &
> > +                            DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
> > +                           DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
> > +             val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
> > +                   preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
> > +
> > +             if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
> > +                     val |= DP_TRAIN_MAX_SWING_REACHED;
> > +             if (preemphasis == 3)
> > +                     val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> > +
> > +             mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing, preemphasis);
> > +             drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET + lane,
> > +                                val);
> > +     }
> > +
> > +     // Wait for the signal to be stable enough
> > +     usleep_range(2000, 5000);
> > +}
> > +
> > +static void mtk_dp_read_link_status(struct mtk_dp *mtk_dp,
> > +                                 u8 link_status[DP_LINK_STATUS_SIZE])
> > +{
> > +     drm_dp_dpcd_read(&mtk_dp->aux, DP_LANE0_1_STATUS, link_status,
> > +                      DP_LINK_STATUS_SIZE);
> > +}
> > +
> > +static int mtk_dp_train_flow(struct mtk_dp *mtk_dp, int target_link_rate,
> > +                          int target_lane_count)
> > +{
> > +     u8 link_status[DP_LINK_STATUS_SIZE] = {};
> > +     u8 lane_adjust[2] = {};
> > +     bool pass_tps1 = false;
> > +     bool pass_tps2_3 = false;
> > +     int train_retries;
> > +     int status_control;
> > +     int iteration_count;
> > +     u8 prev_lane_adjust;
> > +     u8 val;
> > +
> > +     drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET, target_link_rate);
> > +     drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> > +                        target_lane_count | DP_LANE_COUNT_ENHANCED_FRAME_EN);
> > +
> > +     if (mtk_dp->train_info.sink_ssc)
> > +             drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
> > +                                DP_SPREAD_AMP_0_5);
> > +
> > +     train_retries = 0;
> > +     status_control = 0;
> > +     iteration_count = 1;
> > +     prev_lane_adjust = 0xFF;
> > +
> > +     mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
> > +     mtk_dp_phy_configure(mtk_dp, target_link_rate, target_lane_count);
> > +
> > +     dev_dbg(mtk_dp->dev,
> > +             "Link train target_link_rate = 0x%x, target_lane_count = 0x%x\n",
> > +             target_link_rate, target_lane_count);
> > +
> > +     do {
> > +             train_retries++;
> > +             if (!mtk_dp->train_info.cable_plugged_in ||
> > +                 ((mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) !=
> > +                  0x0)) {
> > +                     return -ENODEV;
> > +             }
> > +
> > +             if (mtk_dp->train_state < MTK_DP_TRAIN_STATE_TRAINING)
> > +                     return -EAGAIN;
> > +
> > +             if (!pass_tps1) {
> > +                     mtk_dp_training_set_scramble(mtk_dp, false);
> > +
> > +                     if (status_control == 0) {
> > +                             status_control = 1;
> > +                             mtk_dp_train_set_pattern(mtk_dp, 1);
> > +                             val = DP_LINK_SCRAMBLING_DISABLE |
> > +                                   DP_TRAINING_PATTERN_1;
> > +                             drm_dp_dpcd_writeb(
> > +                                     &mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> > +                                     DP_LINK_SCRAMBLING_DISABLE |
> > +                                             DP_TRAINING_PATTERN_1);
> > +                             drm_dp_dpcd_read(&mtk_dp->aux,
> > +                                              DP_ADJUST_REQUEST_LANE0_1,
> > +                                              lane_adjust,
> > +                                              sizeof(lane_adjust));
> > +                             iteration_count++;
> > +
> > +                             mtk_dp_train_update_swing_pre(
> > +                                     mtk_dp, target_lane_count, lane_adjust);
> > +                     }
> > +
> > +                     drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
> > +                                                            mtk_dp->rx_cap);
> > +                     mtk_dp_read_link_status(mtk_dp, link_status);
> > +
> > +                     if (drm_dp_clock_recovery_ok(link_status,
> > +                                                  target_lane_count)) {
> > +                             mtk_dp->train_info.cr_done = true;
> > +                             pass_tps1 = true;
> > +                             train_retries = 0;
> > +                             iteration_count = 1;
> > +                             dev_dbg(mtk_dp->dev, "Link train CR pass\n");
> > +                     } else if (prev_lane_adjust == link_status[4]) {
> > +                             iteration_count++;
> > +                             if (prev_lane_adjust &
> > +                                 DP_ADJUST_VOLTAGE_SWING_LANE0_MASK)
> > +                                     break;
> > +                     } else {
> > +                             prev_lane_adjust = link_status[4];
> > +                     }
> > +                     dev_dbg(mtk_dp->dev, "Link train CQ fail\n");
> > +             } else if (pass_tps1 && !pass_tps2_3) {
> > +                     if (status_control == 1) {
> > +                             status_control = 2;
> > +                             if (mtk_dp->train_info.tps4) {
> > +                                     mtk_dp_train_set_pattern(mtk_dp, 4);
> > +                                     val = DP_TRAINING_PATTERN_4;
> > +                             } else if (mtk_dp->train_info.tps3) {
> > +                                     mtk_dp_train_set_pattern(mtk_dp, 3);
> > +                                     val = DP_LINK_SCRAMBLING_DISABLE |
> > +                                           DP_TRAINING_PATTERN_3;
> > +                             } else {
> > +                                     mtk_dp_train_set_pattern(mtk_dp, 2);
> > +                                     val = DP_LINK_SCRAMBLING_DISABLE |
> > +                                           DP_TRAINING_PATTERN_2;
> > +                             }
> > +                             drm_dp_dpcd_writeb(&mtk_dp->aux,
> > +                                                DP_TRAINING_PATTERN_SET,
> > +                                                val);
> > +
> > +                             drm_dp_dpcd_read(&mtk_dp->aux,
> > +                                              DP_ADJUST_REQUEST_LANE0_1,
> > +                                              lane_adjust,
> > +                                              sizeof(lane_adjust));
> > +
> > +                             iteration_count++;
> > +                             mtk_dp_train_update_swing_pre(
> > +                                     mtk_dp, target_lane_count, lane_adjust);
> > +                     }
> > +
> > +                     drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
> > +                                                        mtk_dp->rx_cap);
> > +
> > +                     mtk_dp_read_link_status(mtk_dp, link_status);
> > +
> > +                     if (!drm_dp_clock_recovery_ok(link_status,
> > +                                                   target_lane_count)) {
> > +                             mtk_dp->train_info.cr_done = false;
> > +                             mtk_dp->train_info.eq_done = false;
> > +                             dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
> > +                             break;
> > +                     }
> > +
> > +                     if (drm_dp_channel_eq_ok(link_status,
> > +                                              target_lane_count)) {
> > +                             mtk_dp->train_info.eq_done = true;
> > +                             pass_tps2_3 = true;
> > +                             dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
> > +                             break;
> > +                     }
> > +
> > +                     if (prev_lane_adjust == link_status[4])
> > +                             iteration_count++;
> > +                     else
> > +                             prev_lane_adjust = link_status[4];
> > +             }
> > +
> > +             drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
> > +                              lane_adjust, sizeof(lane_adjust));
> > +             mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
> > +                                           lane_adjust);
> > +     } while (train_retries < MTK_DP_TRAIN_RETRY_LIMIT &&
> > +              iteration_count < MTK_DP_TRAIN_MAX_ITERATIONS);
> > +
> > +     drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> > +                        DP_TRAINING_PATTERN_DISABLE);
> > +     mtk_dp_train_set_pattern(mtk_dp, 0);
> > +
> > +     if (pass_tps2_3) {
> > +             mtk_dp->train_info.link_rate = target_link_rate;
> > +             mtk_dp->train_info.lane_count = target_lane_count;
> > +
> > +             mtk_dp_training_set_scramble(mtk_dp, true);
> > +
> > +             drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> > +                                target_lane_count |
> > +                                        DP_LANE_COUNT_ENHANCED_FRAME_EN);
> > +             mtk_dp_set_enhanced_frame_mode(mtk_dp, true);
> > +
> > +             return 0;
> > +     }
> > +
> > +     return -ETIMEDOUT;
> > +}
> > +
> > +static bool mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
> > +{
> > +     u8 buf[DP_RECEIVER_CAP_SIZE] = {};
> > +     u8 val;
> > +     struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
> > +
> > +     if (!mtk_dp_plug_state(mtk_dp))
> > +             return false;
> > +
> > +     drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> > +     usleep_range(2000, 5000);
> > +
> > +     drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
> > +
> > +     memcpy(mtk_dp->rx_cap, buf, min(sizeof(mtk_dp->rx_cap), sizeof(buf)));
> > +     mtk_dp->rx_cap[DP_TRAINING_AUX_RD_INTERVAL] &= DP_TRAINING_AUX_RD_MASK;
> > +
> > +     train_info->link_rate =
> > +             min_t(int, MTK_DP_MAX_LINK_RATE, buf[DP_MAX_LINK_RATE]);
> > +     train_info->lane_count =
> > +             min_t(int, MTK_DP_MAX_LANES, drm_dp_max_lane_count(buf));
> > +
> > +     train_info->tps3 = drm_dp_tps3_supported(buf);
> > +     train_info->tps4 = drm_dp_tps4_supported(buf);
> > +
> > +     train_info->sink_ssc =
> > +             !!(buf[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5);
> > +
> > +     train_info->sink_ssc = false;
> > +
> > +     drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
> > +     if (val & DP_MST_CAP) {
> > +             // Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0
> > +             drm_dp_dpcd_readb(&mtk_dp->aux,
> > +                               DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0, &val);
> > +             if (val)
> > +                     drm_dp_dpcd_writeb(&mtk_dp->aux,
> > +                                        DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
> > +                                        val);
> > +     }
> > +
> > +     return true;
> > +}
> > +
> > +static int mtk_dp_edid_parse_audio_capabilities(struct mtk_dp *mtk_dp,
> > +                                             struct mtk_dp_audio_cfg *cfg)
> > +{
> > +     struct cea_sad *sads;
> > +     int sad_count;
> > +     int i;
> > +     int ret = 0;
> > +
> > +     mutex_lock(&mtk_dp->edid_lock);
> > +     if (!mtk_dp->edid) {
> > +             mutex_unlock(&mtk_dp->edid_lock);
> > +             dev_err(mtk_dp->dev, "EDID not found!\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     sad_count = drm_edid_to_sad(mtk_dp->edid, &sads);
> > +     mutex_unlock(&mtk_dp->edid_lock);
> > +     if (sad_count <= 0) {
> > +             drm_info(mtk_dp->drm_dev, "The SADs is NULL\n");
> > +             return 0;
> > +     }
> > +
> > +     for (i = 0; i < sad_count; i++) {
> > +             int sample_rate;
> > +             int word_length;
> > +             // Only PCM supported at the moment
> > +             if (sads[i].format != HDMI_AUDIO_CODING_TYPE_PCM)
> > +                     continue;
> > +
> > +             sample_rate = drm_cea_sad_get_sample_rate(&sads[i]);
> > +             word_length =
> > +                     drm_cea_sad_get_uncompressed_word_length(&sads[i]);
> > +             if (sample_rate <= 0 || word_length <= 0)
> > +                     continue;
> > +
> > +             cfg->channels = sads[i].channels;
> > +             cfg->word_length_bits = word_length;
> > +             cfg->sample_rate = sample_rate;
> > +             ret = 1;
> > +             break;
> > +     }
> > +     kfree(sads);
> > +
> > +     return ret;
> > +}
> > +
> > +static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
> > +{
> > +     phy_reset(mtk_dp->phy);
> > +     mtk_dp_reset_swing_pre_emphasis(mtk_dp);
> > +
> > +     usleep_range(2000, 5000);
> > +}
> > +
> > +static int mtk_dp_train_start(struct mtk_dp *mtk_dp)
> > +{
> > +     int ret = 0;
> > +     int lane_count;
> > +     int link_rate;
> > +     int train_limit;
> > +     int max_link_rate;
> > +     int plug_wait;
> > +
> > +     for (plug_wait = 7; !mtk_dp_plug_state(mtk_dp) && plug_wait > 0;
> > +          --plug_wait)
> > +             usleep_range(1000, 5000);
> > +     if (plug_wait == 0) {
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> > +             return -ENODEV;
> > +     }
> > +
> > +     link_rate = mtk_dp->rx_cap[1];
> > +     lane_count = mtk_dp->rx_cap[2] & 0x1F;
> > +
> > +     mtk_dp->train_info.link_rate = min(MTK_DP_MAX_LINK_RATE, link_rate);
> > +     mtk_dp->train_info.lane_count = min(MTK_DP_MAX_LANES, lane_count);
> > +     link_rate = mtk_dp->train_info.link_rate;
> > +     lane_count = mtk_dp->train_info.lane_count;
> > +
> > +     switch (link_rate) {
> > +     case MTK_DP_LINKRATE_RBR:
> > +     case MTK_DP_LINKRATE_HBR:
> > +     case MTK_DP_LINKRATE_HBR2:
> > +     case MTK_DP_LINKRATE_HBR25:
> > +     case MTK_DP_LINKRATE_HBR3:
> > +             break;
> > +     default:
> > +             mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR3;
> > +             break;
> > +     };
> > +
> > +     max_link_rate = link_rate;
> > +     for (train_limit = 0; train_limit <= 6; ++train_limit) {
> > +             mtk_dp->train_info.cr_done = false;
> > +             mtk_dp->train_info.eq_done = false;
> > +
> > +             mtk_dp_train_change_mode(mtk_dp);
> > +             ret = mtk_dp_train_flow(mtk_dp, link_rate, lane_count);
> > +             if (ret == -ENODEV || ret == -EAGAIN)
> > +                     return ret;
> > +
> > +             if (!mtk_dp->train_info.cr_done) {
> > +                     switch (link_rate) {
> > +                     case MTK_DP_LINKRATE_RBR:
> > +                             lane_count = lane_count / 2;
> > +                             link_rate = max_link_rate;
> > +                             if (lane_count == 0x0) {
> > +                                     mtk_dp->train_state =
> > +                                             MTK_DP_TRAIN_STATE_DPIDLE;
> > +                                     return -EIO;
> > +                             }
> > +                             break;
> > +                     case MTK_DP_LINKRATE_HBR:
> > +                             link_rate = MTK_DP_LINKRATE_RBR;
> > +                             break;
> > +                     case MTK_DP_LINKRATE_HBR2:
> > +                             link_rate = MTK_DP_LINKRATE_HBR;
> > +                             break;
> > +                     case MTK_DP_LINKRATE_HBR3:
> > +                             link_rate = MTK_DP_LINKRATE_HBR2;
> > +                             break;
> > +                     default:
> > +                             return -EINVAL;
> > +                     };
> > +             } else if (!mtk_dp->train_info.eq_done) {
> > +                     lane_count /= 2;
> > +                     if (lane_count == 0)
> > +                             return -EIO;
> > +             } else {
> > +                     return 0;
> > +             }
> > +     }
> > +
> > +     return -ETIMEDOUT;
> > +}
> > +
> > +static int mtk_dp_train_handler(struct mtk_dp *mtk_dp)
> > +{
> > +     int ret = 0;
> > +
> > +     if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
> > +             return ret;
> > +
> > +     switch (mtk_dp->train_state) {
> > +     case MTK_DP_TRAIN_STATE_STARTUP:
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> > +             break;
> > +
> > +     case MTK_DP_TRAIN_STATE_CHECKCAP:
> > +             if (mtk_dp_parse_capabilities(mtk_dp)) {
> > +                     mtk_dp->train_info.check_cap_count = 0;
> > +                     mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
> > +             } else {
> > +                     mtk_dp->train_info.check_cap_count++;
> > +
> > +                     if (mtk_dp->train_info.check_cap_count >
> > +                         MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT) {
> > +                             mtk_dp->train_info.check_cap_count = 0;
> > +                             mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> > +                             ret = -ETIMEDOUT;
> > +                     }
> > +             }
> > +             break;
> > +
> > +     case MTK_DP_TRAIN_STATE_CHECKEDID: {
> > +             int caps_found = mtk_dp_edid_parse_audio_capabilities(
> > +                     mtk_dp, &mtk_dp->info.audio_caps);
> > +             mtk_dp->audio_enable = caps_found > 0;
> > +             if (!mtk_dp->audio_enable)
> > +                     memset(&mtk_dp->info.audio_caps, 0,
> > +                            sizeof(mtk_dp->info.audio_caps));
> > +     }
> > +
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
> > +             break;
> > +
> > +     case MTK_DP_TRAIN_STATE_TRAINING_PRE:
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING;
> > +             break;
> > +
> > +     case MTK_DP_TRAIN_STATE_TRAINING:
> > +             ret = mtk_dp_train_start(mtk_dp);
> > +             if (!ret) {
> > +                     mtk_dp_video_mute(mtk_dp, true);
> > +                     mtk_dp_audio_mute(mtk_dp, true);
> > +                     mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKTIMING;
> > +                     mtk_dp_fec_enable(mtk_dp, mtk_dp->has_fec);
> > +             } else if (ret != -EAGAIN) {
> > +                     mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> > +             }
> > +
> > +             ret = 0;
> > +             break;
> > +
> > +     case MTK_DP_TRAIN_STATE_CHECKTIMING:
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_NORMAL;
> > +             break;
> > +     case MTK_DP_TRAIN_STATE_NORMAL:
> > +             break;
> > +     case MTK_DP_TRAIN_STATE_POWERSAVE:
> > +             break;
> > +     case MTK_DP_TRAIN_STATE_DPIDLE:
> > +             break;
> > +     default:
> > +             break;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     if (enable) {
> > +             mtk_dp_set_tx_out(mtk_dp);
> > +             mtk_dp_video_mute(mtk_dp, false);
> > +     } else {
> > +             mtk_dp_video_mute(mtk_dp, true);
> > +     }
> > +}
> > +
> > +static void mtk_dp_audio_sdp_setup(struct mtk_dp *mtk_dp,
> > +                                struct mtk_dp_audio_cfg *cfg)
> > +{
> > +     struct mtk_dp_sdp_packet packet;
> > +     struct hdmi_audio_infoframe frame;
> > +
> > +     hdmi_audio_infoframe_init(&frame);
> > +     frame.coding_type = HDMI_AUDIO_CODING_TYPE_PCM;
> > +     frame.channels = cfg->channels;
> > +     frame.sample_frequency = cfg->sample_rate;
> > +
> > +     switch (cfg->word_length_bits) {
> > +     case 16:
> > +             frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
> > +             break;
> > +     case 20:
> > +             frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_20;
> > +             break;
> > +     case 24:
> > +     default:
> > +             frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_24;
> > +             break;
> > +     }
> > +
> > +     packet.type = MTK_DP_SDP_AUI;
> > +     hdmi_audio_infoframe_pack_for_dp(&frame, &packet.sdp,
> > +                                      MTK_DP_DP_VERSION_11);
> > +
> > +     mtk_dp_audio_sdp_asp_set_channels(mtk_dp, cfg->channels);
> > +     mtk_dp_setup_sdp(mtk_dp, &packet);
> > +}
> > +
> > +static void mtk_dp_audio_setup(struct mtk_dp *mtk_dp,
> > +                            struct mtk_dp_audio_cfg *cfg)
> > +{
> > +     mtk_dp_audio_sdp_setup(mtk_dp, cfg);
> > +     mtk_dp_audio_channel_status_set(mtk_dp, cfg);
> > +
> > +     mtk_dp_audio_setup_channels(mtk_dp, cfg);
> > +     mtk_dp_audio_set_divider(mtk_dp);
> > +}
> > +
> > +static void mtk_dp_video_config(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_mn_overwrite_disable(mtk_dp);
> > +
> > +     mtk_dp_set_msa(mtk_dp);
> > +
> > +     mtk_dp_set_color_depth(mtk_dp, mtk_dp->info.depth);
> > +     mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
> > +}
> > +
> > +static int mtk_dp_state_handler(struct mtk_dp *mtk_dp)
> > +{
> > +     int ret = 0;
> > +
> > +     switch (mtk_dp->state) {
> > +     case MTK_DP_STATE_INITIAL:
> > +             mtk_dp_video_mute(mtk_dp, true);
> > +             mtk_dp_audio_mute(mtk_dp, true);
> > +             mtk_dp->state = MTK_DP_STATE_IDLE;
> > +             break;
> > +
> > +     case MTK_DP_STATE_IDLE:
> > +             if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
> > +                     mtk_dp->state = MTK_DP_STATE_PREPARE;
> > +             break;
> > +
> > +     case MTK_DP_STATE_PREPARE:
> > +             mtk_dp_video_config(mtk_dp);
> > +             mtk_dp_video_enable(mtk_dp, true);
> > +
> > +             if (mtk_dp->audio_enable) {
> > +                     mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_caps);
> > +                     mtk_dp_audio_mute(mtk_dp, false);
> > +             }
> > +
> > +             mtk_dp->state = MTK_DP_STATE_NORMAL;
> > +             break;
> > +
> > +     case MTK_DP_STATE_NORMAL:
> > +             if (mtk_dp->train_state != MTK_DP_TRAIN_STATE_NORMAL) {
> > +                     mtk_dp_video_mute(mtk_dp, true);
> > +                     mtk_dp_audio_mute(mtk_dp, true);
> > +                     mtk_dp_sdp_stop_sending(mtk_dp);
> > +                     mtk_dp->state = MTK_DP_STATE_IDLE;
> > +             }
> > +             break;
> > +
> > +     default:
> > +             break;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_set_idle_pattern(mtk_dp, true);
> > +     mtk_dp_initialize_priv_data(mtk_dp);
> > +
> > +     mtk_dp_initialize_settings(mtk_dp);
> > +     mtk_dp_initialize_aux_settings(mtk_dp);
> > +     mtk_dp_initialize_digital_settings(mtk_dp);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
> > +                        BIT(RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT),
> > +                        RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK);
> > +     mtk_dp_initialize_hpd_detect_settings(mtk_dp);
> > +
> > +     mtk_dp_digital_sw_reset(mtk_dp);
> > +}
> > +
> > +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
> > +{
> > +     struct mtk_dp *mtk_dp = dev;
> > +     int event;
> > +     u8 buf[DP_RECEIVER_CAP_SIZE] = {};
> > +
> > +     event = mtk_dp_plug_state(mtk_dp) ? connector_status_connected :
> > +                                               connector_status_disconnected;
> > +
> > +     if (event < 0)
> > +             return IRQ_HANDLED;
> > +
> > +     if (mtk_dp->drm_dev) {
> > +             dev_info(mtk_dp->dev, "drm_helper_hpd_irq_event\n");
> > +             drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
> > +     }
> > +
> > +     if (mtk_dp->train_info.cable_state_change) {
> > +             mtk_dp->train_info.cable_state_change = false;
> > +
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > +
> > +             if (!mtk_dp->train_info.cable_plugged_in ||
> > +                 !mtk_dp_plug_state(mtk_dp)) {
> > +                     mtk_dp_video_mute(mtk_dp, true);
> > +                     mtk_dp_audio_mute(mtk_dp, true);
> > +
> > +                     mtk_dp_initialize_priv_data(mtk_dp);
> > +                     mtk_dp_set_idle_pattern(mtk_dp, true);
> > +                     if (mtk_dp->has_fec)
> > +                             mtk_dp_fec_enable(mtk_dp, false);
> > +                     mtk_dp_sdp_stop_sending(mtk_dp);
> > +
> > +                     mtk_dp_edid_free(mtk_dp);
> > +                     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> > +                                        DP_PWR_STATE_BANDGAP_TPLL,
> > +                                        DP_PWR_STATE_MASK);
> > +             } else {
> > +                     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> > +                                        DP_PWR_STATE_BANDGAP_TPLL_LANE,
> > +                                        DP_PWR_STATE_MASK);
> > +                     drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
> > +                     mtk_dp->train_info.link_rate =
> > +                             min_t(int, MTK_DP_MAX_LINK_RATE,
> > +                                   buf[DP_MAX_LINK_RATE]);
> > +                     mtk_dp->train_info.lane_count =
> > +                             min_t(int, MTK_DP_MAX_LANES,
> > +                                   drm_dp_max_lane_count(buf));
> > +             }
> > +     }
> > +
> > +     if (mtk_dp->train_info.irq_status & MTK_DP_HPD_INTERRUPT) {
> > +             dev_info(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n");
> > +             mtk_dp->train_info.irq_status &= ~MTK_DP_HPD_INTERRUPT;
> > +             mtk_dp_hpd_sink_event(mtk_dp);
> > +     }
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t mtk_dp_hpd_isr_handler(struct mtk_dp *mtk_dp)
> > +{
> > +     bool connected;
> > +     u16 swirq_status = mtk_dp_swirq_get_clear(mtk_dp);
> > +     u8 hwirq_status = mtk_dp_hwirq_get_clear(mtk_dp);
> > +     struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
> > +
> > +     train_info->irq_status |= hwirq_status | swirq_status;
> > +
> > +     if (!train_info->irq_status)
> > +             return IRQ_HANDLED;
> > +
> > +     connected = mtk_dp_plug_state(mtk_dp);
> > +     if (connected || !train_info->cable_plugged_in)
> > +             train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
> > +     else if (!connected || train_info->cable_plugged_in)
> > +             train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
> > +
> > +     if (!(train_info->irq_status &
> > +           (MTK_DP_HPD_CONNECT | MTK_DP_HPD_DISCONNECT)))
> > +             return IRQ_HANDLED;
> > +
> > +     if (train_info->irq_status & MTK_DP_HPD_CONNECT) {
> > +             train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
> > +             train_info->cable_plugged_in = true;
> > +     } else {
> > +             train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
> > +             train_info->cable_plugged_in = false;
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > +     }
> > +     train_info->cable_state_change = true;
> > +     return IRQ_WAKE_THREAD;
> > +}
> > +
> > +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
> > +{
> > +     struct mtk_dp *mtk_dp = dev;
> > +     u32 irq_status;
> > +
> > +     irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS);
> > +
> > +     if (!irq_status)
> > +             return IRQ_HANDLED;
> > +
> > +     if (irq_status & RGS_IRQ_STATUS_TRANSMITTER)
> > +             return mtk_dp_hpd_isr_handler(mtk_dp);
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static int mtk_dp_dt_parse_pdata(struct mtk_dp *mtk_dp,
> > +                              struct platform_device *pdev)
> > +{
> > +     struct device *dev = &pdev->dev;
> > +     int ret = 0;
> > +     void __iomem *base;
> > +
> > +     base = devm_platform_ioremap_resource(pdev, 0);
> > +     if (IS_ERR(base))
> > +             return PTR_ERR(base);
> > +
> > +     mtk_dp->regs = devm_regmap_init_mmio(dev, base, &mtk_dp_regmap_config);
> > +     if (IS_ERR(mtk_dp->regs))
> > +             return PTR_ERR(mtk_dp->regs);
> > +
> > +     mtk_dp->dp_tx_clk = devm_clk_get(dev, "faxi");
> > +     if (IS_ERR(mtk_dp->dp_tx_clk)) {
> > +             ret = PTR_ERR(mtk_dp->dp_tx_clk);
> > +             dev_err(dev, "Failed to get dptx clock: %d\n", ret);
> > +             mtk_dp->dp_tx_clk = NULL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void mtk_dp_update_plugged_status(struct mtk_dp *mtk_dp)
> > +{
> > +     bool connected, has_audio;
> > +
> > +     mutex_lock(&mtk_dp->update_plugged_status_lock);
> > +     connected = mtk_dp_plug_state(mtk_dp);
> > +     has_audio = drm_detect_monitor_audio(mtk_dp->edid);
> > +     if (mtk_dp->plugged_cb && mtk_dp->codec_dev)
> > +             mtk_dp->plugged_cb(mtk_dp->codec_dev, connected & has_audio);
> > +     mutex_unlock(&mtk_dp->update_plugged_status_lock);
> > +}
> > +
> > +static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     enum drm_connector_status ret = connector_status_disconnected;
> > +     u8 sink_count = 0;
> > +
> > +     if (mtk_dp_is_edp(mtk_dp))
> > +             return connector_status_connected;
> > +
> > +     if (mtk_dp_plug_state(mtk_dp)) {
> > +             drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
> > +             if (DP_GET_SINK_COUNT(sink_count))
> > +                     ret = connector_status_connected;
> > +     }
> > +
> > +     mtk_dp_update_plugged_status(mtk_dp);
> > +     return ret;
> > +}
> > +
> > +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> > +                                 struct drm_connector *connector)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     bool enabled = mtk_dp->enabled;
> > +     struct edid *new_edid = NULL;
> > +
> > +     if (!enabled)
> > +             drm_bridge_chain_pre_enable(bridge);
> > +
> > +     drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> > +     usleep_range(2000, 5000);
> > +
> > +     if (mtk_dp_plug_state(mtk_dp))
> > +             new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> > +
> > +     if (!enabled)
> > +             drm_bridge_chain_post_disable(bridge);
> > +
> > +     mutex_lock(&mtk_dp->edid_lock);
> > +     kfree(mtk_dp->edid);
> > +     mtk_dp->edid = NULL;
> > +
> > +     if (!new_edid) {
> > +             mutex_unlock(&mtk_dp->edid_lock);
> > +             return NULL;
> > +     }
> > +
> > +     mtk_dp->edid = drm_edid_duplicate(new_edid);
> > +     mutex_unlock(&mtk_dp->edid_lock);
> > +
> > +     return new_edid;
> > +}
> > +
> > +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> > +                                struct drm_dp_aux_msg *msg)
> > +{
> > +     ssize_t err = -EAGAIN;
> > +     struct mtk_dp *mtk_dp;
> > +     bool is_read;
> > +     u8 request;
> > +     size_t accessed_bytes = 0;
> > +     int retry = 3, ret = 0;
> > +
> > +     mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> > +
> > +     if (!mtk_dp->train_info.cable_plugged_in ||
> > +         mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> > +             err = -EAGAIN;
> > +             goto err;
> > +     }
> > +
> > +     switch (msg->request) {
> > +     case DP_AUX_I2C_MOT:
> > +     case DP_AUX_I2C_WRITE:
> > +     case DP_AUX_NATIVE_WRITE:
> > +     case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> > +     case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> > +             request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> > +             is_read = false;
> > +             break;
> > +     case DP_AUX_I2C_READ:
> > +     case DP_AUX_NATIVE_READ:
> > +     case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> > +             request = msg->request;
> > +             is_read = true;
> > +             break;
> > +     default:
> > +             drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> > +                     msg->request);
> > +             err = -EINVAL;
> > +             goto err;
> > +     }
> > +
> > +     if (msg->size == 0) {
> > +             mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> > +                                    msg->address + accessed_bytes,
> > +                                    msg->buffer + accessed_bytes, 0);
> > +     } else {
> > +             while (accessed_bytes < msg->size) {
> > +                     size_t to_access =
> > +                             min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> > +                                   msg->size - accessed_bytes);
> > +                     retry = 3;
> > +                     while (retry--) {
> > +                             ret = mtk_dp_aux_do_transfer(
> > +                                     mtk_dp, is_read, request,
> > +                                     msg->address + accessed_bytes,
> > +                                     msg->buffer + accessed_bytes,
> > +                                     to_access);
> > +                             if (ret == 0)
> > +                                     break;
> > +                             udelay(50);
> > +                     }
> > +                     if (!retry && ret) {
> > +                             drm_info(mtk_dp->drm_dev,
> > +                                      "Failed to do AUX transfer: %d\n",
> > +                                      ret);
> > +                             break;
> > +                     }
> > +                     accessed_bytes += to_access;
> > +             }
> > +     }
> > +err:
> > +     if (!ret) {
> > +             msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > +             ret = msg->size;
> > +     } else {
> > +             msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> > +             return err;
> > +     }
> > +
> > +     msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > +     return msg->size;
> > +}
> > +
> > +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> > +{
> > +     drm_dp_aux_init(&mtk_dp->aux);
> > +     mtk_dp->aux.name = "aux_mtk_dp";
> > +     mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> > +}
> > +
> > +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> > +{
> > +     mutex_lock(&mtk_dp->dp_lock);
> > +
> > +     mtk_dp_hwirq_enable(mtk_dp, false);
> > +     mtk_dp_power_disable(mtk_dp);
> > +     phy_exit(mtk_dp->phy);
> > +     clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > +
> > +     mutex_unlock(&mtk_dp->dp_lock);
> > +}
> > +
> > +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> > +{
> > +     int ret = 0;
> > +
> > +     mutex_lock(&mtk_dp->dp_lock);
> > +
> > +     ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> > +     if (ret < 0) {
> > +             dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> > +             goto err;
> > +     }
> > +     ret = phy_init(mtk_dp->phy);
> > +     if (ret) {
> > +             dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> > +             goto err_phy_init;
> > +     }
> > +     ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> > +     if (ret) {
> > +             dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> > +             goto err_phy_config;
> > +     }
> > +
> > +     mtk_dp_init_port(mtk_dp);
> > +     mtk_dp_power_enable(mtk_dp);
> > +     mtk_dp_hwirq_enable(mtk_dp, true);
> > +
> > +err_phy_config:
> > +     phy_exit(mtk_dp->phy);
> > +err_phy_init:
> > +     clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > +err:
> > +     mutex_unlock(&mtk_dp->dp_lock);
> > +     return ret;
> > +}
> > +
> > +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> > +                             enum drm_bridge_attach_flags flags)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     int ret;
> > +
> > +     if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> > +             dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> > +             return -EINVAL;
> > +     }
> > +
> > +     ret = mtk_dp_poweron(mtk_dp);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (mtk_dp->next_bridge) {
> > +             ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> > +                                     &mtk_dp->bridge, flags);
> > +             if (ret) {
> > +                     drm_warn(mtk_dp->drm_dev,
> > +                              "Failed to attach external bridge: %d\n", ret);
> > +                     goto err_bridge_attach;
> > +             }
> > +     }
> > +
> > +     mtk_dp->drm_dev = bridge->dev;
> > +
> > +     return 0;
> > +
> > +err_bridge_attach:
> > +     mtk_dp_poweroff(mtk_dp);
> > +     return ret;
> > +}
> > +
> > +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +
> > +     mtk_dp->drm_dev = NULL;
> > +
> > +     mtk_dp_poweroff(mtk_dp);
> > +}
> > +
> > +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> > +                                      struct drm_bridge_state *old_state)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +
> > +     mtk_dp_video_mute(mtk_dp, true);
> > +     mtk_dp_audio_mute(mtk_dp, true);
> > +     mtk_dp->state = MTK_DP_STATE_IDLE;
> > +     mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > +
> > +     mtk_dp->enabled = false;
> > +     msleep(100);
> > +}
> > +
> > +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> > +                                       struct drm_display_mode *mode)
> > +{
> > +     struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> > +
> > +     drm_display_mode_to_videomode(mode, &timings->vm);
> > +     timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> > +     timings->htotal = mode->htotal;
> > +     timings->vtotal = mode->vtotal;
> > +}
> > +
> > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > +                                     struct drm_bridge_state *old_state)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     struct drm_connector *conn;
> > +     struct drm_connector_state *conn_state;
> > +     struct drm_crtc *crtc;
> > +     struct drm_crtc_state *crtc_state;
> > +     int ret = 0;
> > +     int i;
> > +
> > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > +                                                     bridge->encoder);
> > +     if (!conn) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Can't enable bridge as connector is missing\n");
> > +             return;
> > +     }
> > +
> > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
>
> This should be protected by a mutex (just like any resource shared
> between KMS and ALSA)
>
Ok.
> > +     conn_state =
> > +             drm_atomic_get_new_connector_state(old_state->base.state, conn);
> > +     if (!conn_state) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Can't enable bridge as connector state is missing\n");
> > +             return;
> > +     }
> > +
> > +     crtc = conn_state->crtc;
> > +     if (!crtc) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Can't enable bridge as connector state doesn't have a crtc\n");
> > +             return;
> > +     }
> > +
> > +     crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> > +     if (!crtc_state) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Can't enable bridge as crtc state is missing\n");
> > +             return;
> > +     }
> > +
> > +     mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);
> > +     if (!mtk_dp_parse_capabilities(mtk_dp)) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Can't enable bridge as nothing is plugged in\n");
> > +             return;
> > +     }
> > +
> > +     //training
> > +     for (i = 0; i < 50; i++) {
> > +             ret = mtk_dp_train_handler(mtk_dp);
> > +             if (ret) {
> > +                     drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> > +                             ret);
> > +                     return;
> > +             }
> > +
> > +             ret = mtk_dp_state_handler(mtk_dp);
> > +             if (ret) {
> > +                     drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> > +                             ret);
> > +                     return;
> > +             }
> > +     }
> > +
> > +     mtk_dp->enabled = true;
> > +     mtk_dp_update_plugged_status(mtk_dp);
> > +}
> > +
> > +static enum drm_mode_status
> > +mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
> > +                      const struct drm_display_info *info,
> > +                      const struct drm_display_mode *mode)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     u32 rx_linkrate;
> > +     u32 bpp;
> > +
> > +     bpp = info->color_formats & DRM_COLOR_FORMAT_YCRCB422 ? 16 : 24;
> > +     rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
> > +     if (rx_linkrate * mtk_dp->train_info.lane_count < mode->clock * bpp / 8)
> > +             return MODE_CLOCK_HIGH;
> > +
> > +     if (mode->clock > 600000)
> > +             return MODE_CLOCK_HIGH;
> > +
> > +     if ((mode->clock * 1000) / (mode->htotal * mode->vtotal) > 62)
> > +             return MODE_CLOCK_HIGH;
> > +
> > +     return MODE_OK;
> > +}
> > +
> > +static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(
> > +     struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
> > +     struct drm_crtc_state *crtc_state,
> > +     struct drm_connector_state *conn_state, unsigned int *num_output_fmts)
> > +{
> > +     u32 *output_fmts;
> > +
> > +     *num_output_fmts = 0;
> > +     output_fmts = kcalloc(1, sizeof(*output_fmts), GFP_KERNEL);
> > +     if (!output_fmts)
> > +             return NULL;
> > +     *num_output_fmts = 1;
> > +     output_fmts[0] = MEDIA_BUS_FMT_FIXED;
> > +     return output_fmts;
> > +}
> > +
> > +static const u32 mt8195_input_fmts[] = {
> > +     MEDIA_BUS_FMT_RGB888_1X24,
> > +     MEDIA_BUS_FMT_YUV8_1X24,
> > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > +};
> > +
> > +static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(
> > +     struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
> > +     struct drm_crtc_state *crtc_state,
> > +     struct drm_connector_state *conn_state, u32 output_fmt,
> > +     unsigned int *num_input_fmts)
> > +{
> > +     u32 *input_fmts;
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> > +     struct drm_display_info *display_info =
> > +             &conn_state->connector->display_info;
> > +     u32 rx_linkrate;
> > +     u32 bpp;
> > +
> > +     bpp = (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422) ? 16 :
> > +                                                                             24;
> > +     rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
> > +     *num_input_fmts = 0;
> > +     input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts), sizeof(*input_fmts),
> > +                          GFP_KERNEL);
> > +     if (!input_fmts)
> > +             return NULL;
> > +
> > +     *num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
> > +
> > +     memcpy(input_fmts, mt8195_input_fmts,
> > +            sizeof(*input_fmts) * ARRAY_SIZE(mt8195_input_fmts));
> > +
> > +     if (((rx_linkrate * mtk_dp->train_info.lane_count) <
> > +          (mode->clock * 24 / 8)) &&
> > +         ((rx_linkrate * mtk_dp->train_info.lane_count) >
> > +          (mode->clock * 16 / 8)) &&
> > +         (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422)) {
> > +             kfree(input_fmts);
> > +             input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL);
> > +             *num_input_fmts = 1;
> > +             input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
> > +     }
> > +
> > +     return input_fmts;
> > +}
> > +
> > +static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
> > +                                   struct drm_bridge_state *bridge_state,
> > +                                   struct drm_crtc_state *crtc_state,
> > +                                   struct drm_connector_state *conn_state)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     unsigned int input_bus_format;
> > +
> > +     input_bus_format = bridge_state->input_bus_cfg.format;
> > +
> > +     dev_info(mtk_dp->dev, "input format 0x%04x, output format 0x%04x\n",
> > +              bridge_state->input_bus_cfg.format,
> > +              bridge_state->output_bus_cfg.format);
> > +
> > +     mtk_dp->input_fmt = input_bus_format;
> > +     if (mtk_dp->input_fmt == MEDIA_BUS_FMT_YUYV8_1X16)
> > +             mtk_dp->info.format = MTK_DP_COLOR_FORMAT_YUV_422;
> > +     else
> > +             mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
> > +     .atomic_check = mtk_dp_bridge_atomic_check,
> > +     .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> > +     .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> > +     .atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts,
> > +     .atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts,
> > +     .atomic_reset = drm_atomic_helper_bridge_reset,
> > +     .attach = mtk_dp_bridge_attach,
> > +     .detach = mtk_dp_bridge_detach,
> > +     .atomic_enable = mtk_dp_bridge_atomic_enable,
> > +     .atomic_disable = mtk_dp_bridge_atomic_disable,
> > +     .mode_valid = mtk_dp_bridge_mode_valid,
> > +     .get_edid = mtk_dp_get_edid,
> > +     .detect = mtk_dp_bdg_detect,
> > +};
> > +
> > +/*
> > + * HDMI audio codec callbacks
> > + */
> > +static int mtk_dp_audio_hw_params(struct device *dev, void *data,
> > +                               struct hdmi_codec_daifmt *daifmt,
> > +                               struct hdmi_codec_params *params)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +     struct mtk_dp_audio_cfg cfg;
> > +
> > +     if (!mtk_dp->enabled) {
> > +             pr_err("%s, DP is not ready!\n", __func__);
> > +             return -ENODEV;
> > +     }
> > +
> > +     cfg.channels = params->cea.channels;
> > +     cfg.sample_rate = params->sample_rate;
> > +     cfg.word_length_bits = 24;
> > +
> > +     mtk_dp_audio_setup(mtk_dp, &cfg);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_audio_startup(struct device *dev, void *data)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +
> > +     mtk_dp_audio_mute(mtk_dp, false);
> > +
> > +     return 0;
> > +}
> > +
> > +static void mtk_dp_audio_shutdown(struct device *dev, void *data)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +
> > +     mtk_dp_audio_mute(mtk_dp, true);
> > +}
> > +
> > +static int mtk_dp_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
> > +                             size_t len)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +
> > +     if (mtk_dp->enabled)
> > +             memcpy(buf, mtk_dp->connector_eld, len);
> > +     else
> > +             memset(buf, 0, len);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_audio_hook_plugged_cb(struct device *dev, void *data,
> > +                                     hdmi_codec_plugged_cb fn,
> > +                                     struct device *codec_dev)
> > +{
> > +     struct mtk_dp *mtk_dp = data;
> > +
> > +     mutex_lock(&mtk_dp->update_plugged_status_lock);
> > +     mtk_dp->plugged_cb = fn;
> > +     mtk_dp->codec_dev = codec_dev;
> > +     mutex_unlock(&mtk_dp->update_plugged_status_lock);
> > +
> > +     mtk_dp_update_plugged_status(mtk_dp);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct hdmi_codec_ops mtk_dp_audio_codec_ops = {
> > +     .hw_params = mtk_dp_audio_hw_params,
> > +     .audio_startup = mtk_dp_audio_startup,
> > +     .audio_shutdown = mtk_dp_audio_shutdown,
> > +     .get_eld = mtk_dp_audio_get_eld,
> > +     .hook_plugged_cb = mtk_dp_audio_hook_plugged_cb,
> > +     .no_capture_mute = 1,
> > +};
> > +
> > +static int mtk_dp_register_audio_driver(struct device *dev)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +     struct hdmi_codec_pdata codec_data = {
> > +             .ops = &mtk_dp_audio_codec_ops,
> > +             .max_i2s_channels = 8,
> > +             .i2s = 1,
> > +             .data = mtk_dp,
> > +     };
> > +     struct platform_device *pdev;
> > +
> > +     pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
> > +                                          PLATFORM_DEVID_AUTO, &codec_data,
> > +                                          sizeof(codec_data));
> > +     if (IS_ERR(pdev))
> > +             return PTR_ERR(pdev);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_probe(struct platform_device *pdev)
> > +{
> > +     struct mtk_dp *mtk_dp;
> > +     struct device *dev = &pdev->dev;
> > +     int ret;
> > +     int irq_num = 0;
> > +     struct drm_panel *panel = NULL;
> > +
> > +     mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> > +     if (!mtk_dp)
> > +             return -ENOMEM;
> > +
> > +     mtk_dp->dev = dev;
> > +
> > +     irq_num = platform_get_irq(pdev, 0);
> > +     if (irq_num < 0) {
> > +             dev_err(dev, "failed to request dp irq resource\n");
> > +             return -EPROBE_DEFER;
> > +     }
> > +
> > +     ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel,
> > +                                       &mtk_dp->next_bridge);
> > +     if (ret == -ENODEV) {
> > +             dev_info(
> > +                     dev,
> > +                     "No panel connected in devicetree, continuing as external DP\n");
> > +             mtk_dp->next_bridge = NULL;
> > +     } else if (ret) {
> > +             dev_err(dev, "Failed to find panel or bridge: %d\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     if (panel) {
> > +             mtk_dp->next_bridge = devm_drm_panel_bridge_add(dev, panel);
> > +             if (IS_ERR(mtk_dp->next_bridge)) {
> > +                     ret = PTR_ERR(mtk_dp->next_bridge);
> > +                     dev_err(dev, "Failed to create bridge: %d\n", ret);
> > +                     return -EPROBE_DEFER;
> > +             }
> > +     }
>
> You should be using devm_drm_of_get_bridge here
>
Ok.
> > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > +     if (ret)
> > +             return ret;
>
> pdata?
>
can you elaborate?
> > +
> > +     mtk_dp_aux_init(mtk_dp);
> > +
> > +     ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
> > +                                     mtk_dp_hpd_event_thread,
> > +                                     IRQ_TYPE_LEVEL_HIGH, dev_name(dev),
> > +                                     mtk_dp);
> > +     if (ret) {
> > +             dev_err(dev, "failed to request mediatek dptx irq\n");
> > +             return -EPROBE_DEFER;
> > +     }
> > +
> > +     mutex_init(&mtk_dp->dp_lock);
> > +     mutex_init(&mtk_dp->edid_lock);
> > +
> > +     platform_set_drvdata(pdev, mtk_dp);
> > +
> > +     if (!mtk_dp_is_edp(mtk_dp)) {
> > +             mutex_init(&mtk_dp->update_plugged_status_lock);
> > +             ret = mtk_dp_register_audio_driver(dev);
> > +             if (ret) {
> > +                     dev_err(dev, "Failed to register audio driver: %d\n",
> > +                             ret);
> > +                     return ret;
> > +             }
> > +     }
> > +
> > +     mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
> > +                                                     PLATFORM_DEVID_AUTO,
> > +                                                     &mtk_dp->regs,
> > +                                                     sizeof(&mtk_dp->regs));
> > +     if (IS_ERR(mtk_dp->phy_dev)) {
> > +             dev_err(dev, "Failed to create device mediatek-dp-phy: %ld\n",
> > +                     PTR_ERR(mtk_dp->phy_dev));
> > +             return PTR_ERR(mtk_dp->phy_dev);
> > +     }
> > +
> > +     mtk_dp_get_calibration_data(mtk_dp);
> > +
> > +     mtk_dp->phy = dev_get_drvdata(&mtk_dp->phy_dev->dev);
> > +     if (IS_ERR(mtk_dp->phy)) {
> > +             dev_err(dev, "Failed to get phy: %ld\n", PTR_ERR(mtk_dp->phy));
> > +             platform_device_unregister(mtk_dp->phy_dev);
> > +             return PTR_ERR(mtk_dp->phy);
> > +     }
> > +
> > +     mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
> > +     mtk_dp->bridge.of_node = dev->of_node;
> > +     if (mtk_dp_is_edp(mtk_dp))
> > +             mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> > +     else
> > +             mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
> > +
> > +     mtk_dp->bridge.ops =
> > +             DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
> > +     drm_bridge_add(&mtk_dp->bridge);
> > +
> > +     pm_runtime_enable(dev);
> > +     pm_runtime_get_sync(dev);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_remove(struct platform_device *pdev)
> > +{
> > +     struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
> > +
> > +     platform_device_unregister(mtk_dp->phy_dev);
> > +
> > +     mtk_dp_video_mute(mtk_dp, true);
> > +     mtk_dp_audio_mute(mtk_dp, true);
> > +
> > +     pm_runtime_disable(&pdev->dev);
> > +
> > +     return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int mtk_dp_suspend(struct device *dev)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +
> > +     mtk_dp_power_disable(mtk_dp);
> > +     mtk_dp_hwirq_enable(mtk_dp, false);
> > +
> > +     pm_runtime_put_sync(dev);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_resume(struct device *dev)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +
> > +     pm_runtime_get_sync(dev);
> > +
> > +     mtk_dp_init_port(mtk_dp);
> > +     mtk_dp_power_enable(mtk_dp);
> > +     mtk_dp_hwirq_enable(mtk_dp, true);
> > +
> > +     return 0;
> > +}
> > +#endif
> > +
> > +static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
> > +
> > +static const struct of_device_id mtk_dp_of_match[] = {
> > +     {
> > +             .compatible = "mediatek,mt8195-dp-tx",
> > +     },
> > +     {},
> > +};
> > +MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
> > +
> > +struct platform_driver mtk_dp_driver = {
> > +     .probe = mtk_dp_probe,
> > +     .remove = mtk_dp_remove,
> > +     .driver = {
> > +             .name = "mediatek-drm-dp",
> > +             .of_match_table = mtk_dp_of_match,
> > +             .pm = &mtk_dp_pm_ops,
> > +     },
> > +};
> > +
> > +MODULE_AUTHOR("Jason-JH.Lin <jason-jh.lin@mediatek.com>");
> > +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> > +MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> > new file mode 100644
> > index 0000000000000..79952ac30e9e6
> > --- /dev/null
> > +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> > @@ -0,0 +1,568 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (c) 2019 MediaTek Inc.
> > + * Copyright (c) 2021 BayLibre
> > + */
> > +#ifndef _MTK_DP_REG_H_
> > +#define _MTK_DP_REG_H_
> > +
> > +#define MTK_DP_SIP_CONTROL_AARCH32 0x82000523
> > +#define MTK_DP_SIP_ATF_VIDEO_UNMUTE 0x20
> > +#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE 0x21
> > +
> > +#define DP_PHY_GLB_BIAS_GEN_00 0x0000
> > +#define RG_XTP_GLB_BIAS_INTR_CTRL (0x1f << 16)
> > +
> > +#define DP_PHY_GLB_DPAUX_TX 0x0008
> > +#define RG_CKM_PT0_CKTX_IMPSEL (0xf << 20)
> > +
> > +#define DP_PHY_LANE_TX_0 0x104
> > +#define RG_XTP_LN0_TX_IMPSEL_PMOS (0xf << 12)
> > +#define RG_XTP_LN0_TX_IMPSEL_NMOS (0xf << 16)
> > +
> > +#define DP_PHY_LANE_TX_1 0x204
> > +#define RG_XTP_LN1_TX_IMPSEL_PMOS (0xf << 12)
> > +#define RG_XTP_LN1_TX_IMPSEL_NMOS (0xf << 16)
> > +
> > +#define DP_PHY_LANE_TX_2 0x304
> > +#define RG_XTP_LN2_TX_IMPSEL_PMOS (0xf << 12)
> > +#define RG_XTP_LN2_TX_IMPSEL_NMOS (0xf << 16)
> > +
> > +#define DP_PHY_LANE_TX_3 0x404
> > +#define RG_XTP_LN3_TX_IMPSEL_PMOS (0xf << 12)
> > +#define RG_XTP_LN3_TX_IMPSEL_NMOS (0xf << 16)
> > +
> > +#define TOP_OFFSET 0x2000
> > +#define ENC0_OFFSET 0x3000
> > +#define ENC1_OFFSET 0x3200
> > +#define TRANS_OFFSET 0x3400
> > +#define AUX_OFFSET 0x3600
> > +#define SEC_OFFSET 0x4000
> > +
> > +#define MTK_DP_HPD_DISCONNECT BIT(1)
> > +#define MTK_DP_HPD_CONNECT BIT(2)
> > +#define MTK_DP_HPD_INTERRUPT BIT(3)
> > +
> > +#define MTK_DP_0034 0x034
> > +#define DA_XTP_GLB_CKDET_EN_FORCE_VAL BIT(15)
> > +#define DA_XTP_GLB_CKDET_EN_FORCE_EN BIT(14)
> > +#define DA_CKM_INTCKTX_EN_FORCE_VAL BIT(13)
> > +#define DA_CKM_INTCKTX_EN_FORCE_EN BIT(12)
> > +#define DA_CKM_CKTX0_EN_FORCE_VAL BIT(11)
> > +#define DA_CKM_CKTX0_EN_FORCE_EN BIT(10)
> > +#define DA_CKM_XTAL_CK_FORCE_VAL BIT(9)
> > +#define DA_CKM_XTAL_CK_FORCE_EN BIT(8)
> > +#define DA_CKM_BIAS_LPF_EN_FORCE_VAL BIT(7)
> > +#define DA_CKM_BIAS_LPF_EN_FORCE_EN BIT(6)
> > +#define DA_CKM_BIAS_EN_FORCE_VAL BIT(5)
> > +#define DA_CKM_BIAS_EN_FORCE_EN BIT(4)
> > +#define DA_XTP_GLB_AVD10_ON_FORCE_VAL BIT(3)
> > +#define DA_XTP_GLB_AVD10_ON_FORCE BIT(2)
> > +#define DA_XTP_GLB_LDO_EN_FORCE_VAL BIT(1)
> > +#define DA_XTP_GLB_LDO_EN_FORCE_EN BIT(0)
> > +
> > +#define MTK_DP_1040 0x1040
> > +#define RG_DPAUX_RX_VALID_DEGLITCH_EN BIT(2)
> > +#define RG_XTP_GLB_CKDET_EN BIT(1)
> > +#define RG_DPAUX_RX_EN BIT(0)
> > +
> > +#define MTK_DP_ENC0_P0_3000 (ENC0_OFFSET + 0x000)
> > +#define LANE_NUM_DP_ENC0_P0_MASK 0x3
> > +#define VIDEO_MUTE_SW_DP_ENC0_P0_MASK 0x4
> > +#define VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT 2
> > +#define VIDEO_MUTE_SEL_DP_ENC0_P0_MASK 0x8
> > +#define VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT 3
> > +#define ENHANCED_FRAME_EN_DP_ENC0_P0_MASK 0x10
> > +#define ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT 4
> > +
> > +#define MTK_DP_ENC0_P0_3004 (ENC0_OFFSET + 0x004)
> > +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK 0x100
> > +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_SHIFT 8
> > +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK 0x200
> > +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT 9
> > +
> > +#define MTK_DP_ENC0_P0_3008 (ENC0_OFFSET + 0x008)
> > +#define VIDEO_M_CODE_SW_0_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_300C (ENC0_OFFSET + 0x00C)
> > +#define VIDEO_M_CODE_SW_1_DP_ENC0_P0_MASK 0xff
> > +
> > +#define MTK_DP_ENC0_P0_3010 (ENC0_OFFSET + 0x010)
> > +#define HTOTAL_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3014 (ENC0_OFFSET + 0x014)
> > +#define VTOTAL_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3018 (ENC0_OFFSET + 0x018)
> > +#define HSTART_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_301C (ENC0_OFFSET + 0x01C)
> > +#define VSTART_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3020 (ENC0_OFFSET + 0x020)
> > +#define HWIDTH_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3024 (ENC0_OFFSET + 0x024)
> > +#define VHEIGHT_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3028 (ENC0_OFFSET + 0x028)
> > +#define HSW_SW_DP_ENC0_P0_MASK 0x7fff
> > +#define HSW_SW_DP_ENC0_P0_SHIFT 0
> > +#define HSP_SW_DP_ENC0_P0_MASK 0x8000
> > +
> > +#define MTK_DP_ENC0_P0_302C (ENC0_OFFSET + 0x02C)
> > +#define VSW_SW_DP_ENC0_P0_MASK 0x7fff
> > +#define VSW_SW_DP_ENC0_P0_SHIFT 0
> > +#define VSP_SW_DP_ENC0_P0_MASK 0x8000
> > +
> > +#define MTK_DP_ENC0_P0_3030 (ENC0_OFFSET + 0x030)
> > +#define HTOTAL_SEL_DP_ENC0_P0_SHIFT 0
> > +#define VTOTAL_SEL_DP_ENC0_P0_SHIFT 1
> > +#define HSTART_SEL_DP_ENC0_P0_SHIFT 2
> > +#define VSTART_SEL_DP_ENC0_P0_SHIFT 3
> > +#define HWIDTH_SEL_DP_ENC0_P0_SHIFT 4
> > +#define VHEIGHT_SEL_DP_ENC0_P0_SHIFT 5
> > +#define HSP_SEL_DP_ENC0_P0_SHIFT 6
> > +#define HSW_SEL_DP_ENC0_P0_SHIFT 7
> > +#define VSP_SEL_DP_ENC0_P0_SHIFT 8
> > +#define VSW_SEL_DP_ENC0_P0_SHIFT 9
> > +#define VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK 0x800
> > +#define VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT 11
> > +#define VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK 0x1000
> > +#define VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT 12
> > +
> > +#define MTK_DP_ENC0_P0_3034 (ENC0_OFFSET + 0x034)
> > +
> > +#define MTK_DP_ENC0_P0_3038 (ENC0_OFFSET + 0x038)
> > +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK 0x800
> > +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_SHIFT 11
> > +
> > +#define MTK_DP_ENC0_P0_303C (ENC0_OFFSET + 0x03C)
> > +#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK 0x3f
> > +#define SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT 0
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK 0x700
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT 8
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT                                     \
> > +     (0 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT                                     \
> > +     (1 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT                                     \
> > +     (2 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT                                      \
> > +     (3 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT                                      \
> > +     (4 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> > +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK 0x7000
> > +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT 12
> > +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB                                     \
> > +     (0 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> > +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422                                \
> > +     (1 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> > +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420                                \
> > +     (2 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> > +#define VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK 0x8000
> > +#define VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT 15
> > +
> > +#define MTK_DP_ENC0_P0_3040 (ENC0_OFFSET + 0x040)
> > +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK 0xfff
> > +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT 0
> > +
> > +#define MTK_DP_ENC0_P0_3044 (ENC0_OFFSET + 0x044)
> > +#define VIDEO_N_CODE_0_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3048 (ENC0_OFFSET + 0x048)
> > +#define VIDEO_N_CODE_1_DP_ENC0_P0_MASK 0xff
> > +
> > +#define MTK_DP_ENC0_P0_304C (ENC0_OFFSET + 0x04C)
> > +#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK 0x4
> > +#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK 0x100
> > +
> > +#define MTK_DP_ENC0_P0_3050 (ENC0_OFFSET + 0x050)
> > +#define VIDEO_N_CODE_MN_GEN_0_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3054 (ENC0_OFFSET + 0x054)
> > +#define VIDEO_N_CODE_MN_GEN_1_DP_ENC0_P0_MASK 0xff
> > +
> > +#define MTK_DP_ENC0_P0_3064 (ENC0_OFFSET + 0x064)
> > +#define HDE_NUM_LAST_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3088 (ENC0_OFFSET + 0x088)
> > +#define AU_EN_DP_ENC0_P0_MASK 0x40
> > +#define AU_EN_DP_ENC0_P0_SHIFT 6
> > +#define AUDIO_8CH_EN_DP_ENC0_P0_MASK 0x80
> > +#define AUDIO_8CH_SEL_DP_ENC0_P0_MASK 0x100
> > +#define AUDIO_2CH_EN_DP_ENC0_P0_MASK 0x4000
> > +#define AUDIO_2CH_SEL_DP_ENC0_P0_MASK 0x8000
> > +
> > +#define MTK_DP_ENC0_P0_308C (ENC0_OFFSET + 0x08C)
> > +#define CH_STATUS_0_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3090 (ENC0_OFFSET + 0x090)
> > +#define CH_STATUS_1_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3094 (ENC0_OFFSET + 0x094)
> > +#define CH_STATUS_2_DP_ENC0_P0_MASK 0xff
> > +
> > +#define MTK_DP_ENC0_P0_30A0 (ENC0_OFFSET + 0x0A0)
> > +
> > +#define MTK_DP_ENC0_P0_30A4 (ENC0_OFFSET + 0x0A4)
> > +#define AU_TS_CFG_DP_ENC0_P0_MASK 0xff
> > +
> > +#define MTK_DP_ENC0_P0_30A8 (ENC0_OFFSET + 0x0A8)
> > +
> > +#define MTK_DP_ENC0_P0_30AC (ENC0_OFFSET + 0x0AC)
> > +
> > +#define MTK_DP_ENC0_P0_30B0 (ENC0_OFFSET + 0x0B0)
> > +
> > +#define MTK_DP_ENC0_P0_30B4 (ENC0_OFFSET + 0x0B4)
> > +#define ISRC_CFG_DP_ENC0_P0_MASK 0xff00
> > +#define ISRC_CFG_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC0_P0_30B8 (ENC0_OFFSET + 0x0B8)
> > +
> > +#define MTK_DP_ENC0_P0_30BC (ENC0_OFFSET + 0x0BC)
> > +#define ISRC_CONT_DP_ENC0_P0_MASK 0x1
> > +#define ISRC_CONT_DP_ENC0_P0_SHIFT 0
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK 0x700
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT 8
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_2                             \
> > +     (1 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_4                             \
> > +     (2 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_8                             \
> > +     (3 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2                             \
> > +     (5 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_4                             \
> > +     (6 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_8                             \
> > +     (7 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +
> > +#define MTK_DP_ENC0_P0_30D8 (ENC0_OFFSET + 0x0D8)
> > +
> > +#define MTK_DP_ENC0_P0_312C (ENC0_OFFSET + 0x12C)
> > +#define ASP_HB2_DP_ENC0_P0_MASK 0xff
> > +#define ASP_HB3_DP_ENC0_P0_MASK 0xff00
> > +#define ASP_HB3_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC0_P0_3130 (ENC0_OFFSET + 0x130)
> > +
> > +#define MTK_DP_ENC0_P0_3138 (ENC0_OFFSET + 0x138)
> > +
> > +#define MTK_DP_ENC0_P0_3154 (ENC0_OFFSET + 0x154)
> > +#define PGEN_HTOTAL_DP_ENC0_P0_MASK 0x3fff
> > +
> > +#define MTK_DP_ENC0_P0_3158 (ENC0_OFFSET + 0x158)
> > +#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK 0x3fff
> > +
> > +#define MTK_DP_ENC0_P0_315C (ENC0_OFFSET + 0x15C)
> > +#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x3fff
> > +
> > +#define MTK_DP_ENC0_P0_3160 (ENC0_OFFSET + 0x160)
> > +#define PGEN_HFDE_START_DP_ENC0_P0_MASK 0x3fff
> > +
> > +#define MTK_DP_ENC0_P0_3164 (ENC0_OFFSET + 0x164)
> > +#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x3fff
> > +
> > +#define MTK_DP_ENC0_P0_3168 (ENC0_OFFSET + 0x168)
> > +#define PGEN_VTOTAL_DP_ENC0_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_ENC0_P0_316C (ENC0_OFFSET + 0x16C)
> > +#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_ENC0_P0_3170 (ENC0_OFFSET + 0x170)
> > +#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_ENC0_P0_3174 (ENC0_OFFSET + 0x174)
> > +#define PGEN_VFDE_START_DP_ENC0_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_ENC0_P0_3178 (ENC0_OFFSET + 0x178)
> > +#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_ENC0_P0_31B0 (ENC0_OFFSET + 0x1B0)
> > +#define PGEN_PATTERN_SEL_SHIFT 4
> > +#define PGEN_PATTERN_SEL_MASK 0x0070
> > +
> > +#define MTK_DP_ENC0_P0_31C8 (ENC0_OFFSET + 0x1C8)
> > +#define VSC_EXT_VESA_HB0_DP_ENC0_P0_MASK 0xff
> > +#define VSC_EXT_VESA_HB1_DP_ENC0_P0_MASK 0xff00
> > +#define VSC_EXT_VESA_HB1_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC0_P0_31CC (ENC0_OFFSET + 0x1CC)
> > +#define VSC_EXT_VESA_HB2_DP_ENC0_P0_MASK 0xff
> > +#define VSC_EXT_VESA_HB2_DP_ENC0_P0_SHIFT 0
> > +#define VSC_EXT_VESA_HB3_DP_ENC0_P0_MASK 0xff00
> > +
> > +#define MTK_DP_ENC0_P0_31D0 (ENC0_OFFSET + 0x1D0)
> > +#define VSC_EXT_CEA_HB0_DP_ENC0_P0_MASK 0xff
> > +#define VSC_EXT_CEA_HB1_DP_ENC0_P0_MASK 0xff00
> > +#define VSC_EXT_CEA_HB1_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC0_P0_31D4 (ENC0_OFFSET + 0x1D4)
> > +#define VSC_EXT_CEA_HB2_DP_ENC0_P0_MASK 0xff
> > +#define VSC_EXT_CEA_HB2_DP_ENC0_P0_SHIFT 0
> > +#define VSC_EXT_CEA_HB3_DP_ENC0_P0_MASK 0xff00
> > +
> > +#define MTK_DP_ENC0_P0_31D8 (ENC0_OFFSET + 0x1D8)
> > +#define VSC_EXT_VESA_NUM_DP_ENC0_P0_MASK 0x3f
> > +#define VSC_EXT_VESA_NUM_DP_ENC0_P0_SHIFT 0
> > +#define VSC_EXT_CEA_NUM_DP_ENC0_P0_MASK 0x3f00
> > +#define VSC_EXT_CEA_NUM_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC0_P0_31DC (ENC0_OFFSET + 0x1DC)
> > +#define HDR0_CFG_DP_ENC0_P0_MASK 0xff
> > +#define HDR0_CFG_DP_ENC0_P0_SHIFT 0
> > +
> > +#define MTK_DP_ENC0_P0_31E8 (ENC0_OFFSET + 0x1E8)
> > +
> > +#define MTK_DP_ENC0_P0_31EC (ENC0_OFFSET + 0x1EC)
> > +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK 0x10
> > +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT 4
> > +#define ISRC1_HB3_DP_ENC0_P0_MASK 0xff00
> > +#define ISRC1_HB3_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC1_P0_3200 (ENC1_OFFSET + 0x000)
> > +
> > +#define MTK_DP_ENC1_P0_3280 (ENC1_OFFSET + 0x080)
> > +#define SDP_PACKET_TYPE_DP_ENC1_P0_MASK 0x1f
> > +#define SDP_PACKET_W_DP_ENC1_P0 0x20
> > +#define SDP_PACKET_W_DP_ENC1_P0_MASK 0x20
> > +#define SDP_PACKET_W_DP_ENC1_P0_SHIFT 5
> > +
> > +#define MTK_DP_ENC1_P0_328C (ENC1_OFFSET + 0x08C)
> > +
> > +#define MTK_DP_ENC1_P0_3290 (ENC1_OFFSET + 0x090)
> > +
> > +#define MTK_DP_ENC1_P0_32A0 (ENC1_OFFSET + 0x0A0)
> > +
> > +#define MTK_DP_ENC1_P0_32A4 (ENC1_OFFSET + 0x0A4)
> > +
> > +#define MTK_DP_ENC1_P0_3300 (ENC1_OFFSET + 0x100)
> > +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK 0x300
> > +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC1_P0_3304 (ENC1_OFFSET + 0x104)
> > +#define AU_PRTY_REGEN_DP_ENC1_P0_MASK 0x100
> > +#define AU_CH_STS_REGEN_DP_ENC1_P0_MASK 0x200
> > +#define AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK 0x1000
> > +
> > +#define MTK_DP_ENC1_P0_3324 (ENC1_OFFSET + 0x124)
> > +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK 0x300
> > +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT 8
> > +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX                                       \
> > +     (0 << AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT)
> > +
> > +#define MTK_DP_ENC1_P0_3364 (ENC1_OFFSET + 0x164)
> > +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK 0xfff
> > +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT 0
> > +#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK 0xf000
> > +#define FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT 12
> > +
> > +#define MTK_DP_ENC1_P0_3368 (ENC1_OFFSET + 0x168)
> > +#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT 0
> > +#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT 4
> > +#define SDP_DP13_EN_DP_ENC1_P0_SHIFT 8
> > +#define BS2BS_MODE_DP_ENC1_P0_MASK 0x3000
> > +#define BS2BS_MODE_DP_ENC1_P0_SHIFT 12
> > +
> > +#define MTK_DP_ENC1_P0_33F4 (ENC1_OFFSET + 0x1F4)
> > +
> > +#define MTK_DP_TRANS_P0_3400 (TRANS_OFFSET + 0x000)
> > +#define PATTERN1_EN_DP_TRANS_P0_MASK 0x1000
> > +#define PATTERN1_EN_DP_TRANS_P0_SHIFT 12
> > +#define PATTERN2_EN_DP_TRANS_P0_MASK 0x2000
> > +#define PATTERN3_EN_DP_TRANS_P0_MASK 0x4000
> > +#define PATTERN4_EN_DP_TRANS_P0_MASK 0x8000
> > +
> > +#define MTK_DP_TRANS_P0_3404 (TRANS_OFFSET + 0x004)
> > +#define DP_SCR_EN_DP_TRANS_P0_MASK 0x1
> > +
> > +#define MTK_DP_TRANS_P0_340C (TRANS_OFFSET + 0x00C)
> > +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK 0x2000
> > +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT 13
> > +
> > +#define MTK_DP_TRANS_P0_3410 (TRANS_OFFSET + 0x010)
> > +#define HPD_DEB_THD_DP_TRANS_P0_MASK 0xf
> > +#define HPD_DEB_THD_DP_TRANS_P0_SHIFT 0
> > +#define HPD_INT_THD_DP_TRANS_P0_MASK 0xf0
> > +#define HPD_INT_THD_DP_TRANS_P0_SHIFT 4
> > +#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US (2 << HPD_INT_THD_DP_TRANS_P0_SHIFT)
> > +#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US                                   \
> > +     (2 << (HPD_INT_THD_DP_TRANS_P0_SHIFT + 2))
> > +#define HPD_DISC_THD_DP_TRANS_P0_MASK 0xf00
> > +#define HPD_DISC_THD_DP_TRANS_P0_SHIFT 8
> > +#define HPD_CONN_THD_DP_TRANS_P0_MASK 0xf000
> > +#define HPD_CONN_THD_DP_TRANS_P0_SHIFT 12
> > +
> > +#define MTK_DP_TRANS_P0_3414 (TRANS_OFFSET + 0x014)
> > +#define HPD_DB_DP_TRANS_P0_MASK 0x4
> > +
> > +#define MTK_DP_TRANS_P0_3418 (TRANS_OFFSET + 0x018)
> > +#define IRQ_CLR_DP_TRANS_P0_MASK 0xf
> > +#define IRQ_MASK_DP_TRANS_P0_MASK 0xf0
> > +#define IRQ_MASK_DP_TRANS_P0_SHIFT 4
> > +#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ (BIT(1) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> > +#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ (BIT(2) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> > +#define IRQ_MASK_DP_TRANS_P0_INT_IRQ (BIT(3) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> > +#define IRQ_STATUS_DP_TRANS_P0_MASK 0xf000
> > +#define IRQ_STATUS_DP_TRANS_P0_SHIFT 12
> > +
> > +#define MTK_DP_TRANS_P0_342C (TRANS_OFFSET + 0x02C)
> > +#define XTAL_FREQ_DP_TRANS_P0_DEFAULT 0x69
> > +#define XTAL_FREQ_DP_TRANS_P0_MASK 0xff
> > +
> > +#define MTK_DP_TRANS_P0_3430 (TRANS_OFFSET + 0x030)
> > +#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK 0x3
> > +#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT BIT(1)
> > +
> > +#define MTK_DP_TRANS_P0_34A4 (TRANS_OFFSET + 0x0A4)
> > +#define LANE_NUM_DP_TRANS_P0_MASK 0xc
> > +#define LANE_NUM_DP_TRANS_P0_SHIFT 2
> > +
> > +#define MTK_DP_TRANS_P0_3540 (TRANS_OFFSET + 0x140)
> > +#define FEC_EN_DP_TRANS_P0_MASK 0x1
> > +#define FEC_EN_DP_TRANS_P0_SHIFT 0
> > +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK 0x8
> > +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT 3
> > +
> > +#define MTK_DP_TRANS_P0_3580 (TRANS_OFFSET + 0x180)
> > +#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK 0x100
> > +#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK 0x200
> > +#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK 0x400
> > +#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK 0x800
> > +
> > +#define MTK_DP_TRANS_P0_35C4 (TRANS_OFFSET + 0x1C4)
> > +#define SW_IRQ_MASK_DP_TRANS_P0_MASK 0xffff
> > +
> > +#define MTK_DP_TRANS_P0_35C8 (TRANS_OFFSET + 0x1C8)
> > +#define SW_IRQ_CLR_DP_TRANS_P0_MASK 0xffff
> > +
> > +#define SW_IRQ_STATUS_DP_TRANS_P0_MASK 0xffff
> > +#define SW_IRQ_STATUS_DP_TRANS_P0_SHIFT 0
> > +
> > +#define MTK_DP_TRANS_P0_35D0 (TRANS_OFFSET + 0x1D0)
> > +#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK 0xffff
> > +
> > +#define MTK_DP_TRANS_P0_35F0 (TRANS_OFFSET + 0x1F0)
> > +
> > +#define MTK_DP_AUX_P0_360C (AUX_OFFSET + 0x00C)
> > +#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_AUX_P0_3614 (AUX_OFFSET + 0x014)
> > +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK 0x7f
> > +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT 0
> > +
> > +#define MTK_DP_AUX_P0_3618 (AUX_OFFSET + 0x018)
> > +#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK 0x200
> > +#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK 0xf
> > +
> > +#define MTK_DP_AUX_P0_3620 (AUX_OFFSET + 0x020)
> > +#define AUX_RD_MODE_AUX_TX_P0_MASK 0x200
> > +#define AUX_RX_FIFO_READ_PULSE_TX_P0_MASK 0x100
> > +#define AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT 8
> > +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK 0xff
> > +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT 0
> > +
> > +#define MTK_DP_AUX_P0_3624 (AUX_OFFSET + 0x024)
> > +#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK 0xf
> > +
> > +#define MTK_DP_AUX_P0_3628 (AUX_OFFSET + 0x028)
> > +#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK 0x3ff
> > +#define AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT 0
> > +#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE                                     \
> > +     (BIT(0) << AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT)
> > +
> > +#define MTK_DP_AUX_P0_362C (AUX_OFFSET + 0x02C)
> > +#define AUX_NO_LENGTH_AUX_TX_P0_MASK 0x1
> > +#define AUX_NO_LENGTH_AUX_TX_P0_SHIFT 0
> > +#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK 0x2
> > +#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK 0xfffc
> > +
> > +#define MTK_DP_AUX_P0_3630 (AUX_OFFSET + 0x030)
> > +#define AUX_TX_REQUEST_READY_AUX_TX_P0_MASK 0x8
> > +#define AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT 3
> > +
> > +#define MTK_DP_AUX_P0_3634 (AUX_OFFSET + 0x034)
> > +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK 0xff00
> > +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT 8
> > +
> > +#define MTK_DP_AUX_P0_3640 (AUX_OFFSET + 0x040)
> > +#define AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK 0x40
> > +#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 6
> > +#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 5
> > +#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 4
> > +#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT 3
> > +#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT 2
> > +#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT 1
> > +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK 0x1
> > +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT 0
> > +
> > +#define MTK_DP_AUX_P0_3644 (AUX_OFFSET + 0x044)
> > +#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK 0xf
> > +
> > +#define MTK_DP_AUX_P0_3648 (AUX_OFFSET + 0x048)
> > +#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK 0xffff
> > +
> > +#define MTK_DP_AUX_P0_364C (AUX_OFFSET + 0x04C)
> > +#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK 0xf
> > +
> > +#define MTK_DP_AUX_P0_3650 (AUX_OFFSET + 0x050)
> > +#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK 0xf000
> > +#define MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT 12
> > +#define PHY_FIFO_RST_AUX_TX_P0_MASK 0x200
> > +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK 0x100
> > +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT 8
> > +
> > +#define MTK_DP_AUX_P0_3658 (AUX_OFFSET + 0x058)
> > +#define AUX_TX_OV_EN_AUX_TX_P0_MASK 0x1
> > +
> > +#define MTK_DP_AUX_P0_3690 (AUX_OFFSET + 0x090)
> > +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK 0x100
> > +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT 8
> > +
> > +#define MTK_DP_AUX_P0_3704 (AUX_OFFSET + 0x104)
> > +#define AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK 0x2
> > +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK 0x4
> > +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT 2
> > +
> > +#define MTK_DP_AUX_P0_3708 (AUX_OFFSET + 0x108)
> > +
> > +#define MTK_DP_AUX_P0_37C8 (AUX_OFFSET + 0x1C8)
> > +#define MTK_ATOP_EN_AUX_TX_P0_MASK 0x1
> > +#define MTK_ATOP_EN_AUX_TX_P0_SHIFT 0
> > +
> > +#define MTK_DP_TOP_PWR_STATE (TOP_OFFSET + 0x00)
> > +#define DP_PWR_STATE_MASK 0x3
> > +#define DP_PWR_STATE_BANDGAP 1
> > +#define DP_PWR_STATE_BANDGAP_TPLL 2
> > +#define DP_PWR_STATE_BANDGAP_TPLL_LANE 3
> > +
> > +#define MTK_DP_TOP_SWING_EMP (TOP_OFFSET + 0x04)
> > +#define DP_TX0_VOLT_SWING_MASK 0x3
> > +#define DP_TX0_VOLT_SWING_SHIFT 0
> > +#define DP_TX0_PRE_EMPH_MASK 0xc
> > +#define DP_TX0_PRE_EMPH_SHIFT 2
> > +#define DP_TX1_VOLT_SWING_MASK 0x300
> > +#define DP_TX1_VOLT_SWING_SHIFT 8
> > +#define DP_TX1_PRE_EMPH_MASK 0xc00
> > +#define DP_TX2_VOLT_SWING_MASK 0x30000
> > +#define DP_TX2_PRE_EMPH_MASK 0xc0000
> > +#define DP_TX3_VOLT_SWING_MASK 0x3000000
> > +#define DP_TX3_PRE_EMPH_MASK 0xc000000
> > +
> > +#define MTK_DP_TOP_RESET_AND_PROBE (TOP_OFFSET + 0x20)
> > +#define SW_RST_B_SHIFT 0
> > +#define SW_RST_B_PHYD (BIT(4) << SW_RST_B_SHIFT)
> > +
> > +#define MTK_DP_TOP_IRQ_STATUS (TOP_OFFSET + 0x28)
> > +#define RGS_IRQ_STATUS_SHIFT 0
> > +#define RGS_IRQ_STATUS_TRANSMITTER (BIT(1) << RGS_IRQ_STATUS_SHIFT)
> > +
> > +#define MTK_DP_TOP_IRQ_MASK (TOP_OFFSET + 0x2C)
> > +#define IRQ_MASK_AUX_TOP_IRQ BIT(2)
> > +
> > +#define MTK_DP_TOP_MEM_PD (TOP_OFFSET + 0x38)
> > +#define MEM_ISO_EN_SHIFT 0
> > +#define FUSE_SEL_SHIFT 2
> > +
> > +#endif /*_MTK_DP_REG_H_*/
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > index 384074f69111b..e6e88e3cd811d 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> >  };
> >
> > +enum TVDPLL_CLK {
> > +     TVDPLL_PLL = 0,
> > +     TVDPLL_D2 = 2,
> > +     TVDPLL_D4 = 4,
> > +     TVDPLL_D8 = 8,
> > +     TVDPLL_D16 = 16,
> > +};
> > +
> >  struct mtk_dpi {
> >       struct drm_encoder encoder;
> >       struct drm_bridge bridge;
> > @@ -71,8 +79,10 @@ struct mtk_dpi {
> >       void __iomem *regs;
> >       struct device *dev;
> >       struct clk *engine_clk;
> > +     struct clk *dpi_ck_cg;
> >       struct clk *pixel_clk;
> >       struct clk *tvd_clk;
> > +     struct clk *pclk_src[5];
> >       int irq;
> >       struct drm_display_mode mode;
> >       const struct mtk_dpi_conf *conf;
> > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> >       u32 hvsize_mask;
> >       u32 channel_swap_shift;
> >       u32 yuv422_en_bit;
> > +     u32 csc_enable_bit;
> >       const struct mtk_dpi_yc_limit *limit;
> >  };
> >
> > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> >
> >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> >  {
> > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> >  }
> >
> >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> >  }
> >
> > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > +{
> > +     u32 matrix_sel = 0;
> > +
> > +     switch (format) {
> > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > +             if (dpi->mode.hdisplay <= 720)
> > +                     matrix_sel = 0x2;
> > +             break;
> > +     default:
> > +             break;
> > +     }
> > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > +}
> > +
> >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> >                                       enum mtk_dpi_out_color_format format)
> >  {
> >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> >               mtk_dpi_config_yuv422_enable(dpi, false);
> > -             if (dpi->conf->csc_support)
> > +             if (dpi->conf->csc_support) {
> >                       mtk_dpi_config_csc_enable(dpi, true);
> > +                     mtk_dpi_matrix_sel(dpi, format);
> > +             }
> >               if (dpi->conf->swap_input_support)
> >                       mtk_dpi_config_swap_input(dpi, false);
> >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> >               mtk_dpi_config_yuv422_enable(dpi, true);
> > -             if (dpi->conf->csc_support)
> > +             if (dpi->conf->csc_support) {
> >                       mtk_dpi_config_csc_enable(dpi, true);
> > +                     mtk_dpi_matrix_sel(dpi, format);
> > +             }
> >               if (dpi->conf->swap_input_support)
> >                       mtk_dpi_config_swap_input(dpi, true);
> >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> >       mtk_dpi_disable(dpi);
> >       clk_disable_unprepare(dpi->pixel_clk);
> >       clk_disable_unprepare(dpi->engine_clk);
> > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > +     clk_disable_unprepare(dpi->tvd_clk);
> >  }
> >
> >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> >       if (++dpi->refcount != 1)
> >               return 0;
> >
> > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > +     if (ret) {
> > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > +             goto err_pixel;
> > +     }
> > +
> >       ret = clk_prepare_enable(dpi->engine_clk);
> >       if (ret) {
> >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> >               goto err_refcount;
> >       }
> >
> > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > +     if (ret) {
> > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > +             goto err_ck_cg;
> > +     }
> > +
> >       ret = clk_prepare_enable(dpi->pixel_clk);
> >       if (ret) {
> >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> >       if (dpi->pinctrl && dpi->pins_dpi)
> >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> >
> > -     mtk_dpi_enable(dpi);
> >       return 0;
> >
> >  err_pixel:
> > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > +err_ck_cg:
> >       clk_disable_unprepare(dpi->engine_clk);
> >  err_refcount:
> >       dpi->refcount--;
> > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> >       pll_rate = clk_get_rate(dpi->tvd_clk);
> >
> >       vm.pixelclock = pll_rate / factor;
> > -     if (dpi->conf->is_dpintf)
> > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > +     if (dpi->conf->is_dpintf) {
> > +             if (factor == 1)
> > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > +             else if (factor == 2)
> > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > +             else if (factor == 4)
> > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > +             else
> > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> >       else
> > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> >       }
> >       mtk_dpi_sw_reset(dpi, false);
> >
> > +     mtk_dpi_enable(dpi);
> > +
> >       return 0;
> >  }
> >
> > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> >       u32 *input_fmts;
> >
> >       *num_input_fmts = 0;
> > -
> >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> >                            GFP_KERNEL);
> >       if (!input_fmts)
> > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> >               if (dpi->conf->num_output_fmts)
> >                       out_bus_format = dpi->conf->output_fmts[0];
> >
> > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> >               bridge_state->input_bus_cfg.format,
> >               bridge_state->output_bus_cfg.format);
> >
> > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > +     else
> > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> >
> >       return 0;
> >  }
> > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> >  };
> >
> > +static const u32 mt8195_output_fmts[] = {
> > +     MEDIA_BUS_FMT_RGB888_1X24,
> > +     MEDIA_BUS_FMT_YUV8_1X24,
> > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > +};
> > +
> >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> >       .c_bottom = 0x0010,
> >       .c_top = 0x0FE0,
> > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> >       .hvsize_mask = HSIZE_MASK,
> >       .channel_swap_shift = CH_SWAP,
> >       .yuv422_en_bit = YUV422_EN,
> > +     .csc_enable_bit = CSC_ENABLE,
> >       .limit = &mtk_dpi_limit,
> >  };
> >
> > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> >       .hvsize_mask = HSIZE_MASK,
> >       .channel_swap_shift = CH_SWAP,
> >       .yuv422_en_bit = YUV422_EN,
> > +     .csc_enable_bit = CSC_ENABLE,
> >       .limit = &mtk_dpi_limit,
> >  };
> >
> > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> >       .hvsize_mask = HSIZE_MASK,
> >       .channel_swap_shift = CH_SWAP,
> >       .yuv422_en_bit = YUV422_EN,
> > +     .csc_enable_bit = CSC_ENABLE,
> >       .limit = &mtk_dpi_limit,
> >  };
> >
> > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> >       .hvsize_mask = HSIZE_MASK,
> >       .channel_swap_shift = CH_SWAP,
> >       .yuv422_en_bit = YUV422_EN,
> > +     .csc_enable_bit = CSC_ENABLE,
> >       .limit = &mtk_dpi_limit,
> >  };
> >
> >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> >       .cal_factor = mt8195_dpintf_calculate_factor,
> > -     .output_fmts = mt8173_output_fmts,
> > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > +     .output_fmts = mt8195_output_fmts,
> > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> >       .is_dpintf = true,
> > +     .csc_support = true,
> >       .dimension_mask = DPINTF_HPW_MASK,
> >       .hvsize_mask = DPINTF_HSIZE_MASK,
> >       .channel_swap_shift = DPINTF_CH_SWAP,
> >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> >       .limit = &mtk_dpintf_limit,
> >  };
> >
> > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> >               return ret;
> >       }
> >
> > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > +             if (ret != -EPROBE_DEFER)
> > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > +                             ret);
> > +
> > +             return ret;
> > +     }
> > +
> >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> >       if (IS_ERR(dpi->pixel_clk)) {
> >               ret = PTR_ERR(dpi->pixel_clk);
> > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> >               return ret;
> >       }
> >
> > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > +
> >       dpi->irq = platform_get_irq(pdev, 0);
> >       if (dpi->irq <= 0)
> >               return -EINVAL;
>
> All those changes look unrelated as well?
>
Do you mean all the changes in mtk_dpi.c ?
They are in support of enabling the mt8195 dpintf driver... so, not
sure they are
unlreated?

> Maxime

Thx,
Guillaume.

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-15 14:33       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-15 14:33 UTC (permalink / raw)
  To: Maxime Ripard, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Daniel Vetter, Chun-Kuang Hu,
	Philipp Zabel, Matthias Brugger
  Cc: kernel test robot, linux-kernel, dri-devel,
	Markus Schneider-Pargmann, linux-mediatek, linux-arm-kernel

Hi Maxime,

Thanks for your review.

Quoting Maxime Ripard (2021-11-15 11:11:29)
> Hi,
>
> On Wed, Nov 10, 2021 at 02:06:23PM +0100, Guillaume Ranquet wrote:
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > according phy driver mediatek-dp-phy.
> >
> > It supports both functional units on the mt8195, the embedded
> > DisplayPort as well as the external DisplayPort units. It offers
> > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > to 4 lanes.
>
> There's a number of checkpatch --strict errors in there, make sure to fix them.
This is a regression I've introduced, I think, when running
clang-format... I'll fix them.

>
> > The driver creates a child device for the phy. The child device will
> > never exist without the parent being active. As they are sharing a
> > register range, the parent passes a regmap pointer to the child so that
> > both can work with the same register range. The phy driver sets device
> > data that is read by the parent to get the phy device that can be used
> > to control the phy properties.
>
> If the PHY is in the same register space than the DP controller, why do
> you need a separate PHY driver in the first place?
>

This has been asked by Chun-Kuang Hu in a previous revision of the series:

https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/


> > This driver is based on an initial version by
> > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > Reported-by: kernel test robot <lkp@intel.com>
>
> > ---
> >  drivers/gpu/drm/drm_edid.c              |    2 +-
> >  drivers/gpu/drm/mediatek/Kconfig        |    7 +
> >  drivers/gpu/drm/mediatek/Makefile       |    2 +
> >  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
> >  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
> >  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
> >  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
> >  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
> >  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
> >  9 files changed, 3799 insertions(+), 13 deletions(-)
> >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
> >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> >
> > diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> > index 500279a82167a..bfd98b50ceb5b 100644
> > --- a/drivers/gpu/drm/drm_edid.c
> > +++ b/drivers/gpu/drm/drm_edid.c
> > @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
> >        * modes and forbids YCRCB422 support for all video modes per
> >        * HDMI 1.3 spec.
> >        */
> > -     info->color_formats = DRM_COLOR_FORMAT_RGB444;
> > +     info->color_formats |= DRM_COLOR_FORMAT_RGB444;
> >
> >       /* YCRCB444 is optional according to spec. */
> >       if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
>
> This looks unrelated?
>
Right, I should have kept it in a separate patch.

> > diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> > index 2976d21e9a34a..029b94c716131 100644
> > --- a/drivers/gpu/drm/mediatek/Kconfig
> > +++ b/drivers/gpu/drm/mediatek/Kconfig
> > @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
> >       select PHY_MTK_HDMI
> >       help
> >         DRM/KMS HDMI driver for Mediatek SoCs
> > +
> > +config MTK_DPTX_SUPPORT
> > +     tristate "DRM DPTX Support for Mediatek SoCs"
> > +     depends on DRM_MEDIATEK
> > +     select PHY_MTK_DP
> > +     help
> > +       DRM/KMS Display Port driver for Mediatek SoCs.
> > diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> > index 29098d7c8307c..d86a6406055e6 100644
> > --- a/drivers/gpu/drm/mediatek/Makefile
> > +++ b/drivers/gpu/drm/mediatek/Makefile
> > @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
> >                         mtk_hdmi_ddc.o
> >
> >  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> > +
> > +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> > new file mode 100644
> > index 0000000000000..83087219d5a5e
> > --- /dev/null
> > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> > @@ -0,0 +1,3094 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2019 MediaTek Inc.
> > + * Copyright (c) 2021 BayLibre
> > + */
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_bridge.h>
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_dp_helper.h>
> > +#include <drm/drm_edid.h>
> > +#include <drm/drm_of.h>
> > +#include <drm/drm_panel.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/drm_probe_helper.h>
> > +#include <linux/arm-smccc.h>
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/errno.h>
> > +#include <linux/kernel.h>
> > +#include <linux/nvmem-consumer.h>
> > +#include <linux/of.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <sound/hdmi-codec.h>
> > +#include <video/videomode.h>
> > +
> > +#include "mtk_dp_reg.h"
> > +
> > +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> > +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> > +
> > +#define MTK_DP_MAX_LANES 4
> > +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> > +
> > +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> > +
> > +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> > +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> > +
> > +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> > +
> > +#define MTK_DP_DP_VERSION_11 0x11
> > +
> > +enum mtk_dp_state {
> > +     MTK_DP_STATE_INITIAL,
> > +     MTK_DP_STATE_IDLE,
> > +     MTK_DP_STATE_PREPARE,
> > +     MTK_DP_STATE_NORMAL,
> > +};
> > +
> > +enum mtk_dp_train_state {
> > +     MTK_DP_TRAIN_STATE_STARTUP = 0,
> > +     MTK_DP_TRAIN_STATE_CHECKCAP,
> > +     MTK_DP_TRAIN_STATE_CHECKEDID,
> > +     MTK_DP_TRAIN_STATE_TRAINING_PRE,
> > +     MTK_DP_TRAIN_STATE_TRAINING,
> > +     MTK_DP_TRAIN_STATE_CHECKTIMING,
> > +     MTK_DP_TRAIN_STATE_NORMAL,
> > +     MTK_DP_TRAIN_STATE_POWERSAVE,
> > +     MTK_DP_TRAIN_STATE_DPIDLE,
> > +};
> > +
> > +struct mtk_dp_timings {
> > +     struct videomode vm;
> > +
> > +     u16 htotal;
> > +     u16 vtotal;
> > +     u8 frame_rate;
> > +     u32 pix_rate_khz;
> > +};
> > +
> > +struct mtk_dp_train_info {
> > +     bool tps3;
> > +     bool tps4;
> > +     bool sink_ssc;
> > +     bool cable_plugged_in;
> > +     bool cable_state_change;
> > +     bool cr_done;
> > +     bool eq_done;
> > +
> > +     // link_rate is in multiple of 0.27Gbps
> > +     int link_rate;
> > +     int lane_count;
> > +
> > +     int irq_status;
> > +     int check_cap_count;
> > +};
> > +
> > +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> > +enum mtk_dp_color_format {
> > +     MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> > +     MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> > +     MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> > +     MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> > +     MTK_DP_COLOR_FORMAT_YONLY = 4,
> > +     MTK_DP_COLOR_FORMAT_RAW = 5,
> > +     MTK_DP_COLOR_FORMAT_RESERVED = 6,
> > +     MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> > +     MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> > +};
> > +
> > +// Multiple of 0.27Gbps
> > +enum mtk_dp_linkrate {
> > +     MTK_DP_LINKRATE_RBR = 0x6,
> > +     MTK_DP_LINKRATE_HBR = 0xA,
> > +     MTK_DP_LINKRATE_HBR2 = 0x14,
> > +     MTK_DP_LINKRATE_HBR25 = 0x19,
> > +     MTK_DP_LINKRATE_HBR3 = 0x1E,
> > +};
> > +
> > +// Same values as used for DP Spec MISC0 bits 5,6,7
> > +enum mtk_dp_color_depth {
> > +     MTK_DP_COLOR_DEPTH_6BIT = 0,
> > +     MTK_DP_COLOR_DEPTH_8BIT = 1,
> > +     MTK_DP_COLOR_DEPTH_10BIT = 2,
> > +     MTK_DP_COLOR_DEPTH_12BIT = 3,
> > +     MTK_DP_COLOR_DEPTH_16BIT = 4,
> > +     MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> > +};
> > +
> > +struct mtk_dp_audio_cfg {
> > +     int sample_rate;
> > +     int word_length_bits;
> > +     int channels;
> > +};
> > +
> > +struct mtk_dp_info {
> > +     enum mtk_dp_color_depth depth;
> > +     enum mtk_dp_color_format format;
> > +     struct mtk_dp_audio_cfg audio_caps;
> > +     struct mtk_dp_timings timings;
> > +};
> > +
> > +struct dp_cal_data {
> > +     unsigned int glb_bias_trim;
> > +     unsigned int clktx_impse;
> > +
> > +     unsigned int ln0_tx_impsel_pmos;
> > +     unsigned int ln0_tx_impsel_nmos;
> > +     unsigned int ln1_tx_impsel_pmos;
> > +     unsigned int ln1_tx_impsel_nmos;
> > +     unsigned int ln2_tx_impsel_pmos;
> > +     unsigned int ln2_tx_impsel_nmos;
> > +     unsigned int ln3_tx_impsel_pmos;
> > +     unsigned int ln3_tx_impsel_nmos;
> > +};
> > +
> > +struct mtk_dp {
> > +     struct device *dev;
> > +     struct platform_device *phy_dev;
> > +     struct phy *phy;
> > +     struct dp_cal_data cal_data;
> > +
> > +     struct drm_device *drm_dev;
> > +     struct drm_bridge bridge;
> > +     struct drm_bridge *next_bridge;
> > +     struct drm_dp_aux aux;
> > +
> > +     struct mutex edid_lock;
> > +     struct edid *edid;
> > +
> > +     u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> > +
> > +     struct mtk_dp_info info;
> > +     enum mtk_dp_state state;
> > +
> > +     struct mtk_dp_train_info train_info;
> > +     enum mtk_dp_train_state train_state;
> > +     unsigned int input_fmt;
> > +
> > +     struct regmap *regs;
> > +     struct clk *dp_tx_clk;
> > +
> > +     bool enabled;
> > +     bool audio_enable;
> > +
> > +     bool has_fec;
> > +     struct mutex dp_lock;
> > +
> > +     struct mutex update_plugged_status_lock;
> > +
> > +     hdmi_codec_plugged_cb plugged_cb;
> > +     struct device *codec_dev;
> > +     u8 connector_eld[MAX_ELD_BYTES];
> > +};
> > +
> > +enum mtk_dp_sdp_type {
> > +     MTK_DP_SDP_NONE = 0x00,
> > +     MTK_DP_SDP_ACM = 0x01,
> > +     MTK_DP_SDP_ISRC = 0x02,
> > +     MTK_DP_SDP_AVI = 0x03,
> > +     MTK_DP_SDP_AUI = 0x04,
> > +     MTK_DP_SDP_SPD = 0x05,
> > +     MTK_DP_SDP_MPEG = 0x06,
> > +     MTK_DP_SDP_NTSC = 0x07,
> > +     MTK_DP_SDP_VSP = 0x08,
> > +     MTK_DP_SDP_VSC = 0x09,
> > +     MTK_DP_SDP_EXT = 0x0A,
> > +     MTK_DP_SDP_PPS0 = 0x0B,
> > +     MTK_DP_SDP_PPS1 = 0x0C,
> > +     MTK_DP_SDP_PPS2 = 0x0D,
> > +     MTK_DP_SDP_PPS3 = 0x0E,
> > +     MTK_DP_SDP_DRM = 0x10,
> > +     MTK_DP_SDP_MAX_NUM
> > +};
> > +
> > +struct mtk_dp_sdp_packet {
> > +     enum mtk_dp_sdp_type type;
> > +     struct dp_sdp sdp;
> > +};
> > +
> > +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> > +union mtk_dp_audio_channel_status {
> > +     struct {
> > +             u8 rev : 1;
> > +             u8 is_lpcm : 1;
> > +             u8 copy_right : 1;
> > +             u8 additional_format_info : 3;
> > +             u8 channel_status_mode : 2;
> > +             u8 category_code;
> > +             u8 src_num : 4;
> > +             u8 channel_num : 4;
> > +             u8 sampling_freq : 4;
> > +             u8 clk_accuracy : 2;
> > +             u8 rev2 : 2;
> > +             u8 word_len : 4;
> > +             u8 original_sampling_freq : 4;
> > +     } iec;
> > +
> > +     u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> > +};
>
> Why don't you leverage hdmi_codec_params and snd_aes_iec958?
>
I'll see if I can make it work with snd_aes_iec958 and get back to you.

> > +static struct regmap_config mtk_dp_regmap_config = {
> > +     .reg_bits = 32,
> > +     .val_bits = 32,
> > +     .reg_stride = 4,
> > +     .max_register = SEC_OFFSET + 0x90,
> > +     .name = "mtk-dp-registers",
> > +};
> > +
> > +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
> > +{
> > +     return mtk_dp->next_bridge != NULL;
> > +}
> > +
> > +static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
> > +{
> > +     return container_of(b, struct mtk_dp, bridge);
> > +}
> > +
> > +static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
> > +{
> > +     u32 read_val;
> > +     int ret;
> > +
> > +     ret = regmap_read(mtk_dp->regs, offset, &read_val);
> > +     if (ret) {
> > +             dev_err(mtk_dp->dev, "Failed to read register 0x%x: %d\n",
> > +                     offset, ret);
> > +             return 0;
> > +     }
> > +
> > +     return read_val;
> > +}
> > +
> > +static void mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
> > +{
> > +     int ret;
> > +
> > +     ret = regmap_write(mtk_dp->regs, offset, val);
> > +     if (ret)
> > +             dev_err(mtk_dp->dev,
> > +                     "Failed to write register 0x%x with value 0x%x: %d\n",
> > +                     offset, val, ret);
> > +}
> > +
> > +static void mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, u32 val,
> > +                            u32 mask)
> > +{
> > +     int ret;
> > +
> > +     ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
> > +     if (ret)
> > +             dev_err(mtk_dp->dev,
> > +                     "Failed to update register 0x%x with value 0x%x, mask 0x%x: %d\n",
> > +                     offset, val, mask, ret);
> > +}
> > +
> > +static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32 offset, u8 *buf,
> > +                                 size_t length)
> > +{
> > +     int i;
> > +     int num_regs = (length + 1) / 2;
> > +
> > +     // 2 bytes per register
> > +     for (i = 0; i < num_regs; i++) {
> > +             u32 val = buf[i * 2] |
> > +                       (i * 2 + 1 < length ? buf[i * 2 + 1] << 8 : 0);
> > +
> > +             mtk_dp_write(mtk_dp, offset + i * 4, val);
> > +     }
> > +}
> > +
> > +static unsigned long mtk_dp_sip_atf_call(unsigned int cmd, unsigned int para)
> > +{
> > +     struct arm_smccc_res res;
> > +
> > +     arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32, cmd, para, 0, 0, 0, 0, 0,
> > +                   &res);
> > +
> > +     pr_debug("[DPTX]%s cmd 0x%x, p1 0x%x, ret 0x%lx-0x%lx", __func__, cmd,
> > +              para, res.a0, res.a1);
> > +     return res.a1;
> > +}
> > +
> > +static void mtk_dp_msa_bypass_disable(struct mtk_dp *mtk_dp)
> > +{
> > +     const u16 bits_to_set =
> > +             BIT(HTOTAL_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(VTOTAL_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(HSTART_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(VSTART_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(HWIDTH_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(VHEIGHT_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(HSP_SEL_DP_ENC0_P0_SHIFT) | BIT(HSW_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(VSP_SEL_DP_ENC0_P0_SHIFT) | BIT(VSW_SEL_DP_ENC0_P0_SHIFT);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, bits_to_set,
> > +                        bits_to_set);
> > +}
> > +
> > +static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
> > +{
> > +     struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010, timings->htotal,
> > +                        HTOTAL_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
> > +                        timings->vm.hsync_len + timings->vm.hback_porch,
> > +                        HSTART_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> > +                        timings->vm.hsync_len << HSW_SW_DP_ENC0_P0_SHIFT,
> > +                        HSW_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028, 0,
> > +                        HSP_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020, timings->vm.hactive,
> > +                        HWIDTH_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014, timings->vtotal,
> > +                        VTOTAL_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
> > +                        timings->vm.vsync_len + timings->vm.vback_porch,
> > +                        VSTART_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> > +                        timings->vm.vsync_len << VSW_SW_DP_ENC0_P0_SHIFT,
> > +                        VSW_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C, 0,
> > +                        VSP_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024, timings->vm.vactive,
> > +                        VHEIGHT_SW_DP_ENC0_P0_MASK);
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064, timings->vm.hactive,
> > +                        HDE_NUM_LAST_DP_ENC0_P0_MASK);
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154, timings->htotal,
> > +                        PGEN_HTOTAL_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
> > +                        timings->vm.hfront_porch,
> > +                        PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C, timings->vm.hsync_len,
> > +                        PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
> > +                        timings->vm.hback_porch + timings->vm.hsync_len,
> > +                        PGEN_HFDE_START_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164, timings->vm.hactive,
> > +                        PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168, timings->vtotal,
> > +                        PGEN_VTOTAL_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
> > +                        timings->vm.vfront_porch,
> > +                        PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170, timings->vm.vsync_len,
> > +                        PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
> > +                        timings->vm.vback_porch + timings->vm.vsync_len,
> > +                        PGEN_VFDE_START_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178, timings->vm.vactive,
> > +                        PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
> > +                                 enum mtk_dp_color_format color_format)
> > +{
> > +     u32 val;
> > +
> > +     mtk_dp->info.format = color_format;
> > +
> > +     // Update MISC0
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> > +                        color_format << DP_TEST_COLOR_FORMAT_SHIFT,
> > +                        DP_TEST_COLOR_FORMAT_MASK);
> > +
> > +     switch (color_format) {
> > +     case MTK_DP_COLOR_FORMAT_YUV_422:
> > +             val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
> > +             break;
> > +     case MTK_DP_COLOR_FORMAT_YUV_420:
> > +             val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420;
> > +             break;
> > +     case MTK_DP_COLOR_FORMAT_YONLY:
> > +     case MTK_DP_COLOR_FORMAT_RAW:
> > +     case MTK_DP_COLOR_FORMAT_RESERVED:
> > +     case MTK_DP_COLOR_FORMAT_UNKNOWN:
> > +             drm_warn(mtk_dp->drm_dev, "Unsupported color format: %d\n",
> > +                      color_format);
> > +             fallthrough;
> > +     case MTK_DP_COLOR_FORMAT_RGB_444:
> > +     case MTK_DP_COLOR_FORMAT_YUV_444:
> > +             val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
> > +             break;
> > +     }
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
> > +                        PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp,
> > +                                enum mtk_dp_color_depth color_depth)
> > +{
> > +     u32 val;
> > +
> > +     mtk_dp->info.depth = color_depth;
> > +
> > +     // Update MISC0
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> > +                        color_depth << DP_TEST_BIT_DEPTH_SHIFT,
> > +                        DP_TEST_BIT_DEPTH_MASK);
> > +
> > +     switch (color_depth) {
> > +     case MTK_DP_COLOR_DEPTH_6BIT:
> > +             val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT;
> > +             break;
> > +     case MTK_DP_COLOR_DEPTH_8BIT:
> > +             val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT;
> > +             break;
> > +     case MTK_DP_COLOR_DEPTH_10BIT:
> > +             val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT;
> > +             break;
> > +     case MTK_DP_COLOR_DEPTH_12BIT:
> > +             val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT;
> > +             break;
> > +     case MTK_DP_COLOR_DEPTH_16BIT:
> > +             val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT;
> > +             break;
> > +     case MTK_DP_COLOR_DEPTH_UNKNOWN:
> > +             drm_warn(mtk_dp->drm_dev, "Unsupported color depth %d\n",
> > +                      color_depth);
> > +             return;
> > +     }
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
> > +                        VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_mn_overwrite_disable(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
> > +                        VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32 val)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> > +                        val << SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT,
> > +                        SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> > +                        BIT(VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT),
> > +                        VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> > +                        0x20 << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
> > +                        SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> > +                        0x20 << SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT,
> > +                        SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
> > +                        2 << VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT,
> > +                        VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> > +                        4 << FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT,
> > +                        FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
> > +     mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368,
> > +                  1 << VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT |
> > +                          1 << VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT |
> > +                          BIT(SDP_DP13_EN_DP_ENC1_P0_SHIFT) |
> > +                          1 << BS2BS_MODE_DP_ENC1_P0_SHIFT);
> > +}
> > +
> > +static void mtk_dp_pg_disable(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038, 0,
> > +                        VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
> > +                        4 << PGEN_PATTERN_SEL_SHIFT, PGEN_PATTERN_SEL_MASK);
> > +}
> > +
> > +static void mtk_dp_audio_setup_channels(struct mtk_dp *mtk_dp,
> > +                                     struct mtk_dp_audio_cfg *cfg)
> > +{
> > +     u32 channel_enable_bits;
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3324,
> > +                        AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX,
> > +                        AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK);
> > +
> > +     //audio channel count change reset
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(9), BIT(9));
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> > +                        AU_PRTY_REGEN_DP_ENC1_P0_MASK,
> > +                        AU_PRTY_REGEN_DP_ENC1_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> > +                        AU_CH_STS_REGEN_DP_ENC1_P0_MASK,
> > +                        AU_CH_STS_REGEN_DP_ENC1_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> > +                        AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK,
> > +                        AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK);
> > +
> > +     switch (cfg->channels) {
> > +     case 2:
> > +             channel_enable_bits = AUDIO_2CH_SEL_DP_ENC0_P0_MASK |
> > +                                   AUDIO_2CH_EN_DP_ENC0_P0_MASK;
> > +             break;
> > +     case 8:
> > +     default:
> > +             channel_enable_bits = AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
> > +                                   AUDIO_8CH_EN_DP_ENC0_P0_MASK;
> > +             break;
> > +     }
> > +     mtk_dp_update_bits(
> > +             mtk_dp, MTK_DP_ENC0_P0_3088,
> > +             channel_enable_bits | AU_EN_DP_ENC0_P0_MASK,
> > +             AUDIO_2CH_SEL_DP_ENC0_P0_MASK | AUDIO_2CH_EN_DP_ENC0_P0_MASK |
> > +                     AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
> > +                     AUDIO_8CH_EN_DP_ENC0_P0_MASK | AU_EN_DP_ENC0_P0_MASK);
> > +
> > +     //audio channel count change reset
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, 0, BIT(9));
> > +
> > +     //enable audio reset
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(0), BIT(0));
> > +}
> > +
> > +static void mtk_dp_audio_channel_status_set(struct mtk_dp *mtk_dp,
> > +                                         struct mtk_dp_audio_cfg *cfg)
> > +{
> > +     union mtk_dp_audio_channel_status channel_status;
> > +
> > +     memset(&channel_status, 0, sizeof(channel_status));
> > +
> > +     switch (cfg->sample_rate) {
> > +     case 32000:
> > +             channel_status.iec.sampling_freq = 3;
> > +             break;
> > +     case 44100:
> > +             channel_status.iec.sampling_freq = 0;
> > +             break;
> > +     case 48000:
> > +             channel_status.iec.sampling_freq = 2;
> > +             break;
> > +     case 88200:
> > +             channel_status.iec.sampling_freq = 8;
> > +             break;
> > +     case 96000:
> > +             channel_status.iec.sampling_freq = 0xA;
> > +             break;
> > +     case 192000:
> > +             channel_status.iec.sampling_freq = 0xE;
> > +             break;
> > +     default:
> > +             channel_status.iec.sampling_freq = 0x1;
> > +             break;
> > +     }
> > +
> > +     switch (cfg->word_length_bits) {
> > +     case 16:
> > +             channel_status.iec.word_len = 0x02;
> > +             break;
> > +     case 20:
> > +             channel_status.iec.word_len = 0x03;
> > +             break;
> > +     case 24:
> > +             channel_status.iec.word_len = 0x0B;
> > +             break;
> > +     }
>
> Yeah, this is provided by hdmi-codec through hdmi_codec_params in
> prepare.
>
Thx for the input, I'll use that instead.

> > +     // IEC 60958 consumer channel status bits
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_308C,
> > +                        channel_status.buf[1] << 8 | channel_status.buf[0],
> > +                        CH_STATUS_0_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3090,
> > +                        channel_status.buf[3] << 8 | channel_status.buf[2],
> > +                        CH_STATUS_1_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3094, channel_status.buf[4],
> > +                        CH_STATUS_2_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_audio_sdp_asp_set_channels(struct mtk_dp *mtk_dp,
> > +                                           int channels)
> > +{
> > +     if (channels != 2 && channels != 8)
> > +             channels = 8;
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_312C,
> > +                        (channels - 1) << ASP_HB3_DP_ENC0_P0_SHIFT,
> > +                        ASP_HB2_DP_ENC0_P0_MASK | ASP_HB3_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_audio_set_divider(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
> > +                        AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2,
> > +                        AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static bool mtk_dp_plug_state(struct mtk_dp *mtk_dp)
> > +{
> > +     return !!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
> > +               HPD_DB_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_sdp_trigger_packet(struct mtk_dp *mtk_dp,
> > +                                   enum mtk_dp_sdp_type type)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, type,
> > +                        SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, SDP_PACKET_W_DP_ENC1_P0,
> > +                        SDP_PACKET_W_DP_ENC1_P0);
> > +}
> > +
> > +static void mtk_dp_sdp_set_data(struct mtk_dp *mtk_dp, u8 *data_bytes)
> > +{
> > +     mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_ENC1_P0_3200, data_bytes, 0x10);
> > +}
> > +
> > +static void mtk_dp_sdp_set_header(struct mtk_dp *mtk_dp,
> > +                               enum mtk_dp_sdp_type type,
> > +                               struct dp_sdp_header *header)
> > +{
> > +     u32 db_addr;
> > +
> > +     switch (type) {
> > +     case MTK_DP_SDP_DRM:
> > +             db_addr = MTK_DP_ENC0_P0_3138;
> > +             break;
> > +     case MTK_DP_SDP_PPS0:
> > +     case MTK_DP_SDP_PPS1:
> > +     case MTK_DP_SDP_PPS2:
> > +     case MTK_DP_SDP_PPS3:
> > +             db_addr = MTK_DP_ENC0_P0_3130;
> > +             break;
> > +     default:
> > +             db_addr = MTK_DP_ENC0_P0_30D8 + (type - MTK_DP_SDP_ACM) * 8;
> > +     }
> > +
> > +     mtk_dp_bulk_16bit_write(mtk_dp, db_addr, (u8 *)header, 4);
> > +}
> > +
> > +static const u32 mtk_dp_sdp_type_to_reg[MTK_DP_SDP_MAX_NUM] = {
> > +     /* MTK_DP_SDP_NONE => */ 0x0,
> > +     /* MTK_DP_SDP_ACM  => */ MTK_DP_ENC0_P0_30B4,
> > +     /* MTK_DP_SDP_ISRC => */ MTK_DP_ENC0_P0_30B4 + 1,
> > +     /* MTK_DP_SDP_AVI  => */ MTK_DP_ENC0_P0_30A4 + 1,
> > +     /* MTK_DP_SDP_AUI  => */ MTK_DP_ENC0_P0_30A8,
> > +     /* MTK_DP_SDP_SPD  => */ MTK_DP_ENC0_P0_30A8 + 1,
> > +     /* MTK_DP_SDP_MPEG => */ MTK_DP_ENC0_P0_30AC,
> > +     /* MTK_DP_SDP_NTSC => */ MTK_DP_ENC0_P0_30AC + 1,
> > +     /* MTK_DP_SDP_VSP  => */ MTK_DP_ENC0_P0_30B0,
> > +     /* MTK_DP_SDP_VSC  => */ MTK_DP_ENC0_P0_30B8,
> > +     /* MTK_DP_SDP_EXT  => */ MTK_DP_ENC0_P0_30B0 + 1,
> > +     /* MTK_DP_SDP_PPS0 => */ MTK_DP_ENC0_P0_31E8,
> > +     /* MTK_DP_SDP_PPS1 => */ MTK_DP_ENC0_P0_31E8,
> > +     /* MTK_DP_SDP_PPS2 => */ MTK_DP_ENC0_P0_31E8,
> > +     /* MTK_DP_SDP_PPS3 => */ MTK_DP_ENC0_P0_31E8,
> > +     /* MTK_DP_SDP_DRM  => */ MTK_DP_ENC0_P0_31DC,
> > +};
> > +
> > +static void mtk_dp_disable_sdp(struct mtk_dp *mtk_dp, enum mtk_dp_sdp_type type)
> > +{
> > +     if (type == MTK_DP_SDP_NONE)
> > +             return;
> > +
> > +     // Disable periodic send
> > +     mtk_dp_update_bits(mtk_dp, mtk_dp_sdp_type_to_reg[type] & 0xfffc, 0,
> > +                        0xFF << ((mtk_dp_sdp_type_to_reg[type] & 3) * 8));
> > +}
> > +
> > +static void mtk_dp_setup_sdp(struct mtk_dp *mtk_dp,
> > +                          struct mtk_dp_sdp_packet *packet)
> > +{
> > +     mtk_dp_sdp_set_data(mtk_dp, packet->sdp.db);
> > +     mtk_dp_sdp_set_header(mtk_dp, packet->type, &packet->sdp.sdp_header);
> > +
> > +     mtk_dp_disable_sdp(mtk_dp, packet->type);
> > +
> > +     switch (packet->type) {
> > +     case MTK_DP_SDP_NONE:
> > +             break;
> > +     case MTK_DP_SDP_ISRC:
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> > +                                0x1C << ISRC1_HB3_DP_ENC0_P0_SHIFT,
> > +                                ISRC1_HB3_DP_ENC0_P0_MASK);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, MTK_DP_SDP_ISRC,
> > +                                SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
> > +
> > +             if (packet->sdp.sdp_header.HB3 & BIT(2))
> > +                     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
> > +                                        BIT(ISRC_CONT_DP_ENC0_P0_SHIFT),
> > +                                        ISRC_CONT_DP_ENC0_P0_MASK);
> > +             else
> > +                     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC, 0,
> > +                                        ISRC_CONT_DP_ENC0_P0_MASK);
> > +
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280,
> > +                                SDP_PACKET_W_DP_ENC1_P0,
> > +                                SDP_PACKET_W_DP_ENC1_P0);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30B4,
> > +                                5 << ISRC_CFG_DP_ENC0_P0_SHIFT,
> > +                                ISRC_CFG_DP_ENC0_P0_MASK);
> > +             break;
> > +     case MTK_DP_SDP_DRM:
> > +             mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31DC,
> > +                                5 << HDR0_CFG_DP_ENC0_P0_SHIFT,
> > +                                HDR0_CFG_DP_ENC0_P0_MASK);
> > +             break;
> > +     case MTK_DP_SDP_ACM:
> > +     case MTK_DP_SDP_AVI:
> > +     case MTK_DP_SDP_AUI:
> > +     case MTK_DP_SDP_SPD:
> > +     case MTK_DP_SDP_MPEG:
> > +     case MTK_DP_SDP_NTSC:
> > +     case MTK_DP_SDP_VSP:
> > +     case MTK_DP_SDP_VSC:
> > +     case MTK_DP_SDP_EXT:
> > +     case MTK_DP_SDP_PPS0:
> > +     case MTK_DP_SDP_PPS1:
> > +     case MTK_DP_SDP_PPS2:
> > +     case MTK_DP_SDP_PPS3:
> > +             mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
> > +             // Enable periodic sending
> > +             mtk_dp_update_bits(
> > +                     mtk_dp, mtk_dp_sdp_type_to_reg[packet->type] & 0xfffc,
> > +                     0x05 << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
> > +                              8),
> > +                     0xff << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
> > +                              8));
> > +             break;
> > +     default:
> > +             break;
> > +     }
> > +}
> > +
> > +static void mtk_dp_sdp_vsc_ext_disable(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A0, 0,
> > +                        BIT(7) | BIT(8) | BIT(12));
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_328C, 0, BIT(7));
> > +}
> > +
> > +static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_write(
> > +             mtk_dp, MTK_DP_AUX_P0_3640,
> > +             BIT(AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT));
> > +}
> > +
> > +static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32 addr)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644, cmd,
> > +                        MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648, addr,
> > +                        MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C, addr >> 16,
> > +                        MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_aux_cmd_complete(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> > +                        BIT(MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT),
> > +                        MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK |
> > +                                PHY_FIFO_RST_AUX_TX_P0_MASK |
> > +                                MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
> > +                        BIT(AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT),
> > +                        AUX_TX_REQUEST_READY_AUX_TX_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> > +                                    size_t length)
> > +{
> > +     mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf, length);
> > +}
> > +
> > +static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> > +                                 size_t length, int read_delay)
> > +{
> > +     int read_pos;
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620, 0,
> > +                        AUX_RD_MODE_AUX_TX_P0_MASK);
> > +
> > +     for (read_pos = 0; read_pos < length; read_pos++) {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> > +                                BIT(AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT),
> > +                                AUX_RX_FIFO_READ_PULSE_TX_P0_MASK);
> > +             usleep_range(read_delay, read_delay * 2);
> > +             buf[read_pos] =
> > +                     (u8)(mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3620) &
> > +                          AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK >>
> > +                                  AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT);
> > +     }
> > +}
> > +
> > +static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t length)
> > +{
> > +     if (length > 0) {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> > +                                (length - 1)
> > +                                        << MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT,
> > +                                MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
> > +                                AUX_NO_LENGTH_AUX_TX_P0_MASK |
> > +                                        AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> > +                                        AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> > +     } else {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> > +                                BIT(AUX_NO_LENGTH_AUX_TX_P0_SHIFT),
> > +                                AUX_NO_LENGTH_AUX_TX_P0_MASK |
> > +                                        AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> > +                                        AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> > +     }
> > +}
> > +
> > +static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp, bool is_read)
> > +{
> > +     int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
> > +
> > +     while (--wait_reply) {
> > +             u32 aux_irq_status;
> > +
> > +             if (is_read) {
> > +                     u32 fifo_status =
> > +                             mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3618);
> > +
> > +                     if (fifo_status &
> > +                         (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
> > +                          AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
> > +                             return 0;
> > +                     }
> > +             }
> > +
> > +             aux_irq_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3640);
> > +             if (aux_irq_status & AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK)
> > +                     return 0;
> > +
> > +             if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK)
> > +                     return -ETIMEDOUT;
> > +
> > +             usleep_range(100, 500);
> > +     }
> > +
> > +     return -ETIMEDOUT;
> > +}
> > +
> > +static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool is_read, u8 cmd,
> > +                               u32 addr, u8 *buf, size_t length)
> > +{
> > +     int ret;
> > +     u32 reply_cmd;
> > +
> > +     if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
> > +                     (cmd == DP_AUX_NATIVE_READ && !length)))
> > +             return -EINVAL;
> > +
> > +     if (!is_read)
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> > +                                BIT(AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT),
> > +                                AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK);
> > +
> > +     mtk_dp_aux_cmd_complete(mtk_dp);
> > +     mtk_dp_aux_irq_clear(mtk_dp);
> > +     usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> > +                  MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> > +
> > +     mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
> > +     mtk_dp_aux_set_length(mtk_dp, length);
> > +
> > +     if (!is_read) {
> > +             if (length)
> > +                     mtk_dp_aux_fill_write_fifo(mtk_dp, buf, length);
> > +
> > +             mtk_dp_update_bits(
> > +                     mtk_dp, MTK_DP_AUX_P0_3704,
> > +                     AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK,
> > +                     AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK);
> > +     }
> > +
> > +     mtk_dp_aux_request_ready(mtk_dp);
> > +
> > +     ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
> > +
> > +     reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
> > +                 AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
> > +
> > +     if (ret || reply_cmd) {
> > +             u32 phy_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3628) &
> > +                              AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
> > +             if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
> > +                     drm_err(mtk_dp->drm_dev,
> > +                             "AUX Rx Aux hang, need SW reset\n");
> > +                     ret = -EIO;
> > +             }
> > +
> > +             mtk_dp_aux_cmd_complete(mtk_dp);
> > +             mtk_dp_aux_irq_clear(mtk_dp);
> > +
> > +             usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> > +                          MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> > +             return -ETIMEDOUT;
> > +     }
> > +
> > +     if (!length) {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
> > +                                AUX_NO_LENGTH_AUX_TX_P0_MASK |
> > +                                        AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> > +                                        AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> > +     } else if (is_read) {
> > +             int read_delay;
> > +
> > +             if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
> > +                 cmd == DP_AUX_I2C_READ)
> > +                     read_delay = 500;
> > +             else
> > +                     read_delay = 100;
> > +
> > +             mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length, read_delay);
> > +     }
> > +
> > +     mtk_dp_aux_cmd_complete(mtk_dp);
> > +     mtk_dp_aux_irq_clear(mtk_dp);
> > +     usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> > +                  MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> > +
> > +     return 0;
> > +}
> > +
> > +static bool mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int lane_num,
> > +                                       int swing_val, int preemphasis)
> > +{
> > +     u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
> > +
> > +     if (lane_num < 0 || lane_num > 3)
> > +             return false;
> > +
> > +     dev_dbg(mtk_dp->dev,
> > +             "link training swing_val= 0x%x, preemphasis = 0x%x\n",
> > +             swing_val, preemphasis);
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> > +                        swing_val << (DP_TX0_VOLT_SWING_SHIFT + lane_shift),
> > +                        DP_TX0_VOLT_SWING_MASK << lane_shift);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> > +                        preemphasis << (DP_TX0_PRE_EMPH_SHIFT + lane_shift),
> > +                        DP_TX0_PRE_EMPH_MASK << lane_shift);
> > +
> > +     return true;
> > +}
> > +
> > +static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP, 0,
> > +                        DP_TX0_VOLT_SWING_MASK | DP_TX1_VOLT_SWING_MASK |
> > +                                DP_TX2_VOLT_SWING_MASK |
> > +                                DP_TX3_VOLT_SWING_MASK |
> > +                                DP_TX0_PRE_EMPH_MASK | DP_TX1_PRE_EMPH_MASK |
> > +                                DP_TX2_PRE_EMPH_MASK | DP_TX3_PRE_EMPH_MASK);
> > +}
> > +
> > +static void mtk_dp_fec_enable(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> > +                        enable ? BIT(FEC_EN_DP_TRANS_P0_SHIFT) : 0,
> > +                        FEC_EN_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
> > +{
> > +     u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
> > +                      SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
> > +
> > +     if (irq_status) {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, irq_status,
> > +                                SW_IRQ_CLR_DP_TRANS_P0_MASK);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, 0,
> > +                                SW_IRQ_CLR_DP_TRANS_P0_MASK);
> > +     }
> > +
> > +     return irq_status;
> > +}
> > +
> > +static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
> > +{
> > +     u8 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
> > +                      IRQ_STATUS_DP_TRANS_P0_MASK) >>
> > +                     IRQ_STATUS_DP_TRANS_P0_SHIFT;
> > +
> > +     if (irq_status) {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, irq_status,
> > +                                IRQ_CLR_DP_TRANS_P0_MASK);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, 0,
> > +                                IRQ_CLR_DP_TRANS_P0_MASK);
> > +     }
> > +
> > +     return irq_status;
> > +}
> > +
> > +static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     u32 val = 0;
> > +
> > +     if (!enable)
> > +             val = IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
> > +                   IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
> > +                   IRQ_MASK_DP_TRANS_P0_INT_IRQ;
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, val,
> > +                        IRQ_MASK_DP_TRANS_P0_MASK);
> > +}
> > +
> > +void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
> > +                        XTAL_FREQ_DP_TRANS_P0_DEFAULT,
> > +                        XTAL_FREQ_DP_TRANS_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> > +                        BIT(FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT),
> > +                        FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> > +                        BIT(AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT),
> > +                        AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
> > +                        SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK, IRQ_MASK_AUX_TOP_IRQ,
> > +                        IRQ_MASK_AUX_TOP_IRQ);
> > +}
> > +
> > +static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp *mtk_dp)
> > +{
> > +     // Debounce threshold
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> > +                        8 << HPD_DEB_THD_DP_TRANS_P0_SHIFT,
> > +                        HPD_DEB_THD_DP_TRANS_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> > +                        (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
> > +                         HPD_INT_THD_DP_TRANS_P0_UPPER_1100US)
> > +                                << HPD_INT_THD_DP_TRANS_P0_SHIFT,
> > +                        HPD_INT_THD_DP_TRANS_P0_MASK);
> > +
> > +     // Connect threshold 1.5ms + 5 x 0.1ms = 2ms
> > +     // Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> > +                        (5 << HPD_DISC_THD_DP_TRANS_P0_SHIFT) |
> > +                                (5 << HPD_CONN_THD_DP_TRANS_P0_SHIFT),
> > +                        HPD_DISC_THD_DP_TRANS_P0_MASK |
> > +                                HPD_CONN_THD_DP_TRANS_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
> > +                        HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
> > +                        HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
> > +{
> > +     // modify timeout threshold = 1595 [12 : 8]
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C, 0x1595,
> > +                        AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658, 0,
> > +                        AUX_TX_OV_EN_AUX_TX_P0_MASK);
> > +     // 25 for 26M
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
> > +                        25 << AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT,
> > +                        AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
> > +     // 13 for 26M
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
> > +                        13 << AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT,
> > +                        AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
> > +                        BIT(MTK_ATOP_EN_AUX_TX_P0_SHIFT),
> > +                        MTK_ATOP_EN_AUX_TX_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_initialize_digital_settings(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
> > +                        VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
> > +     mtk_dp_set_color_format(mtk_dp, MTK_DP_COLOR_FORMAT_RGB_444);
> > +     mtk_dp_set_color_depth(mtk_dp, MTK_DP_COLOR_DEPTH_8BIT);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
> > +                        1 << BS2BS_MODE_DP_ENC1_P0_SHIFT,
> > +                        BS2BS_MODE_DP_ENC1_P0_MASK);
> > +
> > +     // dp tx encoder reset all sw
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> > +                        BIT(DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT),
> > +                        DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
> > +     usleep_range(1000, 5000);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
> > +                        DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> > +                        BIT(DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT),
> > +                        DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
> > +     usleep_range(1000, 5000);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C, 0,
> > +                        DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
> > +                        lanes == 0 ? 0 : BIT(3), BIT(3) | BIT(2));
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, lanes,
> > +                        LANE_NUM_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
> > +                        lanes << LANE_NUM_DP_TRANS_P0_SHIFT,
> > +                        LANE_NUM_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static int link_rate_to_mb_per_s(struct mtk_dp *mtk_dp,
> > +                              enum mtk_dp_linkrate linkrate)
> > +{
> > +     switch (linkrate) {
> > +     default:
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Implementation error, unknown linkrate %d\n",
> > +                     linkrate);
> > +             fallthrough;
> > +     case MTK_DP_LINKRATE_RBR:
> > +             return 1620;
> > +     case MTK_DP_LINKRATE_HBR:
> > +             return 2700;
> > +     case MTK_DP_LINKRATE_HBR2:
> > +             return 5400;
> > +     case MTK_DP_LINKRATE_HBR3:
> > +             return 8100;
> > +     }
> > +}
> > +
> > +static u32 check_cal_data_valid(u32 min, u32 max, u32 val, u32 default_val)
> > +{
> > +     if (val < min || val > max)
> > +             return default_val;
> > +
> > +     return val;
> > +}
> > +
> > +static int mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
> > +{
> > +     struct dp_cal_data *cal_data = &mtk_dp->cal_data;
> > +     struct device *dev = mtk_dp->dev;
> > +     struct nvmem_cell *cell;
> > +     u32 *buf;
> > +     size_t len;
> > +
> > +     cell = nvmem_cell_get(dev, "dp_calibration_data");
> > +     if (IS_ERR(cell)) {
> > +             dev_err(dev,
> > +                     "Error: Failed to get nvmem cell dp_calibration_data\n");
> > +             return PTR_ERR(cell);
> > +     }
> > +
> > +     buf = (u32 *)nvmem_cell_read(cell, &len);
> > +     nvmem_cell_put(cell);
> > +
> > +     if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
> > +             dev_err(dev,
> > +                     "Error: Failed to read nvmem_cell_read fail len %ld\n",
> > +                     (len / sizeof(u32)));
> > +             return PTR_ERR(buf);
> > +     }
> > +
> > +     if (mtk_dp_is_edp(mtk_dp)) {
> > +             cal_data->glb_bias_trim = check_cal_data_valid(
> > +                     1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf);
> > +             cal_data->clktx_impse =
> > +                     check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8);
> > +             cal_data->ln0_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8);
> > +             cal_data->ln0_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8);
> > +             cal_data->ln1_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8);
> > +             cal_data->ln1_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8);
> > +             cal_data->ln2_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8);
> > +             cal_data->ln2_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8);
> > +             cal_data->ln3_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8);
> > +             cal_data->ln3_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8);
> > +     } else {
> > +             cal_data->glb_bias_trim = check_cal_data_valid(
> > +                     1, 0x1e, (buf[0] >> 27) & 0x1f, 0xf);
> > +             cal_data->clktx_impse =
> > +                     check_cal_data_valid(1, 0xe, (buf[0] >> 13) & 0xf, 0x8);
> > +             cal_data->ln0_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 28) & 0xf, 0x8);
> > +             cal_data->ln0_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 24) & 0xf, 0x8);
> > +             cal_data->ln1_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 20) & 0xf, 0x8);
> > +             cal_data->ln1_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 16) & 0xf, 0x8);
> > +             cal_data->ln2_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 12) & 0xf, 0x8);
> > +             cal_data->ln2_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 8) & 0xf, 0x8);
> > +             cal_data->ln3_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 4) & 0xf, 0x8);
> > +             cal_data->ln3_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, buf[1] & 0xf, 0x8);
> > +     }
> > +
> > +     kfree(buf);
> > +
> > +     return 0;
> > +}
> > +
> > +void mtk_dp_set_cal_data(struct mtk_dp *mtk_dp)
> > +{
> > +     struct dp_cal_data *cal_data = &mtk_dp->cal_data;
> > +
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
> > +                        cal_data->clktx_impse << 20, RG_CKM_PT0_CKTX_IMPSEL);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
> > +                        cal_data->glb_bias_trim << 16,
> > +                        RG_XTP_GLB_BIAS_INTR_CTRL);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> > +                        cal_data->ln0_tx_impsel_pmos << 12,
> > +                        RG_XTP_LN0_TX_IMPSEL_PMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> > +                        cal_data->ln0_tx_impsel_nmos << 16,
> > +                        RG_XTP_LN0_TX_IMPSEL_NMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> > +                        cal_data->ln1_tx_impsel_pmos << 12,
> > +                        RG_XTP_LN1_TX_IMPSEL_PMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> > +                        cal_data->ln1_tx_impsel_nmos << 16,
> > +                        RG_XTP_LN1_TX_IMPSEL_NMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> > +                        cal_data->ln2_tx_impsel_pmos << 12,
> > +                        RG_XTP_LN2_TX_IMPSEL_PMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> > +                        cal_data->ln2_tx_impsel_nmos << 16,
> > +                        RG_XTP_LN2_TX_IMPSEL_NMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> > +                        cal_data->ln3_tx_impsel_pmos << 12,
> > +                        RG_XTP_LN3_TX_IMPSEL_PMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> > +                        cal_data->ln3_tx_impsel_nmos << 16,
> > +                        RG_XTP_LN3_TX_IMPSEL_NMOS);
> > +}
> > +
> > +static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
> > +                             enum mtk_dp_linkrate link_rate, int lane_count)
> > +{
> > +     int ret;
> > +     union phy_configure_opts
> > +             phy_opts = { .dp = {
> > +                                  .link_rate = link_rate_to_mb_per_s(
> > +                                          mtk_dp, link_rate),
> > +                                  .set_rate = 1,
> > +                                  .lanes = lane_count,
> > +                                  .set_lanes = 1,
> > +                                  .ssc = mtk_dp->train_info.sink_ssc,
> > +                          } };
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP,
> > +                        DP_PWR_STATE_MASK);
> > +
> > +     ret = phy_configure(mtk_dp->phy, &phy_opts);
> > +
> > +     mtk_dp_set_cal_data(mtk_dp);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> > +                        DP_PWR_STATE_BANDGAP_TPLL_LANE, DP_PWR_STATE_MASK);
> > +     return ret;
> > +}
> > +
> > +static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     const u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
> > +                     POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
> > +                     POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
> > +                     POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580, enable ? val : 0, val);
> > +}
> > +
> > +static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int pattern)
> > +{
> > +     if (pattern < 0 || pattern > 4) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Implementation error, no such pattern %d\n", pattern);
> > +             return;
> > +     }
> > +
> > +     if (pattern == 1) // TPS1
> > +             mtk_dp_set_idle_pattern(mtk_dp, false);
> > +
> > +     mtk_dp_update_bits(
> > +             mtk_dp, MTK_DP_TRANS_P0_3400,
> > +             pattern ? BIT(pattern - 1) << PATTERN1_EN_DP_TRANS_P0_SHIFT : 0,
> > +             PATTERN1_EN_DP_TRANS_P0_MASK | PATTERN2_EN_DP_TRANS_P0_MASK |
> > +                     PATTERN3_EN_DP_TRANS_P0_MASK |
> > +                     PATTERN4_EN_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> > +                        enable ? BIT(ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT) : 0,
> > +                        ENHANCED_FRAME_EN_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
> > +                        enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
> > +                        DP_SCR_EN_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     u32 val = BIT(VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT);
> > +
> > +     if (enable)
> > +             val |= BIT(VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, val,
> > +                        VIDEO_MUTE_SEL_DP_ENC0_P0_MASK |
> > +                                VIDEO_MUTE_SW_DP_ENC0_P0_MASK);
> > +
> > +     if (mtk_dp_is_edp(mtk_dp))
> > +             mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable);
> > +     else
> > +             mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable);
> > +}
> > +
> > +static void mtk_dp_audio_mute(struct mtk_dp *mtk_dp, bool mute)
> > +{
> > +     if (mute) {
> > +             mtk_dp_update_bits(
> > +                     mtk_dp, MTK_DP_ENC0_P0_3030,
> > +                     BIT(VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT) |
> > +                             BIT(VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT),
> > +                     VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
> > +                             VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
> > +
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088, 0,
> > +                                AU_EN_DP_ENC0_P0_MASK);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0,
> > +                                AU_TS_CFG_DP_ENC0_P0_MASK);
> > +
> > +     } else {
> > +             mtk_dp_update_bits(
> > +                     mtk_dp, MTK_DP_ENC0_P0_3030, 0,
> > +                     VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
> > +                             VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
> > +
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088,
> > +                                BIT(AU_EN_DP_ENC0_P0_SHIFT),
> > +                                AU_EN_DP_ENC0_P0_MASK);
> > +             // Send one every two frames
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0x0F,
> > +                                AU_TS_CFG_DP_ENC0_P0_MASK);
> > +     }
> > +}
> > +
> > +static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, 0,
> > +                        SW_RST_B_PHYD);
> > +     usleep_range(10, 200);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, SW_RST_B_PHYD,
> > +                        SW_RST_B_PHYD);
> > +}
> > +
> > +static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
> > +     usleep_range(10, 200);
> > +     mtk_dp_write(mtk_dp, MTK_DP_0034,
> > +                  DA_CKM_CKTX0_EN_FORCE_EN | DA_CKM_BIAS_LPF_EN_FORCE_VAL |
> > +                          DA_CKM_BIAS_EN_FORCE_VAL |
> > +                          DA_XTP_GLB_LDO_EN_FORCE_VAL |
> > +                          DA_XTP_GLB_AVD10_ON_FORCE_VAL);
> > +     // Disable RX
> > +     mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
> > +     mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
> > +                  0x550 | BIT(FUSE_SEL_SHIFT) | BIT(MEM_ISO_EN_SHIFT));
> > +}
> > +
> > +static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR2;
> > +     mtk_dp->train_info.lane_count = MTK_DP_MAX_LANES;
> > +     mtk_dp->train_info.irq_status = 0;
> > +     mtk_dp->train_info.cable_plugged_in = false;
> > +     mtk_dp->train_info.cable_state_change = false;
> > +     mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > +     mtk_dp->state = MTK_DP_STATE_INITIAL;
> > +
> > +     mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
> > +     mtk_dp->info.depth = MTK_DP_COLOR_DEPTH_8BIT;
> > +     memset(&mtk_dp->info.timings, 0, sizeof(struct mtk_dp_timings));
> > +     mtk_dp->info.timings.frame_rate = 60;
> > +
> > +     mtk_dp->has_fec = false;
> > +     mtk_dp->audio_enable = false;
> > +}
> > +
> > +static void mtk_dp_sdp_set_down_cnt_init(struct mtk_dp *mtk_dp,
> > +                                      u32 sram_read_start)
> > +{
> > +     u32 sdp_down_cnt_init = 0;
> > +     u32 dc_offset;
> > +
> > +     if (mtk_dp->info.timings.pix_rate_khz > 0)
> > +             sdp_down_cnt_init = sram_read_start *
> > +                                 mtk_dp->train_info.link_rate * 2700 * 8 /
> > +                                 (mtk_dp->info.timings.pix_rate_khz * 4);
> > +
> > +     switch (mtk_dp->train_info.lane_count) {
> > +     case 1:
> > +             sdp_down_cnt_init = sdp_down_cnt_init > 0x1A ?
> > +                                               sdp_down_cnt_init :
> > +                                               0x1A; //26
> > +             break;
> > +     case 2:
> > +             // case for LowResolution && High Audio Sample Rate
> > +             dc_offset = mtk_dp->info.timings.vtotal <= 525 ? 0x04 : 0x00;
> > +             sdp_down_cnt_init = sdp_down_cnt_init > 0x10 ?
> > +                                               sdp_down_cnt_init :
> > +                                               0x10 + dc_offset; //20 or 16
> > +             break;
> > +     case 4:
> > +     default:
> > +             sdp_down_cnt_init =
> > +                     sdp_down_cnt_init > 0x06 ? sdp_down_cnt_init : 0x06; //6
> > +             break;
> > +     }
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> > +                        sdp_down_cnt_init
> > +                                << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
> > +                        SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_sdp_set_down_cnt_init_in_hblank(struct mtk_dp *mtk_dp)
> > +{
> > +     int pix_clk_mhz;
> > +     u32 dc_offset;
> > +     u32 spd_down_cnt_init = 0;
> > +
> > +     pix_clk_mhz = mtk_dp->info.format == MTK_DP_COLOR_FORMAT_YUV_420 ?
> > +                                 mtk_dp->info.timings.pix_rate_khz / 2000 :
> > +                                 mtk_dp->info.timings.pix_rate_khz / 1000;
> > +
> > +     switch (mtk_dp->train_info.lane_count) {
> > +     case 1:
> > +             spd_down_cnt_init = 0x20;
> > +             break;
> > +     case 2:
> > +             dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x14 : 0x00;
> > +             spd_down_cnt_init = 0x18 + dc_offset;
> > +             break;
> > +     case 4:
> > +     default:
> > +             dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x08 : 0x00;
> > +             if (pix_clk_mhz > mtk_dp->train_info.link_rate * 27)
> > +                     spd_down_cnt_init = 0x8;
> > +             else
> > +                     spd_down_cnt_init = 0x10 + dc_offset;
> > +             break;
> > +     }
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364, spd_down_cnt_init,
> > +                        SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
> > +{
> > +     u32 sram_read_start = MTK_DP_TBC_BUF_READ_START_ADDR;
> > +
> > +     if (mtk_dp->train_info.lane_count > 0) {
> > +             sram_read_start = min_t(
> > +                     u32, MTK_DP_TBC_BUF_READ_START_ADDR,
> > +                     mtk_dp->info.timings.vm.hactive /
> > +                             (mtk_dp->train_info.lane_count * 4 * 2 * 2));
> > +             mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
> > +     }
> > +
> > +     mtk_dp_setup_encoder(mtk_dp);
> > +     mtk_dp_sdp_set_down_cnt_init_in_hblank(mtk_dp);
> > +     mtk_dp_sdp_set_down_cnt_init(mtk_dp, sram_read_start);
> > +}
> > +
> > +static void mtk_dp_calculate_pixrate(struct mtk_dp *mtk_dp)
> > +{
> > +     int target_frame_rate = 60;
> > +     int target_pixel_clk;
> > +
> > +     if (mtk_dp->info.timings.frame_rate > 0) {
> > +             target_frame_rate = mtk_dp->info.timings.frame_rate;
> > +             target_pixel_clk = (int)mtk_dp->info.timings.htotal *
> > +                                (int)mtk_dp->info.timings.vtotal *
> > +                                target_frame_rate;
> > +     } else if (mtk_dp->info.timings.pix_rate_khz > 0) {
> > +             target_pixel_clk = mtk_dp->info.timings.pix_rate_khz * 1000;
> > +     } else {
> > +             target_pixel_clk = (int)mtk_dp->info.timings.htotal *
> > +                                (int)mtk_dp->info.timings.vtotal *
> > +                                target_frame_rate;
> > +     }
> > +
> > +     if (target_pixel_clk > 0)
> > +             mtk_dp->info.timings.pix_rate_khz = target_pixel_clk / 1000;
> > +}
> > +
> > +static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_msa_bypass_disable(mtk_dp);
> > +     mtk_dp_calculate_pixrate(mtk_dp);
> > +     mtk_dp_pg_disable(mtk_dp);
> > +     mtk_dp_setup_tu(mtk_dp);
> > +}
> > +
> > +static void mtk_dp_edid_free(struct mtk_dp *mtk_dp)
> > +{
> > +     mutex_lock(&mtk_dp->edid_lock);
> > +     kfree(mtk_dp->edid);
> > +     mtk_dp->edid = NULL;
> > +     mutex_unlock(&mtk_dp->edid_lock);
> > +}
> > +
> > +static void mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp)
> > +{
> > +     ssize_t ret;
> > +     u8 sink_count;
> > +     u8 link_status[DP_LINK_STATUS_SIZE] = {};
> > +     u32 sink_count_reg;
> > +     u32 link_status_reg;
> > +     bool locked;
> > +
> > +     sink_count_reg = DP_SINK_COUNT_ESI;
> > +     link_status_reg = DP_LANE0_1_STATUS;
> > +
> > +     ret = drm_dp_dpcd_readb(&mtk_dp->aux, sink_count_reg, &sink_count);
> > +     if (ret < 0) {
> > +             drm_info(mtk_dp->drm_dev, "Read sink count failed: %ld\n", ret);
> > +             return;
> > +     }
> > +
> > +     ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
> > +     if (ret < 0) {
> > +             drm_info(mtk_dp->drm_dev,
> > +                      "Read DP_SINK_COUNT_ESI failed: %ld\n", ret);
> > +             return;
> > +     }
> > +
> > +     ret = drm_dp_dpcd_read(&mtk_dp->aux, link_status_reg, link_status,
> > +                            sizeof(link_status));
> > +     if (!ret) {
> > +             drm_info(mtk_dp->drm_dev, "Read link status failed: %ld\n",
> > +                      ret);
> > +             return;
> > +     }
> > +
> > +     locked = drm_dp_channel_eq_ok(link_status,
> > +                                   mtk_dp->train_info.lane_count);
> > +     if (!locked && mtk_dp->train_state > MTK_DP_TRAIN_STATE_TRAINING_PRE)
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
> > +
> > +     if (link_status[1] & DP_REMOTE_CONTROL_COMMAND_PENDING)
> > +             drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR,
> > +                                DP_REMOTE_CONTROL_COMMAND_PENDING);
> > +
> > +     if (DP_GET_SINK_COUNT(sink_count) &&
> > +         (link_status[2] & DP_DOWNSTREAM_PORT_STATUS_CHANGED)) {
> > +             mtk_dp_edid_free(mtk_dp);
> > +             mtk_dp->train_info.check_cap_count = 0;
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
> > +             msleep(20);
> > +     }
> > +}
> > +
> > +static void mtk_dp_sdp_stop_sending(struct mtk_dp *mtk_dp)
> > +{
> > +     u8 packet_type;
> > +
> > +     for (packet_type = MTK_DP_SDP_ACM; packet_type < MTK_DP_SDP_MAX_NUM;
> > +          packet_type++)
> > +             mtk_dp_disable_sdp(mtk_dp, packet_type);
> > +
> > +     mtk_dp_sdp_vsc_ext_disable(mtk_dp);
> > +}
> > +
> > +static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes,
> > +                                       u8 dpcd_adjust_req[2])
> > +{
> > +     int lane;
> > +
> > +     for (lane = 0; lane < lanes; ++lane) {
> > +             u8 val;
> > +             u8 swing;
> > +             u8 preemphasis;
> > +             int index = lane / 2;
> > +             int shift = lane % 2 ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
> > +
> > +             swing = (dpcd_adjust_req[index] >> shift) &
> > +                     DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
> > +             preemphasis = ((dpcd_adjust_req[index] >> shift) &
> > +                            DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
> > +                           DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
> > +             val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
> > +                   preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
> > +
> > +             if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
> > +                     val |= DP_TRAIN_MAX_SWING_REACHED;
> > +             if (preemphasis == 3)
> > +                     val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> > +
> > +             mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing, preemphasis);
> > +             drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET + lane,
> > +                                val);
> > +     }
> > +
> > +     // Wait for the signal to be stable enough
> > +     usleep_range(2000, 5000);
> > +}
> > +
> > +static void mtk_dp_read_link_status(struct mtk_dp *mtk_dp,
> > +                                 u8 link_status[DP_LINK_STATUS_SIZE])
> > +{
> > +     drm_dp_dpcd_read(&mtk_dp->aux, DP_LANE0_1_STATUS, link_status,
> > +                      DP_LINK_STATUS_SIZE);
> > +}
> > +
> > +static int mtk_dp_train_flow(struct mtk_dp *mtk_dp, int target_link_rate,
> > +                          int target_lane_count)
> > +{
> > +     u8 link_status[DP_LINK_STATUS_SIZE] = {};
> > +     u8 lane_adjust[2] = {};
> > +     bool pass_tps1 = false;
> > +     bool pass_tps2_3 = false;
> > +     int train_retries;
> > +     int status_control;
> > +     int iteration_count;
> > +     u8 prev_lane_adjust;
> > +     u8 val;
> > +
> > +     drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET, target_link_rate);
> > +     drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> > +                        target_lane_count | DP_LANE_COUNT_ENHANCED_FRAME_EN);
> > +
> > +     if (mtk_dp->train_info.sink_ssc)
> > +             drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
> > +                                DP_SPREAD_AMP_0_5);
> > +
> > +     train_retries = 0;
> > +     status_control = 0;
> > +     iteration_count = 1;
> > +     prev_lane_adjust = 0xFF;
> > +
> > +     mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
> > +     mtk_dp_phy_configure(mtk_dp, target_link_rate, target_lane_count);
> > +
> > +     dev_dbg(mtk_dp->dev,
> > +             "Link train target_link_rate = 0x%x, target_lane_count = 0x%x\n",
> > +             target_link_rate, target_lane_count);
> > +
> > +     do {
> > +             train_retries++;
> > +             if (!mtk_dp->train_info.cable_plugged_in ||
> > +                 ((mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) !=
> > +                  0x0)) {
> > +                     return -ENODEV;
> > +             }
> > +
> > +             if (mtk_dp->train_state < MTK_DP_TRAIN_STATE_TRAINING)
> > +                     return -EAGAIN;
> > +
> > +             if (!pass_tps1) {
> > +                     mtk_dp_training_set_scramble(mtk_dp, false);
> > +
> > +                     if (status_control == 0) {
> > +                             status_control = 1;
> > +                             mtk_dp_train_set_pattern(mtk_dp, 1);
> > +                             val = DP_LINK_SCRAMBLING_DISABLE |
> > +                                   DP_TRAINING_PATTERN_1;
> > +                             drm_dp_dpcd_writeb(
> > +                                     &mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> > +                                     DP_LINK_SCRAMBLING_DISABLE |
> > +                                             DP_TRAINING_PATTERN_1);
> > +                             drm_dp_dpcd_read(&mtk_dp->aux,
> > +                                              DP_ADJUST_REQUEST_LANE0_1,
> > +                                              lane_adjust,
> > +                                              sizeof(lane_adjust));
> > +                             iteration_count++;
> > +
> > +                             mtk_dp_train_update_swing_pre(
> > +                                     mtk_dp, target_lane_count, lane_adjust);
> > +                     }
> > +
> > +                     drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
> > +                                                            mtk_dp->rx_cap);
> > +                     mtk_dp_read_link_status(mtk_dp, link_status);
> > +
> > +                     if (drm_dp_clock_recovery_ok(link_status,
> > +                                                  target_lane_count)) {
> > +                             mtk_dp->train_info.cr_done = true;
> > +                             pass_tps1 = true;
> > +                             train_retries = 0;
> > +                             iteration_count = 1;
> > +                             dev_dbg(mtk_dp->dev, "Link train CR pass\n");
> > +                     } else if (prev_lane_adjust == link_status[4]) {
> > +                             iteration_count++;
> > +                             if (prev_lane_adjust &
> > +                                 DP_ADJUST_VOLTAGE_SWING_LANE0_MASK)
> > +                                     break;
> > +                     } else {
> > +                             prev_lane_adjust = link_status[4];
> > +                     }
> > +                     dev_dbg(mtk_dp->dev, "Link train CQ fail\n");
> > +             } else if (pass_tps1 && !pass_tps2_3) {
> > +                     if (status_control == 1) {
> > +                             status_control = 2;
> > +                             if (mtk_dp->train_info.tps4) {
> > +                                     mtk_dp_train_set_pattern(mtk_dp, 4);
> > +                                     val = DP_TRAINING_PATTERN_4;
> > +                             } else if (mtk_dp->train_info.tps3) {
> > +                                     mtk_dp_train_set_pattern(mtk_dp, 3);
> > +                                     val = DP_LINK_SCRAMBLING_DISABLE |
> > +                                           DP_TRAINING_PATTERN_3;
> > +                             } else {
> > +                                     mtk_dp_train_set_pattern(mtk_dp, 2);
> > +                                     val = DP_LINK_SCRAMBLING_DISABLE |
> > +                                           DP_TRAINING_PATTERN_2;
> > +                             }
> > +                             drm_dp_dpcd_writeb(&mtk_dp->aux,
> > +                                                DP_TRAINING_PATTERN_SET,
> > +                                                val);
> > +
> > +                             drm_dp_dpcd_read(&mtk_dp->aux,
> > +                                              DP_ADJUST_REQUEST_LANE0_1,
> > +                                              lane_adjust,
> > +                                              sizeof(lane_adjust));
> > +
> > +                             iteration_count++;
> > +                             mtk_dp_train_update_swing_pre(
> > +                                     mtk_dp, target_lane_count, lane_adjust);
> > +                     }
> > +
> > +                     drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
> > +                                                        mtk_dp->rx_cap);
> > +
> > +                     mtk_dp_read_link_status(mtk_dp, link_status);
> > +
> > +                     if (!drm_dp_clock_recovery_ok(link_status,
> > +                                                   target_lane_count)) {
> > +                             mtk_dp->train_info.cr_done = false;
> > +                             mtk_dp->train_info.eq_done = false;
> > +                             dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
> > +                             break;
> > +                     }
> > +
> > +                     if (drm_dp_channel_eq_ok(link_status,
> > +                                              target_lane_count)) {
> > +                             mtk_dp->train_info.eq_done = true;
> > +                             pass_tps2_3 = true;
> > +                             dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
> > +                             break;
> > +                     }
> > +
> > +                     if (prev_lane_adjust == link_status[4])
> > +                             iteration_count++;
> > +                     else
> > +                             prev_lane_adjust = link_status[4];
> > +             }
> > +
> > +             drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
> > +                              lane_adjust, sizeof(lane_adjust));
> > +             mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
> > +                                           lane_adjust);
> > +     } while (train_retries < MTK_DP_TRAIN_RETRY_LIMIT &&
> > +              iteration_count < MTK_DP_TRAIN_MAX_ITERATIONS);
> > +
> > +     drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> > +                        DP_TRAINING_PATTERN_DISABLE);
> > +     mtk_dp_train_set_pattern(mtk_dp, 0);
> > +
> > +     if (pass_tps2_3) {
> > +             mtk_dp->train_info.link_rate = target_link_rate;
> > +             mtk_dp->train_info.lane_count = target_lane_count;
> > +
> > +             mtk_dp_training_set_scramble(mtk_dp, true);
> > +
> > +             drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> > +                                target_lane_count |
> > +                                        DP_LANE_COUNT_ENHANCED_FRAME_EN);
> > +             mtk_dp_set_enhanced_frame_mode(mtk_dp, true);
> > +
> > +             return 0;
> > +     }
> > +
> > +     return -ETIMEDOUT;
> > +}
> > +
> > +static bool mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
> > +{
> > +     u8 buf[DP_RECEIVER_CAP_SIZE] = {};
> > +     u8 val;
> > +     struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
> > +
> > +     if (!mtk_dp_plug_state(mtk_dp))
> > +             return false;
> > +
> > +     drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> > +     usleep_range(2000, 5000);
> > +
> > +     drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
> > +
> > +     memcpy(mtk_dp->rx_cap, buf, min(sizeof(mtk_dp->rx_cap), sizeof(buf)));
> > +     mtk_dp->rx_cap[DP_TRAINING_AUX_RD_INTERVAL] &= DP_TRAINING_AUX_RD_MASK;
> > +
> > +     train_info->link_rate =
> > +             min_t(int, MTK_DP_MAX_LINK_RATE, buf[DP_MAX_LINK_RATE]);
> > +     train_info->lane_count =
> > +             min_t(int, MTK_DP_MAX_LANES, drm_dp_max_lane_count(buf));
> > +
> > +     train_info->tps3 = drm_dp_tps3_supported(buf);
> > +     train_info->tps4 = drm_dp_tps4_supported(buf);
> > +
> > +     train_info->sink_ssc =
> > +             !!(buf[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5);
> > +
> > +     train_info->sink_ssc = false;
> > +
> > +     drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
> > +     if (val & DP_MST_CAP) {
> > +             // Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0
> > +             drm_dp_dpcd_readb(&mtk_dp->aux,
> > +                               DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0, &val);
> > +             if (val)
> > +                     drm_dp_dpcd_writeb(&mtk_dp->aux,
> > +                                        DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
> > +                                        val);
> > +     }
> > +
> > +     return true;
> > +}
> > +
> > +static int mtk_dp_edid_parse_audio_capabilities(struct mtk_dp *mtk_dp,
> > +                                             struct mtk_dp_audio_cfg *cfg)
> > +{
> > +     struct cea_sad *sads;
> > +     int sad_count;
> > +     int i;
> > +     int ret = 0;
> > +
> > +     mutex_lock(&mtk_dp->edid_lock);
> > +     if (!mtk_dp->edid) {
> > +             mutex_unlock(&mtk_dp->edid_lock);
> > +             dev_err(mtk_dp->dev, "EDID not found!\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     sad_count = drm_edid_to_sad(mtk_dp->edid, &sads);
> > +     mutex_unlock(&mtk_dp->edid_lock);
> > +     if (sad_count <= 0) {
> > +             drm_info(mtk_dp->drm_dev, "The SADs is NULL\n");
> > +             return 0;
> > +     }
> > +
> > +     for (i = 0; i < sad_count; i++) {
> > +             int sample_rate;
> > +             int word_length;
> > +             // Only PCM supported at the moment
> > +             if (sads[i].format != HDMI_AUDIO_CODING_TYPE_PCM)
> > +                     continue;
> > +
> > +             sample_rate = drm_cea_sad_get_sample_rate(&sads[i]);
> > +             word_length =
> > +                     drm_cea_sad_get_uncompressed_word_length(&sads[i]);
> > +             if (sample_rate <= 0 || word_length <= 0)
> > +                     continue;
> > +
> > +             cfg->channels = sads[i].channels;
> > +             cfg->word_length_bits = word_length;
> > +             cfg->sample_rate = sample_rate;
> > +             ret = 1;
> > +             break;
> > +     }
> > +     kfree(sads);
> > +
> > +     return ret;
> > +}
> > +
> > +static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
> > +{
> > +     phy_reset(mtk_dp->phy);
> > +     mtk_dp_reset_swing_pre_emphasis(mtk_dp);
> > +
> > +     usleep_range(2000, 5000);
> > +}
> > +
> > +static int mtk_dp_train_start(struct mtk_dp *mtk_dp)
> > +{
> > +     int ret = 0;
> > +     int lane_count;
> > +     int link_rate;
> > +     int train_limit;
> > +     int max_link_rate;
> > +     int plug_wait;
> > +
> > +     for (plug_wait = 7; !mtk_dp_plug_state(mtk_dp) && plug_wait > 0;
> > +          --plug_wait)
> > +             usleep_range(1000, 5000);
> > +     if (plug_wait == 0) {
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> > +             return -ENODEV;
> > +     }
> > +
> > +     link_rate = mtk_dp->rx_cap[1];
> > +     lane_count = mtk_dp->rx_cap[2] & 0x1F;
> > +
> > +     mtk_dp->train_info.link_rate = min(MTK_DP_MAX_LINK_RATE, link_rate);
> > +     mtk_dp->train_info.lane_count = min(MTK_DP_MAX_LANES, lane_count);
> > +     link_rate = mtk_dp->train_info.link_rate;
> > +     lane_count = mtk_dp->train_info.lane_count;
> > +
> > +     switch (link_rate) {
> > +     case MTK_DP_LINKRATE_RBR:
> > +     case MTK_DP_LINKRATE_HBR:
> > +     case MTK_DP_LINKRATE_HBR2:
> > +     case MTK_DP_LINKRATE_HBR25:
> > +     case MTK_DP_LINKRATE_HBR3:
> > +             break;
> > +     default:
> > +             mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR3;
> > +             break;
> > +     };
> > +
> > +     max_link_rate = link_rate;
> > +     for (train_limit = 0; train_limit <= 6; ++train_limit) {
> > +             mtk_dp->train_info.cr_done = false;
> > +             mtk_dp->train_info.eq_done = false;
> > +
> > +             mtk_dp_train_change_mode(mtk_dp);
> > +             ret = mtk_dp_train_flow(mtk_dp, link_rate, lane_count);
> > +             if (ret == -ENODEV || ret == -EAGAIN)
> > +                     return ret;
> > +
> > +             if (!mtk_dp->train_info.cr_done) {
> > +                     switch (link_rate) {
> > +                     case MTK_DP_LINKRATE_RBR:
> > +                             lane_count = lane_count / 2;
> > +                             link_rate = max_link_rate;
> > +                             if (lane_count == 0x0) {
> > +                                     mtk_dp->train_state =
> > +                                             MTK_DP_TRAIN_STATE_DPIDLE;
> > +                                     return -EIO;
> > +                             }
> > +                             break;
> > +                     case MTK_DP_LINKRATE_HBR:
> > +                             link_rate = MTK_DP_LINKRATE_RBR;
> > +                             break;
> > +                     case MTK_DP_LINKRATE_HBR2:
> > +                             link_rate = MTK_DP_LINKRATE_HBR;
> > +                             break;
> > +                     case MTK_DP_LINKRATE_HBR3:
> > +                             link_rate = MTK_DP_LINKRATE_HBR2;
> > +                             break;
> > +                     default:
> > +                             return -EINVAL;
> > +                     };
> > +             } else if (!mtk_dp->train_info.eq_done) {
> > +                     lane_count /= 2;
> > +                     if (lane_count == 0)
> > +                             return -EIO;
> > +             } else {
> > +                     return 0;
> > +             }
> > +     }
> > +
> > +     return -ETIMEDOUT;
> > +}
> > +
> > +static int mtk_dp_train_handler(struct mtk_dp *mtk_dp)
> > +{
> > +     int ret = 0;
> > +
> > +     if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
> > +             return ret;
> > +
> > +     switch (mtk_dp->train_state) {
> > +     case MTK_DP_TRAIN_STATE_STARTUP:
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> > +             break;
> > +
> > +     case MTK_DP_TRAIN_STATE_CHECKCAP:
> > +             if (mtk_dp_parse_capabilities(mtk_dp)) {
> > +                     mtk_dp->train_info.check_cap_count = 0;
> > +                     mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
> > +             } else {
> > +                     mtk_dp->train_info.check_cap_count++;
> > +
> > +                     if (mtk_dp->train_info.check_cap_count >
> > +                         MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT) {
> > +                             mtk_dp->train_info.check_cap_count = 0;
> > +                             mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> > +                             ret = -ETIMEDOUT;
> > +                     }
> > +             }
> > +             break;
> > +
> > +     case MTK_DP_TRAIN_STATE_CHECKEDID: {
> > +             int caps_found = mtk_dp_edid_parse_audio_capabilities(
> > +                     mtk_dp, &mtk_dp->info.audio_caps);
> > +             mtk_dp->audio_enable = caps_found > 0;
> > +             if (!mtk_dp->audio_enable)
> > +                     memset(&mtk_dp->info.audio_caps, 0,
> > +                            sizeof(mtk_dp->info.audio_caps));
> > +     }
> > +
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
> > +             break;
> > +
> > +     case MTK_DP_TRAIN_STATE_TRAINING_PRE:
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING;
> > +             break;
> > +
> > +     case MTK_DP_TRAIN_STATE_TRAINING:
> > +             ret = mtk_dp_train_start(mtk_dp);
> > +             if (!ret) {
> > +                     mtk_dp_video_mute(mtk_dp, true);
> > +                     mtk_dp_audio_mute(mtk_dp, true);
> > +                     mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKTIMING;
> > +                     mtk_dp_fec_enable(mtk_dp, mtk_dp->has_fec);
> > +             } else if (ret != -EAGAIN) {
> > +                     mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> > +             }
> > +
> > +             ret = 0;
> > +             break;
> > +
> > +     case MTK_DP_TRAIN_STATE_CHECKTIMING:
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_NORMAL;
> > +             break;
> > +     case MTK_DP_TRAIN_STATE_NORMAL:
> > +             break;
> > +     case MTK_DP_TRAIN_STATE_POWERSAVE:
> > +             break;
> > +     case MTK_DP_TRAIN_STATE_DPIDLE:
> > +             break;
> > +     default:
> > +             break;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     if (enable) {
> > +             mtk_dp_set_tx_out(mtk_dp);
> > +             mtk_dp_video_mute(mtk_dp, false);
> > +     } else {
> > +             mtk_dp_video_mute(mtk_dp, true);
> > +     }
> > +}
> > +
> > +static void mtk_dp_audio_sdp_setup(struct mtk_dp *mtk_dp,
> > +                                struct mtk_dp_audio_cfg *cfg)
> > +{
> > +     struct mtk_dp_sdp_packet packet;
> > +     struct hdmi_audio_infoframe frame;
> > +
> > +     hdmi_audio_infoframe_init(&frame);
> > +     frame.coding_type = HDMI_AUDIO_CODING_TYPE_PCM;
> > +     frame.channels = cfg->channels;
> > +     frame.sample_frequency = cfg->sample_rate;
> > +
> > +     switch (cfg->word_length_bits) {
> > +     case 16:
> > +             frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
> > +             break;
> > +     case 20:
> > +             frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_20;
> > +             break;
> > +     case 24:
> > +     default:
> > +             frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_24;
> > +             break;
> > +     }
> > +
> > +     packet.type = MTK_DP_SDP_AUI;
> > +     hdmi_audio_infoframe_pack_for_dp(&frame, &packet.sdp,
> > +                                      MTK_DP_DP_VERSION_11);
> > +
> > +     mtk_dp_audio_sdp_asp_set_channels(mtk_dp, cfg->channels);
> > +     mtk_dp_setup_sdp(mtk_dp, &packet);
> > +}
> > +
> > +static void mtk_dp_audio_setup(struct mtk_dp *mtk_dp,
> > +                            struct mtk_dp_audio_cfg *cfg)
> > +{
> > +     mtk_dp_audio_sdp_setup(mtk_dp, cfg);
> > +     mtk_dp_audio_channel_status_set(mtk_dp, cfg);
> > +
> > +     mtk_dp_audio_setup_channels(mtk_dp, cfg);
> > +     mtk_dp_audio_set_divider(mtk_dp);
> > +}
> > +
> > +static void mtk_dp_video_config(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_mn_overwrite_disable(mtk_dp);
> > +
> > +     mtk_dp_set_msa(mtk_dp);
> > +
> > +     mtk_dp_set_color_depth(mtk_dp, mtk_dp->info.depth);
> > +     mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
> > +}
> > +
> > +static int mtk_dp_state_handler(struct mtk_dp *mtk_dp)
> > +{
> > +     int ret = 0;
> > +
> > +     switch (mtk_dp->state) {
> > +     case MTK_DP_STATE_INITIAL:
> > +             mtk_dp_video_mute(mtk_dp, true);
> > +             mtk_dp_audio_mute(mtk_dp, true);
> > +             mtk_dp->state = MTK_DP_STATE_IDLE;
> > +             break;
> > +
> > +     case MTK_DP_STATE_IDLE:
> > +             if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
> > +                     mtk_dp->state = MTK_DP_STATE_PREPARE;
> > +             break;
> > +
> > +     case MTK_DP_STATE_PREPARE:
> > +             mtk_dp_video_config(mtk_dp);
> > +             mtk_dp_video_enable(mtk_dp, true);
> > +
> > +             if (mtk_dp->audio_enable) {
> > +                     mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_caps);
> > +                     mtk_dp_audio_mute(mtk_dp, false);
> > +             }
> > +
> > +             mtk_dp->state = MTK_DP_STATE_NORMAL;
> > +             break;
> > +
> > +     case MTK_DP_STATE_NORMAL:
> > +             if (mtk_dp->train_state != MTK_DP_TRAIN_STATE_NORMAL) {
> > +                     mtk_dp_video_mute(mtk_dp, true);
> > +                     mtk_dp_audio_mute(mtk_dp, true);
> > +                     mtk_dp_sdp_stop_sending(mtk_dp);
> > +                     mtk_dp->state = MTK_DP_STATE_IDLE;
> > +             }
> > +             break;
> > +
> > +     default:
> > +             break;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_set_idle_pattern(mtk_dp, true);
> > +     mtk_dp_initialize_priv_data(mtk_dp);
> > +
> > +     mtk_dp_initialize_settings(mtk_dp);
> > +     mtk_dp_initialize_aux_settings(mtk_dp);
> > +     mtk_dp_initialize_digital_settings(mtk_dp);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
> > +                        BIT(RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT),
> > +                        RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK);
> > +     mtk_dp_initialize_hpd_detect_settings(mtk_dp);
> > +
> > +     mtk_dp_digital_sw_reset(mtk_dp);
> > +}
> > +
> > +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
> > +{
> > +     struct mtk_dp *mtk_dp = dev;
> > +     int event;
> > +     u8 buf[DP_RECEIVER_CAP_SIZE] = {};
> > +
> > +     event = mtk_dp_plug_state(mtk_dp) ? connector_status_connected :
> > +                                               connector_status_disconnected;
> > +
> > +     if (event < 0)
> > +             return IRQ_HANDLED;
> > +
> > +     if (mtk_dp->drm_dev) {
> > +             dev_info(mtk_dp->dev, "drm_helper_hpd_irq_event\n");
> > +             drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
> > +     }
> > +
> > +     if (mtk_dp->train_info.cable_state_change) {
> > +             mtk_dp->train_info.cable_state_change = false;
> > +
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > +
> > +             if (!mtk_dp->train_info.cable_plugged_in ||
> > +                 !mtk_dp_plug_state(mtk_dp)) {
> > +                     mtk_dp_video_mute(mtk_dp, true);
> > +                     mtk_dp_audio_mute(mtk_dp, true);
> > +
> > +                     mtk_dp_initialize_priv_data(mtk_dp);
> > +                     mtk_dp_set_idle_pattern(mtk_dp, true);
> > +                     if (mtk_dp->has_fec)
> > +                             mtk_dp_fec_enable(mtk_dp, false);
> > +                     mtk_dp_sdp_stop_sending(mtk_dp);
> > +
> > +                     mtk_dp_edid_free(mtk_dp);
> > +                     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> > +                                        DP_PWR_STATE_BANDGAP_TPLL,
> > +                                        DP_PWR_STATE_MASK);
> > +             } else {
> > +                     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> > +                                        DP_PWR_STATE_BANDGAP_TPLL_LANE,
> > +                                        DP_PWR_STATE_MASK);
> > +                     drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
> > +                     mtk_dp->train_info.link_rate =
> > +                             min_t(int, MTK_DP_MAX_LINK_RATE,
> > +                                   buf[DP_MAX_LINK_RATE]);
> > +                     mtk_dp->train_info.lane_count =
> > +                             min_t(int, MTK_DP_MAX_LANES,
> > +                                   drm_dp_max_lane_count(buf));
> > +             }
> > +     }
> > +
> > +     if (mtk_dp->train_info.irq_status & MTK_DP_HPD_INTERRUPT) {
> > +             dev_info(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n");
> > +             mtk_dp->train_info.irq_status &= ~MTK_DP_HPD_INTERRUPT;
> > +             mtk_dp_hpd_sink_event(mtk_dp);
> > +     }
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t mtk_dp_hpd_isr_handler(struct mtk_dp *mtk_dp)
> > +{
> > +     bool connected;
> > +     u16 swirq_status = mtk_dp_swirq_get_clear(mtk_dp);
> > +     u8 hwirq_status = mtk_dp_hwirq_get_clear(mtk_dp);
> > +     struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
> > +
> > +     train_info->irq_status |= hwirq_status | swirq_status;
> > +
> > +     if (!train_info->irq_status)
> > +             return IRQ_HANDLED;
> > +
> > +     connected = mtk_dp_plug_state(mtk_dp);
> > +     if (connected || !train_info->cable_plugged_in)
> > +             train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
> > +     else if (!connected || train_info->cable_plugged_in)
> > +             train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
> > +
> > +     if (!(train_info->irq_status &
> > +           (MTK_DP_HPD_CONNECT | MTK_DP_HPD_DISCONNECT)))
> > +             return IRQ_HANDLED;
> > +
> > +     if (train_info->irq_status & MTK_DP_HPD_CONNECT) {
> > +             train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
> > +             train_info->cable_plugged_in = true;
> > +     } else {
> > +             train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
> > +             train_info->cable_plugged_in = false;
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > +     }
> > +     train_info->cable_state_change = true;
> > +     return IRQ_WAKE_THREAD;
> > +}
> > +
> > +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
> > +{
> > +     struct mtk_dp *mtk_dp = dev;
> > +     u32 irq_status;
> > +
> > +     irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS);
> > +
> > +     if (!irq_status)
> > +             return IRQ_HANDLED;
> > +
> > +     if (irq_status & RGS_IRQ_STATUS_TRANSMITTER)
> > +             return mtk_dp_hpd_isr_handler(mtk_dp);
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static int mtk_dp_dt_parse_pdata(struct mtk_dp *mtk_dp,
> > +                              struct platform_device *pdev)
> > +{
> > +     struct device *dev = &pdev->dev;
> > +     int ret = 0;
> > +     void __iomem *base;
> > +
> > +     base = devm_platform_ioremap_resource(pdev, 0);
> > +     if (IS_ERR(base))
> > +             return PTR_ERR(base);
> > +
> > +     mtk_dp->regs = devm_regmap_init_mmio(dev, base, &mtk_dp_regmap_config);
> > +     if (IS_ERR(mtk_dp->regs))
> > +             return PTR_ERR(mtk_dp->regs);
> > +
> > +     mtk_dp->dp_tx_clk = devm_clk_get(dev, "faxi");
> > +     if (IS_ERR(mtk_dp->dp_tx_clk)) {
> > +             ret = PTR_ERR(mtk_dp->dp_tx_clk);
> > +             dev_err(dev, "Failed to get dptx clock: %d\n", ret);
> > +             mtk_dp->dp_tx_clk = NULL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void mtk_dp_update_plugged_status(struct mtk_dp *mtk_dp)
> > +{
> > +     bool connected, has_audio;
> > +
> > +     mutex_lock(&mtk_dp->update_plugged_status_lock);
> > +     connected = mtk_dp_plug_state(mtk_dp);
> > +     has_audio = drm_detect_monitor_audio(mtk_dp->edid);
> > +     if (mtk_dp->plugged_cb && mtk_dp->codec_dev)
> > +             mtk_dp->plugged_cb(mtk_dp->codec_dev, connected & has_audio);
> > +     mutex_unlock(&mtk_dp->update_plugged_status_lock);
> > +}
> > +
> > +static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     enum drm_connector_status ret = connector_status_disconnected;
> > +     u8 sink_count = 0;
> > +
> > +     if (mtk_dp_is_edp(mtk_dp))
> > +             return connector_status_connected;
> > +
> > +     if (mtk_dp_plug_state(mtk_dp)) {
> > +             drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
> > +             if (DP_GET_SINK_COUNT(sink_count))
> > +                     ret = connector_status_connected;
> > +     }
> > +
> > +     mtk_dp_update_plugged_status(mtk_dp);
> > +     return ret;
> > +}
> > +
> > +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> > +                                 struct drm_connector *connector)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     bool enabled = mtk_dp->enabled;
> > +     struct edid *new_edid = NULL;
> > +
> > +     if (!enabled)
> > +             drm_bridge_chain_pre_enable(bridge);
> > +
> > +     drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> > +     usleep_range(2000, 5000);
> > +
> > +     if (mtk_dp_plug_state(mtk_dp))
> > +             new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> > +
> > +     if (!enabled)
> > +             drm_bridge_chain_post_disable(bridge);
> > +
> > +     mutex_lock(&mtk_dp->edid_lock);
> > +     kfree(mtk_dp->edid);
> > +     mtk_dp->edid = NULL;
> > +
> > +     if (!new_edid) {
> > +             mutex_unlock(&mtk_dp->edid_lock);
> > +             return NULL;
> > +     }
> > +
> > +     mtk_dp->edid = drm_edid_duplicate(new_edid);
> > +     mutex_unlock(&mtk_dp->edid_lock);
> > +
> > +     return new_edid;
> > +}
> > +
> > +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> > +                                struct drm_dp_aux_msg *msg)
> > +{
> > +     ssize_t err = -EAGAIN;
> > +     struct mtk_dp *mtk_dp;
> > +     bool is_read;
> > +     u8 request;
> > +     size_t accessed_bytes = 0;
> > +     int retry = 3, ret = 0;
> > +
> > +     mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> > +
> > +     if (!mtk_dp->train_info.cable_plugged_in ||
> > +         mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> > +             err = -EAGAIN;
> > +             goto err;
> > +     }
> > +
> > +     switch (msg->request) {
> > +     case DP_AUX_I2C_MOT:
> > +     case DP_AUX_I2C_WRITE:
> > +     case DP_AUX_NATIVE_WRITE:
> > +     case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> > +     case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> > +             request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> > +             is_read = false;
> > +             break;
> > +     case DP_AUX_I2C_READ:
> > +     case DP_AUX_NATIVE_READ:
> > +     case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> > +             request = msg->request;
> > +             is_read = true;
> > +             break;
> > +     default:
> > +             drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> > +                     msg->request);
> > +             err = -EINVAL;
> > +             goto err;
> > +     }
> > +
> > +     if (msg->size == 0) {
> > +             mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> > +                                    msg->address + accessed_bytes,
> > +                                    msg->buffer + accessed_bytes, 0);
> > +     } else {
> > +             while (accessed_bytes < msg->size) {
> > +                     size_t to_access =
> > +                             min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> > +                                   msg->size - accessed_bytes);
> > +                     retry = 3;
> > +                     while (retry--) {
> > +                             ret = mtk_dp_aux_do_transfer(
> > +                                     mtk_dp, is_read, request,
> > +                                     msg->address + accessed_bytes,
> > +                                     msg->buffer + accessed_bytes,
> > +                                     to_access);
> > +                             if (ret == 0)
> > +                                     break;
> > +                             udelay(50);
> > +                     }
> > +                     if (!retry && ret) {
> > +                             drm_info(mtk_dp->drm_dev,
> > +                                      "Failed to do AUX transfer: %d\n",
> > +                                      ret);
> > +                             break;
> > +                     }
> > +                     accessed_bytes += to_access;
> > +             }
> > +     }
> > +err:
> > +     if (!ret) {
> > +             msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > +             ret = msg->size;
> > +     } else {
> > +             msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> > +             return err;
> > +     }
> > +
> > +     msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > +     return msg->size;
> > +}
> > +
> > +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> > +{
> > +     drm_dp_aux_init(&mtk_dp->aux);
> > +     mtk_dp->aux.name = "aux_mtk_dp";
> > +     mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> > +}
> > +
> > +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> > +{
> > +     mutex_lock(&mtk_dp->dp_lock);
> > +
> > +     mtk_dp_hwirq_enable(mtk_dp, false);
> > +     mtk_dp_power_disable(mtk_dp);
> > +     phy_exit(mtk_dp->phy);
> > +     clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > +
> > +     mutex_unlock(&mtk_dp->dp_lock);
> > +}
> > +
> > +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> > +{
> > +     int ret = 0;
> > +
> > +     mutex_lock(&mtk_dp->dp_lock);
> > +
> > +     ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> > +     if (ret < 0) {
> > +             dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> > +             goto err;
> > +     }
> > +     ret = phy_init(mtk_dp->phy);
> > +     if (ret) {
> > +             dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> > +             goto err_phy_init;
> > +     }
> > +     ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> > +     if (ret) {
> > +             dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> > +             goto err_phy_config;
> > +     }
> > +
> > +     mtk_dp_init_port(mtk_dp);
> > +     mtk_dp_power_enable(mtk_dp);
> > +     mtk_dp_hwirq_enable(mtk_dp, true);
> > +
> > +err_phy_config:
> > +     phy_exit(mtk_dp->phy);
> > +err_phy_init:
> > +     clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > +err:
> > +     mutex_unlock(&mtk_dp->dp_lock);
> > +     return ret;
> > +}
> > +
> > +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> > +                             enum drm_bridge_attach_flags flags)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     int ret;
> > +
> > +     if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> > +             dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> > +             return -EINVAL;
> > +     }
> > +
> > +     ret = mtk_dp_poweron(mtk_dp);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (mtk_dp->next_bridge) {
> > +             ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> > +                                     &mtk_dp->bridge, flags);
> > +             if (ret) {
> > +                     drm_warn(mtk_dp->drm_dev,
> > +                              "Failed to attach external bridge: %d\n", ret);
> > +                     goto err_bridge_attach;
> > +             }
> > +     }
> > +
> > +     mtk_dp->drm_dev = bridge->dev;
> > +
> > +     return 0;
> > +
> > +err_bridge_attach:
> > +     mtk_dp_poweroff(mtk_dp);
> > +     return ret;
> > +}
> > +
> > +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +
> > +     mtk_dp->drm_dev = NULL;
> > +
> > +     mtk_dp_poweroff(mtk_dp);
> > +}
> > +
> > +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> > +                                      struct drm_bridge_state *old_state)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +
> > +     mtk_dp_video_mute(mtk_dp, true);
> > +     mtk_dp_audio_mute(mtk_dp, true);
> > +     mtk_dp->state = MTK_DP_STATE_IDLE;
> > +     mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > +
> > +     mtk_dp->enabled = false;
> > +     msleep(100);
> > +}
> > +
> > +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> > +                                       struct drm_display_mode *mode)
> > +{
> > +     struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> > +
> > +     drm_display_mode_to_videomode(mode, &timings->vm);
> > +     timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> > +     timings->htotal = mode->htotal;
> > +     timings->vtotal = mode->vtotal;
> > +}
> > +
> > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > +                                     struct drm_bridge_state *old_state)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     struct drm_connector *conn;
> > +     struct drm_connector_state *conn_state;
> > +     struct drm_crtc *crtc;
> > +     struct drm_crtc_state *crtc_state;
> > +     int ret = 0;
> > +     int i;
> > +
> > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > +                                                     bridge->encoder);
> > +     if (!conn) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Can't enable bridge as connector is missing\n");
> > +             return;
> > +     }
> > +
> > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
>
> This should be protected by a mutex (just like any resource shared
> between KMS and ALSA)
>
Ok.
> > +     conn_state =
> > +             drm_atomic_get_new_connector_state(old_state->base.state, conn);
> > +     if (!conn_state) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Can't enable bridge as connector state is missing\n");
> > +             return;
> > +     }
> > +
> > +     crtc = conn_state->crtc;
> > +     if (!crtc) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Can't enable bridge as connector state doesn't have a crtc\n");
> > +             return;
> > +     }
> > +
> > +     crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> > +     if (!crtc_state) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Can't enable bridge as crtc state is missing\n");
> > +             return;
> > +     }
> > +
> > +     mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);
> > +     if (!mtk_dp_parse_capabilities(mtk_dp)) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Can't enable bridge as nothing is plugged in\n");
> > +             return;
> > +     }
> > +
> > +     //training
> > +     for (i = 0; i < 50; i++) {
> > +             ret = mtk_dp_train_handler(mtk_dp);
> > +             if (ret) {
> > +                     drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> > +                             ret);
> > +                     return;
> > +             }
> > +
> > +             ret = mtk_dp_state_handler(mtk_dp);
> > +             if (ret) {
> > +                     drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> > +                             ret);
> > +                     return;
> > +             }
> > +     }
> > +
> > +     mtk_dp->enabled = true;
> > +     mtk_dp_update_plugged_status(mtk_dp);
> > +}
> > +
> > +static enum drm_mode_status
> > +mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
> > +                      const struct drm_display_info *info,
> > +                      const struct drm_display_mode *mode)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     u32 rx_linkrate;
> > +     u32 bpp;
> > +
> > +     bpp = info->color_formats & DRM_COLOR_FORMAT_YCRCB422 ? 16 : 24;
> > +     rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
> > +     if (rx_linkrate * mtk_dp->train_info.lane_count < mode->clock * bpp / 8)
> > +             return MODE_CLOCK_HIGH;
> > +
> > +     if (mode->clock > 600000)
> > +             return MODE_CLOCK_HIGH;
> > +
> > +     if ((mode->clock * 1000) / (mode->htotal * mode->vtotal) > 62)
> > +             return MODE_CLOCK_HIGH;
> > +
> > +     return MODE_OK;
> > +}
> > +
> > +static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(
> > +     struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
> > +     struct drm_crtc_state *crtc_state,
> > +     struct drm_connector_state *conn_state, unsigned int *num_output_fmts)
> > +{
> > +     u32 *output_fmts;
> > +
> > +     *num_output_fmts = 0;
> > +     output_fmts = kcalloc(1, sizeof(*output_fmts), GFP_KERNEL);
> > +     if (!output_fmts)
> > +             return NULL;
> > +     *num_output_fmts = 1;
> > +     output_fmts[0] = MEDIA_BUS_FMT_FIXED;
> > +     return output_fmts;
> > +}
> > +
> > +static const u32 mt8195_input_fmts[] = {
> > +     MEDIA_BUS_FMT_RGB888_1X24,
> > +     MEDIA_BUS_FMT_YUV8_1X24,
> > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > +};
> > +
> > +static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(
> > +     struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
> > +     struct drm_crtc_state *crtc_state,
> > +     struct drm_connector_state *conn_state, u32 output_fmt,
> > +     unsigned int *num_input_fmts)
> > +{
> > +     u32 *input_fmts;
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> > +     struct drm_display_info *display_info =
> > +             &conn_state->connector->display_info;
> > +     u32 rx_linkrate;
> > +     u32 bpp;
> > +
> > +     bpp = (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422) ? 16 :
> > +                                                                             24;
> > +     rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
> > +     *num_input_fmts = 0;
> > +     input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts), sizeof(*input_fmts),
> > +                          GFP_KERNEL);
> > +     if (!input_fmts)
> > +             return NULL;
> > +
> > +     *num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
> > +
> > +     memcpy(input_fmts, mt8195_input_fmts,
> > +            sizeof(*input_fmts) * ARRAY_SIZE(mt8195_input_fmts));
> > +
> > +     if (((rx_linkrate * mtk_dp->train_info.lane_count) <
> > +          (mode->clock * 24 / 8)) &&
> > +         ((rx_linkrate * mtk_dp->train_info.lane_count) >
> > +          (mode->clock * 16 / 8)) &&
> > +         (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422)) {
> > +             kfree(input_fmts);
> > +             input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL);
> > +             *num_input_fmts = 1;
> > +             input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
> > +     }
> > +
> > +     return input_fmts;
> > +}
> > +
> > +static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
> > +                                   struct drm_bridge_state *bridge_state,
> > +                                   struct drm_crtc_state *crtc_state,
> > +                                   struct drm_connector_state *conn_state)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     unsigned int input_bus_format;
> > +
> > +     input_bus_format = bridge_state->input_bus_cfg.format;
> > +
> > +     dev_info(mtk_dp->dev, "input format 0x%04x, output format 0x%04x\n",
> > +              bridge_state->input_bus_cfg.format,
> > +              bridge_state->output_bus_cfg.format);
> > +
> > +     mtk_dp->input_fmt = input_bus_format;
> > +     if (mtk_dp->input_fmt == MEDIA_BUS_FMT_YUYV8_1X16)
> > +             mtk_dp->info.format = MTK_DP_COLOR_FORMAT_YUV_422;
> > +     else
> > +             mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
> > +     .atomic_check = mtk_dp_bridge_atomic_check,
> > +     .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> > +     .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> > +     .atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts,
> > +     .atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts,
> > +     .atomic_reset = drm_atomic_helper_bridge_reset,
> > +     .attach = mtk_dp_bridge_attach,
> > +     .detach = mtk_dp_bridge_detach,
> > +     .atomic_enable = mtk_dp_bridge_atomic_enable,
> > +     .atomic_disable = mtk_dp_bridge_atomic_disable,
> > +     .mode_valid = mtk_dp_bridge_mode_valid,
> > +     .get_edid = mtk_dp_get_edid,
> > +     .detect = mtk_dp_bdg_detect,
> > +};
> > +
> > +/*
> > + * HDMI audio codec callbacks
> > + */
> > +static int mtk_dp_audio_hw_params(struct device *dev, void *data,
> > +                               struct hdmi_codec_daifmt *daifmt,
> > +                               struct hdmi_codec_params *params)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +     struct mtk_dp_audio_cfg cfg;
> > +
> > +     if (!mtk_dp->enabled) {
> > +             pr_err("%s, DP is not ready!\n", __func__);
> > +             return -ENODEV;
> > +     }
> > +
> > +     cfg.channels = params->cea.channels;
> > +     cfg.sample_rate = params->sample_rate;
> > +     cfg.word_length_bits = 24;
> > +
> > +     mtk_dp_audio_setup(mtk_dp, &cfg);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_audio_startup(struct device *dev, void *data)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +
> > +     mtk_dp_audio_mute(mtk_dp, false);
> > +
> > +     return 0;
> > +}
> > +
> > +static void mtk_dp_audio_shutdown(struct device *dev, void *data)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +
> > +     mtk_dp_audio_mute(mtk_dp, true);
> > +}
> > +
> > +static int mtk_dp_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
> > +                             size_t len)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +
> > +     if (mtk_dp->enabled)
> > +             memcpy(buf, mtk_dp->connector_eld, len);
> > +     else
> > +             memset(buf, 0, len);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_audio_hook_plugged_cb(struct device *dev, void *data,
> > +                                     hdmi_codec_plugged_cb fn,
> > +                                     struct device *codec_dev)
> > +{
> > +     struct mtk_dp *mtk_dp = data;
> > +
> > +     mutex_lock(&mtk_dp->update_plugged_status_lock);
> > +     mtk_dp->plugged_cb = fn;
> > +     mtk_dp->codec_dev = codec_dev;
> > +     mutex_unlock(&mtk_dp->update_plugged_status_lock);
> > +
> > +     mtk_dp_update_plugged_status(mtk_dp);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct hdmi_codec_ops mtk_dp_audio_codec_ops = {
> > +     .hw_params = mtk_dp_audio_hw_params,
> > +     .audio_startup = mtk_dp_audio_startup,
> > +     .audio_shutdown = mtk_dp_audio_shutdown,
> > +     .get_eld = mtk_dp_audio_get_eld,
> > +     .hook_plugged_cb = mtk_dp_audio_hook_plugged_cb,
> > +     .no_capture_mute = 1,
> > +};
> > +
> > +static int mtk_dp_register_audio_driver(struct device *dev)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +     struct hdmi_codec_pdata codec_data = {
> > +             .ops = &mtk_dp_audio_codec_ops,
> > +             .max_i2s_channels = 8,
> > +             .i2s = 1,
> > +             .data = mtk_dp,
> > +     };
> > +     struct platform_device *pdev;
> > +
> > +     pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
> > +                                          PLATFORM_DEVID_AUTO, &codec_data,
> > +                                          sizeof(codec_data));
> > +     if (IS_ERR(pdev))
> > +             return PTR_ERR(pdev);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_probe(struct platform_device *pdev)
> > +{
> > +     struct mtk_dp *mtk_dp;
> > +     struct device *dev = &pdev->dev;
> > +     int ret;
> > +     int irq_num = 0;
> > +     struct drm_panel *panel = NULL;
> > +
> > +     mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> > +     if (!mtk_dp)
> > +             return -ENOMEM;
> > +
> > +     mtk_dp->dev = dev;
> > +
> > +     irq_num = platform_get_irq(pdev, 0);
> > +     if (irq_num < 0) {
> > +             dev_err(dev, "failed to request dp irq resource\n");
> > +             return -EPROBE_DEFER;
> > +     }
> > +
> > +     ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel,
> > +                                       &mtk_dp->next_bridge);
> > +     if (ret == -ENODEV) {
> > +             dev_info(
> > +                     dev,
> > +                     "No panel connected in devicetree, continuing as external DP\n");
> > +             mtk_dp->next_bridge = NULL;
> > +     } else if (ret) {
> > +             dev_err(dev, "Failed to find panel or bridge: %d\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     if (panel) {
> > +             mtk_dp->next_bridge = devm_drm_panel_bridge_add(dev, panel);
> > +             if (IS_ERR(mtk_dp->next_bridge)) {
> > +                     ret = PTR_ERR(mtk_dp->next_bridge);
> > +                     dev_err(dev, "Failed to create bridge: %d\n", ret);
> > +                     return -EPROBE_DEFER;
> > +             }
> > +     }
>
> You should be using devm_drm_of_get_bridge here
>
Ok.
> > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > +     if (ret)
> > +             return ret;
>
> pdata?
>
can you elaborate?
> > +
> > +     mtk_dp_aux_init(mtk_dp);
> > +
> > +     ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
> > +                                     mtk_dp_hpd_event_thread,
> > +                                     IRQ_TYPE_LEVEL_HIGH, dev_name(dev),
> > +                                     mtk_dp);
> > +     if (ret) {
> > +             dev_err(dev, "failed to request mediatek dptx irq\n");
> > +             return -EPROBE_DEFER;
> > +     }
> > +
> > +     mutex_init(&mtk_dp->dp_lock);
> > +     mutex_init(&mtk_dp->edid_lock);
> > +
> > +     platform_set_drvdata(pdev, mtk_dp);
> > +
> > +     if (!mtk_dp_is_edp(mtk_dp)) {
> > +             mutex_init(&mtk_dp->update_plugged_status_lock);
> > +             ret = mtk_dp_register_audio_driver(dev);
> > +             if (ret) {
> > +                     dev_err(dev, "Failed to register audio driver: %d\n",
> > +                             ret);
> > +                     return ret;
> > +             }
> > +     }
> > +
> > +     mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
> > +                                                     PLATFORM_DEVID_AUTO,
> > +                                                     &mtk_dp->regs,
> > +                                                     sizeof(&mtk_dp->regs));
> > +     if (IS_ERR(mtk_dp->phy_dev)) {
> > +             dev_err(dev, "Failed to create device mediatek-dp-phy: %ld\n",
> > +                     PTR_ERR(mtk_dp->phy_dev));
> > +             return PTR_ERR(mtk_dp->phy_dev);
> > +     }
> > +
> > +     mtk_dp_get_calibration_data(mtk_dp);
> > +
> > +     mtk_dp->phy = dev_get_drvdata(&mtk_dp->phy_dev->dev);
> > +     if (IS_ERR(mtk_dp->phy)) {
> > +             dev_err(dev, "Failed to get phy: %ld\n", PTR_ERR(mtk_dp->phy));
> > +             platform_device_unregister(mtk_dp->phy_dev);
> > +             return PTR_ERR(mtk_dp->phy);
> > +     }
> > +
> > +     mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
> > +     mtk_dp->bridge.of_node = dev->of_node;
> > +     if (mtk_dp_is_edp(mtk_dp))
> > +             mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> > +     else
> > +             mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
> > +
> > +     mtk_dp->bridge.ops =
> > +             DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
> > +     drm_bridge_add(&mtk_dp->bridge);
> > +
> > +     pm_runtime_enable(dev);
> > +     pm_runtime_get_sync(dev);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_remove(struct platform_device *pdev)
> > +{
> > +     struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
> > +
> > +     platform_device_unregister(mtk_dp->phy_dev);
> > +
> > +     mtk_dp_video_mute(mtk_dp, true);
> > +     mtk_dp_audio_mute(mtk_dp, true);
> > +
> > +     pm_runtime_disable(&pdev->dev);
> > +
> > +     return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int mtk_dp_suspend(struct device *dev)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +
> > +     mtk_dp_power_disable(mtk_dp);
> > +     mtk_dp_hwirq_enable(mtk_dp, false);
> > +
> > +     pm_runtime_put_sync(dev);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_resume(struct device *dev)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +
> > +     pm_runtime_get_sync(dev);
> > +
> > +     mtk_dp_init_port(mtk_dp);
> > +     mtk_dp_power_enable(mtk_dp);
> > +     mtk_dp_hwirq_enable(mtk_dp, true);
> > +
> > +     return 0;
> > +}
> > +#endif
> > +
> > +static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
> > +
> > +static const struct of_device_id mtk_dp_of_match[] = {
> > +     {
> > +             .compatible = "mediatek,mt8195-dp-tx",
> > +     },
> > +     {},
> > +};
> > +MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
> > +
> > +struct platform_driver mtk_dp_driver = {
> > +     .probe = mtk_dp_probe,
> > +     .remove = mtk_dp_remove,
> > +     .driver = {
> > +             .name = "mediatek-drm-dp",
> > +             .of_match_table = mtk_dp_of_match,
> > +             .pm = &mtk_dp_pm_ops,
> > +     },
> > +};
> > +
> > +MODULE_AUTHOR("Jason-JH.Lin <jason-jh.lin@mediatek.com>");
> > +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> > +MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> > new file mode 100644
> > index 0000000000000..79952ac30e9e6
> > --- /dev/null
> > +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> > @@ -0,0 +1,568 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (c) 2019 MediaTek Inc.
> > + * Copyright (c) 2021 BayLibre
> > + */
> > +#ifndef _MTK_DP_REG_H_
> > +#define _MTK_DP_REG_H_
> > +
> > +#define MTK_DP_SIP_CONTROL_AARCH32 0x82000523
> > +#define MTK_DP_SIP_ATF_VIDEO_UNMUTE 0x20
> > +#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE 0x21
> > +
> > +#define DP_PHY_GLB_BIAS_GEN_00 0x0000
> > +#define RG_XTP_GLB_BIAS_INTR_CTRL (0x1f << 16)
> > +
> > +#define DP_PHY_GLB_DPAUX_TX 0x0008
> > +#define RG_CKM_PT0_CKTX_IMPSEL (0xf << 20)
> > +
> > +#define DP_PHY_LANE_TX_0 0x104
> > +#define RG_XTP_LN0_TX_IMPSEL_PMOS (0xf << 12)
> > +#define RG_XTP_LN0_TX_IMPSEL_NMOS (0xf << 16)
> > +
> > +#define DP_PHY_LANE_TX_1 0x204
> > +#define RG_XTP_LN1_TX_IMPSEL_PMOS (0xf << 12)
> > +#define RG_XTP_LN1_TX_IMPSEL_NMOS (0xf << 16)
> > +
> > +#define DP_PHY_LANE_TX_2 0x304
> > +#define RG_XTP_LN2_TX_IMPSEL_PMOS (0xf << 12)
> > +#define RG_XTP_LN2_TX_IMPSEL_NMOS (0xf << 16)
> > +
> > +#define DP_PHY_LANE_TX_3 0x404
> > +#define RG_XTP_LN3_TX_IMPSEL_PMOS (0xf << 12)
> > +#define RG_XTP_LN3_TX_IMPSEL_NMOS (0xf << 16)
> > +
> > +#define TOP_OFFSET 0x2000
> > +#define ENC0_OFFSET 0x3000
> > +#define ENC1_OFFSET 0x3200
> > +#define TRANS_OFFSET 0x3400
> > +#define AUX_OFFSET 0x3600
> > +#define SEC_OFFSET 0x4000
> > +
> > +#define MTK_DP_HPD_DISCONNECT BIT(1)
> > +#define MTK_DP_HPD_CONNECT BIT(2)
> > +#define MTK_DP_HPD_INTERRUPT BIT(3)
> > +
> > +#define MTK_DP_0034 0x034
> > +#define DA_XTP_GLB_CKDET_EN_FORCE_VAL BIT(15)
> > +#define DA_XTP_GLB_CKDET_EN_FORCE_EN BIT(14)
> > +#define DA_CKM_INTCKTX_EN_FORCE_VAL BIT(13)
> > +#define DA_CKM_INTCKTX_EN_FORCE_EN BIT(12)
> > +#define DA_CKM_CKTX0_EN_FORCE_VAL BIT(11)
> > +#define DA_CKM_CKTX0_EN_FORCE_EN BIT(10)
> > +#define DA_CKM_XTAL_CK_FORCE_VAL BIT(9)
> > +#define DA_CKM_XTAL_CK_FORCE_EN BIT(8)
> > +#define DA_CKM_BIAS_LPF_EN_FORCE_VAL BIT(7)
> > +#define DA_CKM_BIAS_LPF_EN_FORCE_EN BIT(6)
> > +#define DA_CKM_BIAS_EN_FORCE_VAL BIT(5)
> > +#define DA_CKM_BIAS_EN_FORCE_EN BIT(4)
> > +#define DA_XTP_GLB_AVD10_ON_FORCE_VAL BIT(3)
> > +#define DA_XTP_GLB_AVD10_ON_FORCE BIT(2)
> > +#define DA_XTP_GLB_LDO_EN_FORCE_VAL BIT(1)
> > +#define DA_XTP_GLB_LDO_EN_FORCE_EN BIT(0)
> > +
> > +#define MTK_DP_1040 0x1040
> > +#define RG_DPAUX_RX_VALID_DEGLITCH_EN BIT(2)
> > +#define RG_XTP_GLB_CKDET_EN BIT(1)
> > +#define RG_DPAUX_RX_EN BIT(0)
> > +
> > +#define MTK_DP_ENC0_P0_3000 (ENC0_OFFSET + 0x000)
> > +#define LANE_NUM_DP_ENC0_P0_MASK 0x3
> > +#define VIDEO_MUTE_SW_DP_ENC0_P0_MASK 0x4
> > +#define VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT 2
> > +#define VIDEO_MUTE_SEL_DP_ENC0_P0_MASK 0x8
> > +#define VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT 3
> > +#define ENHANCED_FRAME_EN_DP_ENC0_P0_MASK 0x10
> > +#define ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT 4
> > +
> > +#define MTK_DP_ENC0_P0_3004 (ENC0_OFFSET + 0x004)
> > +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK 0x100
> > +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_SHIFT 8
> > +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK 0x200
> > +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT 9
> > +
> > +#define MTK_DP_ENC0_P0_3008 (ENC0_OFFSET + 0x008)
> > +#define VIDEO_M_CODE_SW_0_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_300C (ENC0_OFFSET + 0x00C)
> > +#define VIDEO_M_CODE_SW_1_DP_ENC0_P0_MASK 0xff
> > +
> > +#define MTK_DP_ENC0_P0_3010 (ENC0_OFFSET + 0x010)
> > +#define HTOTAL_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3014 (ENC0_OFFSET + 0x014)
> > +#define VTOTAL_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3018 (ENC0_OFFSET + 0x018)
> > +#define HSTART_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_301C (ENC0_OFFSET + 0x01C)
> > +#define VSTART_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3020 (ENC0_OFFSET + 0x020)
> > +#define HWIDTH_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3024 (ENC0_OFFSET + 0x024)
> > +#define VHEIGHT_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3028 (ENC0_OFFSET + 0x028)
> > +#define HSW_SW_DP_ENC0_P0_MASK 0x7fff
> > +#define HSW_SW_DP_ENC0_P0_SHIFT 0
> > +#define HSP_SW_DP_ENC0_P0_MASK 0x8000
> > +
> > +#define MTK_DP_ENC0_P0_302C (ENC0_OFFSET + 0x02C)
> > +#define VSW_SW_DP_ENC0_P0_MASK 0x7fff
> > +#define VSW_SW_DP_ENC0_P0_SHIFT 0
> > +#define VSP_SW_DP_ENC0_P0_MASK 0x8000
> > +
> > +#define MTK_DP_ENC0_P0_3030 (ENC0_OFFSET + 0x030)
> > +#define HTOTAL_SEL_DP_ENC0_P0_SHIFT 0
> > +#define VTOTAL_SEL_DP_ENC0_P0_SHIFT 1
> > +#define HSTART_SEL_DP_ENC0_P0_SHIFT 2
> > +#define VSTART_SEL_DP_ENC0_P0_SHIFT 3
> > +#define HWIDTH_SEL_DP_ENC0_P0_SHIFT 4
> > +#define VHEIGHT_SEL_DP_ENC0_P0_SHIFT 5
> > +#define HSP_SEL_DP_ENC0_P0_SHIFT 6
> > +#define HSW_SEL_DP_ENC0_P0_SHIFT 7
> > +#define VSP_SEL_DP_ENC0_P0_SHIFT 8
> > +#define VSW_SEL_DP_ENC0_P0_SHIFT 9
> > +#define VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK 0x800
> > +#define VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT 11
> > +#define VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK 0x1000
> > +#define VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT 12
> > +
> > +#define MTK_DP_ENC0_P0_3034 (ENC0_OFFSET + 0x034)
> > +
> > +#define MTK_DP_ENC0_P0_3038 (ENC0_OFFSET + 0x038)
> > +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK 0x800
> > +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_SHIFT 11
> > +
> > +#define MTK_DP_ENC0_P0_303C (ENC0_OFFSET + 0x03C)
> > +#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK 0x3f
> > +#define SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT 0
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK 0x700
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT 8
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT                                     \
> > +     (0 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT                                     \
> > +     (1 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT                                     \
> > +     (2 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT                                      \
> > +     (3 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT                                      \
> > +     (4 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> > +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK 0x7000
> > +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT 12
> > +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB                                     \
> > +     (0 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> > +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422                                \
> > +     (1 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> > +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420                                \
> > +     (2 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> > +#define VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK 0x8000
> > +#define VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT 15
> > +
> > +#define MTK_DP_ENC0_P0_3040 (ENC0_OFFSET + 0x040)
> > +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK 0xfff
> > +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT 0
> > +
> > +#define MTK_DP_ENC0_P0_3044 (ENC0_OFFSET + 0x044)
> > +#define VIDEO_N_CODE_0_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3048 (ENC0_OFFSET + 0x048)
> > +#define VIDEO_N_CODE_1_DP_ENC0_P0_MASK 0xff
> > +
> > +#define MTK_DP_ENC0_P0_304C (ENC0_OFFSET + 0x04C)
> > +#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK 0x4
> > +#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK 0x100
> > +
> > +#define MTK_DP_ENC0_P0_3050 (ENC0_OFFSET + 0x050)
> > +#define VIDEO_N_CODE_MN_GEN_0_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3054 (ENC0_OFFSET + 0x054)
> > +#define VIDEO_N_CODE_MN_GEN_1_DP_ENC0_P0_MASK 0xff
> > +
> > +#define MTK_DP_ENC0_P0_3064 (ENC0_OFFSET + 0x064)
> > +#define HDE_NUM_LAST_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3088 (ENC0_OFFSET + 0x088)
> > +#define AU_EN_DP_ENC0_P0_MASK 0x40
> > +#define AU_EN_DP_ENC0_P0_SHIFT 6
> > +#define AUDIO_8CH_EN_DP_ENC0_P0_MASK 0x80
> > +#define AUDIO_8CH_SEL_DP_ENC0_P0_MASK 0x100
> > +#define AUDIO_2CH_EN_DP_ENC0_P0_MASK 0x4000
> > +#define AUDIO_2CH_SEL_DP_ENC0_P0_MASK 0x8000
> > +
> > +#define MTK_DP_ENC0_P0_308C (ENC0_OFFSET + 0x08C)
> > +#define CH_STATUS_0_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3090 (ENC0_OFFSET + 0x090)
> > +#define CH_STATUS_1_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3094 (ENC0_OFFSET + 0x094)
> > +#define CH_STATUS_2_DP_ENC0_P0_MASK 0xff
> > +
> > +#define MTK_DP_ENC0_P0_30A0 (ENC0_OFFSET + 0x0A0)
> > +
> > +#define MTK_DP_ENC0_P0_30A4 (ENC0_OFFSET + 0x0A4)
> > +#define AU_TS_CFG_DP_ENC0_P0_MASK 0xff
> > +
> > +#define MTK_DP_ENC0_P0_30A8 (ENC0_OFFSET + 0x0A8)
> > +
> > +#define MTK_DP_ENC0_P0_30AC (ENC0_OFFSET + 0x0AC)
> > +
> > +#define MTK_DP_ENC0_P0_30B0 (ENC0_OFFSET + 0x0B0)
> > +
> > +#define MTK_DP_ENC0_P0_30B4 (ENC0_OFFSET + 0x0B4)
> > +#define ISRC_CFG_DP_ENC0_P0_MASK 0xff00
> > +#define ISRC_CFG_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC0_P0_30B8 (ENC0_OFFSET + 0x0B8)
> > +
> > +#define MTK_DP_ENC0_P0_30BC (ENC0_OFFSET + 0x0BC)
> > +#define ISRC_CONT_DP_ENC0_P0_MASK 0x1
> > +#define ISRC_CONT_DP_ENC0_P0_SHIFT 0
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK 0x700
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT 8
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_2                             \
> > +     (1 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_4                             \
> > +     (2 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_8                             \
> > +     (3 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2                             \
> > +     (5 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_4                             \
> > +     (6 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_8                             \
> > +     (7 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +
> > +#define MTK_DP_ENC0_P0_30D8 (ENC0_OFFSET + 0x0D8)
> > +
> > +#define MTK_DP_ENC0_P0_312C (ENC0_OFFSET + 0x12C)
> > +#define ASP_HB2_DP_ENC0_P0_MASK 0xff
> > +#define ASP_HB3_DP_ENC0_P0_MASK 0xff00
> > +#define ASP_HB3_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC0_P0_3130 (ENC0_OFFSET + 0x130)
> > +
> > +#define MTK_DP_ENC0_P0_3138 (ENC0_OFFSET + 0x138)
> > +
> > +#define MTK_DP_ENC0_P0_3154 (ENC0_OFFSET + 0x154)
> > +#define PGEN_HTOTAL_DP_ENC0_P0_MASK 0x3fff
> > +
> > +#define MTK_DP_ENC0_P0_3158 (ENC0_OFFSET + 0x158)
> > +#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK 0x3fff
> > +
> > +#define MTK_DP_ENC0_P0_315C (ENC0_OFFSET + 0x15C)
> > +#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x3fff
> > +
> > +#define MTK_DP_ENC0_P0_3160 (ENC0_OFFSET + 0x160)
> > +#define PGEN_HFDE_START_DP_ENC0_P0_MASK 0x3fff
> > +
> > +#define MTK_DP_ENC0_P0_3164 (ENC0_OFFSET + 0x164)
> > +#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x3fff
> > +
> > +#define MTK_DP_ENC0_P0_3168 (ENC0_OFFSET + 0x168)
> > +#define PGEN_VTOTAL_DP_ENC0_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_ENC0_P0_316C (ENC0_OFFSET + 0x16C)
> > +#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_ENC0_P0_3170 (ENC0_OFFSET + 0x170)
> > +#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_ENC0_P0_3174 (ENC0_OFFSET + 0x174)
> > +#define PGEN_VFDE_START_DP_ENC0_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_ENC0_P0_3178 (ENC0_OFFSET + 0x178)
> > +#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_ENC0_P0_31B0 (ENC0_OFFSET + 0x1B0)
> > +#define PGEN_PATTERN_SEL_SHIFT 4
> > +#define PGEN_PATTERN_SEL_MASK 0x0070
> > +
> > +#define MTK_DP_ENC0_P0_31C8 (ENC0_OFFSET + 0x1C8)
> > +#define VSC_EXT_VESA_HB0_DP_ENC0_P0_MASK 0xff
> > +#define VSC_EXT_VESA_HB1_DP_ENC0_P0_MASK 0xff00
> > +#define VSC_EXT_VESA_HB1_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC0_P0_31CC (ENC0_OFFSET + 0x1CC)
> > +#define VSC_EXT_VESA_HB2_DP_ENC0_P0_MASK 0xff
> > +#define VSC_EXT_VESA_HB2_DP_ENC0_P0_SHIFT 0
> > +#define VSC_EXT_VESA_HB3_DP_ENC0_P0_MASK 0xff00
> > +
> > +#define MTK_DP_ENC0_P0_31D0 (ENC0_OFFSET + 0x1D0)
> > +#define VSC_EXT_CEA_HB0_DP_ENC0_P0_MASK 0xff
> > +#define VSC_EXT_CEA_HB1_DP_ENC0_P0_MASK 0xff00
> > +#define VSC_EXT_CEA_HB1_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC0_P0_31D4 (ENC0_OFFSET + 0x1D4)
> > +#define VSC_EXT_CEA_HB2_DP_ENC0_P0_MASK 0xff
> > +#define VSC_EXT_CEA_HB2_DP_ENC0_P0_SHIFT 0
> > +#define VSC_EXT_CEA_HB3_DP_ENC0_P0_MASK 0xff00
> > +
> > +#define MTK_DP_ENC0_P0_31D8 (ENC0_OFFSET + 0x1D8)
> > +#define VSC_EXT_VESA_NUM_DP_ENC0_P0_MASK 0x3f
> > +#define VSC_EXT_VESA_NUM_DP_ENC0_P0_SHIFT 0
> > +#define VSC_EXT_CEA_NUM_DP_ENC0_P0_MASK 0x3f00
> > +#define VSC_EXT_CEA_NUM_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC0_P0_31DC (ENC0_OFFSET + 0x1DC)
> > +#define HDR0_CFG_DP_ENC0_P0_MASK 0xff
> > +#define HDR0_CFG_DP_ENC0_P0_SHIFT 0
> > +
> > +#define MTK_DP_ENC0_P0_31E8 (ENC0_OFFSET + 0x1E8)
> > +
> > +#define MTK_DP_ENC0_P0_31EC (ENC0_OFFSET + 0x1EC)
> > +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK 0x10
> > +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT 4
> > +#define ISRC1_HB3_DP_ENC0_P0_MASK 0xff00
> > +#define ISRC1_HB3_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC1_P0_3200 (ENC1_OFFSET + 0x000)
> > +
> > +#define MTK_DP_ENC1_P0_3280 (ENC1_OFFSET + 0x080)
> > +#define SDP_PACKET_TYPE_DP_ENC1_P0_MASK 0x1f
> > +#define SDP_PACKET_W_DP_ENC1_P0 0x20
> > +#define SDP_PACKET_W_DP_ENC1_P0_MASK 0x20
> > +#define SDP_PACKET_W_DP_ENC1_P0_SHIFT 5
> > +
> > +#define MTK_DP_ENC1_P0_328C (ENC1_OFFSET + 0x08C)
> > +
> > +#define MTK_DP_ENC1_P0_3290 (ENC1_OFFSET + 0x090)
> > +
> > +#define MTK_DP_ENC1_P0_32A0 (ENC1_OFFSET + 0x0A0)
> > +
> > +#define MTK_DP_ENC1_P0_32A4 (ENC1_OFFSET + 0x0A4)
> > +
> > +#define MTK_DP_ENC1_P0_3300 (ENC1_OFFSET + 0x100)
> > +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK 0x300
> > +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC1_P0_3304 (ENC1_OFFSET + 0x104)
> > +#define AU_PRTY_REGEN_DP_ENC1_P0_MASK 0x100
> > +#define AU_CH_STS_REGEN_DP_ENC1_P0_MASK 0x200
> > +#define AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK 0x1000
> > +
> > +#define MTK_DP_ENC1_P0_3324 (ENC1_OFFSET + 0x124)
> > +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK 0x300
> > +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT 8
> > +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX                                       \
> > +     (0 << AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT)
> > +
> > +#define MTK_DP_ENC1_P0_3364 (ENC1_OFFSET + 0x164)
> > +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK 0xfff
> > +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT 0
> > +#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK 0xf000
> > +#define FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT 12
> > +
> > +#define MTK_DP_ENC1_P0_3368 (ENC1_OFFSET + 0x168)
> > +#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT 0
> > +#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT 4
> > +#define SDP_DP13_EN_DP_ENC1_P0_SHIFT 8
> > +#define BS2BS_MODE_DP_ENC1_P0_MASK 0x3000
> > +#define BS2BS_MODE_DP_ENC1_P0_SHIFT 12
> > +
> > +#define MTK_DP_ENC1_P0_33F4 (ENC1_OFFSET + 0x1F4)
> > +
> > +#define MTK_DP_TRANS_P0_3400 (TRANS_OFFSET + 0x000)
> > +#define PATTERN1_EN_DP_TRANS_P0_MASK 0x1000
> > +#define PATTERN1_EN_DP_TRANS_P0_SHIFT 12
> > +#define PATTERN2_EN_DP_TRANS_P0_MASK 0x2000
> > +#define PATTERN3_EN_DP_TRANS_P0_MASK 0x4000
> > +#define PATTERN4_EN_DP_TRANS_P0_MASK 0x8000
> > +
> > +#define MTK_DP_TRANS_P0_3404 (TRANS_OFFSET + 0x004)
> > +#define DP_SCR_EN_DP_TRANS_P0_MASK 0x1
> > +
> > +#define MTK_DP_TRANS_P0_340C (TRANS_OFFSET + 0x00C)
> > +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK 0x2000
> > +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT 13
> > +
> > +#define MTK_DP_TRANS_P0_3410 (TRANS_OFFSET + 0x010)
> > +#define HPD_DEB_THD_DP_TRANS_P0_MASK 0xf
> > +#define HPD_DEB_THD_DP_TRANS_P0_SHIFT 0
> > +#define HPD_INT_THD_DP_TRANS_P0_MASK 0xf0
> > +#define HPD_INT_THD_DP_TRANS_P0_SHIFT 4
> > +#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US (2 << HPD_INT_THD_DP_TRANS_P0_SHIFT)
> > +#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US                                   \
> > +     (2 << (HPD_INT_THD_DP_TRANS_P0_SHIFT + 2))
> > +#define HPD_DISC_THD_DP_TRANS_P0_MASK 0xf00
> > +#define HPD_DISC_THD_DP_TRANS_P0_SHIFT 8
> > +#define HPD_CONN_THD_DP_TRANS_P0_MASK 0xf000
> > +#define HPD_CONN_THD_DP_TRANS_P0_SHIFT 12
> > +
> > +#define MTK_DP_TRANS_P0_3414 (TRANS_OFFSET + 0x014)
> > +#define HPD_DB_DP_TRANS_P0_MASK 0x4
> > +
> > +#define MTK_DP_TRANS_P0_3418 (TRANS_OFFSET + 0x018)
> > +#define IRQ_CLR_DP_TRANS_P0_MASK 0xf
> > +#define IRQ_MASK_DP_TRANS_P0_MASK 0xf0
> > +#define IRQ_MASK_DP_TRANS_P0_SHIFT 4
> > +#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ (BIT(1) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> > +#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ (BIT(2) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> > +#define IRQ_MASK_DP_TRANS_P0_INT_IRQ (BIT(3) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> > +#define IRQ_STATUS_DP_TRANS_P0_MASK 0xf000
> > +#define IRQ_STATUS_DP_TRANS_P0_SHIFT 12
> > +
> > +#define MTK_DP_TRANS_P0_342C (TRANS_OFFSET + 0x02C)
> > +#define XTAL_FREQ_DP_TRANS_P0_DEFAULT 0x69
> > +#define XTAL_FREQ_DP_TRANS_P0_MASK 0xff
> > +
> > +#define MTK_DP_TRANS_P0_3430 (TRANS_OFFSET + 0x030)
> > +#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK 0x3
> > +#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT BIT(1)
> > +
> > +#define MTK_DP_TRANS_P0_34A4 (TRANS_OFFSET + 0x0A4)
> > +#define LANE_NUM_DP_TRANS_P0_MASK 0xc
> > +#define LANE_NUM_DP_TRANS_P0_SHIFT 2
> > +
> > +#define MTK_DP_TRANS_P0_3540 (TRANS_OFFSET + 0x140)
> > +#define FEC_EN_DP_TRANS_P0_MASK 0x1
> > +#define FEC_EN_DP_TRANS_P0_SHIFT 0
> > +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK 0x8
> > +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT 3
> > +
> > +#define MTK_DP_TRANS_P0_3580 (TRANS_OFFSET + 0x180)
> > +#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK 0x100
> > +#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK 0x200
> > +#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK 0x400
> > +#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK 0x800
> > +
> > +#define MTK_DP_TRANS_P0_35C4 (TRANS_OFFSET + 0x1C4)
> > +#define SW_IRQ_MASK_DP_TRANS_P0_MASK 0xffff
> > +
> > +#define MTK_DP_TRANS_P0_35C8 (TRANS_OFFSET + 0x1C8)
> > +#define SW_IRQ_CLR_DP_TRANS_P0_MASK 0xffff
> > +
> > +#define SW_IRQ_STATUS_DP_TRANS_P0_MASK 0xffff
> > +#define SW_IRQ_STATUS_DP_TRANS_P0_SHIFT 0
> > +
> > +#define MTK_DP_TRANS_P0_35D0 (TRANS_OFFSET + 0x1D0)
> > +#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK 0xffff
> > +
> > +#define MTK_DP_TRANS_P0_35F0 (TRANS_OFFSET + 0x1F0)
> > +
> > +#define MTK_DP_AUX_P0_360C (AUX_OFFSET + 0x00C)
> > +#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_AUX_P0_3614 (AUX_OFFSET + 0x014)
> > +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK 0x7f
> > +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT 0
> > +
> > +#define MTK_DP_AUX_P0_3618 (AUX_OFFSET + 0x018)
> > +#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK 0x200
> > +#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK 0xf
> > +
> > +#define MTK_DP_AUX_P0_3620 (AUX_OFFSET + 0x020)
> > +#define AUX_RD_MODE_AUX_TX_P0_MASK 0x200
> > +#define AUX_RX_FIFO_READ_PULSE_TX_P0_MASK 0x100
> > +#define AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT 8
> > +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK 0xff
> > +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT 0
> > +
> > +#define MTK_DP_AUX_P0_3624 (AUX_OFFSET + 0x024)
> > +#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK 0xf
> > +
> > +#define MTK_DP_AUX_P0_3628 (AUX_OFFSET + 0x028)
> > +#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK 0x3ff
> > +#define AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT 0
> > +#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE                                     \
> > +     (BIT(0) << AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT)
> > +
> > +#define MTK_DP_AUX_P0_362C (AUX_OFFSET + 0x02C)
> > +#define AUX_NO_LENGTH_AUX_TX_P0_MASK 0x1
> > +#define AUX_NO_LENGTH_AUX_TX_P0_SHIFT 0
> > +#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK 0x2
> > +#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK 0xfffc
> > +
> > +#define MTK_DP_AUX_P0_3630 (AUX_OFFSET + 0x030)
> > +#define AUX_TX_REQUEST_READY_AUX_TX_P0_MASK 0x8
> > +#define AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT 3
> > +
> > +#define MTK_DP_AUX_P0_3634 (AUX_OFFSET + 0x034)
> > +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK 0xff00
> > +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT 8
> > +
> > +#define MTK_DP_AUX_P0_3640 (AUX_OFFSET + 0x040)
> > +#define AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK 0x40
> > +#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 6
> > +#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 5
> > +#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 4
> > +#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT 3
> > +#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT 2
> > +#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT 1
> > +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK 0x1
> > +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT 0
> > +
> > +#define MTK_DP_AUX_P0_3644 (AUX_OFFSET + 0x044)
> > +#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK 0xf
> > +
> > +#define MTK_DP_AUX_P0_3648 (AUX_OFFSET + 0x048)
> > +#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK 0xffff
> > +
> > +#define MTK_DP_AUX_P0_364C (AUX_OFFSET + 0x04C)
> > +#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK 0xf
> > +
> > +#define MTK_DP_AUX_P0_3650 (AUX_OFFSET + 0x050)
> > +#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK 0xf000
> > +#define MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT 12
> > +#define PHY_FIFO_RST_AUX_TX_P0_MASK 0x200
> > +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK 0x100
> > +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT 8
> > +
> > +#define MTK_DP_AUX_P0_3658 (AUX_OFFSET + 0x058)
> > +#define AUX_TX_OV_EN_AUX_TX_P0_MASK 0x1
> > +
> > +#define MTK_DP_AUX_P0_3690 (AUX_OFFSET + 0x090)
> > +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK 0x100
> > +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT 8
> > +
> > +#define MTK_DP_AUX_P0_3704 (AUX_OFFSET + 0x104)
> > +#define AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK 0x2
> > +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK 0x4
> > +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT 2
> > +
> > +#define MTK_DP_AUX_P0_3708 (AUX_OFFSET + 0x108)
> > +
> > +#define MTK_DP_AUX_P0_37C8 (AUX_OFFSET + 0x1C8)
> > +#define MTK_ATOP_EN_AUX_TX_P0_MASK 0x1
> > +#define MTK_ATOP_EN_AUX_TX_P0_SHIFT 0
> > +
> > +#define MTK_DP_TOP_PWR_STATE (TOP_OFFSET + 0x00)
> > +#define DP_PWR_STATE_MASK 0x3
> > +#define DP_PWR_STATE_BANDGAP 1
> > +#define DP_PWR_STATE_BANDGAP_TPLL 2
> > +#define DP_PWR_STATE_BANDGAP_TPLL_LANE 3
> > +
> > +#define MTK_DP_TOP_SWING_EMP (TOP_OFFSET + 0x04)
> > +#define DP_TX0_VOLT_SWING_MASK 0x3
> > +#define DP_TX0_VOLT_SWING_SHIFT 0
> > +#define DP_TX0_PRE_EMPH_MASK 0xc
> > +#define DP_TX0_PRE_EMPH_SHIFT 2
> > +#define DP_TX1_VOLT_SWING_MASK 0x300
> > +#define DP_TX1_VOLT_SWING_SHIFT 8
> > +#define DP_TX1_PRE_EMPH_MASK 0xc00
> > +#define DP_TX2_VOLT_SWING_MASK 0x30000
> > +#define DP_TX2_PRE_EMPH_MASK 0xc0000
> > +#define DP_TX3_VOLT_SWING_MASK 0x3000000
> > +#define DP_TX3_PRE_EMPH_MASK 0xc000000
> > +
> > +#define MTK_DP_TOP_RESET_AND_PROBE (TOP_OFFSET + 0x20)
> > +#define SW_RST_B_SHIFT 0
> > +#define SW_RST_B_PHYD (BIT(4) << SW_RST_B_SHIFT)
> > +
> > +#define MTK_DP_TOP_IRQ_STATUS (TOP_OFFSET + 0x28)
> > +#define RGS_IRQ_STATUS_SHIFT 0
> > +#define RGS_IRQ_STATUS_TRANSMITTER (BIT(1) << RGS_IRQ_STATUS_SHIFT)
> > +
> > +#define MTK_DP_TOP_IRQ_MASK (TOP_OFFSET + 0x2C)
> > +#define IRQ_MASK_AUX_TOP_IRQ BIT(2)
> > +
> > +#define MTK_DP_TOP_MEM_PD (TOP_OFFSET + 0x38)
> > +#define MEM_ISO_EN_SHIFT 0
> > +#define FUSE_SEL_SHIFT 2
> > +
> > +#endif /*_MTK_DP_REG_H_*/
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > index 384074f69111b..e6e88e3cd811d 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> >  };
> >
> > +enum TVDPLL_CLK {
> > +     TVDPLL_PLL = 0,
> > +     TVDPLL_D2 = 2,
> > +     TVDPLL_D4 = 4,
> > +     TVDPLL_D8 = 8,
> > +     TVDPLL_D16 = 16,
> > +};
> > +
> >  struct mtk_dpi {
> >       struct drm_encoder encoder;
> >       struct drm_bridge bridge;
> > @@ -71,8 +79,10 @@ struct mtk_dpi {
> >       void __iomem *regs;
> >       struct device *dev;
> >       struct clk *engine_clk;
> > +     struct clk *dpi_ck_cg;
> >       struct clk *pixel_clk;
> >       struct clk *tvd_clk;
> > +     struct clk *pclk_src[5];
> >       int irq;
> >       struct drm_display_mode mode;
> >       const struct mtk_dpi_conf *conf;
> > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> >       u32 hvsize_mask;
> >       u32 channel_swap_shift;
> >       u32 yuv422_en_bit;
> > +     u32 csc_enable_bit;
> >       const struct mtk_dpi_yc_limit *limit;
> >  };
> >
> > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> >
> >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> >  {
> > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> >  }
> >
> >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> >  }
> >
> > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > +{
> > +     u32 matrix_sel = 0;
> > +
> > +     switch (format) {
> > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > +             if (dpi->mode.hdisplay <= 720)
> > +                     matrix_sel = 0x2;
> > +             break;
> > +     default:
> > +             break;
> > +     }
> > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > +}
> > +
> >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> >                                       enum mtk_dpi_out_color_format format)
> >  {
> >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> >               mtk_dpi_config_yuv422_enable(dpi, false);
> > -             if (dpi->conf->csc_support)
> > +             if (dpi->conf->csc_support) {
> >                       mtk_dpi_config_csc_enable(dpi, true);
> > +                     mtk_dpi_matrix_sel(dpi, format);
> > +             }
> >               if (dpi->conf->swap_input_support)
> >                       mtk_dpi_config_swap_input(dpi, false);
> >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> >               mtk_dpi_config_yuv422_enable(dpi, true);
> > -             if (dpi->conf->csc_support)
> > +             if (dpi->conf->csc_support) {
> >                       mtk_dpi_config_csc_enable(dpi, true);
> > +                     mtk_dpi_matrix_sel(dpi, format);
> > +             }
> >               if (dpi->conf->swap_input_support)
> >                       mtk_dpi_config_swap_input(dpi, true);
> >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> >       mtk_dpi_disable(dpi);
> >       clk_disable_unprepare(dpi->pixel_clk);
> >       clk_disable_unprepare(dpi->engine_clk);
> > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > +     clk_disable_unprepare(dpi->tvd_clk);
> >  }
> >
> >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> >       if (++dpi->refcount != 1)
> >               return 0;
> >
> > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > +     if (ret) {
> > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > +             goto err_pixel;
> > +     }
> > +
> >       ret = clk_prepare_enable(dpi->engine_clk);
> >       if (ret) {
> >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> >               goto err_refcount;
> >       }
> >
> > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > +     if (ret) {
> > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > +             goto err_ck_cg;
> > +     }
> > +
> >       ret = clk_prepare_enable(dpi->pixel_clk);
> >       if (ret) {
> >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> >       if (dpi->pinctrl && dpi->pins_dpi)
> >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> >
> > -     mtk_dpi_enable(dpi);
> >       return 0;
> >
> >  err_pixel:
> > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > +err_ck_cg:
> >       clk_disable_unprepare(dpi->engine_clk);
> >  err_refcount:
> >       dpi->refcount--;
> > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> >       pll_rate = clk_get_rate(dpi->tvd_clk);
> >
> >       vm.pixelclock = pll_rate / factor;
> > -     if (dpi->conf->is_dpintf)
> > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > +     if (dpi->conf->is_dpintf) {
> > +             if (factor == 1)
> > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > +             else if (factor == 2)
> > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > +             else if (factor == 4)
> > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > +             else
> > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> >       else
> > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> >       }
> >       mtk_dpi_sw_reset(dpi, false);
> >
> > +     mtk_dpi_enable(dpi);
> > +
> >       return 0;
> >  }
> >
> > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> >       u32 *input_fmts;
> >
> >       *num_input_fmts = 0;
> > -
> >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> >                            GFP_KERNEL);
> >       if (!input_fmts)
> > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> >               if (dpi->conf->num_output_fmts)
> >                       out_bus_format = dpi->conf->output_fmts[0];
> >
> > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> >               bridge_state->input_bus_cfg.format,
> >               bridge_state->output_bus_cfg.format);
> >
> > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > +     else
> > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> >
> >       return 0;
> >  }
> > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> >  };
> >
> > +static const u32 mt8195_output_fmts[] = {
> > +     MEDIA_BUS_FMT_RGB888_1X24,
> > +     MEDIA_BUS_FMT_YUV8_1X24,
> > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > +};
> > +
> >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> >       .c_bottom = 0x0010,
> >       .c_top = 0x0FE0,
> > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> >       .hvsize_mask = HSIZE_MASK,
> >       .channel_swap_shift = CH_SWAP,
> >       .yuv422_en_bit = YUV422_EN,
> > +     .csc_enable_bit = CSC_ENABLE,
> >       .limit = &mtk_dpi_limit,
> >  };
> >
> > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> >       .hvsize_mask = HSIZE_MASK,
> >       .channel_swap_shift = CH_SWAP,
> >       .yuv422_en_bit = YUV422_EN,
> > +     .csc_enable_bit = CSC_ENABLE,
> >       .limit = &mtk_dpi_limit,
> >  };
> >
> > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> >       .hvsize_mask = HSIZE_MASK,
> >       .channel_swap_shift = CH_SWAP,
> >       .yuv422_en_bit = YUV422_EN,
> > +     .csc_enable_bit = CSC_ENABLE,
> >       .limit = &mtk_dpi_limit,
> >  };
> >
> > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> >       .hvsize_mask = HSIZE_MASK,
> >       .channel_swap_shift = CH_SWAP,
> >       .yuv422_en_bit = YUV422_EN,
> > +     .csc_enable_bit = CSC_ENABLE,
> >       .limit = &mtk_dpi_limit,
> >  };
> >
> >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> >       .cal_factor = mt8195_dpintf_calculate_factor,
> > -     .output_fmts = mt8173_output_fmts,
> > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > +     .output_fmts = mt8195_output_fmts,
> > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> >       .is_dpintf = true,
> > +     .csc_support = true,
> >       .dimension_mask = DPINTF_HPW_MASK,
> >       .hvsize_mask = DPINTF_HSIZE_MASK,
> >       .channel_swap_shift = DPINTF_CH_SWAP,
> >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> >       .limit = &mtk_dpintf_limit,
> >  };
> >
> > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> >               return ret;
> >       }
> >
> > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > +             if (ret != -EPROBE_DEFER)
> > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > +                             ret);
> > +
> > +             return ret;
> > +     }
> > +
> >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> >       if (IS_ERR(dpi->pixel_clk)) {
> >               ret = PTR_ERR(dpi->pixel_clk);
> > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> >               return ret;
> >       }
> >
> > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > +
> >       dpi->irq = platform_get_irq(pdev, 0);
> >       if (dpi->irq <= 0)
> >               return -EINVAL;
>
> All those changes look unrelated as well?
>
Do you mean all the changes in mtk_dpi.c ?
They are in support of enabling the mt8195 dpintf driver... so, not
sure they are
unlreated?

> Maxime

Thx,
Guillaume.

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-15 14:33       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-15 14:33 UTC (permalink / raw)
  To: Maxime Ripard, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Daniel Vetter, Chun-Kuang Hu,
	Philipp Zabel, Matthias Brugger
  Cc: Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Hi Maxime,

Thanks for your review.

Quoting Maxime Ripard (2021-11-15 11:11:29)
> Hi,
>
> On Wed, Nov 10, 2021 at 02:06:23PM +0100, Guillaume Ranquet wrote:
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > according phy driver mediatek-dp-phy.
> >
> > It supports both functional units on the mt8195, the embedded
> > DisplayPort as well as the external DisplayPort units. It offers
> > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > to 4 lanes.
>
> There's a number of checkpatch --strict errors in there, make sure to fix them.
This is a regression I've introduced, I think, when running
clang-format... I'll fix them.

>
> > The driver creates a child device for the phy. The child device will
> > never exist without the parent being active. As they are sharing a
> > register range, the parent passes a regmap pointer to the child so that
> > both can work with the same register range. The phy driver sets device
> > data that is read by the parent to get the phy device that can be used
> > to control the phy properties.
>
> If the PHY is in the same register space than the DP controller, why do
> you need a separate PHY driver in the first place?
>

This has been asked by Chun-Kuang Hu in a previous revision of the series:

https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/


> > This driver is based on an initial version by
> > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > Reported-by: kernel test robot <lkp@intel.com>
>
> > ---
> >  drivers/gpu/drm/drm_edid.c              |    2 +-
> >  drivers/gpu/drm/mediatek/Kconfig        |    7 +
> >  drivers/gpu/drm/mediatek/Makefile       |    2 +
> >  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
> >  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
> >  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
> >  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
> >  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
> >  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
> >  9 files changed, 3799 insertions(+), 13 deletions(-)
> >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
> >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> >
> > diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> > index 500279a82167a..bfd98b50ceb5b 100644
> > --- a/drivers/gpu/drm/drm_edid.c
> > +++ b/drivers/gpu/drm/drm_edid.c
> > @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
> >        * modes and forbids YCRCB422 support for all video modes per
> >        * HDMI 1.3 spec.
> >        */
> > -     info->color_formats = DRM_COLOR_FORMAT_RGB444;
> > +     info->color_formats |= DRM_COLOR_FORMAT_RGB444;
> >
> >       /* YCRCB444 is optional according to spec. */
> >       if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
>
> This looks unrelated?
>
Right, I should have kept it in a separate patch.

> > diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> > index 2976d21e9a34a..029b94c716131 100644
> > --- a/drivers/gpu/drm/mediatek/Kconfig
> > +++ b/drivers/gpu/drm/mediatek/Kconfig
> > @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
> >       select PHY_MTK_HDMI
> >       help
> >         DRM/KMS HDMI driver for Mediatek SoCs
> > +
> > +config MTK_DPTX_SUPPORT
> > +     tristate "DRM DPTX Support for Mediatek SoCs"
> > +     depends on DRM_MEDIATEK
> > +     select PHY_MTK_DP
> > +     help
> > +       DRM/KMS Display Port driver for Mediatek SoCs.
> > diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> > index 29098d7c8307c..d86a6406055e6 100644
> > --- a/drivers/gpu/drm/mediatek/Makefile
> > +++ b/drivers/gpu/drm/mediatek/Makefile
> > @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
> >                         mtk_hdmi_ddc.o
> >
> >  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> > +
> > +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> > new file mode 100644
> > index 0000000000000..83087219d5a5e
> > --- /dev/null
> > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> > @@ -0,0 +1,3094 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2019 MediaTek Inc.
> > + * Copyright (c) 2021 BayLibre
> > + */
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_bridge.h>
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_dp_helper.h>
> > +#include <drm/drm_edid.h>
> > +#include <drm/drm_of.h>
> > +#include <drm/drm_panel.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/drm_probe_helper.h>
> > +#include <linux/arm-smccc.h>
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/errno.h>
> > +#include <linux/kernel.h>
> > +#include <linux/nvmem-consumer.h>
> > +#include <linux/of.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <sound/hdmi-codec.h>
> > +#include <video/videomode.h>
> > +
> > +#include "mtk_dp_reg.h"
> > +
> > +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> > +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> > +
> > +#define MTK_DP_MAX_LANES 4
> > +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> > +
> > +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> > +
> > +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> > +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> > +
> > +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> > +
> > +#define MTK_DP_DP_VERSION_11 0x11
> > +
> > +enum mtk_dp_state {
> > +     MTK_DP_STATE_INITIAL,
> > +     MTK_DP_STATE_IDLE,
> > +     MTK_DP_STATE_PREPARE,
> > +     MTK_DP_STATE_NORMAL,
> > +};
> > +
> > +enum mtk_dp_train_state {
> > +     MTK_DP_TRAIN_STATE_STARTUP = 0,
> > +     MTK_DP_TRAIN_STATE_CHECKCAP,
> > +     MTK_DP_TRAIN_STATE_CHECKEDID,
> > +     MTK_DP_TRAIN_STATE_TRAINING_PRE,
> > +     MTK_DP_TRAIN_STATE_TRAINING,
> > +     MTK_DP_TRAIN_STATE_CHECKTIMING,
> > +     MTK_DP_TRAIN_STATE_NORMAL,
> > +     MTK_DP_TRAIN_STATE_POWERSAVE,
> > +     MTK_DP_TRAIN_STATE_DPIDLE,
> > +};
> > +
> > +struct mtk_dp_timings {
> > +     struct videomode vm;
> > +
> > +     u16 htotal;
> > +     u16 vtotal;
> > +     u8 frame_rate;
> > +     u32 pix_rate_khz;
> > +};
> > +
> > +struct mtk_dp_train_info {
> > +     bool tps3;
> > +     bool tps4;
> > +     bool sink_ssc;
> > +     bool cable_plugged_in;
> > +     bool cable_state_change;
> > +     bool cr_done;
> > +     bool eq_done;
> > +
> > +     // link_rate is in multiple of 0.27Gbps
> > +     int link_rate;
> > +     int lane_count;
> > +
> > +     int irq_status;
> > +     int check_cap_count;
> > +};
> > +
> > +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> > +enum mtk_dp_color_format {
> > +     MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> > +     MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> > +     MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> > +     MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> > +     MTK_DP_COLOR_FORMAT_YONLY = 4,
> > +     MTK_DP_COLOR_FORMAT_RAW = 5,
> > +     MTK_DP_COLOR_FORMAT_RESERVED = 6,
> > +     MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> > +     MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> > +};
> > +
> > +// Multiple of 0.27Gbps
> > +enum mtk_dp_linkrate {
> > +     MTK_DP_LINKRATE_RBR = 0x6,
> > +     MTK_DP_LINKRATE_HBR = 0xA,
> > +     MTK_DP_LINKRATE_HBR2 = 0x14,
> > +     MTK_DP_LINKRATE_HBR25 = 0x19,
> > +     MTK_DP_LINKRATE_HBR3 = 0x1E,
> > +};
> > +
> > +// Same values as used for DP Spec MISC0 bits 5,6,7
> > +enum mtk_dp_color_depth {
> > +     MTK_DP_COLOR_DEPTH_6BIT = 0,
> > +     MTK_DP_COLOR_DEPTH_8BIT = 1,
> > +     MTK_DP_COLOR_DEPTH_10BIT = 2,
> > +     MTK_DP_COLOR_DEPTH_12BIT = 3,
> > +     MTK_DP_COLOR_DEPTH_16BIT = 4,
> > +     MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> > +};
> > +
> > +struct mtk_dp_audio_cfg {
> > +     int sample_rate;
> > +     int word_length_bits;
> > +     int channels;
> > +};
> > +
> > +struct mtk_dp_info {
> > +     enum mtk_dp_color_depth depth;
> > +     enum mtk_dp_color_format format;
> > +     struct mtk_dp_audio_cfg audio_caps;
> > +     struct mtk_dp_timings timings;
> > +};
> > +
> > +struct dp_cal_data {
> > +     unsigned int glb_bias_trim;
> > +     unsigned int clktx_impse;
> > +
> > +     unsigned int ln0_tx_impsel_pmos;
> > +     unsigned int ln0_tx_impsel_nmos;
> > +     unsigned int ln1_tx_impsel_pmos;
> > +     unsigned int ln1_tx_impsel_nmos;
> > +     unsigned int ln2_tx_impsel_pmos;
> > +     unsigned int ln2_tx_impsel_nmos;
> > +     unsigned int ln3_tx_impsel_pmos;
> > +     unsigned int ln3_tx_impsel_nmos;
> > +};
> > +
> > +struct mtk_dp {
> > +     struct device *dev;
> > +     struct platform_device *phy_dev;
> > +     struct phy *phy;
> > +     struct dp_cal_data cal_data;
> > +
> > +     struct drm_device *drm_dev;
> > +     struct drm_bridge bridge;
> > +     struct drm_bridge *next_bridge;
> > +     struct drm_dp_aux aux;
> > +
> > +     struct mutex edid_lock;
> > +     struct edid *edid;
> > +
> > +     u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> > +
> > +     struct mtk_dp_info info;
> > +     enum mtk_dp_state state;
> > +
> > +     struct mtk_dp_train_info train_info;
> > +     enum mtk_dp_train_state train_state;
> > +     unsigned int input_fmt;
> > +
> > +     struct regmap *regs;
> > +     struct clk *dp_tx_clk;
> > +
> > +     bool enabled;
> > +     bool audio_enable;
> > +
> > +     bool has_fec;
> > +     struct mutex dp_lock;
> > +
> > +     struct mutex update_plugged_status_lock;
> > +
> > +     hdmi_codec_plugged_cb plugged_cb;
> > +     struct device *codec_dev;
> > +     u8 connector_eld[MAX_ELD_BYTES];
> > +};
> > +
> > +enum mtk_dp_sdp_type {
> > +     MTK_DP_SDP_NONE = 0x00,
> > +     MTK_DP_SDP_ACM = 0x01,
> > +     MTK_DP_SDP_ISRC = 0x02,
> > +     MTK_DP_SDP_AVI = 0x03,
> > +     MTK_DP_SDP_AUI = 0x04,
> > +     MTK_DP_SDP_SPD = 0x05,
> > +     MTK_DP_SDP_MPEG = 0x06,
> > +     MTK_DP_SDP_NTSC = 0x07,
> > +     MTK_DP_SDP_VSP = 0x08,
> > +     MTK_DP_SDP_VSC = 0x09,
> > +     MTK_DP_SDP_EXT = 0x0A,
> > +     MTK_DP_SDP_PPS0 = 0x0B,
> > +     MTK_DP_SDP_PPS1 = 0x0C,
> > +     MTK_DP_SDP_PPS2 = 0x0D,
> > +     MTK_DP_SDP_PPS3 = 0x0E,
> > +     MTK_DP_SDP_DRM = 0x10,
> > +     MTK_DP_SDP_MAX_NUM
> > +};
> > +
> > +struct mtk_dp_sdp_packet {
> > +     enum mtk_dp_sdp_type type;
> > +     struct dp_sdp sdp;
> > +};
> > +
> > +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> > +union mtk_dp_audio_channel_status {
> > +     struct {
> > +             u8 rev : 1;
> > +             u8 is_lpcm : 1;
> > +             u8 copy_right : 1;
> > +             u8 additional_format_info : 3;
> > +             u8 channel_status_mode : 2;
> > +             u8 category_code;
> > +             u8 src_num : 4;
> > +             u8 channel_num : 4;
> > +             u8 sampling_freq : 4;
> > +             u8 clk_accuracy : 2;
> > +             u8 rev2 : 2;
> > +             u8 word_len : 4;
> > +             u8 original_sampling_freq : 4;
> > +     } iec;
> > +
> > +     u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> > +};
>
> Why don't you leverage hdmi_codec_params and snd_aes_iec958?
>
I'll see if I can make it work with snd_aes_iec958 and get back to you.

> > +static struct regmap_config mtk_dp_regmap_config = {
> > +     .reg_bits = 32,
> > +     .val_bits = 32,
> > +     .reg_stride = 4,
> > +     .max_register = SEC_OFFSET + 0x90,
> > +     .name = "mtk-dp-registers",
> > +};
> > +
> > +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
> > +{
> > +     return mtk_dp->next_bridge != NULL;
> > +}
> > +
> > +static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
> > +{
> > +     return container_of(b, struct mtk_dp, bridge);
> > +}
> > +
> > +static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
> > +{
> > +     u32 read_val;
> > +     int ret;
> > +
> > +     ret = regmap_read(mtk_dp->regs, offset, &read_val);
> > +     if (ret) {
> > +             dev_err(mtk_dp->dev, "Failed to read register 0x%x: %d\n",
> > +                     offset, ret);
> > +             return 0;
> > +     }
> > +
> > +     return read_val;
> > +}
> > +
> > +static void mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
> > +{
> > +     int ret;
> > +
> > +     ret = regmap_write(mtk_dp->regs, offset, val);
> > +     if (ret)
> > +             dev_err(mtk_dp->dev,
> > +                     "Failed to write register 0x%x with value 0x%x: %d\n",
> > +                     offset, val, ret);
> > +}
> > +
> > +static void mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, u32 val,
> > +                            u32 mask)
> > +{
> > +     int ret;
> > +
> > +     ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
> > +     if (ret)
> > +             dev_err(mtk_dp->dev,
> > +                     "Failed to update register 0x%x with value 0x%x, mask 0x%x: %d\n",
> > +                     offset, val, mask, ret);
> > +}
> > +
> > +static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32 offset, u8 *buf,
> > +                                 size_t length)
> > +{
> > +     int i;
> > +     int num_regs = (length + 1) / 2;
> > +
> > +     // 2 bytes per register
> > +     for (i = 0; i < num_regs; i++) {
> > +             u32 val = buf[i * 2] |
> > +                       (i * 2 + 1 < length ? buf[i * 2 + 1] << 8 : 0);
> > +
> > +             mtk_dp_write(mtk_dp, offset + i * 4, val);
> > +     }
> > +}
> > +
> > +static unsigned long mtk_dp_sip_atf_call(unsigned int cmd, unsigned int para)
> > +{
> > +     struct arm_smccc_res res;
> > +
> > +     arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32, cmd, para, 0, 0, 0, 0, 0,
> > +                   &res);
> > +
> > +     pr_debug("[DPTX]%s cmd 0x%x, p1 0x%x, ret 0x%lx-0x%lx", __func__, cmd,
> > +              para, res.a0, res.a1);
> > +     return res.a1;
> > +}
> > +
> > +static void mtk_dp_msa_bypass_disable(struct mtk_dp *mtk_dp)
> > +{
> > +     const u16 bits_to_set =
> > +             BIT(HTOTAL_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(VTOTAL_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(HSTART_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(VSTART_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(HWIDTH_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(VHEIGHT_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(HSP_SEL_DP_ENC0_P0_SHIFT) | BIT(HSW_SEL_DP_ENC0_P0_SHIFT) |
> > +             BIT(VSP_SEL_DP_ENC0_P0_SHIFT) | BIT(VSW_SEL_DP_ENC0_P0_SHIFT);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, bits_to_set,
> > +                        bits_to_set);
> > +}
> > +
> > +static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
> > +{
> > +     struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010, timings->htotal,
> > +                        HTOTAL_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
> > +                        timings->vm.hsync_len + timings->vm.hback_porch,
> > +                        HSTART_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> > +                        timings->vm.hsync_len << HSW_SW_DP_ENC0_P0_SHIFT,
> > +                        HSW_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028, 0,
> > +                        HSP_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020, timings->vm.hactive,
> > +                        HWIDTH_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014, timings->vtotal,
> > +                        VTOTAL_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
> > +                        timings->vm.vsync_len + timings->vm.vback_porch,
> > +                        VSTART_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> > +                        timings->vm.vsync_len << VSW_SW_DP_ENC0_P0_SHIFT,
> > +                        VSW_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C, 0,
> > +                        VSP_SW_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024, timings->vm.vactive,
> > +                        VHEIGHT_SW_DP_ENC0_P0_MASK);
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064, timings->vm.hactive,
> > +                        HDE_NUM_LAST_DP_ENC0_P0_MASK);
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154, timings->htotal,
> > +                        PGEN_HTOTAL_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
> > +                        timings->vm.hfront_porch,
> > +                        PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C, timings->vm.hsync_len,
> > +                        PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
> > +                        timings->vm.hback_porch + timings->vm.hsync_len,
> > +                        PGEN_HFDE_START_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164, timings->vm.hactive,
> > +                        PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168, timings->vtotal,
> > +                        PGEN_VTOTAL_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
> > +                        timings->vm.vfront_porch,
> > +                        PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170, timings->vm.vsync_len,
> > +                        PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
> > +                        timings->vm.vback_porch + timings->vm.vsync_len,
> > +                        PGEN_VFDE_START_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178, timings->vm.vactive,
> > +                        PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
> > +                                 enum mtk_dp_color_format color_format)
> > +{
> > +     u32 val;
> > +
> > +     mtk_dp->info.format = color_format;
> > +
> > +     // Update MISC0
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> > +                        color_format << DP_TEST_COLOR_FORMAT_SHIFT,
> > +                        DP_TEST_COLOR_FORMAT_MASK);
> > +
> > +     switch (color_format) {
> > +     case MTK_DP_COLOR_FORMAT_YUV_422:
> > +             val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
> > +             break;
> > +     case MTK_DP_COLOR_FORMAT_YUV_420:
> > +             val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420;
> > +             break;
> > +     case MTK_DP_COLOR_FORMAT_YONLY:
> > +     case MTK_DP_COLOR_FORMAT_RAW:
> > +     case MTK_DP_COLOR_FORMAT_RESERVED:
> > +     case MTK_DP_COLOR_FORMAT_UNKNOWN:
> > +             drm_warn(mtk_dp->drm_dev, "Unsupported color format: %d\n",
> > +                      color_format);
> > +             fallthrough;
> > +     case MTK_DP_COLOR_FORMAT_RGB_444:
> > +     case MTK_DP_COLOR_FORMAT_YUV_444:
> > +             val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
> > +             break;
> > +     }
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
> > +                        PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp,
> > +                                enum mtk_dp_color_depth color_depth)
> > +{
> > +     u32 val;
> > +
> > +     mtk_dp->info.depth = color_depth;
> > +
> > +     // Update MISC0
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> > +                        color_depth << DP_TEST_BIT_DEPTH_SHIFT,
> > +                        DP_TEST_BIT_DEPTH_MASK);
> > +
> > +     switch (color_depth) {
> > +     case MTK_DP_COLOR_DEPTH_6BIT:
> > +             val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT;
> > +             break;
> > +     case MTK_DP_COLOR_DEPTH_8BIT:
> > +             val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT;
> > +             break;
> > +     case MTK_DP_COLOR_DEPTH_10BIT:
> > +             val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT;
> > +             break;
> > +     case MTK_DP_COLOR_DEPTH_12BIT:
> > +             val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT;
> > +             break;
> > +     case MTK_DP_COLOR_DEPTH_16BIT:
> > +             val = VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT;
> > +             break;
> > +     case MTK_DP_COLOR_DEPTH_UNKNOWN:
> > +             drm_warn(mtk_dp->drm_dev, "Unsupported color depth %d\n",
> > +                      color_depth);
> > +             return;
> > +     }
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C, val,
> > +                        VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_mn_overwrite_disable(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
> > +                        VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32 val)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> > +                        val << SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT,
> > +                        SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> > +                        BIT(VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT),
> > +                        VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> > +                        0x20 << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
> > +                        SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> > +                        0x20 << SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT,
> > +                        SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
> > +                        2 << VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT,
> > +                        VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> > +                        4 << FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT,
> > +                        FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
> > +     mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368,
> > +                  1 << VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT |
> > +                          1 << VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT |
> > +                          BIT(SDP_DP13_EN_DP_ENC1_P0_SHIFT) |
> > +                          1 << BS2BS_MODE_DP_ENC1_P0_SHIFT);
> > +}
> > +
> > +static void mtk_dp_pg_disable(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038, 0,
> > +                        VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
> > +                        4 << PGEN_PATTERN_SEL_SHIFT, PGEN_PATTERN_SEL_MASK);
> > +}
> > +
> > +static void mtk_dp_audio_setup_channels(struct mtk_dp *mtk_dp,
> > +                                     struct mtk_dp_audio_cfg *cfg)
> > +{
> > +     u32 channel_enable_bits;
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3324,
> > +                        AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX,
> > +                        AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK);
> > +
> > +     //audio channel count change reset
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(9), BIT(9));
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> > +                        AU_PRTY_REGEN_DP_ENC1_P0_MASK,
> > +                        AU_PRTY_REGEN_DP_ENC1_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> > +                        AU_CH_STS_REGEN_DP_ENC1_P0_MASK,
> > +                        AU_CH_STS_REGEN_DP_ENC1_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
> > +                        AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK,
> > +                        AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK);
> > +
> > +     switch (cfg->channels) {
> > +     case 2:
> > +             channel_enable_bits = AUDIO_2CH_SEL_DP_ENC0_P0_MASK |
> > +                                   AUDIO_2CH_EN_DP_ENC0_P0_MASK;
> > +             break;
> > +     case 8:
> > +     default:
> > +             channel_enable_bits = AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
> > +                                   AUDIO_8CH_EN_DP_ENC0_P0_MASK;
> > +             break;
> > +     }
> > +     mtk_dp_update_bits(
> > +             mtk_dp, MTK_DP_ENC0_P0_3088,
> > +             channel_enable_bits | AU_EN_DP_ENC0_P0_MASK,
> > +             AUDIO_2CH_SEL_DP_ENC0_P0_MASK | AUDIO_2CH_EN_DP_ENC0_P0_MASK |
> > +                     AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
> > +                     AUDIO_8CH_EN_DP_ENC0_P0_MASK | AU_EN_DP_ENC0_P0_MASK);
> > +
> > +     //audio channel count change reset
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, 0, BIT(9));
> > +
> > +     //enable audio reset
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, BIT(0), BIT(0));
> > +}
> > +
> > +static void mtk_dp_audio_channel_status_set(struct mtk_dp *mtk_dp,
> > +                                         struct mtk_dp_audio_cfg *cfg)
> > +{
> > +     union mtk_dp_audio_channel_status channel_status;
> > +
> > +     memset(&channel_status, 0, sizeof(channel_status));
> > +
> > +     switch (cfg->sample_rate) {
> > +     case 32000:
> > +             channel_status.iec.sampling_freq = 3;
> > +             break;
> > +     case 44100:
> > +             channel_status.iec.sampling_freq = 0;
> > +             break;
> > +     case 48000:
> > +             channel_status.iec.sampling_freq = 2;
> > +             break;
> > +     case 88200:
> > +             channel_status.iec.sampling_freq = 8;
> > +             break;
> > +     case 96000:
> > +             channel_status.iec.sampling_freq = 0xA;
> > +             break;
> > +     case 192000:
> > +             channel_status.iec.sampling_freq = 0xE;
> > +             break;
> > +     default:
> > +             channel_status.iec.sampling_freq = 0x1;
> > +             break;
> > +     }
> > +
> > +     switch (cfg->word_length_bits) {
> > +     case 16:
> > +             channel_status.iec.word_len = 0x02;
> > +             break;
> > +     case 20:
> > +             channel_status.iec.word_len = 0x03;
> > +             break;
> > +     case 24:
> > +             channel_status.iec.word_len = 0x0B;
> > +             break;
> > +     }
>
> Yeah, this is provided by hdmi-codec through hdmi_codec_params in
> prepare.
>
Thx for the input, I'll use that instead.

> > +     // IEC 60958 consumer channel status bits
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_308C,
> > +                        channel_status.buf[1] << 8 | channel_status.buf[0],
> > +                        CH_STATUS_0_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3090,
> > +                        channel_status.buf[3] << 8 | channel_status.buf[2],
> > +                        CH_STATUS_1_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3094, channel_status.buf[4],
> > +                        CH_STATUS_2_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_audio_sdp_asp_set_channels(struct mtk_dp *mtk_dp,
> > +                                           int channels)
> > +{
> > +     if (channels != 2 && channels != 8)
> > +             channels = 8;
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_312C,
> > +                        (channels - 1) << ASP_HB3_DP_ENC0_P0_SHIFT,
> > +                        ASP_HB2_DP_ENC0_P0_MASK | ASP_HB3_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_audio_set_divider(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
> > +                        AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2,
> > +                        AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static bool mtk_dp_plug_state(struct mtk_dp *mtk_dp)
> > +{
> > +     return !!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
> > +               HPD_DB_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_sdp_trigger_packet(struct mtk_dp *mtk_dp,
> > +                                   enum mtk_dp_sdp_type type)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, type,
> > +                        SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, SDP_PACKET_W_DP_ENC1_P0,
> > +                        SDP_PACKET_W_DP_ENC1_P0);
> > +}
> > +
> > +static void mtk_dp_sdp_set_data(struct mtk_dp *mtk_dp, u8 *data_bytes)
> > +{
> > +     mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_ENC1_P0_3200, data_bytes, 0x10);
> > +}
> > +
> > +static void mtk_dp_sdp_set_header(struct mtk_dp *mtk_dp,
> > +                               enum mtk_dp_sdp_type type,
> > +                               struct dp_sdp_header *header)
> > +{
> > +     u32 db_addr;
> > +
> > +     switch (type) {
> > +     case MTK_DP_SDP_DRM:
> > +             db_addr = MTK_DP_ENC0_P0_3138;
> > +             break;
> > +     case MTK_DP_SDP_PPS0:
> > +     case MTK_DP_SDP_PPS1:
> > +     case MTK_DP_SDP_PPS2:
> > +     case MTK_DP_SDP_PPS3:
> > +             db_addr = MTK_DP_ENC0_P0_3130;
> > +             break;
> > +     default:
> > +             db_addr = MTK_DP_ENC0_P0_30D8 + (type - MTK_DP_SDP_ACM) * 8;
> > +     }
> > +
> > +     mtk_dp_bulk_16bit_write(mtk_dp, db_addr, (u8 *)header, 4);
> > +}
> > +
> > +static const u32 mtk_dp_sdp_type_to_reg[MTK_DP_SDP_MAX_NUM] = {
> > +     /* MTK_DP_SDP_NONE => */ 0x0,
> > +     /* MTK_DP_SDP_ACM  => */ MTK_DP_ENC0_P0_30B4,
> > +     /* MTK_DP_SDP_ISRC => */ MTK_DP_ENC0_P0_30B4 + 1,
> > +     /* MTK_DP_SDP_AVI  => */ MTK_DP_ENC0_P0_30A4 + 1,
> > +     /* MTK_DP_SDP_AUI  => */ MTK_DP_ENC0_P0_30A8,
> > +     /* MTK_DP_SDP_SPD  => */ MTK_DP_ENC0_P0_30A8 + 1,
> > +     /* MTK_DP_SDP_MPEG => */ MTK_DP_ENC0_P0_30AC,
> > +     /* MTK_DP_SDP_NTSC => */ MTK_DP_ENC0_P0_30AC + 1,
> > +     /* MTK_DP_SDP_VSP  => */ MTK_DP_ENC0_P0_30B0,
> > +     /* MTK_DP_SDP_VSC  => */ MTK_DP_ENC0_P0_30B8,
> > +     /* MTK_DP_SDP_EXT  => */ MTK_DP_ENC0_P0_30B0 + 1,
> > +     /* MTK_DP_SDP_PPS0 => */ MTK_DP_ENC0_P0_31E8,
> > +     /* MTK_DP_SDP_PPS1 => */ MTK_DP_ENC0_P0_31E8,
> > +     /* MTK_DP_SDP_PPS2 => */ MTK_DP_ENC0_P0_31E8,
> > +     /* MTK_DP_SDP_PPS3 => */ MTK_DP_ENC0_P0_31E8,
> > +     /* MTK_DP_SDP_DRM  => */ MTK_DP_ENC0_P0_31DC,
> > +};
> > +
> > +static void mtk_dp_disable_sdp(struct mtk_dp *mtk_dp, enum mtk_dp_sdp_type type)
> > +{
> > +     if (type == MTK_DP_SDP_NONE)
> > +             return;
> > +
> > +     // Disable periodic send
> > +     mtk_dp_update_bits(mtk_dp, mtk_dp_sdp_type_to_reg[type] & 0xfffc, 0,
> > +                        0xFF << ((mtk_dp_sdp_type_to_reg[type] & 3) * 8));
> > +}
> > +
> > +static void mtk_dp_setup_sdp(struct mtk_dp *mtk_dp,
> > +                          struct mtk_dp_sdp_packet *packet)
> > +{
> > +     mtk_dp_sdp_set_data(mtk_dp, packet->sdp.db);
> > +     mtk_dp_sdp_set_header(mtk_dp, packet->type, &packet->sdp.sdp_header);
> > +
> > +     mtk_dp_disable_sdp(mtk_dp, packet->type);
> > +
> > +     switch (packet->type) {
> > +     case MTK_DP_SDP_NONE:
> > +             break;
> > +     case MTK_DP_SDP_ISRC:
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> > +                                0x1C << ISRC1_HB3_DP_ENC0_P0_SHIFT,
> > +                                ISRC1_HB3_DP_ENC0_P0_MASK);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280, MTK_DP_SDP_ISRC,
> > +                                SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
> > +
> > +             if (packet->sdp.sdp_header.HB3 & BIT(2))
> > +                     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
> > +                                        BIT(ISRC_CONT_DP_ENC0_P0_SHIFT),
> > +                                        ISRC_CONT_DP_ENC0_P0_MASK);
> > +             else
> > +                     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC, 0,
> > +                                        ISRC_CONT_DP_ENC0_P0_MASK);
> > +
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280,
> > +                                SDP_PACKET_W_DP_ENC1_P0,
> > +                                SDP_PACKET_W_DP_ENC1_P0);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30B4,
> > +                                5 << ISRC_CFG_DP_ENC0_P0_SHIFT,
> > +                                ISRC_CFG_DP_ENC0_P0_MASK);
> > +             break;
> > +     case MTK_DP_SDP_DRM:
> > +             mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31DC,
> > +                                5 << HDR0_CFG_DP_ENC0_P0_SHIFT,
> > +                                HDR0_CFG_DP_ENC0_P0_MASK);
> > +             break;
> > +     case MTK_DP_SDP_ACM:
> > +     case MTK_DP_SDP_AVI:
> > +     case MTK_DP_SDP_AUI:
> > +     case MTK_DP_SDP_SPD:
> > +     case MTK_DP_SDP_MPEG:
> > +     case MTK_DP_SDP_NTSC:
> > +     case MTK_DP_SDP_VSP:
> > +     case MTK_DP_SDP_VSC:
> > +     case MTK_DP_SDP_EXT:
> > +     case MTK_DP_SDP_PPS0:
> > +     case MTK_DP_SDP_PPS1:
> > +     case MTK_DP_SDP_PPS2:
> > +     case MTK_DP_SDP_PPS3:
> > +             mtk_dp_sdp_trigger_packet(mtk_dp, packet->type);
> > +             // Enable periodic sending
> > +             mtk_dp_update_bits(
> > +                     mtk_dp, mtk_dp_sdp_type_to_reg[packet->type] & 0xfffc,
> > +                     0x05 << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
> > +                              8),
> > +                     0xff << ((mtk_dp_sdp_type_to_reg[packet->type] & 3) *
> > +                              8));
> > +             break;
> > +     default:
> > +             break;
> > +     }
> > +}
> > +
> > +static void mtk_dp_sdp_vsc_ext_disable(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A0, 0,
> > +                        BIT(7) | BIT(8) | BIT(12));
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_328C, 0, BIT(7));
> > +}
> > +
> > +static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_write(
> > +             mtk_dp, MTK_DP_AUX_P0_3640,
> > +             BIT(AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT) |
> > +                     BIT(AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT));
> > +}
> > +
> > +static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32 addr)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644, cmd,
> > +                        MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648, addr,
> > +                        MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C, addr >> 16,
> > +                        MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_aux_cmd_complete(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> > +                        BIT(MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT),
> > +                        MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK |
> > +                                PHY_FIFO_RST_AUX_TX_P0_MASK |
> > +                                MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
> > +                        BIT(AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT),
> > +                        AUX_TX_REQUEST_READY_AUX_TX_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> > +                                    size_t length)
> > +{
> > +     mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf, length);
> > +}
> > +
> > +static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> > +                                 size_t length, int read_delay)
> > +{
> > +     int read_pos;
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620, 0,
> > +                        AUX_RD_MODE_AUX_TX_P0_MASK);
> > +
> > +     for (read_pos = 0; read_pos < length; read_pos++) {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> > +                                BIT(AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT),
> > +                                AUX_RX_FIFO_READ_PULSE_TX_P0_MASK);
> > +             usleep_range(read_delay, read_delay * 2);
> > +             buf[read_pos] =
> > +                     (u8)(mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3620) &
> > +                          AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK >>
> > +                                  AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT);
> > +     }
> > +}
> > +
> > +static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t length)
> > +{
> > +     if (length > 0) {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> > +                                (length - 1)
> > +                                        << MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT,
> > +                                MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
> > +                                AUX_NO_LENGTH_AUX_TX_P0_MASK |
> > +                                        AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> > +                                        AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> > +     } else {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> > +                                BIT(AUX_NO_LENGTH_AUX_TX_P0_SHIFT),
> > +                                AUX_NO_LENGTH_AUX_TX_P0_MASK |
> > +                                        AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> > +                                        AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> > +     }
> > +}
> > +
> > +static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp, bool is_read)
> > +{
> > +     int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
> > +
> > +     while (--wait_reply) {
> > +             u32 aux_irq_status;
> > +
> > +             if (is_read) {
> > +                     u32 fifo_status =
> > +                             mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3618);
> > +
> > +                     if (fifo_status &
> > +                         (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
> > +                          AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
> > +                             return 0;
> > +                     }
> > +             }
> > +
> > +             aux_irq_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3640);
> > +             if (aux_irq_status & AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK)
> > +                     return 0;
> > +
> > +             if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK)
> > +                     return -ETIMEDOUT;
> > +
> > +             usleep_range(100, 500);
> > +     }
> > +
> > +     return -ETIMEDOUT;
> > +}
> > +
> > +static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool is_read, u8 cmd,
> > +                               u32 addr, u8 *buf, size_t length)
> > +{
> > +     int ret;
> > +     u32 reply_cmd;
> > +
> > +     if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
> > +                     (cmd == DP_AUX_NATIVE_READ && !length)))
> > +             return -EINVAL;
> > +
> > +     if (!is_read)
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> > +                                BIT(AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT),
> > +                                AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK);
> > +
> > +     mtk_dp_aux_cmd_complete(mtk_dp);
> > +     mtk_dp_aux_irq_clear(mtk_dp);
> > +     usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> > +                  MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> > +
> > +     mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
> > +     mtk_dp_aux_set_length(mtk_dp, length);
> > +
> > +     if (!is_read) {
> > +             if (length)
> > +                     mtk_dp_aux_fill_write_fifo(mtk_dp, buf, length);
> > +
> > +             mtk_dp_update_bits(
> > +                     mtk_dp, MTK_DP_AUX_P0_3704,
> > +                     AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK,
> > +                     AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK);
> > +     }
> > +
> > +     mtk_dp_aux_request_ready(mtk_dp);
> > +
> > +     ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
> > +
> > +     reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
> > +                 AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
> > +
> > +     if (ret || reply_cmd) {
> > +             u32 phy_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3628) &
> > +                              AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
> > +             if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
> > +                     drm_err(mtk_dp->drm_dev,
> > +                             "AUX Rx Aux hang, need SW reset\n");
> > +                     ret = -EIO;
> > +             }
> > +
> > +             mtk_dp_aux_cmd_complete(mtk_dp);
> > +             mtk_dp_aux_irq_clear(mtk_dp);
> > +
> > +             usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> > +                          MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> > +             return -ETIMEDOUT;
> > +     }
> > +
> > +     if (!length) {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C, 0,
> > +                                AUX_NO_LENGTH_AUX_TX_P0_MASK |
> > +                                        AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> > +                                        AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> > +     } else if (is_read) {
> > +             int read_delay;
> > +
> > +             if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
> > +                 cmd == DP_AUX_I2C_READ)
> > +                     read_delay = 500;
> > +             else
> > +                     read_delay = 100;
> > +
> > +             mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length, read_delay);
> > +     }
> > +
> > +     mtk_dp_aux_cmd_complete(mtk_dp);
> > +     mtk_dp_aux_irq_clear(mtk_dp);
> > +     usleep_range(MTK_DP_AUX_WRITE_READ_WAIT_TIME_US,
> > +                  MTK_DP_AUX_WRITE_READ_WAIT_TIME_US * 2);
> > +
> > +     return 0;
> > +}
> > +
> > +static bool mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int lane_num,
> > +                                       int swing_val, int preemphasis)
> > +{
> > +     u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
> > +
> > +     if (lane_num < 0 || lane_num > 3)
> > +             return false;
> > +
> > +     dev_dbg(mtk_dp->dev,
> > +             "link training swing_val= 0x%x, preemphasis = 0x%x\n",
> > +             swing_val, preemphasis);
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> > +                        swing_val << (DP_TX0_VOLT_SWING_SHIFT + lane_shift),
> > +                        DP_TX0_VOLT_SWING_MASK << lane_shift);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> > +                        preemphasis << (DP_TX0_PRE_EMPH_SHIFT + lane_shift),
> > +                        DP_TX0_PRE_EMPH_MASK << lane_shift);
> > +
> > +     return true;
> > +}
> > +
> > +static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP, 0,
> > +                        DP_TX0_VOLT_SWING_MASK | DP_TX1_VOLT_SWING_MASK |
> > +                                DP_TX2_VOLT_SWING_MASK |
> > +                                DP_TX3_VOLT_SWING_MASK |
> > +                                DP_TX0_PRE_EMPH_MASK | DP_TX1_PRE_EMPH_MASK |
> > +                                DP_TX2_PRE_EMPH_MASK | DP_TX3_PRE_EMPH_MASK);
> > +}
> > +
> > +static void mtk_dp_fec_enable(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> > +                        enable ? BIT(FEC_EN_DP_TRANS_P0_SHIFT) : 0,
> > +                        FEC_EN_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
> > +{
> > +     u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
> > +                      SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
> > +
> > +     if (irq_status) {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, irq_status,
> > +                                SW_IRQ_CLR_DP_TRANS_P0_MASK);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8, 0,
> > +                                SW_IRQ_CLR_DP_TRANS_P0_MASK);
> > +     }
> > +
> > +     return irq_status;
> > +}
> > +
> > +static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
> > +{
> > +     u8 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
> > +                      IRQ_STATUS_DP_TRANS_P0_MASK) >>
> > +                     IRQ_STATUS_DP_TRANS_P0_SHIFT;
> > +
> > +     if (irq_status) {
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, irq_status,
> > +                                IRQ_CLR_DP_TRANS_P0_MASK);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, 0,
> > +                                IRQ_CLR_DP_TRANS_P0_MASK);
> > +     }
> > +
> > +     return irq_status;
> > +}
> > +
> > +static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     u32 val = 0;
> > +
> > +     if (!enable)
> > +             val = IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
> > +                   IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
> > +                   IRQ_MASK_DP_TRANS_P0_INT_IRQ;
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418, val,
> > +                        IRQ_MASK_DP_TRANS_P0_MASK);
> > +}
> > +
> > +void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
> > +                        XTAL_FREQ_DP_TRANS_P0_DEFAULT,
> > +                        XTAL_FREQ_DP_TRANS_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> > +                        BIT(FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT),
> > +                        FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> > +                        BIT(AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT),
> > +                        AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
> > +                        SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK, IRQ_MASK_AUX_TOP_IRQ,
> > +                        IRQ_MASK_AUX_TOP_IRQ);
> > +}
> > +
> > +static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp *mtk_dp)
> > +{
> > +     // Debounce threshold
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> > +                        8 << HPD_DEB_THD_DP_TRANS_P0_SHIFT,
> > +                        HPD_DEB_THD_DP_TRANS_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> > +                        (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
> > +                         HPD_INT_THD_DP_TRANS_P0_UPPER_1100US)
> > +                                << HPD_INT_THD_DP_TRANS_P0_SHIFT,
> > +                        HPD_INT_THD_DP_TRANS_P0_MASK);
> > +
> > +     // Connect threshold 1.5ms + 5 x 0.1ms = 2ms
> > +     // Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> > +                        (5 << HPD_DISC_THD_DP_TRANS_P0_SHIFT) |
> > +                                (5 << HPD_CONN_THD_DP_TRANS_P0_SHIFT),
> > +                        HPD_DISC_THD_DP_TRANS_P0_MASK |
> > +                                HPD_CONN_THD_DP_TRANS_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
> > +                        HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
> > +                        HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
> > +{
> > +     // modify timeout threshold = 1595 [12 : 8]
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C, 0x1595,
> > +                        AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658, 0,
> > +                        AUX_TX_OV_EN_AUX_TX_P0_MASK);
> > +     // 25 for 26M
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
> > +                        25 << AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT,
> > +                        AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
> > +     // 13 for 26M
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
> > +                        13 << AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT,
> > +                        AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
> > +                        BIT(MTK_ATOP_EN_AUX_TX_P0_SHIFT),
> > +                        MTK_ATOP_EN_AUX_TX_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_initialize_digital_settings(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C, 0,
> > +                        VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
> > +     mtk_dp_set_color_format(mtk_dp, MTK_DP_COLOR_FORMAT_RGB_444);
> > +     mtk_dp_set_color_depth(mtk_dp, MTK_DP_COLOR_DEPTH_8BIT);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
> > +                        1 << BS2BS_MODE_DP_ENC1_P0_SHIFT,
> > +                        BS2BS_MODE_DP_ENC1_P0_MASK);
> > +
> > +     // dp tx encoder reset all sw
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> > +                        BIT(DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT),
> > +                        DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
> > +     usleep_range(1000, 5000);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004, 0,
> > +                        DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> > +                        BIT(DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT),
> > +                        DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
> > +     usleep_range(1000, 5000);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C, 0,
> > +                        DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
> > +                        lanes == 0 ? 0 : BIT(3), BIT(3) | BIT(2));
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, lanes,
> > +                        LANE_NUM_DP_ENC0_P0_MASK);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
> > +                        lanes << LANE_NUM_DP_TRANS_P0_SHIFT,
> > +                        LANE_NUM_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static int link_rate_to_mb_per_s(struct mtk_dp *mtk_dp,
> > +                              enum mtk_dp_linkrate linkrate)
> > +{
> > +     switch (linkrate) {
> > +     default:
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Implementation error, unknown linkrate %d\n",
> > +                     linkrate);
> > +             fallthrough;
> > +     case MTK_DP_LINKRATE_RBR:
> > +             return 1620;
> > +     case MTK_DP_LINKRATE_HBR:
> > +             return 2700;
> > +     case MTK_DP_LINKRATE_HBR2:
> > +             return 5400;
> > +     case MTK_DP_LINKRATE_HBR3:
> > +             return 8100;
> > +     }
> > +}
> > +
> > +static u32 check_cal_data_valid(u32 min, u32 max, u32 val, u32 default_val)
> > +{
> > +     if (val < min || val > max)
> > +             return default_val;
> > +
> > +     return val;
> > +}
> > +
> > +static int mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
> > +{
> > +     struct dp_cal_data *cal_data = &mtk_dp->cal_data;
> > +     struct device *dev = mtk_dp->dev;
> > +     struct nvmem_cell *cell;
> > +     u32 *buf;
> > +     size_t len;
> > +
> > +     cell = nvmem_cell_get(dev, "dp_calibration_data");
> > +     if (IS_ERR(cell)) {
> > +             dev_err(dev,
> > +                     "Error: Failed to get nvmem cell dp_calibration_data\n");
> > +             return PTR_ERR(cell);
> > +     }
> > +
> > +     buf = (u32 *)nvmem_cell_read(cell, &len);
> > +     nvmem_cell_put(cell);
> > +
> > +     if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
> > +             dev_err(dev,
> > +                     "Error: Failed to read nvmem_cell_read fail len %ld\n",
> > +                     (len / sizeof(u32)));
> > +             return PTR_ERR(buf);
> > +     }
> > +
> > +     if (mtk_dp_is_edp(mtk_dp)) {
> > +             cal_data->glb_bias_trim = check_cal_data_valid(
> > +                     1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf);
> > +             cal_data->clktx_impse =
> > +                     check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8);
> > +             cal_data->ln0_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8);
> > +             cal_data->ln0_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8);
> > +             cal_data->ln1_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8);
> > +             cal_data->ln1_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8);
> > +             cal_data->ln2_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8);
> > +             cal_data->ln2_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8);
> > +             cal_data->ln3_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8);
> > +             cal_data->ln3_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8);
> > +     } else {
> > +             cal_data->glb_bias_trim = check_cal_data_valid(
> > +                     1, 0x1e, (buf[0] >> 27) & 0x1f, 0xf);
> > +             cal_data->clktx_impse =
> > +                     check_cal_data_valid(1, 0xe, (buf[0] >> 13) & 0xf, 0x8);
> > +             cal_data->ln0_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 28) & 0xf, 0x8);
> > +             cal_data->ln0_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 24) & 0xf, 0x8);
> > +             cal_data->ln1_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 20) & 0xf, 0x8);
> > +             cal_data->ln1_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 16) & 0xf, 0x8);
> > +             cal_data->ln2_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 12) & 0xf, 0x8);
> > +             cal_data->ln2_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 8) & 0xf, 0x8);
> > +             cal_data->ln3_tx_impsel_pmos =
> > +                     check_cal_data_valid(1, 0xe, (buf[1] >> 4) & 0xf, 0x8);
> > +             cal_data->ln3_tx_impsel_nmos =
> > +                     check_cal_data_valid(1, 0xe, buf[1] & 0xf, 0x8);
> > +     }
> > +
> > +     kfree(buf);
> > +
> > +     return 0;
> > +}
> > +
> > +void mtk_dp_set_cal_data(struct mtk_dp *mtk_dp)
> > +{
> > +     struct dp_cal_data *cal_data = &mtk_dp->cal_data;
> > +
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
> > +                        cal_data->clktx_impse << 20, RG_CKM_PT0_CKTX_IMPSEL);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
> > +                        cal_data->glb_bias_trim << 16,
> > +                        RG_XTP_GLB_BIAS_INTR_CTRL);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> > +                        cal_data->ln0_tx_impsel_pmos << 12,
> > +                        RG_XTP_LN0_TX_IMPSEL_PMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> > +                        cal_data->ln0_tx_impsel_nmos << 16,
> > +                        RG_XTP_LN0_TX_IMPSEL_NMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> > +                        cal_data->ln1_tx_impsel_pmos << 12,
> > +                        RG_XTP_LN1_TX_IMPSEL_PMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> > +                        cal_data->ln1_tx_impsel_nmos << 16,
> > +                        RG_XTP_LN1_TX_IMPSEL_NMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> > +                        cal_data->ln2_tx_impsel_pmos << 12,
> > +                        RG_XTP_LN2_TX_IMPSEL_PMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> > +                        cal_data->ln2_tx_impsel_nmos << 16,
> > +                        RG_XTP_LN2_TX_IMPSEL_NMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> > +                        cal_data->ln3_tx_impsel_pmos << 12,
> > +                        RG_XTP_LN3_TX_IMPSEL_PMOS);
> > +     mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> > +                        cal_data->ln3_tx_impsel_nmos << 16,
> > +                        RG_XTP_LN3_TX_IMPSEL_NMOS);
> > +}
> > +
> > +static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
> > +                             enum mtk_dp_linkrate link_rate, int lane_count)
> > +{
> > +     int ret;
> > +     union phy_configure_opts
> > +             phy_opts = { .dp = {
> > +                                  .link_rate = link_rate_to_mb_per_s(
> > +                                          mtk_dp, link_rate),
> > +                                  .set_rate = 1,
> > +                                  .lanes = lane_count,
> > +                                  .set_lanes = 1,
> > +                                  .ssc = mtk_dp->train_info.sink_ssc,
> > +                          } };
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP,
> > +                        DP_PWR_STATE_MASK);
> > +
> > +     ret = phy_configure(mtk_dp->phy, &phy_opts);
> > +
> > +     mtk_dp_set_cal_data(mtk_dp);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> > +                        DP_PWR_STATE_BANDGAP_TPLL_LANE, DP_PWR_STATE_MASK);
> > +     return ret;
> > +}
> > +
> > +static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     const u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
> > +                     POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
> > +                     POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
> > +                     POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580, enable ? val : 0, val);
> > +}
> > +
> > +static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int pattern)
> > +{
> > +     if (pattern < 0 || pattern > 4) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Implementation error, no such pattern %d\n", pattern);
> > +             return;
> > +     }
> > +
> > +     if (pattern == 1) // TPS1
> > +             mtk_dp_set_idle_pattern(mtk_dp, false);
> > +
> > +     mtk_dp_update_bits(
> > +             mtk_dp, MTK_DP_TRANS_P0_3400,
> > +             pattern ? BIT(pattern - 1) << PATTERN1_EN_DP_TRANS_P0_SHIFT : 0,
> > +             PATTERN1_EN_DP_TRANS_P0_MASK | PATTERN2_EN_DP_TRANS_P0_MASK |
> > +                     PATTERN3_EN_DP_TRANS_P0_MASK |
> > +                     PATTERN4_EN_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> > +                        enable ? BIT(ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT) : 0,
> > +                        ENHANCED_FRAME_EN_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
> > +                        enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
> > +                        DP_SCR_EN_DP_TRANS_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     u32 val = BIT(VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT);
> > +
> > +     if (enable)
> > +             val |= BIT(VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, val,
> > +                        VIDEO_MUTE_SEL_DP_ENC0_P0_MASK |
> > +                                VIDEO_MUTE_SW_DP_ENC0_P0_MASK);
> > +
> > +     if (mtk_dp_is_edp(mtk_dp))
> > +             mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable);
> > +     else
> > +             mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable);
> > +}
> > +
> > +static void mtk_dp_audio_mute(struct mtk_dp *mtk_dp, bool mute)
> > +{
> > +     if (mute) {
> > +             mtk_dp_update_bits(
> > +                     mtk_dp, MTK_DP_ENC0_P0_3030,
> > +                     BIT(VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT) |
> > +                             BIT(VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT),
> > +                     VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
> > +                             VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
> > +
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088, 0,
> > +                                AU_EN_DP_ENC0_P0_MASK);
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0,
> > +                                AU_TS_CFG_DP_ENC0_P0_MASK);
> > +
> > +     } else {
> > +             mtk_dp_update_bits(
> > +                     mtk_dp, MTK_DP_ENC0_P0_3030, 0,
> > +                     VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK |
> > +                             VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK);
> > +
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088,
> > +                                BIT(AU_EN_DP_ENC0_P0_SHIFT),
> > +                                AU_EN_DP_ENC0_P0_MASK);
> > +             // Send one every two frames
> > +             mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4, 0x0F,
> > +                                AU_TS_CFG_DP_ENC0_P0_MASK);
> > +     }
> > +}
> > +
> > +static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, 0,
> > +                        SW_RST_B_PHYD);
> > +     usleep_range(10, 200);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE, SW_RST_B_PHYD,
> > +                        SW_RST_B_PHYD);
> > +}
> > +
> > +static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
> > +     usleep_range(10, 200);
> > +     mtk_dp_write(mtk_dp, MTK_DP_0034,
> > +                  DA_CKM_CKTX0_EN_FORCE_EN | DA_CKM_BIAS_LPF_EN_FORCE_VAL |
> > +                          DA_CKM_BIAS_EN_FORCE_VAL |
> > +                          DA_XTP_GLB_LDO_EN_FORCE_VAL |
> > +                          DA_XTP_GLB_AVD10_ON_FORCE_VAL);
> > +     // Disable RX
> > +     mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
> > +     mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
> > +                  0x550 | BIT(FUSE_SEL_SHIFT) | BIT(MEM_ISO_EN_SHIFT));
> > +}
> > +
> > +static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR2;
> > +     mtk_dp->train_info.lane_count = MTK_DP_MAX_LANES;
> > +     mtk_dp->train_info.irq_status = 0;
> > +     mtk_dp->train_info.cable_plugged_in = false;
> > +     mtk_dp->train_info.cable_state_change = false;
> > +     mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > +     mtk_dp->state = MTK_DP_STATE_INITIAL;
> > +
> > +     mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
> > +     mtk_dp->info.depth = MTK_DP_COLOR_DEPTH_8BIT;
> > +     memset(&mtk_dp->info.timings, 0, sizeof(struct mtk_dp_timings));
> > +     mtk_dp->info.timings.frame_rate = 60;
> > +
> > +     mtk_dp->has_fec = false;
> > +     mtk_dp->audio_enable = false;
> > +}
> > +
> > +static void mtk_dp_sdp_set_down_cnt_init(struct mtk_dp *mtk_dp,
> > +                                      u32 sram_read_start)
> > +{
> > +     u32 sdp_down_cnt_init = 0;
> > +     u32 dc_offset;
> > +
> > +     if (mtk_dp->info.timings.pix_rate_khz > 0)
> > +             sdp_down_cnt_init = sram_read_start *
> > +                                 mtk_dp->train_info.link_rate * 2700 * 8 /
> > +                                 (mtk_dp->info.timings.pix_rate_khz * 4);
> > +
> > +     switch (mtk_dp->train_info.lane_count) {
> > +     case 1:
> > +             sdp_down_cnt_init = sdp_down_cnt_init > 0x1A ?
> > +                                               sdp_down_cnt_init :
> > +                                               0x1A; //26
> > +             break;
> > +     case 2:
> > +             // case for LowResolution && High Audio Sample Rate
> > +             dc_offset = mtk_dp->info.timings.vtotal <= 525 ? 0x04 : 0x00;
> > +             sdp_down_cnt_init = sdp_down_cnt_init > 0x10 ?
> > +                                               sdp_down_cnt_init :
> > +                                               0x10 + dc_offset; //20 or 16
> > +             break;
> > +     case 4:
> > +     default:
> > +             sdp_down_cnt_init =
> > +                     sdp_down_cnt_init > 0x06 ? sdp_down_cnt_init : 0x06; //6
> > +             break;
> > +     }
> > +
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> > +                        sdp_down_cnt_init
> > +                                << SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT,
> > +                        SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_sdp_set_down_cnt_init_in_hblank(struct mtk_dp *mtk_dp)
> > +{
> > +     int pix_clk_mhz;
> > +     u32 dc_offset;
> > +     u32 spd_down_cnt_init = 0;
> > +
> > +     pix_clk_mhz = mtk_dp->info.format == MTK_DP_COLOR_FORMAT_YUV_420 ?
> > +                                 mtk_dp->info.timings.pix_rate_khz / 2000 :
> > +                                 mtk_dp->info.timings.pix_rate_khz / 1000;
> > +
> > +     switch (mtk_dp->train_info.lane_count) {
> > +     case 1:
> > +             spd_down_cnt_init = 0x20;
> > +             break;
> > +     case 2:
> > +             dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x14 : 0x00;
> > +             spd_down_cnt_init = 0x18 + dc_offset;
> > +             break;
> > +     case 4:
> > +     default:
> > +             dc_offset = (mtk_dp->info.timings.vtotal <= 525) ? 0x08 : 0x00;
> > +             if (pix_clk_mhz > mtk_dp->train_info.link_rate * 27)
> > +                     spd_down_cnt_init = 0x8;
> > +             else
> > +                     spd_down_cnt_init = 0x10 + dc_offset;
> > +             break;
> > +     }
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364, spd_down_cnt_init,
> > +                        SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
> > +}
> > +
> > +static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
> > +{
> > +     u32 sram_read_start = MTK_DP_TBC_BUF_READ_START_ADDR;
> > +
> > +     if (mtk_dp->train_info.lane_count > 0) {
> > +             sram_read_start = min_t(
> > +                     u32, MTK_DP_TBC_BUF_READ_START_ADDR,
> > +                     mtk_dp->info.timings.vm.hactive /
> > +                             (mtk_dp->train_info.lane_count * 4 * 2 * 2));
> > +             mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
> > +     }
> > +
> > +     mtk_dp_setup_encoder(mtk_dp);
> > +     mtk_dp_sdp_set_down_cnt_init_in_hblank(mtk_dp);
> > +     mtk_dp_sdp_set_down_cnt_init(mtk_dp, sram_read_start);
> > +}
> > +
> > +static void mtk_dp_calculate_pixrate(struct mtk_dp *mtk_dp)
> > +{
> > +     int target_frame_rate = 60;
> > +     int target_pixel_clk;
> > +
> > +     if (mtk_dp->info.timings.frame_rate > 0) {
> > +             target_frame_rate = mtk_dp->info.timings.frame_rate;
> > +             target_pixel_clk = (int)mtk_dp->info.timings.htotal *
> > +                                (int)mtk_dp->info.timings.vtotal *
> > +                                target_frame_rate;
> > +     } else if (mtk_dp->info.timings.pix_rate_khz > 0) {
> > +             target_pixel_clk = mtk_dp->info.timings.pix_rate_khz * 1000;
> > +     } else {
> > +             target_pixel_clk = (int)mtk_dp->info.timings.htotal *
> > +                                (int)mtk_dp->info.timings.vtotal *
> > +                                target_frame_rate;
> > +     }
> > +
> > +     if (target_pixel_clk > 0)
> > +             mtk_dp->info.timings.pix_rate_khz = target_pixel_clk / 1000;
> > +}
> > +
> > +static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_msa_bypass_disable(mtk_dp);
> > +     mtk_dp_calculate_pixrate(mtk_dp);
> > +     mtk_dp_pg_disable(mtk_dp);
> > +     mtk_dp_setup_tu(mtk_dp);
> > +}
> > +
> > +static void mtk_dp_edid_free(struct mtk_dp *mtk_dp)
> > +{
> > +     mutex_lock(&mtk_dp->edid_lock);
> > +     kfree(mtk_dp->edid);
> > +     mtk_dp->edid = NULL;
> > +     mutex_unlock(&mtk_dp->edid_lock);
> > +}
> > +
> > +static void mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp)
> > +{
> > +     ssize_t ret;
> > +     u8 sink_count;
> > +     u8 link_status[DP_LINK_STATUS_SIZE] = {};
> > +     u32 sink_count_reg;
> > +     u32 link_status_reg;
> > +     bool locked;
> > +
> > +     sink_count_reg = DP_SINK_COUNT_ESI;
> > +     link_status_reg = DP_LANE0_1_STATUS;
> > +
> > +     ret = drm_dp_dpcd_readb(&mtk_dp->aux, sink_count_reg, &sink_count);
> > +     if (ret < 0) {
> > +             drm_info(mtk_dp->drm_dev, "Read sink count failed: %ld\n", ret);
> > +             return;
> > +     }
> > +
> > +     ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
> > +     if (ret < 0) {
> > +             drm_info(mtk_dp->drm_dev,
> > +                      "Read DP_SINK_COUNT_ESI failed: %ld\n", ret);
> > +             return;
> > +     }
> > +
> > +     ret = drm_dp_dpcd_read(&mtk_dp->aux, link_status_reg, link_status,
> > +                            sizeof(link_status));
> > +     if (!ret) {
> > +             drm_info(mtk_dp->drm_dev, "Read link status failed: %ld\n",
> > +                      ret);
> > +             return;
> > +     }
> > +
> > +     locked = drm_dp_channel_eq_ok(link_status,
> > +                                   mtk_dp->train_info.lane_count);
> > +     if (!locked && mtk_dp->train_state > MTK_DP_TRAIN_STATE_TRAINING_PRE)
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
> > +
> > +     if (link_status[1] & DP_REMOTE_CONTROL_COMMAND_PENDING)
> > +             drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR,
> > +                                DP_REMOTE_CONTROL_COMMAND_PENDING);
> > +
> > +     if (DP_GET_SINK_COUNT(sink_count) &&
> > +         (link_status[2] & DP_DOWNSTREAM_PORT_STATUS_CHANGED)) {
> > +             mtk_dp_edid_free(mtk_dp);
> > +             mtk_dp->train_info.check_cap_count = 0;
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
> > +             msleep(20);
> > +     }
> > +}
> > +
> > +static void mtk_dp_sdp_stop_sending(struct mtk_dp *mtk_dp)
> > +{
> > +     u8 packet_type;
> > +
> > +     for (packet_type = MTK_DP_SDP_ACM; packet_type < MTK_DP_SDP_MAX_NUM;
> > +          packet_type++)
> > +             mtk_dp_disable_sdp(mtk_dp, packet_type);
> > +
> > +     mtk_dp_sdp_vsc_ext_disable(mtk_dp);
> > +}
> > +
> > +static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes,
> > +                                       u8 dpcd_adjust_req[2])
> > +{
> > +     int lane;
> > +
> > +     for (lane = 0; lane < lanes; ++lane) {
> > +             u8 val;
> > +             u8 swing;
> > +             u8 preemphasis;
> > +             int index = lane / 2;
> > +             int shift = lane % 2 ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
> > +
> > +             swing = (dpcd_adjust_req[index] >> shift) &
> > +                     DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
> > +             preemphasis = ((dpcd_adjust_req[index] >> shift) &
> > +                            DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
> > +                           DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
> > +             val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
> > +                   preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
> > +
> > +             if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
> > +                     val |= DP_TRAIN_MAX_SWING_REACHED;
> > +             if (preemphasis == 3)
> > +                     val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> > +
> > +             mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing, preemphasis);
> > +             drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET + lane,
> > +                                val);
> > +     }
> > +
> > +     // Wait for the signal to be stable enough
> > +     usleep_range(2000, 5000);
> > +}
> > +
> > +static void mtk_dp_read_link_status(struct mtk_dp *mtk_dp,
> > +                                 u8 link_status[DP_LINK_STATUS_SIZE])
> > +{
> > +     drm_dp_dpcd_read(&mtk_dp->aux, DP_LANE0_1_STATUS, link_status,
> > +                      DP_LINK_STATUS_SIZE);
> > +}
> > +
> > +static int mtk_dp_train_flow(struct mtk_dp *mtk_dp, int target_link_rate,
> > +                          int target_lane_count)
> > +{
> > +     u8 link_status[DP_LINK_STATUS_SIZE] = {};
> > +     u8 lane_adjust[2] = {};
> > +     bool pass_tps1 = false;
> > +     bool pass_tps2_3 = false;
> > +     int train_retries;
> > +     int status_control;
> > +     int iteration_count;
> > +     u8 prev_lane_adjust;
> > +     u8 val;
> > +
> > +     drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET, target_link_rate);
> > +     drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> > +                        target_lane_count | DP_LANE_COUNT_ENHANCED_FRAME_EN);
> > +
> > +     if (mtk_dp->train_info.sink_ssc)
> > +             drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
> > +                                DP_SPREAD_AMP_0_5);
> > +
> > +     train_retries = 0;
> > +     status_control = 0;
> > +     iteration_count = 1;
> > +     prev_lane_adjust = 0xFF;
> > +
> > +     mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
> > +     mtk_dp_phy_configure(mtk_dp, target_link_rate, target_lane_count);
> > +
> > +     dev_dbg(mtk_dp->dev,
> > +             "Link train target_link_rate = 0x%x, target_lane_count = 0x%x\n",
> > +             target_link_rate, target_lane_count);
> > +
> > +     do {
> > +             train_retries++;
> > +             if (!mtk_dp->train_info.cable_plugged_in ||
> > +                 ((mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) !=
> > +                  0x0)) {
> > +                     return -ENODEV;
> > +             }
> > +
> > +             if (mtk_dp->train_state < MTK_DP_TRAIN_STATE_TRAINING)
> > +                     return -EAGAIN;
> > +
> > +             if (!pass_tps1) {
> > +                     mtk_dp_training_set_scramble(mtk_dp, false);
> > +
> > +                     if (status_control == 0) {
> > +                             status_control = 1;
> > +                             mtk_dp_train_set_pattern(mtk_dp, 1);
> > +                             val = DP_LINK_SCRAMBLING_DISABLE |
> > +                                   DP_TRAINING_PATTERN_1;
> > +                             drm_dp_dpcd_writeb(
> > +                                     &mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> > +                                     DP_LINK_SCRAMBLING_DISABLE |
> > +                                             DP_TRAINING_PATTERN_1);
> > +                             drm_dp_dpcd_read(&mtk_dp->aux,
> > +                                              DP_ADJUST_REQUEST_LANE0_1,
> > +                                              lane_adjust,
> > +                                              sizeof(lane_adjust));
> > +                             iteration_count++;
> > +
> > +                             mtk_dp_train_update_swing_pre(
> > +                                     mtk_dp, target_lane_count, lane_adjust);
> > +                     }
> > +
> > +                     drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
> > +                                                            mtk_dp->rx_cap);
> > +                     mtk_dp_read_link_status(mtk_dp, link_status);
> > +
> > +                     if (drm_dp_clock_recovery_ok(link_status,
> > +                                                  target_lane_count)) {
> > +                             mtk_dp->train_info.cr_done = true;
> > +                             pass_tps1 = true;
> > +                             train_retries = 0;
> > +                             iteration_count = 1;
> > +                             dev_dbg(mtk_dp->dev, "Link train CR pass\n");
> > +                     } else if (prev_lane_adjust == link_status[4]) {
> > +                             iteration_count++;
> > +                             if (prev_lane_adjust &
> > +                                 DP_ADJUST_VOLTAGE_SWING_LANE0_MASK)
> > +                                     break;
> > +                     } else {
> > +                             prev_lane_adjust = link_status[4];
> > +                     }
> > +                     dev_dbg(mtk_dp->dev, "Link train CQ fail\n");
> > +             } else if (pass_tps1 && !pass_tps2_3) {
> > +                     if (status_control == 1) {
> > +                             status_control = 2;
> > +                             if (mtk_dp->train_info.tps4) {
> > +                                     mtk_dp_train_set_pattern(mtk_dp, 4);
> > +                                     val = DP_TRAINING_PATTERN_4;
> > +                             } else if (mtk_dp->train_info.tps3) {
> > +                                     mtk_dp_train_set_pattern(mtk_dp, 3);
> > +                                     val = DP_LINK_SCRAMBLING_DISABLE |
> > +                                           DP_TRAINING_PATTERN_3;
> > +                             } else {
> > +                                     mtk_dp_train_set_pattern(mtk_dp, 2);
> > +                                     val = DP_LINK_SCRAMBLING_DISABLE |
> > +                                           DP_TRAINING_PATTERN_2;
> > +                             }
> > +                             drm_dp_dpcd_writeb(&mtk_dp->aux,
> > +                                                DP_TRAINING_PATTERN_SET,
> > +                                                val);
> > +
> > +                             drm_dp_dpcd_read(&mtk_dp->aux,
> > +                                              DP_ADJUST_REQUEST_LANE0_1,
> > +                                              lane_adjust,
> > +                                              sizeof(lane_adjust));
> > +
> > +                             iteration_count++;
> > +                             mtk_dp_train_update_swing_pre(
> > +                                     mtk_dp, target_lane_count, lane_adjust);
> > +                     }
> > +
> > +                     drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
> > +                                                        mtk_dp->rx_cap);
> > +
> > +                     mtk_dp_read_link_status(mtk_dp, link_status);
> > +
> > +                     if (!drm_dp_clock_recovery_ok(link_status,
> > +                                                   target_lane_count)) {
> > +                             mtk_dp->train_info.cr_done = false;
> > +                             mtk_dp->train_info.eq_done = false;
> > +                             dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
> > +                             break;
> > +                     }
> > +
> > +                     if (drm_dp_channel_eq_ok(link_status,
> > +                                              target_lane_count)) {
> > +                             mtk_dp->train_info.eq_done = true;
> > +                             pass_tps2_3 = true;
> > +                             dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
> > +                             break;
> > +                     }
> > +
> > +                     if (prev_lane_adjust == link_status[4])
> > +                             iteration_count++;
> > +                     else
> > +                             prev_lane_adjust = link_status[4];
> > +             }
> > +
> > +             drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
> > +                              lane_adjust, sizeof(lane_adjust));
> > +             mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
> > +                                           lane_adjust);
> > +     } while (train_retries < MTK_DP_TRAIN_RETRY_LIMIT &&
> > +              iteration_count < MTK_DP_TRAIN_MAX_ITERATIONS);
> > +
> > +     drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> > +                        DP_TRAINING_PATTERN_DISABLE);
> > +     mtk_dp_train_set_pattern(mtk_dp, 0);
> > +
> > +     if (pass_tps2_3) {
> > +             mtk_dp->train_info.link_rate = target_link_rate;
> > +             mtk_dp->train_info.lane_count = target_lane_count;
> > +
> > +             mtk_dp_training_set_scramble(mtk_dp, true);
> > +
> > +             drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> > +                                target_lane_count |
> > +                                        DP_LANE_COUNT_ENHANCED_FRAME_EN);
> > +             mtk_dp_set_enhanced_frame_mode(mtk_dp, true);
> > +
> > +             return 0;
> > +     }
> > +
> > +     return -ETIMEDOUT;
> > +}
> > +
> > +static bool mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
> > +{
> > +     u8 buf[DP_RECEIVER_CAP_SIZE] = {};
> > +     u8 val;
> > +     struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
> > +
> > +     if (!mtk_dp_plug_state(mtk_dp))
> > +             return false;
> > +
> > +     drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> > +     usleep_range(2000, 5000);
> > +
> > +     drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
> > +
> > +     memcpy(mtk_dp->rx_cap, buf, min(sizeof(mtk_dp->rx_cap), sizeof(buf)));
> > +     mtk_dp->rx_cap[DP_TRAINING_AUX_RD_INTERVAL] &= DP_TRAINING_AUX_RD_MASK;
> > +
> > +     train_info->link_rate =
> > +             min_t(int, MTK_DP_MAX_LINK_RATE, buf[DP_MAX_LINK_RATE]);
> > +     train_info->lane_count =
> > +             min_t(int, MTK_DP_MAX_LANES, drm_dp_max_lane_count(buf));
> > +
> > +     train_info->tps3 = drm_dp_tps3_supported(buf);
> > +     train_info->tps4 = drm_dp_tps4_supported(buf);
> > +
> > +     train_info->sink_ssc =
> > +             !!(buf[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5);
> > +
> > +     train_info->sink_ssc = false;
> > +
> > +     drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
> > +     if (val & DP_MST_CAP) {
> > +             // Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0
> > +             drm_dp_dpcd_readb(&mtk_dp->aux,
> > +                               DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0, &val);
> > +             if (val)
> > +                     drm_dp_dpcd_writeb(&mtk_dp->aux,
> > +                                        DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
> > +                                        val);
> > +     }
> > +
> > +     return true;
> > +}
> > +
> > +static int mtk_dp_edid_parse_audio_capabilities(struct mtk_dp *mtk_dp,
> > +                                             struct mtk_dp_audio_cfg *cfg)
> > +{
> > +     struct cea_sad *sads;
> > +     int sad_count;
> > +     int i;
> > +     int ret = 0;
> > +
> > +     mutex_lock(&mtk_dp->edid_lock);
> > +     if (!mtk_dp->edid) {
> > +             mutex_unlock(&mtk_dp->edid_lock);
> > +             dev_err(mtk_dp->dev, "EDID not found!\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     sad_count = drm_edid_to_sad(mtk_dp->edid, &sads);
> > +     mutex_unlock(&mtk_dp->edid_lock);
> > +     if (sad_count <= 0) {
> > +             drm_info(mtk_dp->drm_dev, "The SADs is NULL\n");
> > +             return 0;
> > +     }
> > +
> > +     for (i = 0; i < sad_count; i++) {
> > +             int sample_rate;
> > +             int word_length;
> > +             // Only PCM supported at the moment
> > +             if (sads[i].format != HDMI_AUDIO_CODING_TYPE_PCM)
> > +                     continue;
> > +
> > +             sample_rate = drm_cea_sad_get_sample_rate(&sads[i]);
> > +             word_length =
> > +                     drm_cea_sad_get_uncompressed_word_length(&sads[i]);
> > +             if (sample_rate <= 0 || word_length <= 0)
> > +                     continue;
> > +
> > +             cfg->channels = sads[i].channels;
> > +             cfg->word_length_bits = word_length;
> > +             cfg->sample_rate = sample_rate;
> > +             ret = 1;
> > +             break;
> > +     }
> > +     kfree(sads);
> > +
> > +     return ret;
> > +}
> > +
> > +static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
> > +{
> > +     phy_reset(mtk_dp->phy);
> > +     mtk_dp_reset_swing_pre_emphasis(mtk_dp);
> > +
> > +     usleep_range(2000, 5000);
> > +}
> > +
> > +static int mtk_dp_train_start(struct mtk_dp *mtk_dp)
> > +{
> > +     int ret = 0;
> > +     int lane_count;
> > +     int link_rate;
> > +     int train_limit;
> > +     int max_link_rate;
> > +     int plug_wait;
> > +
> > +     for (plug_wait = 7; !mtk_dp_plug_state(mtk_dp) && plug_wait > 0;
> > +          --plug_wait)
> > +             usleep_range(1000, 5000);
> > +     if (plug_wait == 0) {
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> > +             return -ENODEV;
> > +     }
> > +
> > +     link_rate = mtk_dp->rx_cap[1];
> > +     lane_count = mtk_dp->rx_cap[2] & 0x1F;
> > +
> > +     mtk_dp->train_info.link_rate = min(MTK_DP_MAX_LINK_RATE, link_rate);
> > +     mtk_dp->train_info.lane_count = min(MTK_DP_MAX_LANES, lane_count);
> > +     link_rate = mtk_dp->train_info.link_rate;
> > +     lane_count = mtk_dp->train_info.lane_count;
> > +
> > +     switch (link_rate) {
> > +     case MTK_DP_LINKRATE_RBR:
> > +     case MTK_DP_LINKRATE_HBR:
> > +     case MTK_DP_LINKRATE_HBR2:
> > +     case MTK_DP_LINKRATE_HBR25:
> > +     case MTK_DP_LINKRATE_HBR3:
> > +             break;
> > +     default:
> > +             mtk_dp->train_info.link_rate = MTK_DP_LINKRATE_HBR3;
> > +             break;
> > +     };
> > +
> > +     max_link_rate = link_rate;
> > +     for (train_limit = 0; train_limit <= 6; ++train_limit) {
> > +             mtk_dp->train_info.cr_done = false;
> > +             mtk_dp->train_info.eq_done = false;
> > +
> > +             mtk_dp_train_change_mode(mtk_dp);
> > +             ret = mtk_dp_train_flow(mtk_dp, link_rate, lane_count);
> > +             if (ret == -ENODEV || ret == -EAGAIN)
> > +                     return ret;
> > +
> > +             if (!mtk_dp->train_info.cr_done) {
> > +                     switch (link_rate) {
> > +                     case MTK_DP_LINKRATE_RBR:
> > +                             lane_count = lane_count / 2;
> > +                             link_rate = max_link_rate;
> > +                             if (lane_count == 0x0) {
> > +                                     mtk_dp->train_state =
> > +                                             MTK_DP_TRAIN_STATE_DPIDLE;
> > +                                     return -EIO;
> > +                             }
> > +                             break;
> > +                     case MTK_DP_LINKRATE_HBR:
> > +                             link_rate = MTK_DP_LINKRATE_RBR;
> > +                             break;
> > +                     case MTK_DP_LINKRATE_HBR2:
> > +                             link_rate = MTK_DP_LINKRATE_HBR;
> > +                             break;
> > +                     case MTK_DP_LINKRATE_HBR3:
> > +                             link_rate = MTK_DP_LINKRATE_HBR2;
> > +                             break;
> > +                     default:
> > +                             return -EINVAL;
> > +                     };
> > +             } else if (!mtk_dp->train_info.eq_done) {
> > +                     lane_count /= 2;
> > +                     if (lane_count == 0)
> > +                             return -EIO;
> > +             } else {
> > +                     return 0;
> > +             }
> > +     }
> > +
> > +     return -ETIMEDOUT;
> > +}
> > +
> > +static int mtk_dp_train_handler(struct mtk_dp *mtk_dp)
> > +{
> > +     int ret = 0;
> > +
> > +     if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
> > +             return ret;
> > +
> > +     switch (mtk_dp->train_state) {
> > +     case MTK_DP_TRAIN_STATE_STARTUP:
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> > +             break;
> > +
> > +     case MTK_DP_TRAIN_STATE_CHECKCAP:
> > +             if (mtk_dp_parse_capabilities(mtk_dp)) {
> > +                     mtk_dp->train_info.check_cap_count = 0;
> > +                     mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID;
> > +             } else {
> > +                     mtk_dp->train_info.check_cap_count++;
> > +
> > +                     if (mtk_dp->train_info.check_cap_count >
> > +                         MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT) {
> > +                             mtk_dp->train_info.check_cap_count = 0;
> > +                             mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> > +                             ret = -ETIMEDOUT;
> > +                     }
> > +             }
> > +             break;
> > +
> > +     case MTK_DP_TRAIN_STATE_CHECKEDID: {
> > +             int caps_found = mtk_dp_edid_parse_audio_capabilities(
> > +                     mtk_dp, &mtk_dp->info.audio_caps);
> > +             mtk_dp->audio_enable = caps_found > 0;
> > +             if (!mtk_dp->audio_enable)
> > +                     memset(&mtk_dp->info.audio_caps, 0,
> > +                            sizeof(mtk_dp->info.audio_caps));
> > +     }
> > +
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE;
> > +             break;
> > +
> > +     case MTK_DP_TRAIN_STATE_TRAINING_PRE:
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING;
> > +             break;
> > +
> > +     case MTK_DP_TRAIN_STATE_TRAINING:
> > +             ret = mtk_dp_train_start(mtk_dp);
> > +             if (!ret) {
> > +                     mtk_dp_video_mute(mtk_dp, true);
> > +                     mtk_dp_audio_mute(mtk_dp, true);
> > +                     mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKTIMING;
> > +                     mtk_dp_fec_enable(mtk_dp, mtk_dp->has_fec);
> > +             } else if (ret != -EAGAIN) {
> > +                     mtk_dp->train_state = MTK_DP_TRAIN_STATE_DPIDLE;
> > +             }
> > +
> > +             ret = 0;
> > +             break;
> > +
> > +     case MTK_DP_TRAIN_STATE_CHECKTIMING:
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_NORMAL;
> > +             break;
> > +     case MTK_DP_TRAIN_STATE_NORMAL:
> > +             break;
> > +     case MTK_DP_TRAIN_STATE_POWERSAVE:
> > +             break;
> > +     case MTK_DP_TRAIN_STATE_DPIDLE:
> > +             break;
> > +     default:
> > +             break;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
> > +{
> > +     if (enable) {
> > +             mtk_dp_set_tx_out(mtk_dp);
> > +             mtk_dp_video_mute(mtk_dp, false);
> > +     } else {
> > +             mtk_dp_video_mute(mtk_dp, true);
> > +     }
> > +}
> > +
> > +static void mtk_dp_audio_sdp_setup(struct mtk_dp *mtk_dp,
> > +                                struct mtk_dp_audio_cfg *cfg)
> > +{
> > +     struct mtk_dp_sdp_packet packet;
> > +     struct hdmi_audio_infoframe frame;
> > +
> > +     hdmi_audio_infoframe_init(&frame);
> > +     frame.coding_type = HDMI_AUDIO_CODING_TYPE_PCM;
> > +     frame.channels = cfg->channels;
> > +     frame.sample_frequency = cfg->sample_rate;
> > +
> > +     switch (cfg->word_length_bits) {
> > +     case 16:
> > +             frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
> > +             break;
> > +     case 20:
> > +             frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_20;
> > +             break;
> > +     case 24:
> > +     default:
> > +             frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_24;
> > +             break;
> > +     }
> > +
> > +     packet.type = MTK_DP_SDP_AUI;
> > +     hdmi_audio_infoframe_pack_for_dp(&frame, &packet.sdp,
> > +                                      MTK_DP_DP_VERSION_11);
> > +
> > +     mtk_dp_audio_sdp_asp_set_channels(mtk_dp, cfg->channels);
> > +     mtk_dp_setup_sdp(mtk_dp, &packet);
> > +}
> > +
> > +static void mtk_dp_audio_setup(struct mtk_dp *mtk_dp,
> > +                            struct mtk_dp_audio_cfg *cfg)
> > +{
> > +     mtk_dp_audio_sdp_setup(mtk_dp, cfg);
> > +     mtk_dp_audio_channel_status_set(mtk_dp, cfg);
> > +
> > +     mtk_dp_audio_setup_channels(mtk_dp, cfg);
> > +     mtk_dp_audio_set_divider(mtk_dp);
> > +}
> > +
> > +static void mtk_dp_video_config(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_mn_overwrite_disable(mtk_dp);
> > +
> > +     mtk_dp_set_msa(mtk_dp);
> > +
> > +     mtk_dp_set_color_depth(mtk_dp, mtk_dp->info.depth);
> > +     mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
> > +}
> > +
> > +static int mtk_dp_state_handler(struct mtk_dp *mtk_dp)
> > +{
> > +     int ret = 0;
> > +
> > +     switch (mtk_dp->state) {
> > +     case MTK_DP_STATE_INITIAL:
> > +             mtk_dp_video_mute(mtk_dp, true);
> > +             mtk_dp_audio_mute(mtk_dp, true);
> > +             mtk_dp->state = MTK_DP_STATE_IDLE;
> > +             break;
> > +
> > +     case MTK_DP_STATE_IDLE:
> > +             if (mtk_dp->train_state == MTK_DP_TRAIN_STATE_NORMAL)
> > +                     mtk_dp->state = MTK_DP_STATE_PREPARE;
> > +             break;
> > +
> > +     case MTK_DP_STATE_PREPARE:
> > +             mtk_dp_video_config(mtk_dp);
> > +             mtk_dp_video_enable(mtk_dp, true);
> > +
> > +             if (mtk_dp->audio_enable) {
> > +                     mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_caps);
> > +                     mtk_dp_audio_mute(mtk_dp, false);
> > +             }
> > +
> > +             mtk_dp->state = MTK_DP_STATE_NORMAL;
> > +             break;
> > +
> > +     case MTK_DP_STATE_NORMAL:
> > +             if (mtk_dp->train_state != MTK_DP_TRAIN_STATE_NORMAL) {
> > +                     mtk_dp_video_mute(mtk_dp, true);
> > +                     mtk_dp_audio_mute(mtk_dp, true);
> > +                     mtk_dp_sdp_stop_sending(mtk_dp);
> > +                     mtk_dp->state = MTK_DP_STATE_IDLE;
> > +             }
> > +             break;
> > +
> > +     default:
> > +             break;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
> > +{
> > +     mtk_dp_set_idle_pattern(mtk_dp, true);
> > +     mtk_dp_initialize_priv_data(mtk_dp);
> > +
> > +     mtk_dp_initialize_settings(mtk_dp);
> > +     mtk_dp_initialize_aux_settings(mtk_dp);
> > +     mtk_dp_initialize_digital_settings(mtk_dp);
> > +     mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
> > +                        BIT(RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT),
> > +                        RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK);
> > +     mtk_dp_initialize_hpd_detect_settings(mtk_dp);
> > +
> > +     mtk_dp_digital_sw_reset(mtk_dp);
> > +}
> > +
> > +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
> > +{
> > +     struct mtk_dp *mtk_dp = dev;
> > +     int event;
> > +     u8 buf[DP_RECEIVER_CAP_SIZE] = {};
> > +
> > +     event = mtk_dp_plug_state(mtk_dp) ? connector_status_connected :
> > +                                               connector_status_disconnected;
> > +
> > +     if (event < 0)
> > +             return IRQ_HANDLED;
> > +
> > +     if (mtk_dp->drm_dev) {
> > +             dev_info(mtk_dp->dev, "drm_helper_hpd_irq_event\n");
> > +             drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
> > +     }
> > +
> > +     if (mtk_dp->train_info.cable_state_change) {
> > +             mtk_dp->train_info.cable_state_change = false;
> > +
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > +
> > +             if (!mtk_dp->train_info.cable_plugged_in ||
> > +                 !mtk_dp_plug_state(mtk_dp)) {
> > +                     mtk_dp_video_mute(mtk_dp, true);
> > +                     mtk_dp_audio_mute(mtk_dp, true);
> > +
> > +                     mtk_dp_initialize_priv_data(mtk_dp);
> > +                     mtk_dp_set_idle_pattern(mtk_dp, true);
> > +                     if (mtk_dp->has_fec)
> > +                             mtk_dp_fec_enable(mtk_dp, false);
> > +                     mtk_dp_sdp_stop_sending(mtk_dp);
> > +
> > +                     mtk_dp_edid_free(mtk_dp);
> > +                     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> > +                                        DP_PWR_STATE_BANDGAP_TPLL,
> > +                                        DP_PWR_STATE_MASK);
> > +             } else {
> > +                     mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> > +                                        DP_PWR_STATE_BANDGAP_TPLL_LANE,
> > +                                        DP_PWR_STATE_MASK);
> > +                     drm_dp_read_dpcd_caps(&mtk_dp->aux, buf);
> > +                     mtk_dp->train_info.link_rate =
> > +                             min_t(int, MTK_DP_MAX_LINK_RATE,
> > +                                   buf[DP_MAX_LINK_RATE]);
> > +                     mtk_dp->train_info.lane_count =
> > +                             min_t(int, MTK_DP_MAX_LANES,
> > +                                   drm_dp_max_lane_count(buf));
> > +             }
> > +     }
> > +
> > +     if (mtk_dp->train_info.irq_status & MTK_DP_HPD_INTERRUPT) {
> > +             dev_info(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n");
> > +             mtk_dp->train_info.irq_status &= ~MTK_DP_HPD_INTERRUPT;
> > +             mtk_dp_hpd_sink_event(mtk_dp);
> > +     }
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t mtk_dp_hpd_isr_handler(struct mtk_dp *mtk_dp)
> > +{
> > +     bool connected;
> > +     u16 swirq_status = mtk_dp_swirq_get_clear(mtk_dp);
> > +     u8 hwirq_status = mtk_dp_hwirq_get_clear(mtk_dp);
> > +     struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
> > +
> > +     train_info->irq_status |= hwirq_status | swirq_status;
> > +
> > +     if (!train_info->irq_status)
> > +             return IRQ_HANDLED;
> > +
> > +     connected = mtk_dp_plug_state(mtk_dp);
> > +     if (connected || !train_info->cable_plugged_in)
> > +             train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
> > +     else if (!connected || train_info->cable_plugged_in)
> > +             train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
> > +
> > +     if (!(train_info->irq_status &
> > +           (MTK_DP_HPD_CONNECT | MTK_DP_HPD_DISCONNECT)))
> > +             return IRQ_HANDLED;
> > +
> > +     if (train_info->irq_status & MTK_DP_HPD_CONNECT) {
> > +             train_info->irq_status &= ~MTK_DP_HPD_CONNECT;
> > +             train_info->cable_plugged_in = true;
> > +     } else {
> > +             train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT;
> > +             train_info->cable_plugged_in = false;
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > +     }
> > +     train_info->cable_state_change = true;
> > +     return IRQ_WAKE_THREAD;
> > +}
> > +
> > +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
> > +{
> > +     struct mtk_dp *mtk_dp = dev;
> > +     u32 irq_status;
> > +
> > +     irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS);
> > +
> > +     if (!irq_status)
> > +             return IRQ_HANDLED;
> > +
> > +     if (irq_status & RGS_IRQ_STATUS_TRANSMITTER)
> > +             return mtk_dp_hpd_isr_handler(mtk_dp);
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static int mtk_dp_dt_parse_pdata(struct mtk_dp *mtk_dp,
> > +                              struct platform_device *pdev)
> > +{
> > +     struct device *dev = &pdev->dev;
> > +     int ret = 0;
> > +     void __iomem *base;
> > +
> > +     base = devm_platform_ioremap_resource(pdev, 0);
> > +     if (IS_ERR(base))
> > +             return PTR_ERR(base);
> > +
> > +     mtk_dp->regs = devm_regmap_init_mmio(dev, base, &mtk_dp_regmap_config);
> > +     if (IS_ERR(mtk_dp->regs))
> > +             return PTR_ERR(mtk_dp->regs);
> > +
> > +     mtk_dp->dp_tx_clk = devm_clk_get(dev, "faxi");
> > +     if (IS_ERR(mtk_dp->dp_tx_clk)) {
> > +             ret = PTR_ERR(mtk_dp->dp_tx_clk);
> > +             dev_err(dev, "Failed to get dptx clock: %d\n", ret);
> > +             mtk_dp->dp_tx_clk = NULL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void mtk_dp_update_plugged_status(struct mtk_dp *mtk_dp)
> > +{
> > +     bool connected, has_audio;
> > +
> > +     mutex_lock(&mtk_dp->update_plugged_status_lock);
> > +     connected = mtk_dp_plug_state(mtk_dp);
> > +     has_audio = drm_detect_monitor_audio(mtk_dp->edid);
> > +     if (mtk_dp->plugged_cb && mtk_dp->codec_dev)
> > +             mtk_dp->plugged_cb(mtk_dp->codec_dev, connected & has_audio);
> > +     mutex_unlock(&mtk_dp->update_plugged_status_lock);
> > +}
> > +
> > +static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     enum drm_connector_status ret = connector_status_disconnected;
> > +     u8 sink_count = 0;
> > +
> > +     if (mtk_dp_is_edp(mtk_dp))
> > +             return connector_status_connected;
> > +
> > +     if (mtk_dp_plug_state(mtk_dp)) {
> > +             drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
> > +             if (DP_GET_SINK_COUNT(sink_count))
> > +                     ret = connector_status_connected;
> > +     }
> > +
> > +     mtk_dp_update_plugged_status(mtk_dp);
> > +     return ret;
> > +}
> > +
> > +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> > +                                 struct drm_connector *connector)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     bool enabled = mtk_dp->enabled;
> > +     struct edid *new_edid = NULL;
> > +
> > +     if (!enabled)
> > +             drm_bridge_chain_pre_enable(bridge);
> > +
> > +     drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> > +     usleep_range(2000, 5000);
> > +
> > +     if (mtk_dp_plug_state(mtk_dp))
> > +             new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> > +
> > +     if (!enabled)
> > +             drm_bridge_chain_post_disable(bridge);
> > +
> > +     mutex_lock(&mtk_dp->edid_lock);
> > +     kfree(mtk_dp->edid);
> > +     mtk_dp->edid = NULL;
> > +
> > +     if (!new_edid) {
> > +             mutex_unlock(&mtk_dp->edid_lock);
> > +             return NULL;
> > +     }
> > +
> > +     mtk_dp->edid = drm_edid_duplicate(new_edid);
> > +     mutex_unlock(&mtk_dp->edid_lock);
> > +
> > +     return new_edid;
> > +}
> > +
> > +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> > +                                struct drm_dp_aux_msg *msg)
> > +{
> > +     ssize_t err = -EAGAIN;
> > +     struct mtk_dp *mtk_dp;
> > +     bool is_read;
> > +     u8 request;
> > +     size_t accessed_bytes = 0;
> > +     int retry = 3, ret = 0;
> > +
> > +     mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> > +
> > +     if (!mtk_dp->train_info.cable_plugged_in ||
> > +         mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> > +             mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> > +             err = -EAGAIN;
> > +             goto err;
> > +     }
> > +
> > +     switch (msg->request) {
> > +     case DP_AUX_I2C_MOT:
> > +     case DP_AUX_I2C_WRITE:
> > +     case DP_AUX_NATIVE_WRITE:
> > +     case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> > +     case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> > +             request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> > +             is_read = false;
> > +             break;
> > +     case DP_AUX_I2C_READ:
> > +     case DP_AUX_NATIVE_READ:
> > +     case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> > +             request = msg->request;
> > +             is_read = true;
> > +             break;
> > +     default:
> > +             drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> > +                     msg->request);
> > +             err = -EINVAL;
> > +             goto err;
> > +     }
> > +
> > +     if (msg->size == 0) {
> > +             mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> > +                                    msg->address + accessed_bytes,
> > +                                    msg->buffer + accessed_bytes, 0);
> > +     } else {
> > +             while (accessed_bytes < msg->size) {
> > +                     size_t to_access =
> > +                             min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> > +                                   msg->size - accessed_bytes);
> > +                     retry = 3;
> > +                     while (retry--) {
> > +                             ret = mtk_dp_aux_do_transfer(
> > +                                     mtk_dp, is_read, request,
> > +                                     msg->address + accessed_bytes,
> > +                                     msg->buffer + accessed_bytes,
> > +                                     to_access);
> > +                             if (ret == 0)
> > +                                     break;
> > +                             udelay(50);
> > +                     }
> > +                     if (!retry && ret) {
> > +                             drm_info(mtk_dp->drm_dev,
> > +                                      "Failed to do AUX transfer: %d\n",
> > +                                      ret);
> > +                             break;
> > +                     }
> > +                     accessed_bytes += to_access;
> > +             }
> > +     }
> > +err:
> > +     if (!ret) {
> > +             msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > +             ret = msg->size;
> > +     } else {
> > +             msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> > +             return err;
> > +     }
> > +
> > +     msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > +     return msg->size;
> > +}
> > +
> > +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> > +{
> > +     drm_dp_aux_init(&mtk_dp->aux);
> > +     mtk_dp->aux.name = "aux_mtk_dp";
> > +     mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> > +}
> > +
> > +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> > +{
> > +     mutex_lock(&mtk_dp->dp_lock);
> > +
> > +     mtk_dp_hwirq_enable(mtk_dp, false);
> > +     mtk_dp_power_disable(mtk_dp);
> > +     phy_exit(mtk_dp->phy);
> > +     clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > +
> > +     mutex_unlock(&mtk_dp->dp_lock);
> > +}
> > +
> > +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> > +{
> > +     int ret = 0;
> > +
> > +     mutex_lock(&mtk_dp->dp_lock);
> > +
> > +     ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> > +     if (ret < 0) {
> > +             dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> > +             goto err;
> > +     }
> > +     ret = phy_init(mtk_dp->phy);
> > +     if (ret) {
> > +             dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> > +             goto err_phy_init;
> > +     }
> > +     ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> > +     if (ret) {
> > +             dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> > +             goto err_phy_config;
> > +     }
> > +
> > +     mtk_dp_init_port(mtk_dp);
> > +     mtk_dp_power_enable(mtk_dp);
> > +     mtk_dp_hwirq_enable(mtk_dp, true);
> > +
> > +err_phy_config:
> > +     phy_exit(mtk_dp->phy);
> > +err_phy_init:
> > +     clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > +err:
> > +     mutex_unlock(&mtk_dp->dp_lock);
> > +     return ret;
> > +}
> > +
> > +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> > +                             enum drm_bridge_attach_flags flags)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     int ret;
> > +
> > +     if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> > +             dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> > +             return -EINVAL;
> > +     }
> > +
> > +     ret = mtk_dp_poweron(mtk_dp);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (mtk_dp->next_bridge) {
> > +             ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> > +                                     &mtk_dp->bridge, flags);
> > +             if (ret) {
> > +                     drm_warn(mtk_dp->drm_dev,
> > +                              "Failed to attach external bridge: %d\n", ret);
> > +                     goto err_bridge_attach;
> > +             }
> > +     }
> > +
> > +     mtk_dp->drm_dev = bridge->dev;
> > +
> > +     return 0;
> > +
> > +err_bridge_attach:
> > +     mtk_dp_poweroff(mtk_dp);
> > +     return ret;
> > +}
> > +
> > +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +
> > +     mtk_dp->drm_dev = NULL;
> > +
> > +     mtk_dp_poweroff(mtk_dp);
> > +}
> > +
> > +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> > +                                      struct drm_bridge_state *old_state)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +
> > +     mtk_dp_video_mute(mtk_dp, true);
> > +     mtk_dp_audio_mute(mtk_dp, true);
> > +     mtk_dp->state = MTK_DP_STATE_IDLE;
> > +     mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > +
> > +     mtk_dp->enabled = false;
> > +     msleep(100);
> > +}
> > +
> > +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> > +                                       struct drm_display_mode *mode)
> > +{
> > +     struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> > +
> > +     drm_display_mode_to_videomode(mode, &timings->vm);
> > +     timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> > +     timings->htotal = mode->htotal;
> > +     timings->vtotal = mode->vtotal;
> > +}
> > +
> > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > +                                     struct drm_bridge_state *old_state)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     struct drm_connector *conn;
> > +     struct drm_connector_state *conn_state;
> > +     struct drm_crtc *crtc;
> > +     struct drm_crtc_state *crtc_state;
> > +     int ret = 0;
> > +     int i;
> > +
> > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > +                                                     bridge->encoder);
> > +     if (!conn) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Can't enable bridge as connector is missing\n");
> > +             return;
> > +     }
> > +
> > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
>
> This should be protected by a mutex (just like any resource shared
> between KMS and ALSA)
>
Ok.
> > +     conn_state =
> > +             drm_atomic_get_new_connector_state(old_state->base.state, conn);
> > +     if (!conn_state) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Can't enable bridge as connector state is missing\n");
> > +             return;
> > +     }
> > +
> > +     crtc = conn_state->crtc;
> > +     if (!crtc) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Can't enable bridge as connector state doesn't have a crtc\n");
> > +             return;
> > +     }
> > +
> > +     crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> > +     if (!crtc_state) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Can't enable bridge as crtc state is missing\n");
> > +             return;
> > +     }
> > +
> > +     mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);
> > +     if (!mtk_dp_parse_capabilities(mtk_dp)) {
> > +             drm_err(mtk_dp->drm_dev,
> > +                     "Can't enable bridge as nothing is plugged in\n");
> > +             return;
> > +     }
> > +
> > +     //training
> > +     for (i = 0; i < 50; i++) {
> > +             ret = mtk_dp_train_handler(mtk_dp);
> > +             if (ret) {
> > +                     drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> > +                             ret);
> > +                     return;
> > +             }
> > +
> > +             ret = mtk_dp_state_handler(mtk_dp);
> > +             if (ret) {
> > +                     drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> > +                             ret);
> > +                     return;
> > +             }
> > +     }
> > +
> > +     mtk_dp->enabled = true;
> > +     mtk_dp_update_plugged_status(mtk_dp);
> > +}
> > +
> > +static enum drm_mode_status
> > +mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
> > +                      const struct drm_display_info *info,
> > +                      const struct drm_display_mode *mode)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     u32 rx_linkrate;
> > +     u32 bpp;
> > +
> > +     bpp = info->color_formats & DRM_COLOR_FORMAT_YCRCB422 ? 16 : 24;
> > +     rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
> > +     if (rx_linkrate * mtk_dp->train_info.lane_count < mode->clock * bpp / 8)
> > +             return MODE_CLOCK_HIGH;
> > +
> > +     if (mode->clock > 600000)
> > +             return MODE_CLOCK_HIGH;
> > +
> > +     if ((mode->clock * 1000) / (mode->htotal * mode->vtotal) > 62)
> > +             return MODE_CLOCK_HIGH;
> > +
> > +     return MODE_OK;
> > +}
> > +
> > +static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(
> > +     struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
> > +     struct drm_crtc_state *crtc_state,
> > +     struct drm_connector_state *conn_state, unsigned int *num_output_fmts)
> > +{
> > +     u32 *output_fmts;
> > +
> > +     *num_output_fmts = 0;
> > +     output_fmts = kcalloc(1, sizeof(*output_fmts), GFP_KERNEL);
> > +     if (!output_fmts)
> > +             return NULL;
> > +     *num_output_fmts = 1;
> > +     output_fmts[0] = MEDIA_BUS_FMT_FIXED;
> > +     return output_fmts;
> > +}
> > +
> > +static const u32 mt8195_input_fmts[] = {
> > +     MEDIA_BUS_FMT_RGB888_1X24,
> > +     MEDIA_BUS_FMT_YUV8_1X24,
> > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > +};
> > +
> > +static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(
> > +     struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
> > +     struct drm_crtc_state *crtc_state,
> > +     struct drm_connector_state *conn_state, u32 output_fmt,
> > +     unsigned int *num_input_fmts)
> > +{
> > +     u32 *input_fmts;
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> > +     struct drm_display_info *display_info =
> > +             &conn_state->connector->display_info;
> > +     u32 rx_linkrate;
> > +     u32 bpp;
> > +
> > +     bpp = (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422) ? 16 :
> > +                                                                             24;
> > +     rx_linkrate = (u32)mtk_dp->train_info.link_rate * 27000;
> > +     *num_input_fmts = 0;
> > +     input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts), sizeof(*input_fmts),
> > +                          GFP_KERNEL);
> > +     if (!input_fmts)
> > +             return NULL;
> > +
> > +     *num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
> > +
> > +     memcpy(input_fmts, mt8195_input_fmts,
> > +            sizeof(*input_fmts) * ARRAY_SIZE(mt8195_input_fmts));
> > +
> > +     if (((rx_linkrate * mtk_dp->train_info.lane_count) <
> > +          (mode->clock * 24 / 8)) &&
> > +         ((rx_linkrate * mtk_dp->train_info.lane_count) >
> > +          (mode->clock * 16 / 8)) &&
> > +         (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422)) {
> > +             kfree(input_fmts);
> > +             input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL);
> > +             *num_input_fmts = 1;
> > +             input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
> > +     }
> > +
> > +     return input_fmts;
> > +}
> > +
> > +static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
> > +                                   struct drm_bridge_state *bridge_state,
> > +                                   struct drm_crtc_state *crtc_state,
> > +                                   struct drm_connector_state *conn_state)
> > +{
> > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +     unsigned int input_bus_format;
> > +
> > +     input_bus_format = bridge_state->input_bus_cfg.format;
> > +
> > +     dev_info(mtk_dp->dev, "input format 0x%04x, output format 0x%04x\n",
> > +              bridge_state->input_bus_cfg.format,
> > +              bridge_state->output_bus_cfg.format);
> > +
> > +     mtk_dp->input_fmt = input_bus_format;
> > +     if (mtk_dp->input_fmt == MEDIA_BUS_FMT_YUYV8_1X16)
> > +             mtk_dp->info.format = MTK_DP_COLOR_FORMAT_YUV_422;
> > +     else
> > +             mtk_dp->info.format = MTK_DP_COLOR_FORMAT_RGB_444;
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
> > +     .atomic_check = mtk_dp_bridge_atomic_check,
> > +     .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> > +     .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> > +     .atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts,
> > +     .atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts,
> > +     .atomic_reset = drm_atomic_helper_bridge_reset,
> > +     .attach = mtk_dp_bridge_attach,
> > +     .detach = mtk_dp_bridge_detach,
> > +     .atomic_enable = mtk_dp_bridge_atomic_enable,
> > +     .atomic_disable = mtk_dp_bridge_atomic_disable,
> > +     .mode_valid = mtk_dp_bridge_mode_valid,
> > +     .get_edid = mtk_dp_get_edid,
> > +     .detect = mtk_dp_bdg_detect,
> > +};
> > +
> > +/*
> > + * HDMI audio codec callbacks
> > + */
> > +static int mtk_dp_audio_hw_params(struct device *dev, void *data,
> > +                               struct hdmi_codec_daifmt *daifmt,
> > +                               struct hdmi_codec_params *params)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +     struct mtk_dp_audio_cfg cfg;
> > +
> > +     if (!mtk_dp->enabled) {
> > +             pr_err("%s, DP is not ready!\n", __func__);
> > +             return -ENODEV;
> > +     }
> > +
> > +     cfg.channels = params->cea.channels;
> > +     cfg.sample_rate = params->sample_rate;
> > +     cfg.word_length_bits = 24;
> > +
> > +     mtk_dp_audio_setup(mtk_dp, &cfg);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_audio_startup(struct device *dev, void *data)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +
> > +     mtk_dp_audio_mute(mtk_dp, false);
> > +
> > +     return 0;
> > +}
> > +
> > +static void mtk_dp_audio_shutdown(struct device *dev, void *data)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +
> > +     mtk_dp_audio_mute(mtk_dp, true);
> > +}
> > +
> > +static int mtk_dp_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
> > +                             size_t len)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +
> > +     if (mtk_dp->enabled)
> > +             memcpy(buf, mtk_dp->connector_eld, len);
> > +     else
> > +             memset(buf, 0, len);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_audio_hook_plugged_cb(struct device *dev, void *data,
> > +                                     hdmi_codec_plugged_cb fn,
> > +                                     struct device *codec_dev)
> > +{
> > +     struct mtk_dp *mtk_dp = data;
> > +
> > +     mutex_lock(&mtk_dp->update_plugged_status_lock);
> > +     mtk_dp->plugged_cb = fn;
> > +     mtk_dp->codec_dev = codec_dev;
> > +     mutex_unlock(&mtk_dp->update_plugged_status_lock);
> > +
> > +     mtk_dp_update_plugged_status(mtk_dp);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct hdmi_codec_ops mtk_dp_audio_codec_ops = {
> > +     .hw_params = mtk_dp_audio_hw_params,
> > +     .audio_startup = mtk_dp_audio_startup,
> > +     .audio_shutdown = mtk_dp_audio_shutdown,
> > +     .get_eld = mtk_dp_audio_get_eld,
> > +     .hook_plugged_cb = mtk_dp_audio_hook_plugged_cb,
> > +     .no_capture_mute = 1,
> > +};
> > +
> > +static int mtk_dp_register_audio_driver(struct device *dev)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +     struct hdmi_codec_pdata codec_data = {
> > +             .ops = &mtk_dp_audio_codec_ops,
> > +             .max_i2s_channels = 8,
> > +             .i2s = 1,
> > +             .data = mtk_dp,
> > +     };
> > +     struct platform_device *pdev;
> > +
> > +     pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
> > +                                          PLATFORM_DEVID_AUTO, &codec_data,
> > +                                          sizeof(codec_data));
> > +     if (IS_ERR(pdev))
> > +             return PTR_ERR(pdev);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_probe(struct platform_device *pdev)
> > +{
> > +     struct mtk_dp *mtk_dp;
> > +     struct device *dev = &pdev->dev;
> > +     int ret;
> > +     int irq_num = 0;
> > +     struct drm_panel *panel = NULL;
> > +
> > +     mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> > +     if (!mtk_dp)
> > +             return -ENOMEM;
> > +
> > +     mtk_dp->dev = dev;
> > +
> > +     irq_num = platform_get_irq(pdev, 0);
> > +     if (irq_num < 0) {
> > +             dev_err(dev, "failed to request dp irq resource\n");
> > +             return -EPROBE_DEFER;
> > +     }
> > +
> > +     ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel,
> > +                                       &mtk_dp->next_bridge);
> > +     if (ret == -ENODEV) {
> > +             dev_info(
> > +                     dev,
> > +                     "No panel connected in devicetree, continuing as external DP\n");
> > +             mtk_dp->next_bridge = NULL;
> > +     } else if (ret) {
> > +             dev_err(dev, "Failed to find panel or bridge: %d\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     if (panel) {
> > +             mtk_dp->next_bridge = devm_drm_panel_bridge_add(dev, panel);
> > +             if (IS_ERR(mtk_dp->next_bridge)) {
> > +                     ret = PTR_ERR(mtk_dp->next_bridge);
> > +                     dev_err(dev, "Failed to create bridge: %d\n", ret);
> > +                     return -EPROBE_DEFER;
> > +             }
> > +     }
>
> You should be using devm_drm_of_get_bridge here
>
Ok.
> > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > +     if (ret)
> > +             return ret;
>
> pdata?
>
can you elaborate?
> > +
> > +     mtk_dp_aux_init(mtk_dp);
> > +
> > +     ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
> > +                                     mtk_dp_hpd_event_thread,
> > +                                     IRQ_TYPE_LEVEL_HIGH, dev_name(dev),
> > +                                     mtk_dp);
> > +     if (ret) {
> > +             dev_err(dev, "failed to request mediatek dptx irq\n");
> > +             return -EPROBE_DEFER;
> > +     }
> > +
> > +     mutex_init(&mtk_dp->dp_lock);
> > +     mutex_init(&mtk_dp->edid_lock);
> > +
> > +     platform_set_drvdata(pdev, mtk_dp);
> > +
> > +     if (!mtk_dp_is_edp(mtk_dp)) {
> > +             mutex_init(&mtk_dp->update_plugged_status_lock);
> > +             ret = mtk_dp_register_audio_driver(dev);
> > +             if (ret) {
> > +                     dev_err(dev, "Failed to register audio driver: %d\n",
> > +                             ret);
> > +                     return ret;
> > +             }
> > +     }
> > +
> > +     mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
> > +                                                     PLATFORM_DEVID_AUTO,
> > +                                                     &mtk_dp->regs,
> > +                                                     sizeof(&mtk_dp->regs));
> > +     if (IS_ERR(mtk_dp->phy_dev)) {
> > +             dev_err(dev, "Failed to create device mediatek-dp-phy: %ld\n",
> > +                     PTR_ERR(mtk_dp->phy_dev));
> > +             return PTR_ERR(mtk_dp->phy_dev);
> > +     }
> > +
> > +     mtk_dp_get_calibration_data(mtk_dp);
> > +
> > +     mtk_dp->phy = dev_get_drvdata(&mtk_dp->phy_dev->dev);
> > +     if (IS_ERR(mtk_dp->phy)) {
> > +             dev_err(dev, "Failed to get phy: %ld\n", PTR_ERR(mtk_dp->phy));
> > +             platform_device_unregister(mtk_dp->phy_dev);
> > +             return PTR_ERR(mtk_dp->phy);
> > +     }
> > +
> > +     mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
> > +     mtk_dp->bridge.of_node = dev->of_node;
> > +     if (mtk_dp_is_edp(mtk_dp))
> > +             mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> > +     else
> > +             mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
> > +
> > +     mtk_dp->bridge.ops =
> > +             DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
> > +     drm_bridge_add(&mtk_dp->bridge);
> > +
> > +     pm_runtime_enable(dev);
> > +     pm_runtime_get_sync(dev);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_remove(struct platform_device *pdev)
> > +{
> > +     struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
> > +
> > +     platform_device_unregister(mtk_dp->phy_dev);
> > +
> > +     mtk_dp_video_mute(mtk_dp, true);
> > +     mtk_dp_audio_mute(mtk_dp, true);
> > +
> > +     pm_runtime_disable(&pdev->dev);
> > +
> > +     return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int mtk_dp_suspend(struct device *dev)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +
> > +     mtk_dp_power_disable(mtk_dp);
> > +     mtk_dp_hwirq_enable(mtk_dp, false);
> > +
> > +     pm_runtime_put_sync(dev);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_resume(struct device *dev)
> > +{
> > +     struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> > +
> > +     pm_runtime_get_sync(dev);
> > +
> > +     mtk_dp_init_port(mtk_dp);
> > +     mtk_dp_power_enable(mtk_dp);
> > +     mtk_dp_hwirq_enable(mtk_dp, true);
> > +
> > +     return 0;
> > +}
> > +#endif
> > +
> > +static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
> > +
> > +static const struct of_device_id mtk_dp_of_match[] = {
> > +     {
> > +             .compatible = "mediatek,mt8195-dp-tx",
> > +     },
> > +     {},
> > +};
> > +MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
> > +
> > +struct platform_driver mtk_dp_driver = {
> > +     .probe = mtk_dp_probe,
> > +     .remove = mtk_dp_remove,
> > +     .driver = {
> > +             .name = "mediatek-drm-dp",
> > +             .of_match_table = mtk_dp_of_match,
> > +             .pm = &mtk_dp_pm_ops,
> > +     },
> > +};
> > +
> > +MODULE_AUTHOR("Jason-JH.Lin <jason-jh.lin@mediatek.com>");
> > +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> > +MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> > new file mode 100644
> > index 0000000000000..79952ac30e9e6
> > --- /dev/null
> > +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> > @@ -0,0 +1,568 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (c) 2019 MediaTek Inc.
> > + * Copyright (c) 2021 BayLibre
> > + */
> > +#ifndef _MTK_DP_REG_H_
> > +#define _MTK_DP_REG_H_
> > +
> > +#define MTK_DP_SIP_CONTROL_AARCH32 0x82000523
> > +#define MTK_DP_SIP_ATF_VIDEO_UNMUTE 0x20
> > +#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE 0x21
> > +
> > +#define DP_PHY_GLB_BIAS_GEN_00 0x0000
> > +#define RG_XTP_GLB_BIAS_INTR_CTRL (0x1f << 16)
> > +
> > +#define DP_PHY_GLB_DPAUX_TX 0x0008
> > +#define RG_CKM_PT0_CKTX_IMPSEL (0xf << 20)
> > +
> > +#define DP_PHY_LANE_TX_0 0x104
> > +#define RG_XTP_LN0_TX_IMPSEL_PMOS (0xf << 12)
> > +#define RG_XTP_LN0_TX_IMPSEL_NMOS (0xf << 16)
> > +
> > +#define DP_PHY_LANE_TX_1 0x204
> > +#define RG_XTP_LN1_TX_IMPSEL_PMOS (0xf << 12)
> > +#define RG_XTP_LN1_TX_IMPSEL_NMOS (0xf << 16)
> > +
> > +#define DP_PHY_LANE_TX_2 0x304
> > +#define RG_XTP_LN2_TX_IMPSEL_PMOS (0xf << 12)
> > +#define RG_XTP_LN2_TX_IMPSEL_NMOS (0xf << 16)
> > +
> > +#define DP_PHY_LANE_TX_3 0x404
> > +#define RG_XTP_LN3_TX_IMPSEL_PMOS (0xf << 12)
> > +#define RG_XTP_LN3_TX_IMPSEL_NMOS (0xf << 16)
> > +
> > +#define TOP_OFFSET 0x2000
> > +#define ENC0_OFFSET 0x3000
> > +#define ENC1_OFFSET 0x3200
> > +#define TRANS_OFFSET 0x3400
> > +#define AUX_OFFSET 0x3600
> > +#define SEC_OFFSET 0x4000
> > +
> > +#define MTK_DP_HPD_DISCONNECT BIT(1)
> > +#define MTK_DP_HPD_CONNECT BIT(2)
> > +#define MTK_DP_HPD_INTERRUPT BIT(3)
> > +
> > +#define MTK_DP_0034 0x034
> > +#define DA_XTP_GLB_CKDET_EN_FORCE_VAL BIT(15)
> > +#define DA_XTP_GLB_CKDET_EN_FORCE_EN BIT(14)
> > +#define DA_CKM_INTCKTX_EN_FORCE_VAL BIT(13)
> > +#define DA_CKM_INTCKTX_EN_FORCE_EN BIT(12)
> > +#define DA_CKM_CKTX0_EN_FORCE_VAL BIT(11)
> > +#define DA_CKM_CKTX0_EN_FORCE_EN BIT(10)
> > +#define DA_CKM_XTAL_CK_FORCE_VAL BIT(9)
> > +#define DA_CKM_XTAL_CK_FORCE_EN BIT(8)
> > +#define DA_CKM_BIAS_LPF_EN_FORCE_VAL BIT(7)
> > +#define DA_CKM_BIAS_LPF_EN_FORCE_EN BIT(6)
> > +#define DA_CKM_BIAS_EN_FORCE_VAL BIT(5)
> > +#define DA_CKM_BIAS_EN_FORCE_EN BIT(4)
> > +#define DA_XTP_GLB_AVD10_ON_FORCE_VAL BIT(3)
> > +#define DA_XTP_GLB_AVD10_ON_FORCE BIT(2)
> > +#define DA_XTP_GLB_LDO_EN_FORCE_VAL BIT(1)
> > +#define DA_XTP_GLB_LDO_EN_FORCE_EN BIT(0)
> > +
> > +#define MTK_DP_1040 0x1040
> > +#define RG_DPAUX_RX_VALID_DEGLITCH_EN BIT(2)
> > +#define RG_XTP_GLB_CKDET_EN BIT(1)
> > +#define RG_DPAUX_RX_EN BIT(0)
> > +
> > +#define MTK_DP_ENC0_P0_3000 (ENC0_OFFSET + 0x000)
> > +#define LANE_NUM_DP_ENC0_P0_MASK 0x3
> > +#define VIDEO_MUTE_SW_DP_ENC0_P0_MASK 0x4
> > +#define VIDEO_MUTE_SW_DP_ENC0_P0_SHIFT 2
> > +#define VIDEO_MUTE_SEL_DP_ENC0_P0_MASK 0x8
> > +#define VIDEO_MUTE_SEL_DP_ENC0_P0_SHIFT 3
> > +#define ENHANCED_FRAME_EN_DP_ENC0_P0_MASK 0x10
> > +#define ENHANCED_FRAME_EN_DP_ENC0_P0_SHIFT 4
> > +
> > +#define MTK_DP_ENC0_P0_3004 (ENC0_OFFSET + 0x004)
> > +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK 0x100
> > +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_SHIFT 8
> > +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_MASK 0x200
> > +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0_SHIFT 9
> > +
> > +#define MTK_DP_ENC0_P0_3008 (ENC0_OFFSET + 0x008)
> > +#define VIDEO_M_CODE_SW_0_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_300C (ENC0_OFFSET + 0x00C)
> > +#define VIDEO_M_CODE_SW_1_DP_ENC0_P0_MASK 0xff
> > +
> > +#define MTK_DP_ENC0_P0_3010 (ENC0_OFFSET + 0x010)
> > +#define HTOTAL_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3014 (ENC0_OFFSET + 0x014)
> > +#define VTOTAL_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3018 (ENC0_OFFSET + 0x018)
> > +#define HSTART_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_301C (ENC0_OFFSET + 0x01C)
> > +#define VSTART_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3020 (ENC0_OFFSET + 0x020)
> > +#define HWIDTH_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3024 (ENC0_OFFSET + 0x024)
> > +#define VHEIGHT_SW_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3028 (ENC0_OFFSET + 0x028)
> > +#define HSW_SW_DP_ENC0_P0_MASK 0x7fff
> > +#define HSW_SW_DP_ENC0_P0_SHIFT 0
> > +#define HSP_SW_DP_ENC0_P0_MASK 0x8000
> > +
> > +#define MTK_DP_ENC0_P0_302C (ENC0_OFFSET + 0x02C)
> > +#define VSW_SW_DP_ENC0_P0_MASK 0x7fff
> > +#define VSW_SW_DP_ENC0_P0_SHIFT 0
> > +#define VSP_SW_DP_ENC0_P0_MASK 0x8000
> > +
> > +#define MTK_DP_ENC0_P0_3030 (ENC0_OFFSET + 0x030)
> > +#define HTOTAL_SEL_DP_ENC0_P0_SHIFT 0
> > +#define VTOTAL_SEL_DP_ENC0_P0_SHIFT 1
> > +#define HSTART_SEL_DP_ENC0_P0_SHIFT 2
> > +#define VSTART_SEL_DP_ENC0_P0_SHIFT 3
> > +#define HWIDTH_SEL_DP_ENC0_P0_SHIFT 4
> > +#define VHEIGHT_SEL_DP_ENC0_P0_SHIFT 5
> > +#define HSP_SEL_DP_ENC0_P0_SHIFT 6
> > +#define HSW_SEL_DP_ENC0_P0_SHIFT 7
> > +#define VSP_SEL_DP_ENC0_P0_SHIFT 8
> > +#define VSW_SEL_DP_ENC0_P0_SHIFT 9
> > +#define VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0_MASK 0x800
> > +#define VBID_AUDIO_MUTE_SW_DP_ENC0_P0_SHIFT 11
> > +#define VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0_MASK 0x1000
> > +#define VBID_AUDIO_MUTE_SEL_DP_ENC0_P0_SHIFT 12
> > +
> > +#define MTK_DP_ENC0_P0_3034 (ENC0_OFFSET + 0x034)
> > +
> > +#define MTK_DP_ENC0_P0_3038 (ENC0_OFFSET + 0x038)
> > +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK 0x800
> > +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_SHIFT 11
> > +
> > +#define MTK_DP_ENC0_P0_303C (ENC0_OFFSET + 0x03C)
> > +#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK 0x3f
> > +#define SRAM_START_READ_THRD_DP_ENC0_P0_SHIFT 0
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK 0x700
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT 8
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT                                     \
> > +     (0 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT                                     \
> > +     (1 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT                                     \
> > +     (2 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT                                      \
> > +     (3 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> > +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT                                      \
> > +     (4 << VIDEO_COLOR_DEPTH_DP_ENC0_P0_SHIFT)
> > +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK 0x7000
> > +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT 12
> > +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB                                     \
> > +     (0 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> > +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422                                \
> > +     (1 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> > +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420                                \
> > +     (2 << PIXEL_ENCODE_FORMAT_DP_ENC0_P0_SHIFT)
> > +#define VIDEO_MN_GEN_EN_DP_ENC0_P0_MASK 0x8000
> > +#define VIDEO_MN_GEN_EN_DP_ENC0_P0_SHIFT 15
> > +
> > +#define MTK_DP_ENC0_P0_3040 (ENC0_OFFSET + 0x040)
> > +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK 0xfff
> > +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_SHIFT 0
> > +
> > +#define MTK_DP_ENC0_P0_3044 (ENC0_OFFSET + 0x044)
> > +#define VIDEO_N_CODE_0_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3048 (ENC0_OFFSET + 0x048)
> > +#define VIDEO_N_CODE_1_DP_ENC0_P0_MASK 0xff
> > +
> > +#define MTK_DP_ENC0_P0_304C (ENC0_OFFSET + 0x04C)
> > +#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK 0x4
> > +#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK 0x100
> > +
> > +#define MTK_DP_ENC0_P0_3050 (ENC0_OFFSET + 0x050)
> > +#define VIDEO_N_CODE_MN_GEN_0_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3054 (ENC0_OFFSET + 0x054)
> > +#define VIDEO_N_CODE_MN_GEN_1_DP_ENC0_P0_MASK 0xff
> > +
> > +#define MTK_DP_ENC0_P0_3064 (ENC0_OFFSET + 0x064)
> > +#define HDE_NUM_LAST_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3088 (ENC0_OFFSET + 0x088)
> > +#define AU_EN_DP_ENC0_P0_MASK 0x40
> > +#define AU_EN_DP_ENC0_P0_SHIFT 6
> > +#define AUDIO_8CH_EN_DP_ENC0_P0_MASK 0x80
> > +#define AUDIO_8CH_SEL_DP_ENC0_P0_MASK 0x100
> > +#define AUDIO_2CH_EN_DP_ENC0_P0_MASK 0x4000
> > +#define AUDIO_2CH_SEL_DP_ENC0_P0_MASK 0x8000
> > +
> > +#define MTK_DP_ENC0_P0_308C (ENC0_OFFSET + 0x08C)
> > +#define CH_STATUS_0_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3090 (ENC0_OFFSET + 0x090)
> > +#define CH_STATUS_1_DP_ENC0_P0_MASK 0xffff
> > +
> > +#define MTK_DP_ENC0_P0_3094 (ENC0_OFFSET + 0x094)
> > +#define CH_STATUS_2_DP_ENC0_P0_MASK 0xff
> > +
> > +#define MTK_DP_ENC0_P0_30A0 (ENC0_OFFSET + 0x0A0)
> > +
> > +#define MTK_DP_ENC0_P0_30A4 (ENC0_OFFSET + 0x0A4)
> > +#define AU_TS_CFG_DP_ENC0_P0_MASK 0xff
> > +
> > +#define MTK_DP_ENC0_P0_30A8 (ENC0_OFFSET + 0x0A8)
> > +
> > +#define MTK_DP_ENC0_P0_30AC (ENC0_OFFSET + 0x0AC)
> > +
> > +#define MTK_DP_ENC0_P0_30B0 (ENC0_OFFSET + 0x0B0)
> > +
> > +#define MTK_DP_ENC0_P0_30B4 (ENC0_OFFSET + 0x0B4)
> > +#define ISRC_CFG_DP_ENC0_P0_MASK 0xff00
> > +#define ISRC_CFG_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC0_P0_30B8 (ENC0_OFFSET + 0x0B8)
> > +
> > +#define MTK_DP_ENC0_P0_30BC (ENC0_OFFSET + 0x0BC)
> > +#define ISRC_CONT_DP_ENC0_P0_MASK 0x1
> > +#define ISRC_CONT_DP_ENC0_P0_SHIFT 0
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK 0x700
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT 8
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_2                             \
> > +     (1 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_4                             \
> > +     (2 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_8                             \
> > +     (3 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2                             \
> > +     (5 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_4                             \
> > +     (6 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_8                             \
> > +     (7 << AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_SHIFT)
> > +
> > +#define MTK_DP_ENC0_P0_30D8 (ENC0_OFFSET + 0x0D8)
> > +
> > +#define MTK_DP_ENC0_P0_312C (ENC0_OFFSET + 0x12C)
> > +#define ASP_HB2_DP_ENC0_P0_MASK 0xff
> > +#define ASP_HB3_DP_ENC0_P0_MASK 0xff00
> > +#define ASP_HB3_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC0_P0_3130 (ENC0_OFFSET + 0x130)
> > +
> > +#define MTK_DP_ENC0_P0_3138 (ENC0_OFFSET + 0x138)
> > +
> > +#define MTK_DP_ENC0_P0_3154 (ENC0_OFFSET + 0x154)
> > +#define PGEN_HTOTAL_DP_ENC0_P0_MASK 0x3fff
> > +
> > +#define MTK_DP_ENC0_P0_3158 (ENC0_OFFSET + 0x158)
> > +#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK 0x3fff
> > +
> > +#define MTK_DP_ENC0_P0_315C (ENC0_OFFSET + 0x15C)
> > +#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x3fff
> > +
> > +#define MTK_DP_ENC0_P0_3160 (ENC0_OFFSET + 0x160)
> > +#define PGEN_HFDE_START_DP_ENC0_P0_MASK 0x3fff
> > +
> > +#define MTK_DP_ENC0_P0_3164 (ENC0_OFFSET + 0x164)
> > +#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x3fff
> > +
> > +#define MTK_DP_ENC0_P0_3168 (ENC0_OFFSET + 0x168)
> > +#define PGEN_VTOTAL_DP_ENC0_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_ENC0_P0_316C (ENC0_OFFSET + 0x16C)
> > +#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_ENC0_P0_3170 (ENC0_OFFSET + 0x170)
> > +#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_ENC0_P0_3174 (ENC0_OFFSET + 0x174)
> > +#define PGEN_VFDE_START_DP_ENC0_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_ENC0_P0_3178 (ENC0_OFFSET + 0x178)
> > +#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_ENC0_P0_31B0 (ENC0_OFFSET + 0x1B0)
> > +#define PGEN_PATTERN_SEL_SHIFT 4
> > +#define PGEN_PATTERN_SEL_MASK 0x0070
> > +
> > +#define MTK_DP_ENC0_P0_31C8 (ENC0_OFFSET + 0x1C8)
> > +#define VSC_EXT_VESA_HB0_DP_ENC0_P0_MASK 0xff
> > +#define VSC_EXT_VESA_HB1_DP_ENC0_P0_MASK 0xff00
> > +#define VSC_EXT_VESA_HB1_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC0_P0_31CC (ENC0_OFFSET + 0x1CC)
> > +#define VSC_EXT_VESA_HB2_DP_ENC0_P0_MASK 0xff
> > +#define VSC_EXT_VESA_HB2_DP_ENC0_P0_SHIFT 0
> > +#define VSC_EXT_VESA_HB3_DP_ENC0_P0_MASK 0xff00
> > +
> > +#define MTK_DP_ENC0_P0_31D0 (ENC0_OFFSET + 0x1D0)
> > +#define VSC_EXT_CEA_HB0_DP_ENC0_P0_MASK 0xff
> > +#define VSC_EXT_CEA_HB1_DP_ENC0_P0_MASK 0xff00
> > +#define VSC_EXT_CEA_HB1_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC0_P0_31D4 (ENC0_OFFSET + 0x1D4)
> > +#define VSC_EXT_CEA_HB2_DP_ENC0_P0_MASK 0xff
> > +#define VSC_EXT_CEA_HB2_DP_ENC0_P0_SHIFT 0
> > +#define VSC_EXT_CEA_HB3_DP_ENC0_P0_MASK 0xff00
> > +
> > +#define MTK_DP_ENC0_P0_31D8 (ENC0_OFFSET + 0x1D8)
> > +#define VSC_EXT_VESA_NUM_DP_ENC0_P0_MASK 0x3f
> > +#define VSC_EXT_VESA_NUM_DP_ENC0_P0_SHIFT 0
> > +#define VSC_EXT_CEA_NUM_DP_ENC0_P0_MASK 0x3f00
> > +#define VSC_EXT_CEA_NUM_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC0_P0_31DC (ENC0_OFFSET + 0x1DC)
> > +#define HDR0_CFG_DP_ENC0_P0_MASK 0xff
> > +#define HDR0_CFG_DP_ENC0_P0_SHIFT 0
> > +
> > +#define MTK_DP_ENC0_P0_31E8 (ENC0_OFFSET + 0x1E8)
> > +
> > +#define MTK_DP_ENC0_P0_31EC (ENC0_OFFSET + 0x1EC)
> > +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK 0x10
> > +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT 4
> > +#define ISRC1_HB3_DP_ENC0_P0_MASK 0xff00
> > +#define ISRC1_HB3_DP_ENC0_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC1_P0_3200 (ENC1_OFFSET + 0x000)
> > +
> > +#define MTK_DP_ENC1_P0_3280 (ENC1_OFFSET + 0x080)
> > +#define SDP_PACKET_TYPE_DP_ENC1_P0_MASK 0x1f
> > +#define SDP_PACKET_W_DP_ENC1_P0 0x20
> > +#define SDP_PACKET_W_DP_ENC1_P0_MASK 0x20
> > +#define SDP_PACKET_W_DP_ENC1_P0_SHIFT 5
> > +
> > +#define MTK_DP_ENC1_P0_328C (ENC1_OFFSET + 0x08C)
> > +
> > +#define MTK_DP_ENC1_P0_3290 (ENC1_OFFSET + 0x090)
> > +
> > +#define MTK_DP_ENC1_P0_32A0 (ENC1_OFFSET + 0x0A0)
> > +
> > +#define MTK_DP_ENC1_P0_32A4 (ENC1_OFFSET + 0x0A4)
> > +
> > +#define MTK_DP_ENC1_P0_3300 (ENC1_OFFSET + 0x100)
> > +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK 0x300
> > +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_SHIFT 8
> > +
> > +#define MTK_DP_ENC1_P0_3304 (ENC1_OFFSET + 0x104)
> > +#define AU_PRTY_REGEN_DP_ENC1_P0_MASK 0x100
> > +#define AU_CH_STS_REGEN_DP_ENC1_P0_MASK 0x200
> > +#define AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK 0x1000
> > +
> > +#define MTK_DP_ENC1_P0_3324 (ENC1_OFFSET + 0x124)
> > +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK 0x300
> > +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT 8
> > +#define AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX                                       \
> > +     (0 << AUDIO_SOURCE_MUX_DP_ENC1_P0_SHIFT)
> > +
> > +#define MTK_DP_ENC1_P0_3364 (ENC1_OFFSET + 0x164)
> > +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK 0xfff
> > +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_SHIFT 0
> > +#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK 0xf000
> > +#define FIFO_READ_START_POINT_DP_ENC1_P0_SHIFT 12
> > +
> > +#define MTK_DP_ENC1_P0_3368 (ENC1_OFFSET + 0x168)
> > +#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0_SHIFT 0
> > +#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0_SHIFT 4
> > +#define SDP_DP13_EN_DP_ENC1_P0_SHIFT 8
> > +#define BS2BS_MODE_DP_ENC1_P0_MASK 0x3000
> > +#define BS2BS_MODE_DP_ENC1_P0_SHIFT 12
> > +
> > +#define MTK_DP_ENC1_P0_33F4 (ENC1_OFFSET + 0x1F4)
> > +
> > +#define MTK_DP_TRANS_P0_3400 (TRANS_OFFSET + 0x000)
> > +#define PATTERN1_EN_DP_TRANS_P0_MASK 0x1000
> > +#define PATTERN1_EN_DP_TRANS_P0_SHIFT 12
> > +#define PATTERN2_EN_DP_TRANS_P0_MASK 0x2000
> > +#define PATTERN3_EN_DP_TRANS_P0_MASK 0x4000
> > +#define PATTERN4_EN_DP_TRANS_P0_MASK 0x8000
> > +
> > +#define MTK_DP_TRANS_P0_3404 (TRANS_OFFSET + 0x004)
> > +#define DP_SCR_EN_DP_TRANS_P0_MASK 0x1
> > +
> > +#define MTK_DP_TRANS_P0_340C (TRANS_OFFSET + 0x00C)
> > +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_MASK 0x2000
> > +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0_SHIFT 13
> > +
> > +#define MTK_DP_TRANS_P0_3410 (TRANS_OFFSET + 0x010)
> > +#define HPD_DEB_THD_DP_TRANS_P0_MASK 0xf
> > +#define HPD_DEB_THD_DP_TRANS_P0_SHIFT 0
> > +#define HPD_INT_THD_DP_TRANS_P0_MASK 0xf0
> > +#define HPD_INT_THD_DP_TRANS_P0_SHIFT 4
> > +#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US (2 << HPD_INT_THD_DP_TRANS_P0_SHIFT)
> > +#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US                                   \
> > +     (2 << (HPD_INT_THD_DP_TRANS_P0_SHIFT + 2))
> > +#define HPD_DISC_THD_DP_TRANS_P0_MASK 0xf00
> > +#define HPD_DISC_THD_DP_TRANS_P0_SHIFT 8
> > +#define HPD_CONN_THD_DP_TRANS_P0_MASK 0xf000
> > +#define HPD_CONN_THD_DP_TRANS_P0_SHIFT 12
> > +
> > +#define MTK_DP_TRANS_P0_3414 (TRANS_OFFSET + 0x014)
> > +#define HPD_DB_DP_TRANS_P0_MASK 0x4
> > +
> > +#define MTK_DP_TRANS_P0_3418 (TRANS_OFFSET + 0x018)
> > +#define IRQ_CLR_DP_TRANS_P0_MASK 0xf
> > +#define IRQ_MASK_DP_TRANS_P0_MASK 0xf0
> > +#define IRQ_MASK_DP_TRANS_P0_SHIFT 4
> > +#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ (BIT(1) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> > +#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ (BIT(2) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> > +#define IRQ_MASK_DP_TRANS_P0_INT_IRQ (BIT(3) << IRQ_MASK_DP_TRANS_P0_SHIFT)
> > +#define IRQ_STATUS_DP_TRANS_P0_MASK 0xf000
> > +#define IRQ_STATUS_DP_TRANS_P0_SHIFT 12
> > +
> > +#define MTK_DP_TRANS_P0_342C (TRANS_OFFSET + 0x02C)
> > +#define XTAL_FREQ_DP_TRANS_P0_DEFAULT 0x69
> > +#define XTAL_FREQ_DP_TRANS_P0_MASK 0xff
> > +
> > +#define MTK_DP_TRANS_P0_3430 (TRANS_OFFSET + 0x030)
> > +#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK 0x3
> > +#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT BIT(1)
> > +
> > +#define MTK_DP_TRANS_P0_34A4 (TRANS_OFFSET + 0x0A4)
> > +#define LANE_NUM_DP_TRANS_P0_MASK 0xc
> > +#define LANE_NUM_DP_TRANS_P0_SHIFT 2
> > +
> > +#define MTK_DP_TRANS_P0_3540 (TRANS_OFFSET + 0x140)
> > +#define FEC_EN_DP_TRANS_P0_MASK 0x1
> > +#define FEC_EN_DP_TRANS_P0_SHIFT 0
> > +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_MASK 0x8
> > +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0_SHIFT 3
> > +
> > +#define MTK_DP_TRANS_P0_3580 (TRANS_OFFSET + 0x180)
> > +#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK 0x100
> > +#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK 0x200
> > +#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK 0x400
> > +#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK 0x800
> > +
> > +#define MTK_DP_TRANS_P0_35C4 (TRANS_OFFSET + 0x1C4)
> > +#define SW_IRQ_MASK_DP_TRANS_P0_MASK 0xffff
> > +
> > +#define MTK_DP_TRANS_P0_35C8 (TRANS_OFFSET + 0x1C8)
> > +#define SW_IRQ_CLR_DP_TRANS_P0_MASK 0xffff
> > +
> > +#define SW_IRQ_STATUS_DP_TRANS_P0_MASK 0xffff
> > +#define SW_IRQ_STATUS_DP_TRANS_P0_SHIFT 0
> > +
> > +#define MTK_DP_TRANS_P0_35D0 (TRANS_OFFSET + 0x1D0)
> > +#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK 0xffff
> > +
> > +#define MTK_DP_TRANS_P0_35F0 (TRANS_OFFSET + 0x1F0)
> > +
> > +#define MTK_DP_AUX_P0_360C (AUX_OFFSET + 0x00C)
> > +#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK 0x1fff
> > +
> > +#define MTK_DP_AUX_P0_3614 (AUX_OFFSET + 0x014)
> > +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK 0x7f
> > +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_SHIFT 0
> > +
> > +#define MTK_DP_AUX_P0_3618 (AUX_OFFSET + 0x018)
> > +#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK 0x200
> > +#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK 0xf
> > +
> > +#define MTK_DP_AUX_P0_3620 (AUX_OFFSET + 0x020)
> > +#define AUX_RD_MODE_AUX_TX_P0_MASK 0x200
> > +#define AUX_RX_FIFO_READ_PULSE_TX_P0_MASK 0x100
> > +#define AUX_RX_FIFO_R_PULSE_TX_P0_SHIFT 8
> > +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK 0xff
> > +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_SHIFT 0
> > +
> > +#define MTK_DP_AUX_P0_3624 (AUX_OFFSET + 0x024)
> > +#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK 0xf
> > +
> > +#define MTK_DP_AUX_P0_3628 (AUX_OFFSET + 0x028)
> > +#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK 0x3ff
> > +#define AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT 0
> > +#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE                                     \
> > +     (BIT(0) << AUX_RX_PHY_STATE_AUX_TX_P0_SHIFT)
> > +
> > +#define MTK_DP_AUX_P0_362C (AUX_OFFSET + 0x02C)
> > +#define AUX_NO_LENGTH_AUX_TX_P0_MASK 0x1
> > +#define AUX_NO_LENGTH_AUX_TX_P0_SHIFT 0
> > +#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK 0x2
> > +#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK 0xfffc
> > +
> > +#define MTK_DP_AUX_P0_3630 (AUX_OFFSET + 0x030)
> > +#define AUX_TX_REQUEST_READY_AUX_TX_P0_MASK 0x8
> > +#define AUX_TX_REQUEST_READY_AUX_TX_P0_SHIFT 3
> > +
> > +#define MTK_DP_AUX_P0_3634 (AUX_OFFSET + 0x034)
> > +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK 0xff00
> > +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_SHIFT 8
> > +
> > +#define MTK_DP_AUX_P0_3640 (AUX_OFFSET + 0x040)
> > +#define AUX_RX_RECV_COMPLETE_IRQ_TX_P0_MASK 0x40
> > +#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 6
> > +#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 5
> > +#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0_SHIFT 4
> > +#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0_SHIFT 3
> > +#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0_SHIFT 2
> > +#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0_SHIFT 1
> > +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_MASK 0x1
> > +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0_SHIFT 0
> > +
> > +#define MTK_DP_AUX_P0_3644 (AUX_OFFSET + 0x044)
> > +#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK 0xf
> > +
> > +#define MTK_DP_AUX_P0_3648 (AUX_OFFSET + 0x048)
> > +#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK 0xffff
> > +
> > +#define MTK_DP_AUX_P0_364C (AUX_OFFSET + 0x04C)
> > +#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK 0xf
> > +
> > +#define MTK_DP_AUX_P0_3650 (AUX_OFFSET + 0x050)
> > +#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK 0xf000
> > +#define MCU_REQ_DATA_NUM_AUX_TX_P0_SHIFT 12
> > +#define PHY_FIFO_RST_AUX_TX_P0_MASK 0x200
> > +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_MASK 0x100
> > +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0_SHIFT 8
> > +
> > +#define MTK_DP_AUX_P0_3658 (AUX_OFFSET + 0x058)
> > +#define AUX_TX_OV_EN_AUX_TX_P0_MASK 0x1
> > +
> > +#define MTK_DP_AUX_P0_3690 (AUX_OFFSET + 0x090)
> > +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_MASK 0x100
> > +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0_SHIFT 8
> > +
> > +#define MTK_DP_AUX_P0_3704 (AUX_OFFSET + 0x104)
> > +#define AUX_TX_FIFO_WRITE_DATA_NEW_MODE_TOGGLE_AUX_TX_P0_MASK 0x2
> > +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_MASK 0x4
> > +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0_SHIFT 2
> > +
> > +#define MTK_DP_AUX_P0_3708 (AUX_OFFSET + 0x108)
> > +
> > +#define MTK_DP_AUX_P0_37C8 (AUX_OFFSET + 0x1C8)
> > +#define MTK_ATOP_EN_AUX_TX_P0_MASK 0x1
> > +#define MTK_ATOP_EN_AUX_TX_P0_SHIFT 0
> > +
> > +#define MTK_DP_TOP_PWR_STATE (TOP_OFFSET + 0x00)
> > +#define DP_PWR_STATE_MASK 0x3
> > +#define DP_PWR_STATE_BANDGAP 1
> > +#define DP_PWR_STATE_BANDGAP_TPLL 2
> > +#define DP_PWR_STATE_BANDGAP_TPLL_LANE 3
> > +
> > +#define MTK_DP_TOP_SWING_EMP (TOP_OFFSET + 0x04)
> > +#define DP_TX0_VOLT_SWING_MASK 0x3
> > +#define DP_TX0_VOLT_SWING_SHIFT 0
> > +#define DP_TX0_PRE_EMPH_MASK 0xc
> > +#define DP_TX0_PRE_EMPH_SHIFT 2
> > +#define DP_TX1_VOLT_SWING_MASK 0x300
> > +#define DP_TX1_VOLT_SWING_SHIFT 8
> > +#define DP_TX1_PRE_EMPH_MASK 0xc00
> > +#define DP_TX2_VOLT_SWING_MASK 0x30000
> > +#define DP_TX2_PRE_EMPH_MASK 0xc0000
> > +#define DP_TX3_VOLT_SWING_MASK 0x3000000
> > +#define DP_TX3_PRE_EMPH_MASK 0xc000000
> > +
> > +#define MTK_DP_TOP_RESET_AND_PROBE (TOP_OFFSET + 0x20)
> > +#define SW_RST_B_SHIFT 0
> > +#define SW_RST_B_PHYD (BIT(4) << SW_RST_B_SHIFT)
> > +
> > +#define MTK_DP_TOP_IRQ_STATUS (TOP_OFFSET + 0x28)
> > +#define RGS_IRQ_STATUS_SHIFT 0
> > +#define RGS_IRQ_STATUS_TRANSMITTER (BIT(1) << RGS_IRQ_STATUS_SHIFT)
> > +
> > +#define MTK_DP_TOP_IRQ_MASK (TOP_OFFSET + 0x2C)
> > +#define IRQ_MASK_AUX_TOP_IRQ BIT(2)
> > +
> > +#define MTK_DP_TOP_MEM_PD (TOP_OFFSET + 0x38)
> > +#define MEM_ISO_EN_SHIFT 0
> > +#define FUSE_SEL_SHIFT 2
> > +
> > +#endif /*_MTK_DP_REG_H_*/
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > index 384074f69111b..e6e88e3cd811d 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> >  };
> >
> > +enum TVDPLL_CLK {
> > +     TVDPLL_PLL = 0,
> > +     TVDPLL_D2 = 2,
> > +     TVDPLL_D4 = 4,
> > +     TVDPLL_D8 = 8,
> > +     TVDPLL_D16 = 16,
> > +};
> > +
> >  struct mtk_dpi {
> >       struct drm_encoder encoder;
> >       struct drm_bridge bridge;
> > @@ -71,8 +79,10 @@ struct mtk_dpi {
> >       void __iomem *regs;
> >       struct device *dev;
> >       struct clk *engine_clk;
> > +     struct clk *dpi_ck_cg;
> >       struct clk *pixel_clk;
> >       struct clk *tvd_clk;
> > +     struct clk *pclk_src[5];
> >       int irq;
> >       struct drm_display_mode mode;
> >       const struct mtk_dpi_conf *conf;
> > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> >       u32 hvsize_mask;
> >       u32 channel_swap_shift;
> >       u32 yuv422_en_bit;
> > +     u32 csc_enable_bit;
> >       const struct mtk_dpi_yc_limit *limit;
> >  };
> >
> > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> >
> >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> >  {
> > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> >  }
> >
> >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> >  }
> >
> > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > +{
> > +     u32 matrix_sel = 0;
> > +
> > +     switch (format) {
> > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > +             if (dpi->mode.hdisplay <= 720)
> > +                     matrix_sel = 0x2;
> > +             break;
> > +     default:
> > +             break;
> > +     }
> > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > +}
> > +
> >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> >                                       enum mtk_dpi_out_color_format format)
> >  {
> >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> >               mtk_dpi_config_yuv422_enable(dpi, false);
> > -             if (dpi->conf->csc_support)
> > +             if (dpi->conf->csc_support) {
> >                       mtk_dpi_config_csc_enable(dpi, true);
> > +                     mtk_dpi_matrix_sel(dpi, format);
> > +             }
> >               if (dpi->conf->swap_input_support)
> >                       mtk_dpi_config_swap_input(dpi, false);
> >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> >               mtk_dpi_config_yuv422_enable(dpi, true);
> > -             if (dpi->conf->csc_support)
> > +             if (dpi->conf->csc_support) {
> >                       mtk_dpi_config_csc_enable(dpi, true);
> > +                     mtk_dpi_matrix_sel(dpi, format);
> > +             }
> >               if (dpi->conf->swap_input_support)
> >                       mtk_dpi_config_swap_input(dpi, true);
> >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> >       mtk_dpi_disable(dpi);
> >       clk_disable_unprepare(dpi->pixel_clk);
> >       clk_disable_unprepare(dpi->engine_clk);
> > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > +     clk_disable_unprepare(dpi->tvd_clk);
> >  }
> >
> >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> >       if (++dpi->refcount != 1)
> >               return 0;
> >
> > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > +     if (ret) {
> > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > +             goto err_pixel;
> > +     }
> > +
> >       ret = clk_prepare_enable(dpi->engine_clk);
> >       if (ret) {
> >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> >               goto err_refcount;
> >       }
> >
> > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > +     if (ret) {
> > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > +             goto err_ck_cg;
> > +     }
> > +
> >       ret = clk_prepare_enable(dpi->pixel_clk);
> >       if (ret) {
> >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> >       if (dpi->pinctrl && dpi->pins_dpi)
> >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> >
> > -     mtk_dpi_enable(dpi);
> >       return 0;
> >
> >  err_pixel:
> > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > +err_ck_cg:
> >       clk_disable_unprepare(dpi->engine_clk);
> >  err_refcount:
> >       dpi->refcount--;
> > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> >       pll_rate = clk_get_rate(dpi->tvd_clk);
> >
> >       vm.pixelclock = pll_rate / factor;
> > -     if (dpi->conf->is_dpintf)
> > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > +     if (dpi->conf->is_dpintf) {
> > +             if (factor == 1)
> > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > +             else if (factor == 2)
> > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > +             else if (factor == 4)
> > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > +             else
> > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> >       else
> > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> >       }
> >       mtk_dpi_sw_reset(dpi, false);
> >
> > +     mtk_dpi_enable(dpi);
> > +
> >       return 0;
> >  }
> >
> > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> >       u32 *input_fmts;
> >
> >       *num_input_fmts = 0;
> > -
> >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> >                            GFP_KERNEL);
> >       if (!input_fmts)
> > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> >               if (dpi->conf->num_output_fmts)
> >                       out_bus_format = dpi->conf->output_fmts[0];
> >
> > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> >               bridge_state->input_bus_cfg.format,
> >               bridge_state->output_bus_cfg.format);
> >
> > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > +     else
> > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> >
> >       return 0;
> >  }
> > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> >  };
> >
> > +static const u32 mt8195_output_fmts[] = {
> > +     MEDIA_BUS_FMT_RGB888_1X24,
> > +     MEDIA_BUS_FMT_YUV8_1X24,
> > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > +};
> > +
> >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> >       .c_bottom = 0x0010,
> >       .c_top = 0x0FE0,
> > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> >       .hvsize_mask = HSIZE_MASK,
> >       .channel_swap_shift = CH_SWAP,
> >       .yuv422_en_bit = YUV422_EN,
> > +     .csc_enable_bit = CSC_ENABLE,
> >       .limit = &mtk_dpi_limit,
> >  };
> >
> > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> >       .hvsize_mask = HSIZE_MASK,
> >       .channel_swap_shift = CH_SWAP,
> >       .yuv422_en_bit = YUV422_EN,
> > +     .csc_enable_bit = CSC_ENABLE,
> >       .limit = &mtk_dpi_limit,
> >  };
> >
> > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> >       .hvsize_mask = HSIZE_MASK,
> >       .channel_swap_shift = CH_SWAP,
> >       .yuv422_en_bit = YUV422_EN,
> > +     .csc_enable_bit = CSC_ENABLE,
> >       .limit = &mtk_dpi_limit,
> >  };
> >
> > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> >       .hvsize_mask = HSIZE_MASK,
> >       .channel_swap_shift = CH_SWAP,
> >       .yuv422_en_bit = YUV422_EN,
> > +     .csc_enable_bit = CSC_ENABLE,
> >       .limit = &mtk_dpi_limit,
> >  };
> >
> >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> >       .cal_factor = mt8195_dpintf_calculate_factor,
> > -     .output_fmts = mt8173_output_fmts,
> > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > +     .output_fmts = mt8195_output_fmts,
> > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> >       .is_dpintf = true,
> > +     .csc_support = true,
> >       .dimension_mask = DPINTF_HPW_MASK,
> >       .hvsize_mask = DPINTF_HSIZE_MASK,
> >       .channel_swap_shift = DPINTF_CH_SWAP,
> >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> >       .limit = &mtk_dpintf_limit,
> >  };
> >
> > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> >               return ret;
> >       }
> >
> > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > +             if (ret != -EPROBE_DEFER)
> > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > +                             ret);
> > +
> > +             return ret;
> > +     }
> > +
> >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> >       if (IS_ERR(dpi->pixel_clk)) {
> >               ret = PTR_ERR(dpi->pixel_clk);
> > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> >               return ret;
> >       }
> >
> > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > +
> >       dpi->irq = platform_get_irq(pdev, 0);
> >       if (dpi->irq <= 0)
> >               return -EINVAL;
>
> All those changes look unrelated as well?
>
Do you mean all the changes in mtk_dpi.c ?
They are in support of enabling the mt8195 dpintf driver... so, not
sure they are
unlreated?

> Maxime

Thx,
Guillaume.

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-11-15 14:33       ` Guillaume Ranquet
  (?)
  (?)
@ 2021-11-16 14:51         ` Maxime Ripard
  -1 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-11-16 14:51 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Chun-Kuang Hu, David Airlie, Thomas Zimmermann, linux-kernel,
	dri-devel, Markus Schneider-Pargmann, linux-mediatek,
	Matthias Brugger, linux-arm-kernel, kernel test robot

[-- Attachment #1: Type: text/plain, Size: 16483 bytes --]

Hi,

On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > The driver creates a child device for the phy. The child device will
> > > never exist without the parent being active. As they are sharing a
> > > register range, the parent passes a regmap pointer to the child so that
> > > both can work with the same register range. The phy driver sets device
> > > data that is read by the parent to get the phy device that can be used
> > > to control the phy properties.
> >
> > If the PHY is in the same register space than the DP controller, why do
> > you need a separate PHY driver in the first place?
>
> This has been asked by Chun-Kuang Hu in a previous revision of the series:
> 
> https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/

It's a bit of a circular argument though :)

It's a separate phy driver because it needs to go through another
maintainer's tree, but it needs to go through another maintainer's tree
because it's a separate phy driver.

It doesn't explain why it needs to be a separate phy driver? Why can't
the phy setup be done directly in the DP driver, if it's essentially a
single device?

That being said, usually what those kind of questions mean is that
you're missing a comment or something in the commit log to provide that
context in the first place, so it would be great to add that context
here.

And it will avoid the situation we're now in where multiple reviewers
ask the same questions over and over again :)

> > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > +                                     struct drm_bridge_state *old_state)
> > > +{
> > > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +     struct drm_connector *conn;
> > > +     struct drm_connector_state *conn_state;
> > > +     struct drm_crtc *crtc;
> > > +     struct drm_crtc_state *crtc_state;
> > > +     int ret = 0;
> > > +     int i;
> > > +
> > > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > +                                                     bridge->encoder);
> > > +     if (!conn) {
> > > +             drm_err(mtk_dp->drm_dev,
> > > +                     "Can't enable bridge as connector is missing\n");
> > > +             return;
> > > +     }
> > > +
> > > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> >
> > This should be protected by a mutex (just like any resource shared
> > between KMS and ALSA)
>
> Ok.

I forgot to ask (even though checkpatch does mention it iirc), but since
you have multiple mutex it would be nice to have a comment for each
mutex stating exactly what it protects, and against what.

It's hard otherwise to remember or figure out what the locks are here
for.

> > > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > > +     if (ret)
> > > +             return ret;
> >
> > pdata?
> >
> can you elaborate?

Sorry, yeah, pdata is usually the abbreviation used in linux for the
platform_data mechanism, but you're using the DT to retrieve your
resources (and platform_data usually don't involve any parsing), so the
name is odd.

> > > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > index 384074f69111b..e6e88e3cd811d 100644
> > > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> > >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> > >  };
> > >
> > > +enum TVDPLL_CLK {
> > > +     TVDPLL_PLL = 0,
> > > +     TVDPLL_D2 = 2,
> > > +     TVDPLL_D4 = 4,
> > > +     TVDPLL_D8 = 8,
> > > +     TVDPLL_D16 = 16,
> > > +};
> > > +
> > >  struct mtk_dpi {
> > >       struct drm_encoder encoder;
> > >       struct drm_bridge bridge;
> > > @@ -71,8 +79,10 @@ struct mtk_dpi {
> > >       void __iomem *regs;
> > >       struct device *dev;
> > >       struct clk *engine_clk;
> > > +     struct clk *dpi_ck_cg;
> > >       struct clk *pixel_clk;
> > >       struct clk *tvd_clk;
> > > +     struct clk *pclk_src[5];
> > >       int irq;
> > >       struct drm_display_mode mode;
> > >       const struct mtk_dpi_conf *conf;
> > > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> > >       u32 hvsize_mask;
> > >       u32 channel_swap_shift;
> > >       u32 yuv422_en_bit;
> > > +     u32 csc_enable_bit;
> > >       const struct mtk_dpi_yc_limit *limit;
> > >  };
> > >
> > > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> > >
> > >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> > >  {
> > > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> > >  }
> > >
> > >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> > >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> > >  }
> > >
> > > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > > +{
> > > +     u32 matrix_sel = 0;
> > > +
> > > +     switch (format) {
> > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > > +             if (dpi->mode.hdisplay <= 720)
> > > +                     matrix_sel = 0x2;
> > > +             break;
> > > +     default:
> > > +             break;
> > > +     }
> > > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > > +}
> > > +
> > >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> > >                                       enum mtk_dpi_out_color_format format)
> > >  {
> > >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> > >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> > >               mtk_dpi_config_yuv422_enable(dpi, false);
> > > -             if (dpi->conf->csc_support)
> > > +             if (dpi->conf->csc_support) {
> > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > +             }
> > >               if (dpi->conf->swap_input_support)
> > >                       mtk_dpi_config_swap_input(dpi, false);
> > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> > >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> > >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> > >               mtk_dpi_config_yuv422_enable(dpi, true);
> > > -             if (dpi->conf->csc_support)
> > > +             if (dpi->conf->csc_support) {
> > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > +             }
> > >               if (dpi->conf->swap_input_support)
> > >                       mtk_dpi_config_swap_input(dpi, true);
> > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> > >       mtk_dpi_disable(dpi);
> > >       clk_disable_unprepare(dpi->pixel_clk);
> > >       clk_disable_unprepare(dpi->engine_clk);
> > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > +     clk_disable_unprepare(dpi->tvd_clk);
> > >  }
> > >
> > >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > >       if (++dpi->refcount != 1)
> > >               return 0;
> > >
> > > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > > +     if (ret) {
> > > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > > +             goto err_pixel;
> > > +     }
> > > +
> > >       ret = clk_prepare_enable(dpi->engine_clk);
> > >       if (ret) {
> > >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> > >               goto err_refcount;
> > >       }
> > >
> > > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > > +     if (ret) {
> > > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > > +             goto err_ck_cg;
> > > +     }
> > > +
> > >       ret = clk_prepare_enable(dpi->pixel_clk);
> > >       if (ret) {
> > >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > >       if (dpi->pinctrl && dpi->pins_dpi)
> > >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> > >
> > > -     mtk_dpi_enable(dpi);
> > >       return 0;
> > >
> > >  err_pixel:
> > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > +err_ck_cg:
> > >       clk_disable_unprepare(dpi->engine_clk);
> > >  err_refcount:
> > >       dpi->refcount--;
> > > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > >       pll_rate = clk_get_rate(dpi->tvd_clk);
> > >
> > >       vm.pixelclock = pll_rate / factor;
> > > -     if (dpi->conf->is_dpintf)
> > > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > +     if (dpi->conf->is_dpintf) {
> > > +             if (factor == 1)
> > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > +             else if (factor == 2)
> > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > > +             else if (factor == 4)
> > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > > +             else
> > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> > >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> > >       else
> > > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > >       }
> > >       mtk_dpi_sw_reset(dpi, false);
> > >
> > > +     mtk_dpi_enable(dpi);
> > > +
> > >       return 0;
> > >  }
> > >
> > > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> > >       u32 *input_fmts;
> > >
> > >       *num_input_fmts = 0;
> > > -
> > >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> > >                            GFP_KERNEL);
> > >       if (!input_fmts)
> > > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > >               if (dpi->conf->num_output_fmts)
> > >                       out_bus_format = dpi->conf->output_fmts[0];
> > >
> > > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > >               bridge_state->input_bus_cfg.format,
> > >               bridge_state->output_bus_cfg.format);
> > >
> > > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> > >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> > >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > > +     else
> > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > >
> > >       return 0;
> > >  }
> > > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> > >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> > >  };
> > >
> > > +static const u32 mt8195_output_fmts[] = {
> > > +     MEDIA_BUS_FMT_RGB888_1X24,
> > > +     MEDIA_BUS_FMT_YUV8_1X24,
> > > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > > +};
> > > +
> > >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> > >       .c_bottom = 0x0010,
> > >       .c_top = 0x0FE0,
> > > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> > >       .hvsize_mask = HSIZE_MASK,
> > >       .channel_swap_shift = CH_SWAP,
> > >       .yuv422_en_bit = YUV422_EN,
> > > +     .csc_enable_bit = CSC_ENABLE,
> > >       .limit = &mtk_dpi_limit,
> > >  };
> > >
> > > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> > >       .hvsize_mask = HSIZE_MASK,
> > >       .channel_swap_shift = CH_SWAP,
> > >       .yuv422_en_bit = YUV422_EN,
> > > +     .csc_enable_bit = CSC_ENABLE,
> > >       .limit = &mtk_dpi_limit,
> > >  };
> > >
> > > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> > >       .hvsize_mask = HSIZE_MASK,
> > >       .channel_swap_shift = CH_SWAP,
> > >       .yuv422_en_bit = YUV422_EN,
> > > +     .csc_enable_bit = CSC_ENABLE,
> > >       .limit = &mtk_dpi_limit,
> > >  };
> > >
> > > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> > >       .hvsize_mask = HSIZE_MASK,
> > >       .channel_swap_shift = CH_SWAP,
> > >       .yuv422_en_bit = YUV422_EN,
> > > +     .csc_enable_bit = CSC_ENABLE,
> > >       .limit = &mtk_dpi_limit,
> > >  };
> > >
> > >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> > >       .cal_factor = mt8195_dpintf_calculate_factor,
> > > -     .output_fmts = mt8173_output_fmts,
> > > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > > +     .output_fmts = mt8195_output_fmts,
> > > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> > >       .is_dpintf = true,
> > > +     .csc_support = true,
> > >       .dimension_mask = DPINTF_HPW_MASK,
> > >       .hvsize_mask = DPINTF_HSIZE_MASK,
> > >       .channel_swap_shift = DPINTF_CH_SWAP,
> > >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> > >       .limit = &mtk_dpintf_limit,
> > >  };
> > >
> > > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > >               return ret;
> > >       }
> > >
> > > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > > +             if (ret != -EPROBE_DEFER)
> > > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > > +                             ret);
> > > +
> > > +             return ret;
> > > +     }
> > > +
> > >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> > >       if (IS_ERR(dpi->pixel_clk)) {
> > >               ret = PTR_ERR(dpi->pixel_clk);
> > > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > >               return ret;
> > >       }
> > >
> > > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > > +
> > >       dpi->irq = platform_get_irq(pdev, 0);
> > >       if (dpi->irq <= 0)
> > >               return -EINVAL;
> >
> > All those changes look unrelated as well?
> >
> Do you mean all the changes in mtk_dpi.c ? They are in support of
> enabling the mt8195 dpintf driver... so, not sure they are unlreated?

I don't know that driver at all, so it's probably related if you say so
:)

The DPI interface seems to be a panel interface, so it's a bit weird to
me why you would need to change things in the panel interface while
claiming to support a DP output.

Especially since it looks like you have some support for CSC, and
support for additional output formats?

I guess the DPI interface feeds the DP that essentially acts as a RGB ->
DP bridge?

If so, then the part adding support for CSC and additional formats
should be split into a separate patch, and it should be mentioned in the
commit log

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-16 14:51         ` Maxime Ripard
  0 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-11-16 14:51 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 16483 bytes --]

Hi,

On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > The driver creates a child device for the phy. The child device will
> > > never exist without the parent being active. As they are sharing a
> > > register range, the parent passes a regmap pointer to the child so that
> > > both can work with the same register range. The phy driver sets device
> > > data that is read by the parent to get the phy device that can be used
> > > to control the phy properties.
> >
> > If the PHY is in the same register space than the DP controller, why do
> > you need a separate PHY driver in the first place?
>
> This has been asked by Chun-Kuang Hu in a previous revision of the series:
> 
> https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/

It's a bit of a circular argument though :)

It's a separate phy driver because it needs to go through another
maintainer's tree, but it needs to go through another maintainer's tree
because it's a separate phy driver.

It doesn't explain why it needs to be a separate phy driver? Why can't
the phy setup be done directly in the DP driver, if it's essentially a
single device?

That being said, usually what those kind of questions mean is that
you're missing a comment or something in the commit log to provide that
context in the first place, so it would be great to add that context
here.

And it will avoid the situation we're now in where multiple reviewers
ask the same questions over and over again :)

> > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > +                                     struct drm_bridge_state *old_state)
> > > +{
> > > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +     struct drm_connector *conn;
> > > +     struct drm_connector_state *conn_state;
> > > +     struct drm_crtc *crtc;
> > > +     struct drm_crtc_state *crtc_state;
> > > +     int ret = 0;
> > > +     int i;
> > > +
> > > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > +                                                     bridge->encoder);
> > > +     if (!conn) {
> > > +             drm_err(mtk_dp->drm_dev,
> > > +                     "Can't enable bridge as connector is missing\n");
> > > +             return;
> > > +     }
> > > +
> > > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> >
> > This should be protected by a mutex (just like any resource shared
> > between KMS and ALSA)
>
> Ok.

I forgot to ask (even though checkpatch does mention it iirc), but since
you have multiple mutex it would be nice to have a comment for each
mutex stating exactly what it protects, and against what.

It's hard otherwise to remember or figure out what the locks are here
for.

> > > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > > +     if (ret)
> > > +             return ret;
> >
> > pdata?
> >
> can you elaborate?

Sorry, yeah, pdata is usually the abbreviation used in linux for the
platform_data mechanism, but you're using the DT to retrieve your
resources (and platform_data usually don't involve any parsing), so the
name is odd.

> > > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > index 384074f69111b..e6e88e3cd811d 100644
> > > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> > >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> > >  };
> > >
> > > +enum TVDPLL_CLK {
> > > +     TVDPLL_PLL = 0,
> > > +     TVDPLL_D2 = 2,
> > > +     TVDPLL_D4 = 4,
> > > +     TVDPLL_D8 = 8,
> > > +     TVDPLL_D16 = 16,
> > > +};
> > > +
> > >  struct mtk_dpi {
> > >       struct drm_encoder encoder;
> > >       struct drm_bridge bridge;
> > > @@ -71,8 +79,10 @@ struct mtk_dpi {
> > >       void __iomem *regs;
> > >       struct device *dev;
> > >       struct clk *engine_clk;
> > > +     struct clk *dpi_ck_cg;
> > >       struct clk *pixel_clk;
> > >       struct clk *tvd_clk;
> > > +     struct clk *pclk_src[5];
> > >       int irq;
> > >       struct drm_display_mode mode;
> > >       const struct mtk_dpi_conf *conf;
> > > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> > >       u32 hvsize_mask;
> > >       u32 channel_swap_shift;
> > >       u32 yuv422_en_bit;
> > > +     u32 csc_enable_bit;
> > >       const struct mtk_dpi_yc_limit *limit;
> > >  };
> > >
> > > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> > >
> > >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> > >  {
> > > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> > >  }
> > >
> > >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> > >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> > >  }
> > >
> > > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > > +{
> > > +     u32 matrix_sel = 0;
> > > +
> > > +     switch (format) {
> > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > > +             if (dpi->mode.hdisplay <= 720)
> > > +                     matrix_sel = 0x2;
> > > +             break;
> > > +     default:
> > > +             break;
> > > +     }
> > > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > > +}
> > > +
> > >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> > >                                       enum mtk_dpi_out_color_format format)
> > >  {
> > >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> > >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> > >               mtk_dpi_config_yuv422_enable(dpi, false);
> > > -             if (dpi->conf->csc_support)
> > > +             if (dpi->conf->csc_support) {
> > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > +             }
> > >               if (dpi->conf->swap_input_support)
> > >                       mtk_dpi_config_swap_input(dpi, false);
> > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> > >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> > >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> > >               mtk_dpi_config_yuv422_enable(dpi, true);
> > > -             if (dpi->conf->csc_support)
> > > +             if (dpi->conf->csc_support) {
> > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > +             }
> > >               if (dpi->conf->swap_input_support)
> > >                       mtk_dpi_config_swap_input(dpi, true);
> > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> > >       mtk_dpi_disable(dpi);
> > >       clk_disable_unprepare(dpi->pixel_clk);
> > >       clk_disable_unprepare(dpi->engine_clk);
> > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > +     clk_disable_unprepare(dpi->tvd_clk);
> > >  }
> > >
> > >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > >       if (++dpi->refcount != 1)
> > >               return 0;
> > >
> > > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > > +     if (ret) {
> > > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > > +             goto err_pixel;
> > > +     }
> > > +
> > >       ret = clk_prepare_enable(dpi->engine_clk);
> > >       if (ret) {
> > >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> > >               goto err_refcount;
> > >       }
> > >
> > > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > > +     if (ret) {
> > > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > > +             goto err_ck_cg;
> > > +     }
> > > +
> > >       ret = clk_prepare_enable(dpi->pixel_clk);
> > >       if (ret) {
> > >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > >       if (dpi->pinctrl && dpi->pins_dpi)
> > >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> > >
> > > -     mtk_dpi_enable(dpi);
> > >       return 0;
> > >
> > >  err_pixel:
> > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > +err_ck_cg:
> > >       clk_disable_unprepare(dpi->engine_clk);
> > >  err_refcount:
> > >       dpi->refcount--;
> > > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > >       pll_rate = clk_get_rate(dpi->tvd_clk);
> > >
> > >       vm.pixelclock = pll_rate / factor;
> > > -     if (dpi->conf->is_dpintf)
> > > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > +     if (dpi->conf->is_dpintf) {
> > > +             if (factor == 1)
> > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > +             else if (factor == 2)
> > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > > +             else if (factor == 4)
> > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > > +             else
> > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> > >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> > >       else
> > > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > >       }
> > >       mtk_dpi_sw_reset(dpi, false);
> > >
> > > +     mtk_dpi_enable(dpi);
> > > +
> > >       return 0;
> > >  }
> > >
> > > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> > >       u32 *input_fmts;
> > >
> > >       *num_input_fmts = 0;
> > > -
> > >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> > >                            GFP_KERNEL);
> > >       if (!input_fmts)
> > > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > >               if (dpi->conf->num_output_fmts)
> > >                       out_bus_format = dpi->conf->output_fmts[0];
> > >
> > > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > >               bridge_state->input_bus_cfg.format,
> > >               bridge_state->output_bus_cfg.format);
> > >
> > > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> > >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> > >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > > +     else
> > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > >
> > >       return 0;
> > >  }
> > > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> > >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> > >  };
> > >
> > > +static const u32 mt8195_output_fmts[] = {
> > > +     MEDIA_BUS_FMT_RGB888_1X24,
> > > +     MEDIA_BUS_FMT_YUV8_1X24,
> > > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > > +};
> > > +
> > >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> > >       .c_bottom = 0x0010,
> > >       .c_top = 0x0FE0,
> > > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> > >       .hvsize_mask = HSIZE_MASK,
> > >       .channel_swap_shift = CH_SWAP,
> > >       .yuv422_en_bit = YUV422_EN,
> > > +     .csc_enable_bit = CSC_ENABLE,
> > >       .limit = &mtk_dpi_limit,
> > >  };
> > >
> > > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> > >       .hvsize_mask = HSIZE_MASK,
> > >       .channel_swap_shift = CH_SWAP,
> > >       .yuv422_en_bit = YUV422_EN,
> > > +     .csc_enable_bit = CSC_ENABLE,
> > >       .limit = &mtk_dpi_limit,
> > >  };
> > >
> > > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> > >       .hvsize_mask = HSIZE_MASK,
> > >       .channel_swap_shift = CH_SWAP,
> > >       .yuv422_en_bit = YUV422_EN,
> > > +     .csc_enable_bit = CSC_ENABLE,
> > >       .limit = &mtk_dpi_limit,
> > >  };
> > >
> > > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> > >       .hvsize_mask = HSIZE_MASK,
> > >       .channel_swap_shift = CH_SWAP,
> > >       .yuv422_en_bit = YUV422_EN,
> > > +     .csc_enable_bit = CSC_ENABLE,
> > >       .limit = &mtk_dpi_limit,
> > >  };
> > >
> > >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> > >       .cal_factor = mt8195_dpintf_calculate_factor,
> > > -     .output_fmts = mt8173_output_fmts,
> > > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > > +     .output_fmts = mt8195_output_fmts,
> > > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> > >       .is_dpintf = true,
> > > +     .csc_support = true,
> > >       .dimension_mask = DPINTF_HPW_MASK,
> > >       .hvsize_mask = DPINTF_HSIZE_MASK,
> > >       .channel_swap_shift = DPINTF_CH_SWAP,
> > >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> > >       .limit = &mtk_dpintf_limit,
> > >  };
> > >
> > > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > >               return ret;
> > >       }
> > >
> > > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > > +             if (ret != -EPROBE_DEFER)
> > > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > > +                             ret);
> > > +
> > > +             return ret;
> > > +     }
> > > +
> > >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> > >       if (IS_ERR(dpi->pixel_clk)) {
> > >               ret = PTR_ERR(dpi->pixel_clk);
> > > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > >               return ret;
> > >       }
> > >
> > > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > > +
> > >       dpi->irq = platform_get_irq(pdev, 0);
> > >       if (dpi->irq <= 0)
> > >               return -EINVAL;
> >
> > All those changes look unrelated as well?
> >
> Do you mean all the changes in mtk_dpi.c ? They are in support of
> enabling the mt8195 dpintf driver... so, not sure they are unlreated?

I don't know that driver at all, so it's probably related if you say so
:)

The DPI interface seems to be a panel interface, so it's a bit weird to
me why you would need to change things in the panel interface while
claiming to support a DP output.

Especially since it looks like you have some support for CSC, and
support for additional output formats?

I guess the DPI interface feeds the DP that essentially acts as a RGB ->
DP bridge?

If so, then the part adding support for CSC and additional formats
should be split into a separate patch, and it should be mentioned in the
commit log

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-16 14:51         ` Maxime Ripard
  0 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-11-16 14:51 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 16483 bytes --]

Hi,

On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > The driver creates a child device for the phy. The child device will
> > > never exist without the parent being active. As they are sharing a
> > > register range, the parent passes a regmap pointer to the child so that
> > > both can work with the same register range. The phy driver sets device
> > > data that is read by the parent to get the phy device that can be used
> > > to control the phy properties.
> >
> > If the PHY is in the same register space than the DP controller, why do
> > you need a separate PHY driver in the first place?
>
> This has been asked by Chun-Kuang Hu in a previous revision of the series:
> 
> https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/

It's a bit of a circular argument though :)

It's a separate phy driver because it needs to go through another
maintainer's tree, but it needs to go through another maintainer's tree
because it's a separate phy driver.

It doesn't explain why it needs to be a separate phy driver? Why can't
the phy setup be done directly in the DP driver, if it's essentially a
single device?

That being said, usually what those kind of questions mean is that
you're missing a comment or something in the commit log to provide that
context in the first place, so it would be great to add that context
here.

And it will avoid the situation we're now in where multiple reviewers
ask the same questions over and over again :)

> > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > +                                     struct drm_bridge_state *old_state)
> > > +{
> > > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +     struct drm_connector *conn;
> > > +     struct drm_connector_state *conn_state;
> > > +     struct drm_crtc *crtc;
> > > +     struct drm_crtc_state *crtc_state;
> > > +     int ret = 0;
> > > +     int i;
> > > +
> > > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > +                                                     bridge->encoder);
> > > +     if (!conn) {
> > > +             drm_err(mtk_dp->drm_dev,
> > > +                     "Can't enable bridge as connector is missing\n");
> > > +             return;
> > > +     }
> > > +
> > > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> >
> > This should be protected by a mutex (just like any resource shared
> > between KMS and ALSA)
>
> Ok.

I forgot to ask (even though checkpatch does mention it iirc), but since
you have multiple mutex it would be nice to have a comment for each
mutex stating exactly what it protects, and against what.

It's hard otherwise to remember or figure out what the locks are here
for.

> > > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > > +     if (ret)
> > > +             return ret;
> >
> > pdata?
> >
> can you elaborate?

Sorry, yeah, pdata is usually the abbreviation used in linux for the
platform_data mechanism, but you're using the DT to retrieve your
resources (and platform_data usually don't involve any parsing), so the
name is odd.

> > > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > index 384074f69111b..e6e88e3cd811d 100644
> > > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> > >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> > >  };
> > >
> > > +enum TVDPLL_CLK {
> > > +     TVDPLL_PLL = 0,
> > > +     TVDPLL_D2 = 2,
> > > +     TVDPLL_D4 = 4,
> > > +     TVDPLL_D8 = 8,
> > > +     TVDPLL_D16 = 16,
> > > +};
> > > +
> > >  struct mtk_dpi {
> > >       struct drm_encoder encoder;
> > >       struct drm_bridge bridge;
> > > @@ -71,8 +79,10 @@ struct mtk_dpi {
> > >       void __iomem *regs;
> > >       struct device *dev;
> > >       struct clk *engine_clk;
> > > +     struct clk *dpi_ck_cg;
> > >       struct clk *pixel_clk;
> > >       struct clk *tvd_clk;
> > > +     struct clk *pclk_src[5];
> > >       int irq;
> > >       struct drm_display_mode mode;
> > >       const struct mtk_dpi_conf *conf;
> > > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> > >       u32 hvsize_mask;
> > >       u32 channel_swap_shift;
> > >       u32 yuv422_en_bit;
> > > +     u32 csc_enable_bit;
> > >       const struct mtk_dpi_yc_limit *limit;
> > >  };
> > >
> > > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> > >
> > >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> > >  {
> > > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> > >  }
> > >
> > >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> > >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> > >  }
> > >
> > > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > > +{
> > > +     u32 matrix_sel = 0;
> > > +
> > > +     switch (format) {
> > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > > +             if (dpi->mode.hdisplay <= 720)
> > > +                     matrix_sel = 0x2;
> > > +             break;
> > > +     default:
> > > +             break;
> > > +     }
> > > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > > +}
> > > +
> > >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> > >                                       enum mtk_dpi_out_color_format format)
> > >  {
> > >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> > >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> > >               mtk_dpi_config_yuv422_enable(dpi, false);
> > > -             if (dpi->conf->csc_support)
> > > +             if (dpi->conf->csc_support) {
> > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > +             }
> > >               if (dpi->conf->swap_input_support)
> > >                       mtk_dpi_config_swap_input(dpi, false);
> > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> > >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> > >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> > >               mtk_dpi_config_yuv422_enable(dpi, true);
> > > -             if (dpi->conf->csc_support)
> > > +             if (dpi->conf->csc_support) {
> > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > +             }
> > >               if (dpi->conf->swap_input_support)
> > >                       mtk_dpi_config_swap_input(dpi, true);
> > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> > >       mtk_dpi_disable(dpi);
> > >       clk_disable_unprepare(dpi->pixel_clk);
> > >       clk_disable_unprepare(dpi->engine_clk);
> > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > +     clk_disable_unprepare(dpi->tvd_clk);
> > >  }
> > >
> > >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > >       if (++dpi->refcount != 1)
> > >               return 0;
> > >
> > > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > > +     if (ret) {
> > > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > > +             goto err_pixel;
> > > +     }
> > > +
> > >       ret = clk_prepare_enable(dpi->engine_clk);
> > >       if (ret) {
> > >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> > >               goto err_refcount;
> > >       }
> > >
> > > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > > +     if (ret) {
> > > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > > +             goto err_ck_cg;
> > > +     }
> > > +
> > >       ret = clk_prepare_enable(dpi->pixel_clk);
> > >       if (ret) {
> > >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > >       if (dpi->pinctrl && dpi->pins_dpi)
> > >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> > >
> > > -     mtk_dpi_enable(dpi);
> > >       return 0;
> > >
> > >  err_pixel:
> > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > +err_ck_cg:
> > >       clk_disable_unprepare(dpi->engine_clk);
> > >  err_refcount:
> > >       dpi->refcount--;
> > > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > >       pll_rate = clk_get_rate(dpi->tvd_clk);
> > >
> > >       vm.pixelclock = pll_rate / factor;
> > > -     if (dpi->conf->is_dpintf)
> > > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > +     if (dpi->conf->is_dpintf) {
> > > +             if (factor == 1)
> > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > +             else if (factor == 2)
> > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > > +             else if (factor == 4)
> > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > > +             else
> > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> > >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> > >       else
> > > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > >       }
> > >       mtk_dpi_sw_reset(dpi, false);
> > >
> > > +     mtk_dpi_enable(dpi);
> > > +
> > >       return 0;
> > >  }
> > >
> > > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> > >       u32 *input_fmts;
> > >
> > >       *num_input_fmts = 0;
> > > -
> > >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> > >                            GFP_KERNEL);
> > >       if (!input_fmts)
> > > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > >               if (dpi->conf->num_output_fmts)
> > >                       out_bus_format = dpi->conf->output_fmts[0];
> > >
> > > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > >               bridge_state->input_bus_cfg.format,
> > >               bridge_state->output_bus_cfg.format);
> > >
> > > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> > >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> > >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > > +     else
> > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > >
> > >       return 0;
> > >  }
> > > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> > >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> > >  };
> > >
> > > +static const u32 mt8195_output_fmts[] = {
> > > +     MEDIA_BUS_FMT_RGB888_1X24,
> > > +     MEDIA_BUS_FMT_YUV8_1X24,
> > > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > > +};
> > > +
> > >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> > >       .c_bottom = 0x0010,
> > >       .c_top = 0x0FE0,
> > > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> > >       .hvsize_mask = HSIZE_MASK,
> > >       .channel_swap_shift = CH_SWAP,
> > >       .yuv422_en_bit = YUV422_EN,
> > > +     .csc_enable_bit = CSC_ENABLE,
> > >       .limit = &mtk_dpi_limit,
> > >  };
> > >
> > > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> > >       .hvsize_mask = HSIZE_MASK,
> > >       .channel_swap_shift = CH_SWAP,
> > >       .yuv422_en_bit = YUV422_EN,
> > > +     .csc_enable_bit = CSC_ENABLE,
> > >       .limit = &mtk_dpi_limit,
> > >  };
> > >
> > > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> > >       .hvsize_mask = HSIZE_MASK,
> > >       .channel_swap_shift = CH_SWAP,
> > >       .yuv422_en_bit = YUV422_EN,
> > > +     .csc_enable_bit = CSC_ENABLE,
> > >       .limit = &mtk_dpi_limit,
> > >  };
> > >
> > > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> > >       .hvsize_mask = HSIZE_MASK,
> > >       .channel_swap_shift = CH_SWAP,
> > >       .yuv422_en_bit = YUV422_EN,
> > > +     .csc_enable_bit = CSC_ENABLE,
> > >       .limit = &mtk_dpi_limit,
> > >  };
> > >
> > >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> > >       .cal_factor = mt8195_dpintf_calculate_factor,
> > > -     .output_fmts = mt8173_output_fmts,
> > > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > > +     .output_fmts = mt8195_output_fmts,
> > > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> > >       .is_dpintf = true,
> > > +     .csc_support = true,
> > >       .dimension_mask = DPINTF_HPW_MASK,
> > >       .hvsize_mask = DPINTF_HSIZE_MASK,
> > >       .channel_swap_shift = DPINTF_CH_SWAP,
> > >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> > >       .limit = &mtk_dpintf_limit,
> > >  };
> > >
> > > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > >               return ret;
> > >       }
> > >
> > > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > > +             if (ret != -EPROBE_DEFER)
> > > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > > +                             ret);
> > > +
> > > +             return ret;
> > > +     }
> > > +
> > >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> > >       if (IS_ERR(dpi->pixel_clk)) {
> > >               ret = PTR_ERR(dpi->pixel_clk);
> > > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > >               return ret;
> > >       }
> > >
> > > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > > +
> > >       dpi->irq = platform_get_irq(pdev, 0);
> > >       if (dpi->irq <= 0)
> > >               return -EINVAL;
> >
> > All those changes look unrelated as well?
> >
> Do you mean all the changes in mtk_dpi.c ? They are in support of
> enabling the mt8195 dpintf driver... so, not sure they are unlreated?

I don't know that driver at all, so it's probably related if you say so
:)

The DPI interface seems to be a panel interface, so it's a bit weird to
me why you would need to change things in the panel interface while
claiming to support a DP output.

Especially since it looks like you have some support for CSC, and
support for additional output formats?

I guess the DPI interface feeds the DP that essentially acts as a RGB ->
DP bridge?

If so, then the part adding support for CSC and additional formats
should be split into a separate patch, and it should be mentioned in the
commit log

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 170 bytes --]

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-16 14:51         ` Maxime Ripard
  0 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-11-16 14:51 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 16483 bytes --]

Hi,

On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > The driver creates a child device for the phy. The child device will
> > > never exist without the parent being active. As they are sharing a
> > > register range, the parent passes a regmap pointer to the child so that
> > > both can work with the same register range. The phy driver sets device
> > > data that is read by the parent to get the phy device that can be used
> > > to control the phy properties.
> >
> > If the PHY is in the same register space than the DP controller, why do
> > you need a separate PHY driver in the first place?
>
> This has been asked by Chun-Kuang Hu in a previous revision of the series:
> 
> https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/

It's a bit of a circular argument though :)

It's a separate phy driver because it needs to go through another
maintainer's tree, but it needs to go through another maintainer's tree
because it's a separate phy driver.

It doesn't explain why it needs to be a separate phy driver? Why can't
the phy setup be done directly in the DP driver, if it's essentially a
single device?

That being said, usually what those kind of questions mean is that
you're missing a comment or something in the commit log to provide that
context in the first place, so it would be great to add that context
here.

And it will avoid the situation we're now in where multiple reviewers
ask the same questions over and over again :)

> > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > +                                     struct drm_bridge_state *old_state)
> > > +{
> > > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +     struct drm_connector *conn;
> > > +     struct drm_connector_state *conn_state;
> > > +     struct drm_crtc *crtc;
> > > +     struct drm_crtc_state *crtc_state;
> > > +     int ret = 0;
> > > +     int i;
> > > +
> > > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > +                                                     bridge->encoder);
> > > +     if (!conn) {
> > > +             drm_err(mtk_dp->drm_dev,
> > > +                     "Can't enable bridge as connector is missing\n");
> > > +             return;
> > > +     }
> > > +
> > > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> >
> > This should be protected by a mutex (just like any resource shared
> > between KMS and ALSA)
>
> Ok.

I forgot to ask (even though checkpatch does mention it iirc), but since
you have multiple mutex it would be nice to have a comment for each
mutex stating exactly what it protects, and against what.

It's hard otherwise to remember or figure out what the locks are here
for.

> > > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > > +     if (ret)
> > > +             return ret;
> >
> > pdata?
> >
> can you elaborate?

Sorry, yeah, pdata is usually the abbreviation used in linux for the
platform_data mechanism, but you're using the DT to retrieve your
resources (and platform_data usually don't involve any parsing), so the
name is odd.

> > > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > index 384074f69111b..e6e88e3cd811d 100644
> > > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> > >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> > >  };
> > >
> > > +enum TVDPLL_CLK {
> > > +     TVDPLL_PLL = 0,
> > > +     TVDPLL_D2 = 2,
> > > +     TVDPLL_D4 = 4,
> > > +     TVDPLL_D8 = 8,
> > > +     TVDPLL_D16 = 16,
> > > +};
> > > +
> > >  struct mtk_dpi {
> > >       struct drm_encoder encoder;
> > >       struct drm_bridge bridge;
> > > @@ -71,8 +79,10 @@ struct mtk_dpi {
> > >       void __iomem *regs;
> > >       struct device *dev;
> > >       struct clk *engine_clk;
> > > +     struct clk *dpi_ck_cg;
> > >       struct clk *pixel_clk;
> > >       struct clk *tvd_clk;
> > > +     struct clk *pclk_src[5];
> > >       int irq;
> > >       struct drm_display_mode mode;
> > >       const struct mtk_dpi_conf *conf;
> > > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> > >       u32 hvsize_mask;
> > >       u32 channel_swap_shift;
> > >       u32 yuv422_en_bit;
> > > +     u32 csc_enable_bit;
> > >       const struct mtk_dpi_yc_limit *limit;
> > >  };
> > >
> > > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> > >
> > >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> > >  {
> > > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> > >  }
> > >
> > >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> > >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> > >  }
> > >
> > > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > > +{
> > > +     u32 matrix_sel = 0;
> > > +
> > > +     switch (format) {
> > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > > +             if (dpi->mode.hdisplay <= 720)
> > > +                     matrix_sel = 0x2;
> > > +             break;
> > > +     default:
> > > +             break;
> > > +     }
> > > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > > +}
> > > +
> > >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> > >                                       enum mtk_dpi_out_color_format format)
> > >  {
> > >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> > >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> > >               mtk_dpi_config_yuv422_enable(dpi, false);
> > > -             if (dpi->conf->csc_support)
> > > +             if (dpi->conf->csc_support) {
> > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > +             }
> > >               if (dpi->conf->swap_input_support)
> > >                       mtk_dpi_config_swap_input(dpi, false);
> > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> > >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> > >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> > >               mtk_dpi_config_yuv422_enable(dpi, true);
> > > -             if (dpi->conf->csc_support)
> > > +             if (dpi->conf->csc_support) {
> > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > +             }
> > >               if (dpi->conf->swap_input_support)
> > >                       mtk_dpi_config_swap_input(dpi, true);
> > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> > >       mtk_dpi_disable(dpi);
> > >       clk_disable_unprepare(dpi->pixel_clk);
> > >       clk_disable_unprepare(dpi->engine_clk);
> > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > +     clk_disable_unprepare(dpi->tvd_clk);
> > >  }
> > >
> > >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > >       if (++dpi->refcount != 1)
> > >               return 0;
> > >
> > > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > > +     if (ret) {
> > > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > > +             goto err_pixel;
> > > +     }
> > > +
> > >       ret = clk_prepare_enable(dpi->engine_clk);
> > >       if (ret) {
> > >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> > >               goto err_refcount;
> > >       }
> > >
> > > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > > +     if (ret) {
> > > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > > +             goto err_ck_cg;
> > > +     }
> > > +
> > >       ret = clk_prepare_enable(dpi->pixel_clk);
> > >       if (ret) {
> > >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > >       if (dpi->pinctrl && dpi->pins_dpi)
> > >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> > >
> > > -     mtk_dpi_enable(dpi);
> > >       return 0;
> > >
> > >  err_pixel:
> > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > +err_ck_cg:
> > >       clk_disable_unprepare(dpi->engine_clk);
> > >  err_refcount:
> > >       dpi->refcount--;
> > > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > >       pll_rate = clk_get_rate(dpi->tvd_clk);
> > >
> > >       vm.pixelclock = pll_rate / factor;
> > > -     if (dpi->conf->is_dpintf)
> > > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > +     if (dpi->conf->is_dpintf) {
> > > +             if (factor == 1)
> > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > +             else if (factor == 2)
> > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > > +             else if (factor == 4)
> > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > > +             else
> > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> > >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> > >       else
> > > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > >       }
> > >       mtk_dpi_sw_reset(dpi, false);
> > >
> > > +     mtk_dpi_enable(dpi);
> > > +
> > >       return 0;
> > >  }
> > >
> > > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> > >       u32 *input_fmts;
> > >
> > >       *num_input_fmts = 0;
> > > -
> > >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> > >                            GFP_KERNEL);
> > >       if (!input_fmts)
> > > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > >               if (dpi->conf->num_output_fmts)
> > >                       out_bus_format = dpi->conf->output_fmts[0];
> > >
> > > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > >               bridge_state->input_bus_cfg.format,
> > >               bridge_state->output_bus_cfg.format);
> > >
> > > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> > >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> > >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > > +     else
> > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > >
> > >       return 0;
> > >  }
> > > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> > >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> > >  };
> > >
> > > +static const u32 mt8195_output_fmts[] = {
> > > +     MEDIA_BUS_FMT_RGB888_1X24,
> > > +     MEDIA_BUS_FMT_YUV8_1X24,
> > > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > > +};
> > > +
> > >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> > >       .c_bottom = 0x0010,
> > >       .c_top = 0x0FE0,
> > > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> > >       .hvsize_mask = HSIZE_MASK,
> > >       .channel_swap_shift = CH_SWAP,
> > >       .yuv422_en_bit = YUV422_EN,
> > > +     .csc_enable_bit = CSC_ENABLE,
> > >       .limit = &mtk_dpi_limit,
> > >  };
> > >
> > > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> > >       .hvsize_mask = HSIZE_MASK,
> > >       .channel_swap_shift = CH_SWAP,
> > >       .yuv422_en_bit = YUV422_EN,
> > > +     .csc_enable_bit = CSC_ENABLE,
> > >       .limit = &mtk_dpi_limit,
> > >  };
> > >
> > > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> > >       .hvsize_mask = HSIZE_MASK,
> > >       .channel_swap_shift = CH_SWAP,
> > >       .yuv422_en_bit = YUV422_EN,
> > > +     .csc_enable_bit = CSC_ENABLE,
> > >       .limit = &mtk_dpi_limit,
> > >  };
> > >
> > > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> > >       .hvsize_mask = HSIZE_MASK,
> > >       .channel_swap_shift = CH_SWAP,
> > >       .yuv422_en_bit = YUV422_EN,
> > > +     .csc_enable_bit = CSC_ENABLE,
> > >       .limit = &mtk_dpi_limit,
> > >  };
> > >
> > >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> > >       .cal_factor = mt8195_dpintf_calculate_factor,
> > > -     .output_fmts = mt8173_output_fmts,
> > > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > > +     .output_fmts = mt8195_output_fmts,
> > > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> > >       .is_dpintf = true,
> > > +     .csc_support = true,
> > >       .dimension_mask = DPINTF_HPW_MASK,
> > >       .hvsize_mask = DPINTF_HSIZE_MASK,
> > >       .channel_swap_shift = DPINTF_CH_SWAP,
> > >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> > >       .limit = &mtk_dpintf_limit,
> > >  };
> > >
> > > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > >               return ret;
> > >       }
> > >
> > > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > > +             if (ret != -EPROBE_DEFER)
> > > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > > +                             ret);
> > > +
> > > +             return ret;
> > > +     }
> > > +
> > >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> > >       if (IS_ERR(dpi->pixel_clk)) {
> > >               ret = PTR_ERR(dpi->pixel_clk);
> > > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > >               return ret;
> > >       }
> > >
> > > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > > +
> > >       dpi->irq = platform_get_irq(pdev, 0);
> > >       if (dpi->irq <= 0)
> > >               return -EINVAL;
> >
> > All those changes look unrelated as well?
> >
> Do you mean all the changes in mtk_dpi.c ? They are in support of
> enabling the mt8195 dpintf driver... so, not sure they are unlreated?

I don't know that driver at all, so it's probably related if you say so
:)

The DPI interface seems to be a panel interface, so it's a bit weird to
me why you would need to change things in the panel interface while
claiming to support a DP output.

Especially since it looks like you have some support for CSC, and
support for additional output formats?

I guess the DPI interface feeds the DP that essentially acts as a RGB ->
DP bridge?

If so, then the part adding support for CSC and additional formats
should be split into a separate patch, and it should be mentioned in the
commit log

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-11-16 14:51         ` Maxime Ripard
  (?)
  (?)
@ 2021-11-24 13:45           ` Guillaume Ranquet
  -1 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-24 13:45 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Hi,
Thanks for all your input, really appreciated.

Quoting Maxime Ripard (2021-11-16 15:51:12)
> Hi,
>
> On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > The driver creates a child device for the phy. The child device will
> > > > never exist without the parent being active. As they are sharing a
> > > > register range, the parent passes a regmap pointer to the child so that
> > > > both can work with the same register range. The phy driver sets device
> > > > data that is read by the parent to get the phy device that can be used
> > > > to control the phy properties.
> > >
> > > If the PHY is in the same register space than the DP controller, why do
> > > you need a separate PHY driver in the first place?
> >
> > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> >
> > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
>
> It's a bit of a circular argument though :)
>
> It's a separate phy driver because it needs to go through another
> maintainer's tree, but it needs to go through another maintainer's tree
> because it's a separate phy driver.
>
> It doesn't explain why it needs to be a separate phy driver? Why can't
> the phy setup be done directly in the DP driver, if it's essentially a
> single device?
>
> That being said, usually what those kind of questions mean is that
> you're missing a comment or something in the commit log to provide that
> context in the first place, so it would be great to add that context
> here.
>
> And it will avoid the situation we're now in where multiple reviewers
> ask the same questions over and over again :)
>
At first I didn't understand your reply, then I realized I gave you
the wrong link...
my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.

The driver and phy were a single driver until v2 of this patch series
and the phy setup
was done directly in the driver (single driver, single C file).
Here's the relevant link to the discussion between Chun-Kuang and Markus

https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t

I'll try to find a way to make it clearer for v7.

> > > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > > +                                     struct drm_bridge_state *old_state)
> > > > +{
> > > > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > > +     struct drm_connector *conn;
> > > > +     struct drm_connector_state *conn_state;
> > > > +     struct drm_crtc *crtc;
> > > > +     struct drm_crtc_state *crtc_state;
> > > > +     int ret = 0;
> > > > +     int i;
> > > > +
> > > > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > > +                                                     bridge->encoder);
> > > > +     if (!conn) {
> > > > +             drm_err(mtk_dp->drm_dev,
> > > > +                     "Can't enable bridge as connector is missing\n");
> > > > +             return;
> > > > +     }
> > > > +
> > > > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > >
> > > This should be protected by a mutex (just like any resource shared
> > > between KMS and ALSA)
> >
> > Ok.
>
> I forgot to ask (even though checkpatch does mention it iirc), but since
> you have multiple mutex it would be nice to have a comment for each
> mutex stating exactly what it protects, and against what.
>
> It's hard otherwise to remember or figure out what the locks are here
> for.
>
> > > > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > > > +     if (ret)
> > > > +             return ret;
> > >
> > > pdata?
> > >
> > can you elaborate?
>
> Sorry, yeah, pdata is usually the abbreviation used in linux for the
> platform_data mechanism, but you're using the DT to retrieve your
> resources (and platform_data usually don't involve any parsing), so the
> name is odd.
>
> > > > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > index 384074f69111b..e6e88e3cd811d 100644
> > > > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> > > >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> > > >  };
> > > >
> > > > +enum TVDPLL_CLK {
> > > > +     TVDPLL_PLL = 0,
> > > > +     TVDPLL_D2 = 2,
> > > > +     TVDPLL_D4 = 4,
> > > > +     TVDPLL_D8 = 8,
> > > > +     TVDPLL_D16 = 16,
> > > > +};
> > > > +
> > > >  struct mtk_dpi {
> > > >       struct drm_encoder encoder;
> > > >       struct drm_bridge bridge;
> > > > @@ -71,8 +79,10 @@ struct mtk_dpi {
> > > >       void __iomem *regs;
> > > >       struct device *dev;
> > > >       struct clk *engine_clk;
> > > > +     struct clk *dpi_ck_cg;
> > > >       struct clk *pixel_clk;
> > > >       struct clk *tvd_clk;
> > > > +     struct clk *pclk_src[5];
> > > >       int irq;
> > > >       struct drm_display_mode mode;
> > > >       const struct mtk_dpi_conf *conf;
> > > > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> > > >       u32 hvsize_mask;
> > > >       u32 channel_swap_shift;
> > > >       u32 yuv422_en_bit;
> > > > +     u32 csc_enable_bit;
> > > >       const struct mtk_dpi_yc_limit *limit;
> > > >  };
> > > >
> > > > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> > > >
> > > >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> > > >  {
> > > > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > > > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> > > >  }
> > > >
> > > >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > > > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> > > >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> > > >  }
> > > >
> > > > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > > > +{
> > > > +     u32 matrix_sel = 0;
> > > > +
> > > > +     switch (format) {
> > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > > > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > > > +             if (dpi->mode.hdisplay <= 720)
> > > > +                     matrix_sel = 0x2;
> > > > +             break;
> > > > +     default:
> > > > +             break;
> > > > +     }
> > > > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > > > +}
> > > > +
> > > >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> > > >                                       enum mtk_dpi_out_color_format format)
> > > >  {
> > > >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> > > >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> > > >               mtk_dpi_config_yuv422_enable(dpi, false);
> > > > -             if (dpi->conf->csc_support)
> > > > +             if (dpi->conf->csc_support) {
> > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > +             }
> > > >               if (dpi->conf->swap_input_support)
> > > >                       mtk_dpi_config_swap_input(dpi, false);
> > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> > > >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> > > >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> > > >               mtk_dpi_config_yuv422_enable(dpi, true);
> > > > -             if (dpi->conf->csc_support)
> > > > +             if (dpi->conf->csc_support) {
> > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > +             }
> > > >               if (dpi->conf->swap_input_support)
> > > >                       mtk_dpi_config_swap_input(dpi, true);
> > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > > > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> > > >       mtk_dpi_disable(dpi);
> > > >       clk_disable_unprepare(dpi->pixel_clk);
> > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > +     clk_disable_unprepare(dpi->tvd_clk);
> > > >  }
> > > >
> > > >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > >       if (++dpi->refcount != 1)
> > > >               return 0;
> > > >
> > > > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > > > +     if (ret) {
> > > > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > > > +             goto err_pixel;
> > > > +     }
> > > > +
> > > >       ret = clk_prepare_enable(dpi->engine_clk);
> > > >       if (ret) {
> > > >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> > > >               goto err_refcount;
> > > >       }
> > > >
> > > > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > > > +     if (ret) {
> > > > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > > > +             goto err_ck_cg;
> > > > +     }
> > > > +
> > > >       ret = clk_prepare_enable(dpi->pixel_clk);
> > > >       if (ret) {
> > > >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > > > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > >       if (dpi->pinctrl && dpi->pins_dpi)
> > > >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> > > >
> > > > -     mtk_dpi_enable(dpi);
> > > >       return 0;
> > > >
> > > >  err_pixel:
> > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > +err_ck_cg:
> > > >       clk_disable_unprepare(dpi->engine_clk);
> > > >  err_refcount:
> > > >       dpi->refcount--;
> > > > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > >       pll_rate = clk_get_rate(dpi->tvd_clk);
> > > >
> > > >       vm.pixelclock = pll_rate / factor;
> > > > -     if (dpi->conf->is_dpintf)
> > > > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > > > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > +     if (dpi->conf->is_dpintf) {
> > > > +             if (factor == 1)
> > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > +             else if (factor == 2)
> > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > > > +             else if (factor == 4)
> > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > > > +             else
> > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> > > >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> > > >       else
> > > > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > >       }
> > > >       mtk_dpi_sw_reset(dpi, false);
> > > >
> > > > +     mtk_dpi_enable(dpi);
> > > > +
> > > >       return 0;
> > > >  }
> > > >
> > > > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> > > >       u32 *input_fmts;
> > > >
> > > >       *num_input_fmts = 0;
> > > > -
> > > >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> > > >                            GFP_KERNEL);
> > > >       if (!input_fmts)
> > > > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > >               if (dpi->conf->num_output_fmts)
> > > >                       out_bus_format = dpi->conf->output_fmts[0];
> > > >
> > > > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > >               bridge_state->input_bus_cfg.format,
> > > >               bridge_state->output_bus_cfg.format);
> > > >
> > > > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> > > >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> > > >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > > > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > > > +     else
> > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > >
> > > >       return 0;
> > > >  }
> > > > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> > > >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> > > >  };
> > > >
> > > > +static const u32 mt8195_output_fmts[] = {
> > > > +     MEDIA_BUS_FMT_RGB888_1X24,
> > > > +     MEDIA_BUS_FMT_YUV8_1X24,
> > > > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > > > +};
> > > > +
> > > >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> > > >       .c_bottom = 0x0010,
> > > >       .c_top = 0x0FE0,
> > > > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> > > >       .hvsize_mask = HSIZE_MASK,
> > > >       .channel_swap_shift = CH_SWAP,
> > > >       .yuv422_en_bit = YUV422_EN,
> > > > +     .csc_enable_bit = CSC_ENABLE,
> > > >       .limit = &mtk_dpi_limit,
> > > >  };
> > > >
> > > > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> > > >       .hvsize_mask = HSIZE_MASK,
> > > >       .channel_swap_shift = CH_SWAP,
> > > >       .yuv422_en_bit = YUV422_EN,
> > > > +     .csc_enable_bit = CSC_ENABLE,
> > > >       .limit = &mtk_dpi_limit,
> > > >  };
> > > >
> > > > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> > > >       .hvsize_mask = HSIZE_MASK,
> > > >       .channel_swap_shift = CH_SWAP,
> > > >       .yuv422_en_bit = YUV422_EN,
> > > > +     .csc_enable_bit = CSC_ENABLE,
> > > >       .limit = &mtk_dpi_limit,
> > > >  };
> > > >
> > > > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> > > >       .hvsize_mask = HSIZE_MASK,
> > > >       .channel_swap_shift = CH_SWAP,
> > > >       .yuv422_en_bit = YUV422_EN,
> > > > +     .csc_enable_bit = CSC_ENABLE,
> > > >       .limit = &mtk_dpi_limit,
> > > >  };
> > > >
> > > >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> > > >       .cal_factor = mt8195_dpintf_calculate_factor,
> > > > -     .output_fmts = mt8173_output_fmts,
> > > > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > > > +     .output_fmts = mt8195_output_fmts,
> > > > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> > > >       .is_dpintf = true,
> > > > +     .csc_support = true,
> > > >       .dimension_mask = DPINTF_HPW_MASK,
> > > >       .hvsize_mask = DPINTF_HSIZE_MASK,
> > > >       .channel_swap_shift = DPINTF_CH_SWAP,
> > > >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > > > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> > > >       .limit = &mtk_dpintf_limit,
> > > >  };
> > > >
> > > > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > >               return ret;
> > > >       }
> > > >
> > > > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > > > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > > > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > > > +             if (ret != -EPROBE_DEFER)
> > > > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > > > +                             ret);
> > > > +
> > > > +             return ret;
> > > > +     }
> > > > +
> > > >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> > > >       if (IS_ERR(dpi->pixel_clk)) {
> > > >               ret = PTR_ERR(dpi->pixel_clk);
> > > > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > >               return ret;
> > > >       }
> > > >
> > > > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > > > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > > > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > > > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > > > +
> > > >       dpi->irq = platform_get_irq(pdev, 0);
> > > >       if (dpi->irq <= 0)
> > > >               return -EINVAL;
> > >
> > > All those changes look unrelated as well?
> > >
> > Do you mean all the changes in mtk_dpi.c ? They are in support of
> > enabling the mt8195 dpintf driver... so, not sure they are unlreated?
>
> I don't know that driver at all, so it's probably related if you say so
> :)
>
> The DPI interface seems to be a panel interface, so it's a bit weird to
> me why you would need to change things in the panel interface while
> claiming to support a DP output.
>
> Especially since it looks like you have some support for CSC, and
> support for additional output formats?
>
> I guess the DPI interface feeds the DP that essentially acts as a RGB ->
> DP bridge?
>
> If so, then the part adding support for CSC and additional formats
> should be split into a separate patch, and it should be mentioned in the
> commit log
>
> Maxime

I see what you mean, I'll split the patches functionally...
I'm discovering the gpu/drm tree, so It may take me a while to get it right :D

Thx,
Guillaume.

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-24 13:45           ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-24 13:45 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Chun-Kuang Hu, David Airlie, Thomas Zimmermann, linux-kernel,
	dri-devel, Markus Schneider-Pargmann, linux-mediatek,
	Matthias Brugger, linux-arm-kernel, kernel test robot

Hi,
Thanks for all your input, really appreciated.

Quoting Maxime Ripard (2021-11-16 15:51:12)
> Hi,
>
> On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > The driver creates a child device for the phy. The child device will
> > > > never exist without the parent being active. As they are sharing a
> > > > register range, the parent passes a regmap pointer to the child so that
> > > > both can work with the same register range. The phy driver sets device
> > > > data that is read by the parent to get the phy device that can be used
> > > > to control the phy properties.
> > >
> > > If the PHY is in the same register space than the DP controller, why do
> > > you need a separate PHY driver in the first place?
> >
> > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> >
> > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
>
> It's a bit of a circular argument though :)
>
> It's a separate phy driver because it needs to go through another
> maintainer's tree, but it needs to go through another maintainer's tree
> because it's a separate phy driver.
>
> It doesn't explain why it needs to be a separate phy driver? Why can't
> the phy setup be done directly in the DP driver, if it's essentially a
> single device?
>
> That being said, usually what those kind of questions mean is that
> you're missing a comment or something in the commit log to provide that
> context in the first place, so it would be great to add that context
> here.
>
> And it will avoid the situation we're now in where multiple reviewers
> ask the same questions over and over again :)
>
At first I didn't understand your reply, then I realized I gave you
the wrong link...
my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.

The driver and phy were a single driver until v2 of this patch series
and the phy setup
was done directly in the driver (single driver, single C file).
Here's the relevant link to the discussion between Chun-Kuang and Markus

https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t

I'll try to find a way to make it clearer for v7.

> > > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > > +                                     struct drm_bridge_state *old_state)
> > > > +{
> > > > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > > +     struct drm_connector *conn;
> > > > +     struct drm_connector_state *conn_state;
> > > > +     struct drm_crtc *crtc;
> > > > +     struct drm_crtc_state *crtc_state;
> > > > +     int ret = 0;
> > > > +     int i;
> > > > +
> > > > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > > +                                                     bridge->encoder);
> > > > +     if (!conn) {
> > > > +             drm_err(mtk_dp->drm_dev,
> > > > +                     "Can't enable bridge as connector is missing\n");
> > > > +             return;
> > > > +     }
> > > > +
> > > > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > >
> > > This should be protected by a mutex (just like any resource shared
> > > between KMS and ALSA)
> >
> > Ok.
>
> I forgot to ask (even though checkpatch does mention it iirc), but since
> you have multiple mutex it would be nice to have a comment for each
> mutex stating exactly what it protects, and against what.
>
> It's hard otherwise to remember or figure out what the locks are here
> for.
>
> > > > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > > > +     if (ret)
> > > > +             return ret;
> > >
> > > pdata?
> > >
> > can you elaborate?
>
> Sorry, yeah, pdata is usually the abbreviation used in linux for the
> platform_data mechanism, but you're using the DT to retrieve your
> resources (and platform_data usually don't involve any parsing), so the
> name is odd.
>
> > > > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > index 384074f69111b..e6e88e3cd811d 100644
> > > > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> > > >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> > > >  };
> > > >
> > > > +enum TVDPLL_CLK {
> > > > +     TVDPLL_PLL = 0,
> > > > +     TVDPLL_D2 = 2,
> > > > +     TVDPLL_D4 = 4,
> > > > +     TVDPLL_D8 = 8,
> > > > +     TVDPLL_D16 = 16,
> > > > +};
> > > > +
> > > >  struct mtk_dpi {
> > > >       struct drm_encoder encoder;
> > > >       struct drm_bridge bridge;
> > > > @@ -71,8 +79,10 @@ struct mtk_dpi {
> > > >       void __iomem *regs;
> > > >       struct device *dev;
> > > >       struct clk *engine_clk;
> > > > +     struct clk *dpi_ck_cg;
> > > >       struct clk *pixel_clk;
> > > >       struct clk *tvd_clk;
> > > > +     struct clk *pclk_src[5];
> > > >       int irq;
> > > >       struct drm_display_mode mode;
> > > >       const struct mtk_dpi_conf *conf;
> > > > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> > > >       u32 hvsize_mask;
> > > >       u32 channel_swap_shift;
> > > >       u32 yuv422_en_bit;
> > > > +     u32 csc_enable_bit;
> > > >       const struct mtk_dpi_yc_limit *limit;
> > > >  };
> > > >
> > > > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> > > >
> > > >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> > > >  {
> > > > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > > > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> > > >  }
> > > >
> > > >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > > > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> > > >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> > > >  }
> > > >
> > > > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > > > +{
> > > > +     u32 matrix_sel = 0;
> > > > +
> > > > +     switch (format) {
> > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > > > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > > > +             if (dpi->mode.hdisplay <= 720)
> > > > +                     matrix_sel = 0x2;
> > > > +             break;
> > > > +     default:
> > > > +             break;
> > > > +     }
> > > > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > > > +}
> > > > +
> > > >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> > > >                                       enum mtk_dpi_out_color_format format)
> > > >  {
> > > >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> > > >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> > > >               mtk_dpi_config_yuv422_enable(dpi, false);
> > > > -             if (dpi->conf->csc_support)
> > > > +             if (dpi->conf->csc_support) {
> > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > +             }
> > > >               if (dpi->conf->swap_input_support)
> > > >                       mtk_dpi_config_swap_input(dpi, false);
> > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> > > >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> > > >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> > > >               mtk_dpi_config_yuv422_enable(dpi, true);
> > > > -             if (dpi->conf->csc_support)
> > > > +             if (dpi->conf->csc_support) {
> > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > +             }
> > > >               if (dpi->conf->swap_input_support)
> > > >                       mtk_dpi_config_swap_input(dpi, true);
> > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > > > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> > > >       mtk_dpi_disable(dpi);
> > > >       clk_disable_unprepare(dpi->pixel_clk);
> > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > +     clk_disable_unprepare(dpi->tvd_clk);
> > > >  }
> > > >
> > > >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > >       if (++dpi->refcount != 1)
> > > >               return 0;
> > > >
> > > > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > > > +     if (ret) {
> > > > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > > > +             goto err_pixel;
> > > > +     }
> > > > +
> > > >       ret = clk_prepare_enable(dpi->engine_clk);
> > > >       if (ret) {
> > > >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> > > >               goto err_refcount;
> > > >       }
> > > >
> > > > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > > > +     if (ret) {
> > > > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > > > +             goto err_ck_cg;
> > > > +     }
> > > > +
> > > >       ret = clk_prepare_enable(dpi->pixel_clk);
> > > >       if (ret) {
> > > >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > > > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > >       if (dpi->pinctrl && dpi->pins_dpi)
> > > >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> > > >
> > > > -     mtk_dpi_enable(dpi);
> > > >       return 0;
> > > >
> > > >  err_pixel:
> > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > +err_ck_cg:
> > > >       clk_disable_unprepare(dpi->engine_clk);
> > > >  err_refcount:
> > > >       dpi->refcount--;
> > > > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > >       pll_rate = clk_get_rate(dpi->tvd_clk);
> > > >
> > > >       vm.pixelclock = pll_rate / factor;
> > > > -     if (dpi->conf->is_dpintf)
> > > > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > > > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > +     if (dpi->conf->is_dpintf) {
> > > > +             if (factor == 1)
> > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > +             else if (factor == 2)
> > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > > > +             else if (factor == 4)
> > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > > > +             else
> > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> > > >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> > > >       else
> > > > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > >       }
> > > >       mtk_dpi_sw_reset(dpi, false);
> > > >
> > > > +     mtk_dpi_enable(dpi);
> > > > +
> > > >       return 0;
> > > >  }
> > > >
> > > > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> > > >       u32 *input_fmts;
> > > >
> > > >       *num_input_fmts = 0;
> > > > -
> > > >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> > > >                            GFP_KERNEL);
> > > >       if (!input_fmts)
> > > > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > >               if (dpi->conf->num_output_fmts)
> > > >                       out_bus_format = dpi->conf->output_fmts[0];
> > > >
> > > > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > >               bridge_state->input_bus_cfg.format,
> > > >               bridge_state->output_bus_cfg.format);
> > > >
> > > > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> > > >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> > > >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > > > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > > > +     else
> > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > >
> > > >       return 0;
> > > >  }
> > > > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> > > >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> > > >  };
> > > >
> > > > +static const u32 mt8195_output_fmts[] = {
> > > > +     MEDIA_BUS_FMT_RGB888_1X24,
> > > > +     MEDIA_BUS_FMT_YUV8_1X24,
> > > > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > > > +};
> > > > +
> > > >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> > > >       .c_bottom = 0x0010,
> > > >       .c_top = 0x0FE0,
> > > > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> > > >       .hvsize_mask = HSIZE_MASK,
> > > >       .channel_swap_shift = CH_SWAP,
> > > >       .yuv422_en_bit = YUV422_EN,
> > > > +     .csc_enable_bit = CSC_ENABLE,
> > > >       .limit = &mtk_dpi_limit,
> > > >  };
> > > >
> > > > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> > > >       .hvsize_mask = HSIZE_MASK,
> > > >       .channel_swap_shift = CH_SWAP,
> > > >       .yuv422_en_bit = YUV422_EN,
> > > > +     .csc_enable_bit = CSC_ENABLE,
> > > >       .limit = &mtk_dpi_limit,
> > > >  };
> > > >
> > > > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> > > >       .hvsize_mask = HSIZE_MASK,
> > > >       .channel_swap_shift = CH_SWAP,
> > > >       .yuv422_en_bit = YUV422_EN,
> > > > +     .csc_enable_bit = CSC_ENABLE,
> > > >       .limit = &mtk_dpi_limit,
> > > >  };
> > > >
> > > > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> > > >       .hvsize_mask = HSIZE_MASK,
> > > >       .channel_swap_shift = CH_SWAP,
> > > >       .yuv422_en_bit = YUV422_EN,
> > > > +     .csc_enable_bit = CSC_ENABLE,
> > > >       .limit = &mtk_dpi_limit,
> > > >  };
> > > >
> > > >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> > > >       .cal_factor = mt8195_dpintf_calculate_factor,
> > > > -     .output_fmts = mt8173_output_fmts,
> > > > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > > > +     .output_fmts = mt8195_output_fmts,
> > > > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> > > >       .is_dpintf = true,
> > > > +     .csc_support = true,
> > > >       .dimension_mask = DPINTF_HPW_MASK,
> > > >       .hvsize_mask = DPINTF_HSIZE_MASK,
> > > >       .channel_swap_shift = DPINTF_CH_SWAP,
> > > >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > > > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> > > >       .limit = &mtk_dpintf_limit,
> > > >  };
> > > >
> > > > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > >               return ret;
> > > >       }
> > > >
> > > > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > > > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > > > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > > > +             if (ret != -EPROBE_DEFER)
> > > > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > > > +                             ret);
> > > > +
> > > > +             return ret;
> > > > +     }
> > > > +
> > > >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> > > >       if (IS_ERR(dpi->pixel_clk)) {
> > > >               ret = PTR_ERR(dpi->pixel_clk);
> > > > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > >               return ret;
> > > >       }
> > > >
> > > > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > > > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > > > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > > > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > > > +
> > > >       dpi->irq = platform_get_irq(pdev, 0);
> > > >       if (dpi->irq <= 0)
> > > >               return -EINVAL;
> > >
> > > All those changes look unrelated as well?
> > >
> > Do you mean all the changes in mtk_dpi.c ? They are in support of
> > enabling the mt8195 dpintf driver... so, not sure they are unlreated?
>
> I don't know that driver at all, so it's probably related if you say so
> :)
>
> The DPI interface seems to be a panel interface, so it's a bit weird to
> me why you would need to change things in the panel interface while
> claiming to support a DP output.
>
> Especially since it looks like you have some support for CSC, and
> support for additional output formats?
>
> I guess the DPI interface feeds the DP that essentially acts as a RGB ->
> DP bridge?
>
> If so, then the part adding support for CSC and additional formats
> should be split into a separate patch, and it should be mentioned in the
> commit log
>
> Maxime

I see what you mean, I'll split the patches functionally...
I'm discovering the gpu/drm tree, so It may take me a while to get it right :D

Thx,
Guillaume.

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-24 13:45           ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-24 13:45 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Hi,
Thanks for all your input, really appreciated.

Quoting Maxime Ripard (2021-11-16 15:51:12)
> Hi,
>
> On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > The driver creates a child device for the phy. The child device will
> > > > never exist without the parent being active. As they are sharing a
> > > > register range, the parent passes a regmap pointer to the child so that
> > > > both can work with the same register range. The phy driver sets device
> > > > data that is read by the parent to get the phy device that can be used
> > > > to control the phy properties.
> > >
> > > If the PHY is in the same register space than the DP controller, why do
> > > you need a separate PHY driver in the first place?
> >
> > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> >
> > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
>
> It's a bit of a circular argument though :)
>
> It's a separate phy driver because it needs to go through another
> maintainer's tree, but it needs to go through another maintainer's tree
> because it's a separate phy driver.
>
> It doesn't explain why it needs to be a separate phy driver? Why can't
> the phy setup be done directly in the DP driver, if it's essentially a
> single device?
>
> That being said, usually what those kind of questions mean is that
> you're missing a comment or something in the commit log to provide that
> context in the first place, so it would be great to add that context
> here.
>
> And it will avoid the situation we're now in where multiple reviewers
> ask the same questions over and over again :)
>
At first I didn't understand your reply, then I realized I gave you
the wrong link...
my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.

The driver and phy were a single driver until v2 of this patch series
and the phy setup
was done directly in the driver (single driver, single C file).
Here's the relevant link to the discussion between Chun-Kuang and Markus

https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t

I'll try to find a way to make it clearer for v7.

> > > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > > +                                     struct drm_bridge_state *old_state)
> > > > +{
> > > > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > > +     struct drm_connector *conn;
> > > > +     struct drm_connector_state *conn_state;
> > > > +     struct drm_crtc *crtc;
> > > > +     struct drm_crtc_state *crtc_state;
> > > > +     int ret = 0;
> > > > +     int i;
> > > > +
> > > > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > > +                                                     bridge->encoder);
> > > > +     if (!conn) {
> > > > +             drm_err(mtk_dp->drm_dev,
> > > > +                     "Can't enable bridge as connector is missing\n");
> > > > +             return;
> > > > +     }
> > > > +
> > > > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > >
> > > This should be protected by a mutex (just like any resource shared
> > > between KMS and ALSA)
> >
> > Ok.
>
> I forgot to ask (even though checkpatch does mention it iirc), but since
> you have multiple mutex it would be nice to have a comment for each
> mutex stating exactly what it protects, and against what.
>
> It's hard otherwise to remember or figure out what the locks are here
> for.
>
> > > > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > > > +     if (ret)
> > > > +             return ret;
> > >
> > > pdata?
> > >
> > can you elaborate?
>
> Sorry, yeah, pdata is usually the abbreviation used in linux for the
> platform_data mechanism, but you're using the DT to retrieve your
> resources (and platform_data usually don't involve any parsing), so the
> name is odd.
>
> > > > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > index 384074f69111b..e6e88e3cd811d 100644
> > > > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> > > >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> > > >  };
> > > >
> > > > +enum TVDPLL_CLK {
> > > > +     TVDPLL_PLL = 0,
> > > > +     TVDPLL_D2 = 2,
> > > > +     TVDPLL_D4 = 4,
> > > > +     TVDPLL_D8 = 8,
> > > > +     TVDPLL_D16 = 16,
> > > > +};
> > > > +
> > > >  struct mtk_dpi {
> > > >       struct drm_encoder encoder;
> > > >       struct drm_bridge bridge;
> > > > @@ -71,8 +79,10 @@ struct mtk_dpi {
> > > >       void __iomem *regs;
> > > >       struct device *dev;
> > > >       struct clk *engine_clk;
> > > > +     struct clk *dpi_ck_cg;
> > > >       struct clk *pixel_clk;
> > > >       struct clk *tvd_clk;
> > > > +     struct clk *pclk_src[5];
> > > >       int irq;
> > > >       struct drm_display_mode mode;
> > > >       const struct mtk_dpi_conf *conf;
> > > > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> > > >       u32 hvsize_mask;
> > > >       u32 channel_swap_shift;
> > > >       u32 yuv422_en_bit;
> > > > +     u32 csc_enable_bit;
> > > >       const struct mtk_dpi_yc_limit *limit;
> > > >  };
> > > >
> > > > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> > > >
> > > >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> > > >  {
> > > > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > > > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> > > >  }
> > > >
> > > >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > > > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> > > >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> > > >  }
> > > >
> > > > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > > > +{
> > > > +     u32 matrix_sel = 0;
> > > > +
> > > > +     switch (format) {
> > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > > > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > > > +             if (dpi->mode.hdisplay <= 720)
> > > > +                     matrix_sel = 0x2;
> > > > +             break;
> > > > +     default:
> > > > +             break;
> > > > +     }
> > > > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > > > +}
> > > > +
> > > >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> > > >                                       enum mtk_dpi_out_color_format format)
> > > >  {
> > > >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> > > >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> > > >               mtk_dpi_config_yuv422_enable(dpi, false);
> > > > -             if (dpi->conf->csc_support)
> > > > +             if (dpi->conf->csc_support) {
> > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > +             }
> > > >               if (dpi->conf->swap_input_support)
> > > >                       mtk_dpi_config_swap_input(dpi, false);
> > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> > > >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> > > >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> > > >               mtk_dpi_config_yuv422_enable(dpi, true);
> > > > -             if (dpi->conf->csc_support)
> > > > +             if (dpi->conf->csc_support) {
> > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > +             }
> > > >               if (dpi->conf->swap_input_support)
> > > >                       mtk_dpi_config_swap_input(dpi, true);
> > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > > > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> > > >       mtk_dpi_disable(dpi);
> > > >       clk_disable_unprepare(dpi->pixel_clk);
> > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > +     clk_disable_unprepare(dpi->tvd_clk);
> > > >  }
> > > >
> > > >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > >       if (++dpi->refcount != 1)
> > > >               return 0;
> > > >
> > > > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > > > +     if (ret) {
> > > > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > > > +             goto err_pixel;
> > > > +     }
> > > > +
> > > >       ret = clk_prepare_enable(dpi->engine_clk);
> > > >       if (ret) {
> > > >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> > > >               goto err_refcount;
> > > >       }
> > > >
> > > > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > > > +     if (ret) {
> > > > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > > > +             goto err_ck_cg;
> > > > +     }
> > > > +
> > > >       ret = clk_prepare_enable(dpi->pixel_clk);
> > > >       if (ret) {
> > > >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > > > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > >       if (dpi->pinctrl && dpi->pins_dpi)
> > > >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> > > >
> > > > -     mtk_dpi_enable(dpi);
> > > >       return 0;
> > > >
> > > >  err_pixel:
> > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > +err_ck_cg:
> > > >       clk_disable_unprepare(dpi->engine_clk);
> > > >  err_refcount:
> > > >       dpi->refcount--;
> > > > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > >       pll_rate = clk_get_rate(dpi->tvd_clk);
> > > >
> > > >       vm.pixelclock = pll_rate / factor;
> > > > -     if (dpi->conf->is_dpintf)
> > > > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > > > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > +     if (dpi->conf->is_dpintf) {
> > > > +             if (factor == 1)
> > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > +             else if (factor == 2)
> > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > > > +             else if (factor == 4)
> > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > > > +             else
> > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> > > >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> > > >       else
> > > > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > >       }
> > > >       mtk_dpi_sw_reset(dpi, false);
> > > >
> > > > +     mtk_dpi_enable(dpi);
> > > > +
> > > >       return 0;
> > > >  }
> > > >
> > > > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> > > >       u32 *input_fmts;
> > > >
> > > >       *num_input_fmts = 0;
> > > > -
> > > >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> > > >                            GFP_KERNEL);
> > > >       if (!input_fmts)
> > > > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > >               if (dpi->conf->num_output_fmts)
> > > >                       out_bus_format = dpi->conf->output_fmts[0];
> > > >
> > > > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > >               bridge_state->input_bus_cfg.format,
> > > >               bridge_state->output_bus_cfg.format);
> > > >
> > > > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> > > >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> > > >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > > > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > > > +     else
> > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > >
> > > >       return 0;
> > > >  }
> > > > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> > > >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> > > >  };
> > > >
> > > > +static const u32 mt8195_output_fmts[] = {
> > > > +     MEDIA_BUS_FMT_RGB888_1X24,
> > > > +     MEDIA_BUS_FMT_YUV8_1X24,
> > > > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > > > +};
> > > > +
> > > >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> > > >       .c_bottom = 0x0010,
> > > >       .c_top = 0x0FE0,
> > > > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> > > >       .hvsize_mask = HSIZE_MASK,
> > > >       .channel_swap_shift = CH_SWAP,
> > > >       .yuv422_en_bit = YUV422_EN,
> > > > +     .csc_enable_bit = CSC_ENABLE,
> > > >       .limit = &mtk_dpi_limit,
> > > >  };
> > > >
> > > > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> > > >       .hvsize_mask = HSIZE_MASK,
> > > >       .channel_swap_shift = CH_SWAP,
> > > >       .yuv422_en_bit = YUV422_EN,
> > > > +     .csc_enable_bit = CSC_ENABLE,
> > > >       .limit = &mtk_dpi_limit,
> > > >  };
> > > >
> > > > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> > > >       .hvsize_mask = HSIZE_MASK,
> > > >       .channel_swap_shift = CH_SWAP,
> > > >       .yuv422_en_bit = YUV422_EN,
> > > > +     .csc_enable_bit = CSC_ENABLE,
> > > >       .limit = &mtk_dpi_limit,
> > > >  };
> > > >
> > > > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> > > >       .hvsize_mask = HSIZE_MASK,
> > > >       .channel_swap_shift = CH_SWAP,
> > > >       .yuv422_en_bit = YUV422_EN,
> > > > +     .csc_enable_bit = CSC_ENABLE,
> > > >       .limit = &mtk_dpi_limit,
> > > >  };
> > > >
> > > >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> > > >       .cal_factor = mt8195_dpintf_calculate_factor,
> > > > -     .output_fmts = mt8173_output_fmts,
> > > > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > > > +     .output_fmts = mt8195_output_fmts,
> > > > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> > > >       .is_dpintf = true,
> > > > +     .csc_support = true,
> > > >       .dimension_mask = DPINTF_HPW_MASK,
> > > >       .hvsize_mask = DPINTF_HSIZE_MASK,
> > > >       .channel_swap_shift = DPINTF_CH_SWAP,
> > > >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > > > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> > > >       .limit = &mtk_dpintf_limit,
> > > >  };
> > > >
> > > > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > >               return ret;
> > > >       }
> > > >
> > > > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > > > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > > > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > > > +             if (ret != -EPROBE_DEFER)
> > > > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > > > +                             ret);
> > > > +
> > > > +             return ret;
> > > > +     }
> > > > +
> > > >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> > > >       if (IS_ERR(dpi->pixel_clk)) {
> > > >               ret = PTR_ERR(dpi->pixel_clk);
> > > > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > >               return ret;
> > > >       }
> > > >
> > > > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > > > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > > > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > > > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > > > +
> > > >       dpi->irq = platform_get_irq(pdev, 0);
> > > >       if (dpi->irq <= 0)
> > > >               return -EINVAL;
> > >
> > > All those changes look unrelated as well?
> > >
> > Do you mean all the changes in mtk_dpi.c ? They are in support of
> > enabling the mt8195 dpintf driver... so, not sure they are unlreated?
>
> I don't know that driver at all, so it's probably related if you say so
> :)
>
> The DPI interface seems to be a panel interface, so it's a bit weird to
> me why you would need to change things in the panel interface while
> claiming to support a DP output.
>
> Especially since it looks like you have some support for CSC, and
> support for additional output formats?
>
> I guess the DPI interface feeds the DP that essentially acts as a RGB ->
> DP bridge?
>
> If so, then the part adding support for CSC and additional formats
> should be split into a separate patch, and it should be mentioned in the
> commit log
>
> Maxime

I see what you mean, I'll split the patches functionally...
I'm discovering the gpu/drm tree, so It may take me a while to get it right :D

Thx,
Guillaume.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-24 13:45           ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-24 13:45 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Hi,
Thanks for all your input, really appreciated.

Quoting Maxime Ripard (2021-11-16 15:51:12)
> Hi,
>
> On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > The driver creates a child device for the phy. The child device will
> > > > never exist without the parent being active. As they are sharing a
> > > > register range, the parent passes a regmap pointer to the child so that
> > > > both can work with the same register range. The phy driver sets device
> > > > data that is read by the parent to get the phy device that can be used
> > > > to control the phy properties.
> > >
> > > If the PHY is in the same register space than the DP controller, why do
> > > you need a separate PHY driver in the first place?
> >
> > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> >
> > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
>
> It's a bit of a circular argument though :)
>
> It's a separate phy driver because it needs to go through another
> maintainer's tree, but it needs to go through another maintainer's tree
> because it's a separate phy driver.
>
> It doesn't explain why it needs to be a separate phy driver? Why can't
> the phy setup be done directly in the DP driver, if it's essentially a
> single device?
>
> That being said, usually what those kind of questions mean is that
> you're missing a comment or something in the commit log to provide that
> context in the first place, so it would be great to add that context
> here.
>
> And it will avoid the situation we're now in where multiple reviewers
> ask the same questions over and over again :)
>
At first I didn't understand your reply, then I realized I gave you
the wrong link...
my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.

The driver and phy were a single driver until v2 of this patch series
and the phy setup
was done directly in the driver (single driver, single C file).
Here's the relevant link to the discussion between Chun-Kuang and Markus

https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t

I'll try to find a way to make it clearer for v7.

> > > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > > +                                     struct drm_bridge_state *old_state)
> > > > +{
> > > > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > > +     struct drm_connector *conn;
> > > > +     struct drm_connector_state *conn_state;
> > > > +     struct drm_crtc *crtc;
> > > > +     struct drm_crtc_state *crtc_state;
> > > > +     int ret = 0;
> > > > +     int i;
> > > > +
> > > > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > > +                                                     bridge->encoder);
> > > > +     if (!conn) {
> > > > +             drm_err(mtk_dp->drm_dev,
> > > > +                     "Can't enable bridge as connector is missing\n");
> > > > +             return;
> > > > +     }
> > > > +
> > > > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > >
> > > This should be protected by a mutex (just like any resource shared
> > > between KMS and ALSA)
> >
> > Ok.
>
> I forgot to ask (even though checkpatch does mention it iirc), but since
> you have multiple mutex it would be nice to have a comment for each
> mutex stating exactly what it protects, and against what.
>
> It's hard otherwise to remember or figure out what the locks are here
> for.
>
> > > > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > > > +     if (ret)
> > > > +             return ret;
> > >
> > > pdata?
> > >
> > can you elaborate?
>
> Sorry, yeah, pdata is usually the abbreviation used in linux for the
> platform_data mechanism, but you're using the DT to retrieve your
> resources (and platform_data usually don't involve any parsing), so the
> name is odd.
>
> > > > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > index 384074f69111b..e6e88e3cd811d 100644
> > > > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> > > >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> > > >  };
> > > >
> > > > +enum TVDPLL_CLK {
> > > > +     TVDPLL_PLL = 0,
> > > > +     TVDPLL_D2 = 2,
> > > > +     TVDPLL_D4 = 4,
> > > > +     TVDPLL_D8 = 8,
> > > > +     TVDPLL_D16 = 16,
> > > > +};
> > > > +
> > > >  struct mtk_dpi {
> > > >       struct drm_encoder encoder;
> > > >       struct drm_bridge bridge;
> > > > @@ -71,8 +79,10 @@ struct mtk_dpi {
> > > >       void __iomem *regs;
> > > >       struct device *dev;
> > > >       struct clk *engine_clk;
> > > > +     struct clk *dpi_ck_cg;
> > > >       struct clk *pixel_clk;
> > > >       struct clk *tvd_clk;
> > > > +     struct clk *pclk_src[5];
> > > >       int irq;
> > > >       struct drm_display_mode mode;
> > > >       const struct mtk_dpi_conf *conf;
> > > > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> > > >       u32 hvsize_mask;
> > > >       u32 channel_swap_shift;
> > > >       u32 yuv422_en_bit;
> > > > +     u32 csc_enable_bit;
> > > >       const struct mtk_dpi_yc_limit *limit;
> > > >  };
> > > >
> > > > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> > > >
> > > >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> > > >  {
> > > > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > > > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> > > >  }
> > > >
> > > >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > > > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> > > >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> > > >  }
> > > >
> > > > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > > > +{
> > > > +     u32 matrix_sel = 0;
> > > > +
> > > > +     switch (format) {
> > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > > > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > > > +             if (dpi->mode.hdisplay <= 720)
> > > > +                     matrix_sel = 0x2;
> > > > +             break;
> > > > +     default:
> > > > +             break;
> > > > +     }
> > > > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > > > +}
> > > > +
> > > >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> > > >                                       enum mtk_dpi_out_color_format format)
> > > >  {
> > > >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> > > >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> > > >               mtk_dpi_config_yuv422_enable(dpi, false);
> > > > -             if (dpi->conf->csc_support)
> > > > +             if (dpi->conf->csc_support) {
> > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > +             }
> > > >               if (dpi->conf->swap_input_support)
> > > >                       mtk_dpi_config_swap_input(dpi, false);
> > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> > > >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> > > >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> > > >               mtk_dpi_config_yuv422_enable(dpi, true);
> > > > -             if (dpi->conf->csc_support)
> > > > +             if (dpi->conf->csc_support) {
> > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > +             }
> > > >               if (dpi->conf->swap_input_support)
> > > >                       mtk_dpi_config_swap_input(dpi, true);
> > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > > > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> > > >       mtk_dpi_disable(dpi);
> > > >       clk_disable_unprepare(dpi->pixel_clk);
> > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > +     clk_disable_unprepare(dpi->tvd_clk);
> > > >  }
> > > >
> > > >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > >       if (++dpi->refcount != 1)
> > > >               return 0;
> > > >
> > > > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > > > +     if (ret) {
> > > > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > > > +             goto err_pixel;
> > > > +     }
> > > > +
> > > >       ret = clk_prepare_enable(dpi->engine_clk);
> > > >       if (ret) {
> > > >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> > > >               goto err_refcount;
> > > >       }
> > > >
> > > > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > > > +     if (ret) {
> > > > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > > > +             goto err_ck_cg;
> > > > +     }
> > > > +
> > > >       ret = clk_prepare_enable(dpi->pixel_clk);
> > > >       if (ret) {
> > > >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > > > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > >       if (dpi->pinctrl && dpi->pins_dpi)
> > > >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> > > >
> > > > -     mtk_dpi_enable(dpi);
> > > >       return 0;
> > > >
> > > >  err_pixel:
> > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > +err_ck_cg:
> > > >       clk_disable_unprepare(dpi->engine_clk);
> > > >  err_refcount:
> > > >       dpi->refcount--;
> > > > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > >       pll_rate = clk_get_rate(dpi->tvd_clk);
> > > >
> > > >       vm.pixelclock = pll_rate / factor;
> > > > -     if (dpi->conf->is_dpintf)
> > > > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > > > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > +     if (dpi->conf->is_dpintf) {
> > > > +             if (factor == 1)
> > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > +             else if (factor == 2)
> > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > > > +             else if (factor == 4)
> > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > > > +             else
> > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> > > >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> > > >       else
> > > > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > >       }
> > > >       mtk_dpi_sw_reset(dpi, false);
> > > >
> > > > +     mtk_dpi_enable(dpi);
> > > > +
> > > >       return 0;
> > > >  }
> > > >
> > > > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> > > >       u32 *input_fmts;
> > > >
> > > >       *num_input_fmts = 0;
> > > > -
> > > >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> > > >                            GFP_KERNEL);
> > > >       if (!input_fmts)
> > > > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > >               if (dpi->conf->num_output_fmts)
> > > >                       out_bus_format = dpi->conf->output_fmts[0];
> > > >
> > > > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > >               bridge_state->input_bus_cfg.format,
> > > >               bridge_state->output_bus_cfg.format);
> > > >
> > > > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> > > >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> > > >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > > > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > > > +     else
> > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > >
> > > >       return 0;
> > > >  }
> > > > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> > > >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> > > >  };
> > > >
> > > > +static const u32 mt8195_output_fmts[] = {
> > > > +     MEDIA_BUS_FMT_RGB888_1X24,
> > > > +     MEDIA_BUS_FMT_YUV8_1X24,
> > > > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > > > +};
> > > > +
> > > >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> > > >       .c_bottom = 0x0010,
> > > >       .c_top = 0x0FE0,
> > > > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> > > >       .hvsize_mask = HSIZE_MASK,
> > > >       .channel_swap_shift = CH_SWAP,
> > > >       .yuv422_en_bit = YUV422_EN,
> > > > +     .csc_enable_bit = CSC_ENABLE,
> > > >       .limit = &mtk_dpi_limit,
> > > >  };
> > > >
> > > > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> > > >       .hvsize_mask = HSIZE_MASK,
> > > >       .channel_swap_shift = CH_SWAP,
> > > >       .yuv422_en_bit = YUV422_EN,
> > > > +     .csc_enable_bit = CSC_ENABLE,
> > > >       .limit = &mtk_dpi_limit,
> > > >  };
> > > >
> > > > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> > > >       .hvsize_mask = HSIZE_MASK,
> > > >       .channel_swap_shift = CH_SWAP,
> > > >       .yuv422_en_bit = YUV422_EN,
> > > > +     .csc_enable_bit = CSC_ENABLE,
> > > >       .limit = &mtk_dpi_limit,
> > > >  };
> > > >
> > > > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> > > >       .hvsize_mask = HSIZE_MASK,
> > > >       .channel_swap_shift = CH_SWAP,
> > > >       .yuv422_en_bit = YUV422_EN,
> > > > +     .csc_enable_bit = CSC_ENABLE,
> > > >       .limit = &mtk_dpi_limit,
> > > >  };
> > > >
> > > >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> > > >       .cal_factor = mt8195_dpintf_calculate_factor,
> > > > -     .output_fmts = mt8173_output_fmts,
> > > > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > > > +     .output_fmts = mt8195_output_fmts,
> > > > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> > > >       .is_dpintf = true,
> > > > +     .csc_support = true,
> > > >       .dimension_mask = DPINTF_HPW_MASK,
> > > >       .hvsize_mask = DPINTF_HSIZE_MASK,
> > > >       .channel_swap_shift = DPINTF_CH_SWAP,
> > > >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > > > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> > > >       .limit = &mtk_dpintf_limit,
> > > >  };
> > > >
> > > > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > >               return ret;
> > > >       }
> > > >
> > > > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > > > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > > > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > > > +             if (ret != -EPROBE_DEFER)
> > > > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > > > +                             ret);
> > > > +
> > > > +             return ret;
> > > > +     }
> > > > +
> > > >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> > > >       if (IS_ERR(dpi->pixel_clk)) {
> > > >               ret = PTR_ERR(dpi->pixel_clk);
> > > > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > >               return ret;
> > > >       }
> > > >
> > > > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > > > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > > > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > > > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > > > +
> > > >       dpi->irq = platform_get_irq(pdev, 0);
> > > >       if (dpi->irq <= 0)
> > > >               return -EINVAL;
> > >
> > > All those changes look unrelated as well?
> > >
> > Do you mean all the changes in mtk_dpi.c ? They are in support of
> > enabling the mt8195 dpintf driver... so, not sure they are unlreated?
>
> I don't know that driver at all, so it's probably related if you say so
> :)
>
> The DPI interface seems to be a panel interface, so it's a bit weird to
> me why you would need to change things in the panel interface while
> claiming to support a DP output.
>
> Especially since it looks like you have some support for CSC, and
> support for additional output formats?
>
> I guess the DPI interface feeds the DP that essentially acts as a RGB ->
> DP bridge?
>
> If so, then the part adding support for CSC and additional formats
> should be split into a separate patch, and it should be mentioned in the
> commit log
>
> Maxime

I see what you mean, I'll split the patches functionally...
I'm discovering the gpu/drm tree, so It may take me a while to get it right :D

Thx,
Guillaume.

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

* Re: [PATCH v6 6/7] phy: phy-mtk-dp: Add driver for DP phy
  2021-11-13  7:48     ` Chunfeng Yun
                         ` (2 preceding siblings ...)
  (?)
@ 2021-11-24 14:37       ` Guillaume Ranquet
  -1 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-24 14:37 UTC (permalink / raw)
  To: Chun-Kuang Hu, Chunfeng Yun, Kishon Vijay Abraham I,
	Matthias Brugger, Philipp Zabel, Vinod Koul
  Cc: Markus Schneider-Pargmann, linux-kernel, linux-arm-kernel,
	linux-mediatek, linux-phy, dri-devel

Hi Chunfeng,

Quoting Chunfeng Yun (2021-11-13 08:48:37)
> On Wed, 2021-11-10 at 14:06 +0100, Guillaume Ranquet wrote:
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This is a new driver that supports the integrated DisplayPort phy for
> > mediatek SoCs, especially the mt8195. The phy is integrated into the
> > DisplayPort controller and will be created by the mtk-dp driver. This
> > driver expects a struct regmap to be able to work on the same
> > registers
> > as the DisplayPort controller. It sets the device data to be the
> > struct
> > phy so that the DisplayPort controller can easily work with it.
> >
> > The driver does not have any devicetree bindings because the
> > datasheet
> > does not list the controller and the phy as distinct units.
> >
> > The interaction with the controller can be covered by the configure
> > callback of the phy framework and its displayport parameters.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > ---
> >  MAINTAINERS                       |   1 +
> >  drivers/phy/mediatek/Kconfig      |   8 ++
> >  drivers/phy/mediatek/Makefile     |   1 +
> >  drivers/phy/mediatek/phy-mtk-dp.c | 219
> > ++++++++++++++++++++++++++++++
> >  4 files changed, 229 insertions(+)
> >  create mode 100644 drivers/phy/mediatek/phy-mtk-dp.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 170bbbeefc3f4..f9a71b6d2a4a9 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -6367,6 +6367,7 @@ L:      linux-mediatek@lists.infradead.org
> > (moderated for non-subscribers)
> >  S:   Supported
> >  F:   Documentation/devicetree/bindings/display/mediatek/
> >  F:   drivers/gpu/drm/mediatek/
> > +F:   drivers/phy/mediatek/phy-mtk-dp.c
> >  F:   drivers/phy/mediatek/phy-mtk-hdmi*
> >  F:   drivers/phy/mediatek/phy-mtk-mipi*
> >
> > diff --git a/drivers/phy/mediatek/Kconfig
> > b/drivers/phy/mediatek/Kconfig
> > index 55f8e6c048ab3..f7ec860590492 100644
> > --- a/drivers/phy/mediatek/Kconfig
> > +++ b/drivers/phy/mediatek/Kconfig
> > @@ -55,3 +55,11 @@ config PHY_MTK_MIPI_DSI
> >       select GENERIC_PHY
> >       help
> >         Support MIPI DSI for Mediatek SoCs.
> > +
> > +config PHY_MTK_DP
> > +     tristate "MediaTek DP-PHY Driver"
> > +     depends on ARCH_MEDIATEK || COMPILE_TEST
> > +     depends on OF
> > +     select GENERIC_PHY
> > +     help
> > +       Support DisplayPort PHY for Mediatek SoCs.
> > diff --git a/drivers/phy/mediatek/Makefile
> > b/drivers/phy/mediatek/Makefile
> > index ace660fbed3a1..4ba1e06504346 100644
> > --- a/drivers/phy/mediatek/Makefile
> > +++ b/drivers/phy/mediatek/Makefile
> > @@ -3,6 +3,7 @@
> >  # Makefile for the phy drivers.
> >  #
> >
> > +obj-$(CONFIG_PHY_MTK_DP)             += phy-mtk-dp.o
> >  obj-$(CONFIG_PHY_MTK_TPHY)           += phy-mtk-tphy.o
> >  obj-$(CONFIG_PHY_MTK_UFS)            += phy-mtk-ufs.o
> >  obj-$(CONFIG_PHY_MTK_XSPHY)          += phy-mtk-xsphy.o
> > diff --git a/drivers/phy/mediatek/phy-mtk-dp.c
> > b/drivers/phy/mediatek/phy-mtk-dp.c
> > new file mode 100644
> > index 0000000000000..4f8d26ec0346b
> > --- /dev/null
> > +++ b/drivers/phy/mediatek/phy-mtk-dp.c
> > @@ -0,0 +1,219 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2021 BayLibre
> > + * Author: Markus Schneider-Pargmann <msp@baylibre.com>
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/of.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +
> > +#define PHY_OFFSET 0x1000
> Why not provide register base address in DTS?
> I find that phy's platform device is registered by dp driver but the
> one automatically created when parse dts?
>
This phy driver has no dt binding whatsoever, as it shares basically
the same hardware as the DP driver,
so everything has to be either hardcoded or passed through from the DP driver.

> > +
> > +#define MTK_DP_PHY_DIG_PLL_CTL_1             (PHY_OFFSET + 0x014)
> > +# define TPLL_SSC_EN                         BIT(3)
> > +
> > +#define MTK_DP_PHY_DIG_BIT_RATE                      (PHY_OFFSET +
> > 0x03C)
> > +# define BIT_RATE_RBR                                0
> > +# define BIT_RATE_HBR                                1
> > +# define BIT_RATE_HBR2                               2
> > +# define BIT_RATE_HBR3                               3
> > +
> > +#define MTK_DP_PHY_DIG_SW_RST                        (PHY_OFFSET +
> > 0x038)
> > +# define DP_GLB_SW_RST_PHYD                  BIT(0)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_3         (PHY_OFFSET + 0x138)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_3         (PHY_OFFSET + 0x238)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_3         (PHY_OFFSET + 0x338)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_3         (PHY_OFFSET + 0x438)
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT   0x10
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT   (0x14 << 8)
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT   (0x18 << 16)
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT   (0x20 << 24)
> > +# define DRIVING_PARAM_3_DEFAULT             (XTP_LN_TX_LCTXC0_SW0_P
> > RE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW0_P
> > RE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW0_P
> > RE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW0_P
> > RE3_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_4         (PHY_OFFSET + 0x13C)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_4         (PHY_OFFSET + 0x23C)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_4         (PHY_OFFSET + 0x33C)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_4         (PHY_OFFSET + 0x43C)
> > +# define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT   0x18
> > +# define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT   (0x1e << 8)
> > +# define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT   (0x24 << 16)
> > +# define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT   (0x20 << 24)
> > +# define DRIVING_PARAM_4_DEFAULT             (XTP_LN_TX_LCTXC0_SW1_P
> > RE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW1_P
> > RE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW1_P
> > RE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW2_P
> > RE0_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_5         (PHY_OFFSET + 0x140)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_5         (PHY_OFFSET + 0x240)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_5         (PHY_OFFSET + 0x340)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_5         (PHY_OFFSET + 0x440)
> > +# define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT   0x28
> > +# define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT   (0x30 << 8)
> > +# define DRIVING_PARAM_5_DEFAULT             (XTP_LN_TX_LCTXC0_SW2_P
> > RE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW3_P
> > RE0_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_6         (PHY_OFFSET + 0x144)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_6         (PHY_OFFSET + 0x244)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_6         (PHY_OFFSET + 0x344)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_6         (PHY_OFFSET + 0x444)
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT  0x00
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT  (0x04 << 8)
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT  (0x08 << 16)
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT  (0x10 << 24)
> > +# define DRIVING_PARAM_6_DEFAULT             (XTP_LN_TX_LCTXCP1_SW0_
> > PRE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW0_
> > PRE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW0_
> > PRE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW0_
> > PRE3_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_7         (PHY_OFFSET + 0x148)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_7         (PHY_OFFSET + 0x248)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_7         (PHY_OFFSET + 0x348)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_7         (PHY_OFFSET + 0x448)
> > +# define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT  0x00
> > +# define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT  (0x06 << 8)
> > +# define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT  (0x0c << 16)
> > +# define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT  (0x00 << 24)
> > +# define DRIVING_PARAM_7_DEFAULT             (XTP_LN_TX_LCTXCP1_SW1_
> > PRE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW1_
> > PRE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW1_
> > PRE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW2_
> > PRE0_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_8         (PHY_OFFSET + 0x14C)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_8         (PHY_OFFSET + 0x24C)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_8         (PHY_OFFSET + 0x34C)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_8         (PHY_OFFSET + 0x44C)
> > +# define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT  0x08
> > +# define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT  (0x00 << 8)
> > +# define DRIVING_PARAM_8_DEFAULT             (XTP_LN_TX_LCTXCP1_SW2_
> > PRE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW3_
> > PRE0_DEFAULT)
> > +
> > +struct mtk_dp_phy {
> > +     struct regmap *regs;
> > +};
> > +
> > +static int mtk_dp_phy_init(struct phy *phy)
> > +{
> > +     struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> > +     u32 driving_params[] = {
> > +             DRIVING_PARAM_3_DEFAULT,
> > +             DRIVING_PARAM_4_DEFAULT,
> > +             DRIVING_PARAM_5_DEFAULT,
> > +             DRIVING_PARAM_6_DEFAULT,
> > +             DRIVING_PARAM_7_DEFAULT,
> > +             DRIVING_PARAM_8_DEFAULT
> > +     };
> > +
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_phy_configure(struct phy *phy, union
> > phy_configure_opts *opts)
> > +{
> > +     struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> > +     u32 val;
> > +
> > +     if (opts->dp.set_rate) {
> > +             switch (opts->dp.link_rate) {
> > +             default:
> > +                     dev_err(&phy->dev,
> > +                             "Implementation error, unknown linkrate
> > %x\n",
> > +                             opts->dp.link_rate);
> > +                     return -EINVAL;
> > +             case 1620:
> > +                     val = BIT_RATE_RBR;
> > +                     break;
> > +             case 2700:
> > +                     val = BIT_RATE_HBR;
> > +                     break;
> > +             case 5400:
> > +                     val = BIT_RATE_HBR2;
> > +                     break;
> > +             case 8100:
> > +                     val = BIT_RATE_HBR3;
> > +                     break;
> > +             }
> > +             regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE,
> > val);
> > +     }
> > +
> > +     regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
> > +                        TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN :
> > 0);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_phy_reset(struct phy *phy)
> > +{
> > +     struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> > +
> > +     regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> > +                        DP_GLB_SW_RST_PHYD, 0);
> > +     usleep_range(50, 200);
> > +     regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> > +                        DP_GLB_SW_RST_PHYD, 1);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct phy_ops mtk_dp_phy_dev_ops = {
> > +     .init = mtk_dp_phy_init,
> > +     .configure = mtk_dp_phy_configure,
> > +     .reset = mtk_dp_phy_reset,
> > +     .owner = THIS_MODULE,
> > +};
> > +
> > +static int mtk_dp_phy_probe(struct platform_device *pdev)
> > +{
> > +     struct device *dev = &pdev->dev;
> > +     struct mtk_dp_phy *dp_phy;
> > +     struct phy *phy;
> > +
> > +     dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
> > +     if (!dp_phy)
> > +             return -ENOMEM;
> > +
> > +     dp_phy->regs = *(struct regmap **)dev->platform_data;
> > +     if (!dp_phy->regs) {
> > +             dev_err(dev, "No data passed, requires struct
> > regmap**\n");
> > +             return -EINVAL;
> > +     }
> Why not get it from dts?
>
No dt bindings, this is passed from the dp driver (which triggers the
probe of the phy btw).

> > +
> > +     phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
> > +     if (IS_ERR(phy)) {
> > +             dev_err(dev, "Failed to create DP PHY: %ld\n",
> > PTR_ERR(phy));
> > +             return PTR_ERR(phy);
> > +     }
> > +     phy_set_drvdata(phy, dp_phy);
> > +
> > +     // Set device data to the phy so that mtk-dp can get it easily
> use /* */
> > +     dev_set_drvdata(dev, phy);
> > +
> > +     return 0;
> > +}
> > +
> > +struct platform_driver mtk_dp_phy_driver = {
> > +     .probe = mtk_dp_phy_probe,
> > +     .driver = {
> > +             .name = "mediatek-dp-phy",
> > +     },
> > +};
> > +module_platform_driver(mtk_dp_phy_driver);
> > +
> > +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> > +MODULE_DESCRIPTION("MediaTek DP PHY Driver");
> > +MODULE_LICENSE("GPL v2");

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 6/7] phy: phy-mtk-dp: Add driver for DP phy
@ 2021-11-24 14:37       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-24 14:37 UTC (permalink / raw)
  To: Chun-Kuang Hu, Chunfeng Yun, Kishon Vijay Abraham I,
	Matthias Brugger, Philipp Zabel, Vinod Koul
  Cc: linux-kernel, dri-devel, Markus Schneider-Pargmann,
	linux-mediatek, linux-phy, linux-arm-kernel

Hi Chunfeng,

Quoting Chunfeng Yun (2021-11-13 08:48:37)
> On Wed, 2021-11-10 at 14:06 +0100, Guillaume Ranquet wrote:
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This is a new driver that supports the integrated DisplayPort phy for
> > mediatek SoCs, especially the mt8195. The phy is integrated into the
> > DisplayPort controller and will be created by the mtk-dp driver. This
> > driver expects a struct regmap to be able to work on the same
> > registers
> > as the DisplayPort controller. It sets the device data to be the
> > struct
> > phy so that the DisplayPort controller can easily work with it.
> >
> > The driver does not have any devicetree bindings because the
> > datasheet
> > does not list the controller and the phy as distinct units.
> >
> > The interaction with the controller can be covered by the configure
> > callback of the phy framework and its displayport parameters.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > ---
> >  MAINTAINERS                       |   1 +
> >  drivers/phy/mediatek/Kconfig      |   8 ++
> >  drivers/phy/mediatek/Makefile     |   1 +
> >  drivers/phy/mediatek/phy-mtk-dp.c | 219
> > ++++++++++++++++++++++++++++++
> >  4 files changed, 229 insertions(+)
> >  create mode 100644 drivers/phy/mediatek/phy-mtk-dp.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 170bbbeefc3f4..f9a71b6d2a4a9 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -6367,6 +6367,7 @@ L:      linux-mediatek@lists.infradead.org
> > (moderated for non-subscribers)
> >  S:   Supported
> >  F:   Documentation/devicetree/bindings/display/mediatek/
> >  F:   drivers/gpu/drm/mediatek/
> > +F:   drivers/phy/mediatek/phy-mtk-dp.c
> >  F:   drivers/phy/mediatek/phy-mtk-hdmi*
> >  F:   drivers/phy/mediatek/phy-mtk-mipi*
> >
> > diff --git a/drivers/phy/mediatek/Kconfig
> > b/drivers/phy/mediatek/Kconfig
> > index 55f8e6c048ab3..f7ec860590492 100644
> > --- a/drivers/phy/mediatek/Kconfig
> > +++ b/drivers/phy/mediatek/Kconfig
> > @@ -55,3 +55,11 @@ config PHY_MTK_MIPI_DSI
> >       select GENERIC_PHY
> >       help
> >         Support MIPI DSI for Mediatek SoCs.
> > +
> > +config PHY_MTK_DP
> > +     tristate "MediaTek DP-PHY Driver"
> > +     depends on ARCH_MEDIATEK || COMPILE_TEST
> > +     depends on OF
> > +     select GENERIC_PHY
> > +     help
> > +       Support DisplayPort PHY for Mediatek SoCs.
> > diff --git a/drivers/phy/mediatek/Makefile
> > b/drivers/phy/mediatek/Makefile
> > index ace660fbed3a1..4ba1e06504346 100644
> > --- a/drivers/phy/mediatek/Makefile
> > +++ b/drivers/phy/mediatek/Makefile
> > @@ -3,6 +3,7 @@
> >  # Makefile for the phy drivers.
> >  #
> >
> > +obj-$(CONFIG_PHY_MTK_DP)             += phy-mtk-dp.o
> >  obj-$(CONFIG_PHY_MTK_TPHY)           += phy-mtk-tphy.o
> >  obj-$(CONFIG_PHY_MTK_UFS)            += phy-mtk-ufs.o
> >  obj-$(CONFIG_PHY_MTK_XSPHY)          += phy-mtk-xsphy.o
> > diff --git a/drivers/phy/mediatek/phy-mtk-dp.c
> > b/drivers/phy/mediatek/phy-mtk-dp.c
> > new file mode 100644
> > index 0000000000000..4f8d26ec0346b
> > --- /dev/null
> > +++ b/drivers/phy/mediatek/phy-mtk-dp.c
> > @@ -0,0 +1,219 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2021 BayLibre
> > + * Author: Markus Schneider-Pargmann <msp@baylibre.com>
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/of.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +
> > +#define PHY_OFFSET 0x1000
> Why not provide register base address in DTS?
> I find that phy's platform device is registered by dp driver but the
> one automatically created when parse dts?
>
This phy driver has no dt binding whatsoever, as it shares basically
the same hardware as the DP driver,
so everything has to be either hardcoded or passed through from the DP driver.

> > +
> > +#define MTK_DP_PHY_DIG_PLL_CTL_1             (PHY_OFFSET + 0x014)
> > +# define TPLL_SSC_EN                         BIT(3)
> > +
> > +#define MTK_DP_PHY_DIG_BIT_RATE                      (PHY_OFFSET +
> > 0x03C)
> > +# define BIT_RATE_RBR                                0
> > +# define BIT_RATE_HBR                                1
> > +# define BIT_RATE_HBR2                               2
> > +# define BIT_RATE_HBR3                               3
> > +
> > +#define MTK_DP_PHY_DIG_SW_RST                        (PHY_OFFSET +
> > 0x038)
> > +# define DP_GLB_SW_RST_PHYD                  BIT(0)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_3         (PHY_OFFSET + 0x138)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_3         (PHY_OFFSET + 0x238)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_3         (PHY_OFFSET + 0x338)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_3         (PHY_OFFSET + 0x438)
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT   0x10
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT   (0x14 << 8)
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT   (0x18 << 16)
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT   (0x20 << 24)
> > +# define DRIVING_PARAM_3_DEFAULT             (XTP_LN_TX_LCTXC0_SW0_P
> > RE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW0_P
> > RE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW0_P
> > RE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW0_P
> > RE3_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_4         (PHY_OFFSET + 0x13C)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_4         (PHY_OFFSET + 0x23C)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_4         (PHY_OFFSET + 0x33C)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_4         (PHY_OFFSET + 0x43C)
> > +# define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT   0x18
> > +# define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT   (0x1e << 8)
> > +# define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT   (0x24 << 16)
> > +# define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT   (0x20 << 24)
> > +# define DRIVING_PARAM_4_DEFAULT             (XTP_LN_TX_LCTXC0_SW1_P
> > RE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW1_P
> > RE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW1_P
> > RE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW2_P
> > RE0_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_5         (PHY_OFFSET + 0x140)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_5         (PHY_OFFSET + 0x240)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_5         (PHY_OFFSET + 0x340)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_5         (PHY_OFFSET + 0x440)
> > +# define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT   0x28
> > +# define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT   (0x30 << 8)
> > +# define DRIVING_PARAM_5_DEFAULT             (XTP_LN_TX_LCTXC0_SW2_P
> > RE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW3_P
> > RE0_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_6         (PHY_OFFSET + 0x144)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_6         (PHY_OFFSET + 0x244)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_6         (PHY_OFFSET + 0x344)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_6         (PHY_OFFSET + 0x444)
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT  0x00
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT  (0x04 << 8)
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT  (0x08 << 16)
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT  (0x10 << 24)
> > +# define DRIVING_PARAM_6_DEFAULT             (XTP_LN_TX_LCTXCP1_SW0_
> > PRE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW0_
> > PRE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW0_
> > PRE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW0_
> > PRE3_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_7         (PHY_OFFSET + 0x148)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_7         (PHY_OFFSET + 0x248)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_7         (PHY_OFFSET + 0x348)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_7         (PHY_OFFSET + 0x448)
> > +# define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT  0x00
> > +# define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT  (0x06 << 8)
> > +# define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT  (0x0c << 16)
> > +# define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT  (0x00 << 24)
> > +# define DRIVING_PARAM_7_DEFAULT             (XTP_LN_TX_LCTXCP1_SW1_
> > PRE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW1_
> > PRE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW1_
> > PRE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW2_
> > PRE0_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_8         (PHY_OFFSET + 0x14C)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_8         (PHY_OFFSET + 0x24C)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_8         (PHY_OFFSET + 0x34C)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_8         (PHY_OFFSET + 0x44C)
> > +# define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT  0x08
> > +# define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT  (0x00 << 8)
> > +# define DRIVING_PARAM_8_DEFAULT             (XTP_LN_TX_LCTXCP1_SW2_
> > PRE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW3_
> > PRE0_DEFAULT)
> > +
> > +struct mtk_dp_phy {
> > +     struct regmap *regs;
> > +};
> > +
> > +static int mtk_dp_phy_init(struct phy *phy)
> > +{
> > +     struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> > +     u32 driving_params[] = {
> > +             DRIVING_PARAM_3_DEFAULT,
> > +             DRIVING_PARAM_4_DEFAULT,
> > +             DRIVING_PARAM_5_DEFAULT,
> > +             DRIVING_PARAM_6_DEFAULT,
> > +             DRIVING_PARAM_7_DEFAULT,
> > +             DRIVING_PARAM_8_DEFAULT
> > +     };
> > +
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_phy_configure(struct phy *phy, union
> > phy_configure_opts *opts)
> > +{
> > +     struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> > +     u32 val;
> > +
> > +     if (opts->dp.set_rate) {
> > +             switch (opts->dp.link_rate) {
> > +             default:
> > +                     dev_err(&phy->dev,
> > +                             "Implementation error, unknown linkrate
> > %x\n",
> > +                             opts->dp.link_rate);
> > +                     return -EINVAL;
> > +             case 1620:
> > +                     val = BIT_RATE_RBR;
> > +                     break;
> > +             case 2700:
> > +                     val = BIT_RATE_HBR;
> > +                     break;
> > +             case 5400:
> > +                     val = BIT_RATE_HBR2;
> > +                     break;
> > +             case 8100:
> > +                     val = BIT_RATE_HBR3;
> > +                     break;
> > +             }
> > +             regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE,
> > val);
> > +     }
> > +
> > +     regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
> > +                        TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN :
> > 0);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_phy_reset(struct phy *phy)
> > +{
> > +     struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> > +
> > +     regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> > +                        DP_GLB_SW_RST_PHYD, 0);
> > +     usleep_range(50, 200);
> > +     regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> > +                        DP_GLB_SW_RST_PHYD, 1);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct phy_ops mtk_dp_phy_dev_ops = {
> > +     .init = mtk_dp_phy_init,
> > +     .configure = mtk_dp_phy_configure,
> > +     .reset = mtk_dp_phy_reset,
> > +     .owner = THIS_MODULE,
> > +};
> > +
> > +static int mtk_dp_phy_probe(struct platform_device *pdev)
> > +{
> > +     struct device *dev = &pdev->dev;
> > +     struct mtk_dp_phy *dp_phy;
> > +     struct phy *phy;
> > +
> > +     dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
> > +     if (!dp_phy)
> > +             return -ENOMEM;
> > +
> > +     dp_phy->regs = *(struct regmap **)dev->platform_data;
> > +     if (!dp_phy->regs) {
> > +             dev_err(dev, "No data passed, requires struct
> > regmap**\n");
> > +             return -EINVAL;
> > +     }
> Why not get it from dts?
>
No dt bindings, this is passed from the dp driver (which triggers the
probe of the phy btw).

> > +
> > +     phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
> > +     if (IS_ERR(phy)) {
> > +             dev_err(dev, "Failed to create DP PHY: %ld\n",
> > PTR_ERR(phy));
> > +             return PTR_ERR(phy);
> > +     }
> > +     phy_set_drvdata(phy, dp_phy);
> > +
> > +     // Set device data to the phy so that mtk-dp can get it easily
> use /* */
> > +     dev_set_drvdata(dev, phy);
> > +
> > +     return 0;
> > +}
> > +
> > +struct platform_driver mtk_dp_phy_driver = {
> > +     .probe = mtk_dp_phy_probe,
> > +     .driver = {
> > +             .name = "mediatek-dp-phy",
> > +     },
> > +};
> > +module_platform_driver(mtk_dp_phy_driver);
> > +
> > +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> > +MODULE_DESCRIPTION("MediaTek DP PHY Driver");
> > +MODULE_LICENSE("GPL v2");

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

* Re: [PATCH v6 6/7] phy: phy-mtk-dp: Add driver for DP phy
@ 2021-11-24 14:37       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-24 14:37 UTC (permalink / raw)
  To: Chun-Kuang Hu, Chunfeng Yun, Kishon Vijay Abraham I,
	Matthias Brugger, Philipp Zabel, Vinod Koul
  Cc: Markus Schneider-Pargmann, linux-kernel, linux-arm-kernel,
	linux-mediatek, linux-phy, dri-devel

Hi Chunfeng,

Quoting Chunfeng Yun (2021-11-13 08:48:37)
> On Wed, 2021-11-10 at 14:06 +0100, Guillaume Ranquet wrote:
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This is a new driver that supports the integrated DisplayPort phy for
> > mediatek SoCs, especially the mt8195. The phy is integrated into the
> > DisplayPort controller and will be created by the mtk-dp driver. This
> > driver expects a struct regmap to be able to work on the same
> > registers
> > as the DisplayPort controller. It sets the device data to be the
> > struct
> > phy so that the DisplayPort controller can easily work with it.
> >
> > The driver does not have any devicetree bindings because the
> > datasheet
> > does not list the controller and the phy as distinct units.
> >
> > The interaction with the controller can be covered by the configure
> > callback of the phy framework and its displayport parameters.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > ---
> >  MAINTAINERS                       |   1 +
> >  drivers/phy/mediatek/Kconfig      |   8 ++
> >  drivers/phy/mediatek/Makefile     |   1 +
> >  drivers/phy/mediatek/phy-mtk-dp.c | 219
> > ++++++++++++++++++++++++++++++
> >  4 files changed, 229 insertions(+)
> >  create mode 100644 drivers/phy/mediatek/phy-mtk-dp.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 170bbbeefc3f4..f9a71b6d2a4a9 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -6367,6 +6367,7 @@ L:      linux-mediatek@lists.infradead.org
> > (moderated for non-subscribers)
> >  S:   Supported
> >  F:   Documentation/devicetree/bindings/display/mediatek/
> >  F:   drivers/gpu/drm/mediatek/
> > +F:   drivers/phy/mediatek/phy-mtk-dp.c
> >  F:   drivers/phy/mediatek/phy-mtk-hdmi*
> >  F:   drivers/phy/mediatek/phy-mtk-mipi*
> >
> > diff --git a/drivers/phy/mediatek/Kconfig
> > b/drivers/phy/mediatek/Kconfig
> > index 55f8e6c048ab3..f7ec860590492 100644
> > --- a/drivers/phy/mediatek/Kconfig
> > +++ b/drivers/phy/mediatek/Kconfig
> > @@ -55,3 +55,11 @@ config PHY_MTK_MIPI_DSI
> >       select GENERIC_PHY
> >       help
> >         Support MIPI DSI for Mediatek SoCs.
> > +
> > +config PHY_MTK_DP
> > +     tristate "MediaTek DP-PHY Driver"
> > +     depends on ARCH_MEDIATEK || COMPILE_TEST
> > +     depends on OF
> > +     select GENERIC_PHY
> > +     help
> > +       Support DisplayPort PHY for Mediatek SoCs.
> > diff --git a/drivers/phy/mediatek/Makefile
> > b/drivers/phy/mediatek/Makefile
> > index ace660fbed3a1..4ba1e06504346 100644
> > --- a/drivers/phy/mediatek/Makefile
> > +++ b/drivers/phy/mediatek/Makefile
> > @@ -3,6 +3,7 @@
> >  # Makefile for the phy drivers.
> >  #
> >
> > +obj-$(CONFIG_PHY_MTK_DP)             += phy-mtk-dp.o
> >  obj-$(CONFIG_PHY_MTK_TPHY)           += phy-mtk-tphy.o
> >  obj-$(CONFIG_PHY_MTK_UFS)            += phy-mtk-ufs.o
> >  obj-$(CONFIG_PHY_MTK_XSPHY)          += phy-mtk-xsphy.o
> > diff --git a/drivers/phy/mediatek/phy-mtk-dp.c
> > b/drivers/phy/mediatek/phy-mtk-dp.c
> > new file mode 100644
> > index 0000000000000..4f8d26ec0346b
> > --- /dev/null
> > +++ b/drivers/phy/mediatek/phy-mtk-dp.c
> > @@ -0,0 +1,219 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2021 BayLibre
> > + * Author: Markus Schneider-Pargmann <msp@baylibre.com>
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/of.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +
> > +#define PHY_OFFSET 0x1000
> Why not provide register base address in DTS?
> I find that phy's platform device is registered by dp driver but the
> one automatically created when parse dts?
>
This phy driver has no dt binding whatsoever, as it shares basically
the same hardware as the DP driver,
so everything has to be either hardcoded or passed through from the DP driver.

> > +
> > +#define MTK_DP_PHY_DIG_PLL_CTL_1             (PHY_OFFSET + 0x014)
> > +# define TPLL_SSC_EN                         BIT(3)
> > +
> > +#define MTK_DP_PHY_DIG_BIT_RATE                      (PHY_OFFSET +
> > 0x03C)
> > +# define BIT_RATE_RBR                                0
> > +# define BIT_RATE_HBR                                1
> > +# define BIT_RATE_HBR2                               2
> > +# define BIT_RATE_HBR3                               3
> > +
> > +#define MTK_DP_PHY_DIG_SW_RST                        (PHY_OFFSET +
> > 0x038)
> > +# define DP_GLB_SW_RST_PHYD                  BIT(0)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_3         (PHY_OFFSET + 0x138)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_3         (PHY_OFFSET + 0x238)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_3         (PHY_OFFSET + 0x338)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_3         (PHY_OFFSET + 0x438)
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT   0x10
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT   (0x14 << 8)
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT   (0x18 << 16)
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT   (0x20 << 24)
> > +# define DRIVING_PARAM_3_DEFAULT             (XTP_LN_TX_LCTXC0_SW0_P
> > RE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW0_P
> > RE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW0_P
> > RE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW0_P
> > RE3_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_4         (PHY_OFFSET + 0x13C)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_4         (PHY_OFFSET + 0x23C)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_4         (PHY_OFFSET + 0x33C)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_4         (PHY_OFFSET + 0x43C)
> > +# define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT   0x18
> > +# define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT   (0x1e << 8)
> > +# define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT   (0x24 << 16)
> > +# define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT   (0x20 << 24)
> > +# define DRIVING_PARAM_4_DEFAULT             (XTP_LN_TX_LCTXC0_SW1_P
> > RE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW1_P
> > RE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW1_P
> > RE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW2_P
> > RE0_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_5         (PHY_OFFSET + 0x140)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_5         (PHY_OFFSET + 0x240)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_5         (PHY_OFFSET + 0x340)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_5         (PHY_OFFSET + 0x440)
> > +# define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT   0x28
> > +# define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT   (0x30 << 8)
> > +# define DRIVING_PARAM_5_DEFAULT             (XTP_LN_TX_LCTXC0_SW2_P
> > RE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW3_P
> > RE0_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_6         (PHY_OFFSET + 0x144)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_6         (PHY_OFFSET + 0x244)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_6         (PHY_OFFSET + 0x344)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_6         (PHY_OFFSET + 0x444)
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT  0x00
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT  (0x04 << 8)
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT  (0x08 << 16)
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT  (0x10 << 24)
> > +# define DRIVING_PARAM_6_DEFAULT             (XTP_LN_TX_LCTXCP1_SW0_
> > PRE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW0_
> > PRE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW0_
> > PRE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW0_
> > PRE3_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_7         (PHY_OFFSET + 0x148)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_7         (PHY_OFFSET + 0x248)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_7         (PHY_OFFSET + 0x348)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_7         (PHY_OFFSET + 0x448)
> > +# define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT  0x00
> > +# define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT  (0x06 << 8)
> > +# define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT  (0x0c << 16)
> > +# define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT  (0x00 << 24)
> > +# define DRIVING_PARAM_7_DEFAULT             (XTP_LN_TX_LCTXCP1_SW1_
> > PRE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW1_
> > PRE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW1_
> > PRE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW2_
> > PRE0_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_8         (PHY_OFFSET + 0x14C)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_8         (PHY_OFFSET + 0x24C)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_8         (PHY_OFFSET + 0x34C)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_8         (PHY_OFFSET + 0x44C)
> > +# define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT  0x08
> > +# define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT  (0x00 << 8)
> > +# define DRIVING_PARAM_8_DEFAULT             (XTP_LN_TX_LCTXCP1_SW2_
> > PRE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW3_
> > PRE0_DEFAULT)
> > +
> > +struct mtk_dp_phy {
> > +     struct regmap *regs;
> > +};
> > +
> > +static int mtk_dp_phy_init(struct phy *phy)
> > +{
> > +     struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> > +     u32 driving_params[] = {
> > +             DRIVING_PARAM_3_DEFAULT,
> > +             DRIVING_PARAM_4_DEFAULT,
> > +             DRIVING_PARAM_5_DEFAULT,
> > +             DRIVING_PARAM_6_DEFAULT,
> > +             DRIVING_PARAM_7_DEFAULT,
> > +             DRIVING_PARAM_8_DEFAULT
> > +     };
> > +
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_phy_configure(struct phy *phy, union
> > phy_configure_opts *opts)
> > +{
> > +     struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> > +     u32 val;
> > +
> > +     if (opts->dp.set_rate) {
> > +             switch (opts->dp.link_rate) {
> > +             default:
> > +                     dev_err(&phy->dev,
> > +                             "Implementation error, unknown linkrate
> > %x\n",
> > +                             opts->dp.link_rate);
> > +                     return -EINVAL;
> > +             case 1620:
> > +                     val = BIT_RATE_RBR;
> > +                     break;
> > +             case 2700:
> > +                     val = BIT_RATE_HBR;
> > +                     break;
> > +             case 5400:
> > +                     val = BIT_RATE_HBR2;
> > +                     break;
> > +             case 8100:
> > +                     val = BIT_RATE_HBR3;
> > +                     break;
> > +             }
> > +             regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE,
> > val);
> > +     }
> > +
> > +     regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
> > +                        TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN :
> > 0);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_phy_reset(struct phy *phy)
> > +{
> > +     struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> > +
> > +     regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> > +                        DP_GLB_SW_RST_PHYD, 0);
> > +     usleep_range(50, 200);
> > +     regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> > +                        DP_GLB_SW_RST_PHYD, 1);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct phy_ops mtk_dp_phy_dev_ops = {
> > +     .init = mtk_dp_phy_init,
> > +     .configure = mtk_dp_phy_configure,
> > +     .reset = mtk_dp_phy_reset,
> > +     .owner = THIS_MODULE,
> > +};
> > +
> > +static int mtk_dp_phy_probe(struct platform_device *pdev)
> > +{
> > +     struct device *dev = &pdev->dev;
> > +     struct mtk_dp_phy *dp_phy;
> > +     struct phy *phy;
> > +
> > +     dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
> > +     if (!dp_phy)
> > +             return -ENOMEM;
> > +
> > +     dp_phy->regs = *(struct regmap **)dev->platform_data;
> > +     if (!dp_phy->regs) {
> > +             dev_err(dev, "No data passed, requires struct
> > regmap**\n");
> > +             return -EINVAL;
> > +     }
> Why not get it from dts?
>
No dt bindings, this is passed from the dp driver (which triggers the
probe of the phy btw).

> > +
> > +     phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
> > +     if (IS_ERR(phy)) {
> > +             dev_err(dev, "Failed to create DP PHY: %ld\n",
> > PTR_ERR(phy));
> > +             return PTR_ERR(phy);
> > +     }
> > +     phy_set_drvdata(phy, dp_phy);
> > +
> > +     // Set device data to the phy so that mtk-dp can get it easily
> use /* */
> > +     dev_set_drvdata(dev, phy);
> > +
> > +     return 0;
> > +}
> > +
> > +struct platform_driver mtk_dp_phy_driver = {
> > +     .probe = mtk_dp_phy_probe,
> > +     .driver = {
> > +             .name = "mediatek-dp-phy",
> > +     },
> > +};
> > +module_platform_driver(mtk_dp_phy_driver);
> > +
> > +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> > +MODULE_DESCRIPTION("MediaTek DP PHY Driver");
> > +MODULE_LICENSE("GPL v2");

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

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

* Re: [PATCH v6 6/7] phy: phy-mtk-dp: Add driver for DP phy
@ 2021-11-24 14:37       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-24 14:37 UTC (permalink / raw)
  To: Chun-Kuang Hu, Chunfeng Yun, Kishon Vijay Abraham I,
	Matthias Brugger, Philipp Zabel, Vinod Koul
  Cc: Markus Schneider-Pargmann, linux-kernel, linux-arm-kernel,
	linux-mediatek, linux-phy, dri-devel

Hi Chunfeng,

Quoting Chunfeng Yun (2021-11-13 08:48:37)
> On Wed, 2021-11-10 at 14:06 +0100, Guillaume Ranquet wrote:
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This is a new driver that supports the integrated DisplayPort phy for
> > mediatek SoCs, especially the mt8195. The phy is integrated into the
> > DisplayPort controller and will be created by the mtk-dp driver. This
> > driver expects a struct regmap to be able to work on the same
> > registers
> > as the DisplayPort controller. It sets the device data to be the
> > struct
> > phy so that the DisplayPort controller can easily work with it.
> >
> > The driver does not have any devicetree bindings because the
> > datasheet
> > does not list the controller and the phy as distinct units.
> >
> > The interaction with the controller can be covered by the configure
> > callback of the phy framework and its displayport parameters.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > ---
> >  MAINTAINERS                       |   1 +
> >  drivers/phy/mediatek/Kconfig      |   8 ++
> >  drivers/phy/mediatek/Makefile     |   1 +
> >  drivers/phy/mediatek/phy-mtk-dp.c | 219
> > ++++++++++++++++++++++++++++++
> >  4 files changed, 229 insertions(+)
> >  create mode 100644 drivers/phy/mediatek/phy-mtk-dp.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 170bbbeefc3f4..f9a71b6d2a4a9 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -6367,6 +6367,7 @@ L:      linux-mediatek@lists.infradead.org
> > (moderated for non-subscribers)
> >  S:   Supported
> >  F:   Documentation/devicetree/bindings/display/mediatek/
> >  F:   drivers/gpu/drm/mediatek/
> > +F:   drivers/phy/mediatek/phy-mtk-dp.c
> >  F:   drivers/phy/mediatek/phy-mtk-hdmi*
> >  F:   drivers/phy/mediatek/phy-mtk-mipi*
> >
> > diff --git a/drivers/phy/mediatek/Kconfig
> > b/drivers/phy/mediatek/Kconfig
> > index 55f8e6c048ab3..f7ec860590492 100644
> > --- a/drivers/phy/mediatek/Kconfig
> > +++ b/drivers/phy/mediatek/Kconfig
> > @@ -55,3 +55,11 @@ config PHY_MTK_MIPI_DSI
> >       select GENERIC_PHY
> >       help
> >         Support MIPI DSI for Mediatek SoCs.
> > +
> > +config PHY_MTK_DP
> > +     tristate "MediaTek DP-PHY Driver"
> > +     depends on ARCH_MEDIATEK || COMPILE_TEST
> > +     depends on OF
> > +     select GENERIC_PHY
> > +     help
> > +       Support DisplayPort PHY for Mediatek SoCs.
> > diff --git a/drivers/phy/mediatek/Makefile
> > b/drivers/phy/mediatek/Makefile
> > index ace660fbed3a1..4ba1e06504346 100644
> > --- a/drivers/phy/mediatek/Makefile
> > +++ b/drivers/phy/mediatek/Makefile
> > @@ -3,6 +3,7 @@
> >  # Makefile for the phy drivers.
> >  #
> >
> > +obj-$(CONFIG_PHY_MTK_DP)             += phy-mtk-dp.o
> >  obj-$(CONFIG_PHY_MTK_TPHY)           += phy-mtk-tphy.o
> >  obj-$(CONFIG_PHY_MTK_UFS)            += phy-mtk-ufs.o
> >  obj-$(CONFIG_PHY_MTK_XSPHY)          += phy-mtk-xsphy.o
> > diff --git a/drivers/phy/mediatek/phy-mtk-dp.c
> > b/drivers/phy/mediatek/phy-mtk-dp.c
> > new file mode 100644
> > index 0000000000000..4f8d26ec0346b
> > --- /dev/null
> > +++ b/drivers/phy/mediatek/phy-mtk-dp.c
> > @@ -0,0 +1,219 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2021 BayLibre
> > + * Author: Markus Schneider-Pargmann <msp@baylibre.com>
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/of.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +
> > +#define PHY_OFFSET 0x1000
> Why not provide register base address in DTS?
> I find that phy's platform device is registered by dp driver but the
> one automatically created when parse dts?
>
This phy driver has no dt binding whatsoever, as it shares basically
the same hardware as the DP driver,
so everything has to be either hardcoded or passed through from the DP driver.

> > +
> > +#define MTK_DP_PHY_DIG_PLL_CTL_1             (PHY_OFFSET + 0x014)
> > +# define TPLL_SSC_EN                         BIT(3)
> > +
> > +#define MTK_DP_PHY_DIG_BIT_RATE                      (PHY_OFFSET +
> > 0x03C)
> > +# define BIT_RATE_RBR                                0
> > +# define BIT_RATE_HBR                                1
> > +# define BIT_RATE_HBR2                               2
> > +# define BIT_RATE_HBR3                               3
> > +
> > +#define MTK_DP_PHY_DIG_SW_RST                        (PHY_OFFSET +
> > 0x038)
> > +# define DP_GLB_SW_RST_PHYD                  BIT(0)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_3         (PHY_OFFSET + 0x138)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_3         (PHY_OFFSET + 0x238)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_3         (PHY_OFFSET + 0x338)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_3         (PHY_OFFSET + 0x438)
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT   0x10
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT   (0x14 << 8)
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT   (0x18 << 16)
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT   (0x20 << 24)
> > +# define DRIVING_PARAM_3_DEFAULT             (XTP_LN_TX_LCTXC0_SW0_P
> > RE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW0_P
> > RE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW0_P
> > RE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW0_P
> > RE3_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_4         (PHY_OFFSET + 0x13C)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_4         (PHY_OFFSET + 0x23C)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_4         (PHY_OFFSET + 0x33C)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_4         (PHY_OFFSET + 0x43C)
> > +# define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT   0x18
> > +# define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT   (0x1e << 8)
> > +# define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT   (0x24 << 16)
> > +# define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT   (0x20 << 24)
> > +# define DRIVING_PARAM_4_DEFAULT             (XTP_LN_TX_LCTXC0_SW1_P
> > RE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW1_P
> > RE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW1_P
> > RE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW2_P
> > RE0_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_5         (PHY_OFFSET + 0x140)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_5         (PHY_OFFSET + 0x240)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_5         (PHY_OFFSET + 0x340)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_5         (PHY_OFFSET + 0x440)
> > +# define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT   0x28
> > +# define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT   (0x30 << 8)
> > +# define DRIVING_PARAM_5_DEFAULT             (XTP_LN_TX_LCTXC0_SW2_P
> > RE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW3_P
> > RE0_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_6         (PHY_OFFSET + 0x144)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_6         (PHY_OFFSET + 0x244)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_6         (PHY_OFFSET + 0x344)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_6         (PHY_OFFSET + 0x444)
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT  0x00
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT  (0x04 << 8)
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT  (0x08 << 16)
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT  (0x10 << 24)
> > +# define DRIVING_PARAM_6_DEFAULT             (XTP_LN_TX_LCTXCP1_SW0_
> > PRE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW0_
> > PRE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW0_
> > PRE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW0_
> > PRE3_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_7         (PHY_OFFSET + 0x148)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_7         (PHY_OFFSET + 0x248)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_7         (PHY_OFFSET + 0x348)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_7         (PHY_OFFSET + 0x448)
> > +# define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT  0x00
> > +# define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT  (0x06 << 8)
> > +# define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT  (0x0c << 16)
> > +# define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT  (0x00 << 24)
> > +# define DRIVING_PARAM_7_DEFAULT             (XTP_LN_TX_LCTXCP1_SW1_
> > PRE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW1_
> > PRE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW1_
> > PRE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW2_
> > PRE0_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_8         (PHY_OFFSET + 0x14C)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_8         (PHY_OFFSET + 0x24C)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_8         (PHY_OFFSET + 0x34C)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_8         (PHY_OFFSET + 0x44C)
> > +# define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT  0x08
> > +# define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT  (0x00 << 8)
> > +# define DRIVING_PARAM_8_DEFAULT             (XTP_LN_TX_LCTXCP1_SW2_
> > PRE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW3_
> > PRE0_DEFAULT)
> > +
> > +struct mtk_dp_phy {
> > +     struct regmap *regs;
> > +};
> > +
> > +static int mtk_dp_phy_init(struct phy *phy)
> > +{
> > +     struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> > +     u32 driving_params[] = {
> > +             DRIVING_PARAM_3_DEFAULT,
> > +             DRIVING_PARAM_4_DEFAULT,
> > +             DRIVING_PARAM_5_DEFAULT,
> > +             DRIVING_PARAM_6_DEFAULT,
> > +             DRIVING_PARAM_7_DEFAULT,
> > +             DRIVING_PARAM_8_DEFAULT
> > +     };
> > +
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_phy_configure(struct phy *phy, union
> > phy_configure_opts *opts)
> > +{
> > +     struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> > +     u32 val;
> > +
> > +     if (opts->dp.set_rate) {
> > +             switch (opts->dp.link_rate) {
> > +             default:
> > +                     dev_err(&phy->dev,
> > +                             "Implementation error, unknown linkrate
> > %x\n",
> > +                             opts->dp.link_rate);
> > +                     return -EINVAL;
> > +             case 1620:
> > +                     val = BIT_RATE_RBR;
> > +                     break;
> > +             case 2700:
> > +                     val = BIT_RATE_HBR;
> > +                     break;
> > +             case 5400:
> > +                     val = BIT_RATE_HBR2;
> > +                     break;
> > +             case 8100:
> > +                     val = BIT_RATE_HBR3;
> > +                     break;
> > +             }
> > +             regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE,
> > val);
> > +     }
> > +
> > +     regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
> > +                        TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN :
> > 0);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_phy_reset(struct phy *phy)
> > +{
> > +     struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> > +
> > +     regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> > +                        DP_GLB_SW_RST_PHYD, 0);
> > +     usleep_range(50, 200);
> > +     regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> > +                        DP_GLB_SW_RST_PHYD, 1);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct phy_ops mtk_dp_phy_dev_ops = {
> > +     .init = mtk_dp_phy_init,
> > +     .configure = mtk_dp_phy_configure,
> > +     .reset = mtk_dp_phy_reset,
> > +     .owner = THIS_MODULE,
> > +};
> > +
> > +static int mtk_dp_phy_probe(struct platform_device *pdev)
> > +{
> > +     struct device *dev = &pdev->dev;
> > +     struct mtk_dp_phy *dp_phy;
> > +     struct phy *phy;
> > +
> > +     dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
> > +     if (!dp_phy)
> > +             return -ENOMEM;
> > +
> > +     dp_phy->regs = *(struct regmap **)dev->platform_data;
> > +     if (!dp_phy->regs) {
> > +             dev_err(dev, "No data passed, requires struct
> > regmap**\n");
> > +             return -EINVAL;
> > +     }
> Why not get it from dts?
>
No dt bindings, this is passed from the dp driver (which triggers the
probe of the phy btw).

> > +
> > +     phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
> > +     if (IS_ERR(phy)) {
> > +             dev_err(dev, "Failed to create DP PHY: %ld\n",
> > PTR_ERR(phy));
> > +             return PTR_ERR(phy);
> > +     }
> > +     phy_set_drvdata(phy, dp_phy);
> > +
> > +     // Set device data to the phy so that mtk-dp can get it easily
> use /* */
> > +     dev_set_drvdata(dev, phy);
> > +
> > +     return 0;
> > +}
> > +
> > +struct platform_driver mtk_dp_phy_driver = {
> > +     .probe = mtk_dp_phy_probe,
> > +     .driver = {
> > +             .name = "mediatek-dp-phy",
> > +     },
> > +};
> > +module_platform_driver(mtk_dp_phy_driver);
> > +
> > +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> > +MODULE_DESCRIPTION("MediaTek DP PHY Driver");
> > +MODULE_LICENSE("GPL v2");

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 6/7] phy: phy-mtk-dp: Add driver for DP phy
@ 2021-11-24 14:37       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-11-24 14:37 UTC (permalink / raw)
  To: Chun-Kuang Hu, Chunfeng Yun, Kishon Vijay Abraham I,
	Matthias Brugger, Philipp Zabel, Vinod Koul
  Cc: Markus Schneider-Pargmann, linux-kernel, linux-arm-kernel,
	linux-mediatek, linux-phy, dri-devel

Hi Chunfeng,

Quoting Chunfeng Yun (2021-11-13 08:48:37)
> On Wed, 2021-11-10 at 14:06 +0100, Guillaume Ranquet wrote:
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This is a new driver that supports the integrated DisplayPort phy for
> > mediatek SoCs, especially the mt8195. The phy is integrated into the
> > DisplayPort controller and will be created by the mtk-dp driver. This
> > driver expects a struct regmap to be able to work on the same
> > registers
> > as the DisplayPort controller. It sets the device data to be the
> > struct
> > phy so that the DisplayPort controller can easily work with it.
> >
> > The driver does not have any devicetree bindings because the
> > datasheet
> > does not list the controller and the phy as distinct units.
> >
> > The interaction with the controller can be covered by the configure
> > callback of the phy framework and its displayport parameters.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > ---
> >  MAINTAINERS                       |   1 +
> >  drivers/phy/mediatek/Kconfig      |   8 ++
> >  drivers/phy/mediatek/Makefile     |   1 +
> >  drivers/phy/mediatek/phy-mtk-dp.c | 219
> > ++++++++++++++++++++++++++++++
> >  4 files changed, 229 insertions(+)
> >  create mode 100644 drivers/phy/mediatek/phy-mtk-dp.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 170bbbeefc3f4..f9a71b6d2a4a9 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -6367,6 +6367,7 @@ L:      linux-mediatek@lists.infradead.org
> > (moderated for non-subscribers)
> >  S:   Supported
> >  F:   Documentation/devicetree/bindings/display/mediatek/
> >  F:   drivers/gpu/drm/mediatek/
> > +F:   drivers/phy/mediatek/phy-mtk-dp.c
> >  F:   drivers/phy/mediatek/phy-mtk-hdmi*
> >  F:   drivers/phy/mediatek/phy-mtk-mipi*
> >
> > diff --git a/drivers/phy/mediatek/Kconfig
> > b/drivers/phy/mediatek/Kconfig
> > index 55f8e6c048ab3..f7ec860590492 100644
> > --- a/drivers/phy/mediatek/Kconfig
> > +++ b/drivers/phy/mediatek/Kconfig
> > @@ -55,3 +55,11 @@ config PHY_MTK_MIPI_DSI
> >       select GENERIC_PHY
> >       help
> >         Support MIPI DSI for Mediatek SoCs.
> > +
> > +config PHY_MTK_DP
> > +     tristate "MediaTek DP-PHY Driver"
> > +     depends on ARCH_MEDIATEK || COMPILE_TEST
> > +     depends on OF
> > +     select GENERIC_PHY
> > +     help
> > +       Support DisplayPort PHY for Mediatek SoCs.
> > diff --git a/drivers/phy/mediatek/Makefile
> > b/drivers/phy/mediatek/Makefile
> > index ace660fbed3a1..4ba1e06504346 100644
> > --- a/drivers/phy/mediatek/Makefile
> > +++ b/drivers/phy/mediatek/Makefile
> > @@ -3,6 +3,7 @@
> >  # Makefile for the phy drivers.
> >  #
> >
> > +obj-$(CONFIG_PHY_MTK_DP)             += phy-mtk-dp.o
> >  obj-$(CONFIG_PHY_MTK_TPHY)           += phy-mtk-tphy.o
> >  obj-$(CONFIG_PHY_MTK_UFS)            += phy-mtk-ufs.o
> >  obj-$(CONFIG_PHY_MTK_XSPHY)          += phy-mtk-xsphy.o
> > diff --git a/drivers/phy/mediatek/phy-mtk-dp.c
> > b/drivers/phy/mediatek/phy-mtk-dp.c
> > new file mode 100644
> > index 0000000000000..4f8d26ec0346b
> > --- /dev/null
> > +++ b/drivers/phy/mediatek/phy-mtk-dp.c
> > @@ -0,0 +1,219 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2021 BayLibre
> > + * Author: Markus Schneider-Pargmann <msp@baylibre.com>
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/of.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +
> > +#define PHY_OFFSET 0x1000
> Why not provide register base address in DTS?
> I find that phy's platform device is registered by dp driver but the
> one automatically created when parse dts?
>
This phy driver has no dt binding whatsoever, as it shares basically
the same hardware as the DP driver,
so everything has to be either hardcoded or passed through from the DP driver.

> > +
> > +#define MTK_DP_PHY_DIG_PLL_CTL_1             (PHY_OFFSET + 0x014)
> > +# define TPLL_SSC_EN                         BIT(3)
> > +
> > +#define MTK_DP_PHY_DIG_BIT_RATE                      (PHY_OFFSET +
> > 0x03C)
> > +# define BIT_RATE_RBR                                0
> > +# define BIT_RATE_HBR                                1
> > +# define BIT_RATE_HBR2                               2
> > +# define BIT_RATE_HBR3                               3
> > +
> > +#define MTK_DP_PHY_DIG_SW_RST                        (PHY_OFFSET +
> > 0x038)
> > +# define DP_GLB_SW_RST_PHYD                  BIT(0)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_3         (PHY_OFFSET + 0x138)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_3         (PHY_OFFSET + 0x238)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_3         (PHY_OFFSET + 0x338)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_3         (PHY_OFFSET + 0x438)
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT   0x10
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT   (0x14 << 8)
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT   (0x18 << 16)
> > +# define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT   (0x20 << 24)
> > +# define DRIVING_PARAM_3_DEFAULT             (XTP_LN_TX_LCTXC0_SW0_P
> > RE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW0_P
> > RE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW0_P
> > RE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW0_P
> > RE3_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_4         (PHY_OFFSET + 0x13C)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_4         (PHY_OFFSET + 0x23C)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_4         (PHY_OFFSET + 0x33C)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_4         (PHY_OFFSET + 0x43C)
> > +# define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT   0x18
> > +# define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT   (0x1e << 8)
> > +# define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT   (0x24 << 16)
> > +# define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT   (0x20 << 24)
> > +# define DRIVING_PARAM_4_DEFAULT             (XTP_LN_TX_LCTXC0_SW1_P
> > RE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW1_P
> > RE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW1_P
> > RE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW2_P
> > RE0_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_5         (PHY_OFFSET + 0x140)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_5         (PHY_OFFSET + 0x240)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_5         (PHY_OFFSET + 0x340)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_5         (PHY_OFFSET + 0x440)
> > +# define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT   0x28
> > +# define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT   (0x30 << 8)
> > +# define DRIVING_PARAM_5_DEFAULT             (XTP_LN_TX_LCTXC0_SW2_P
> > RE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXC0_SW3_P
> > RE0_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_6         (PHY_OFFSET + 0x144)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_6         (PHY_OFFSET + 0x244)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_6         (PHY_OFFSET + 0x344)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_6         (PHY_OFFSET + 0x444)
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT  0x00
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT  (0x04 << 8)
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT  (0x08 << 16)
> > +# define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT  (0x10 << 24)
> > +# define DRIVING_PARAM_6_DEFAULT             (XTP_LN_TX_LCTXCP1_SW0_
> > PRE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW0_
> > PRE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW0_
> > PRE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW0_
> > PRE3_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_7         (PHY_OFFSET + 0x148)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_7         (PHY_OFFSET + 0x248)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_7         (PHY_OFFSET + 0x348)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_7         (PHY_OFFSET + 0x448)
> > +# define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT  0x00
> > +# define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT  (0x06 << 8)
> > +# define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT  (0x0c << 16)
> > +# define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT  (0x00 << 24)
> > +# define DRIVING_PARAM_7_DEFAULT             (XTP_LN_TX_LCTXCP1_SW1_
> > PRE0_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW1_
> > PRE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW1_
> > PRE2_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW2_
> > PRE0_DEFAULT)
> > +
> > +#define MTK_DP_LANE0_DRIVING_PARAM_8         (PHY_OFFSET + 0x14C)
> > +#define MTK_DP_LANE1_DRIVING_PARAM_8         (PHY_OFFSET + 0x24C)
> > +#define MTK_DP_LANE2_DRIVING_PARAM_8         (PHY_OFFSET + 0x34C)
> > +#define MTK_DP_LANE3_DRIVING_PARAM_8         (PHY_OFFSET + 0x44C)
> > +# define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT  0x08
> > +# define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT  (0x00 << 8)
> > +# define DRIVING_PARAM_8_DEFAULT             (XTP_LN_TX_LCTXCP1_SW2_
> > PRE1_DEFAULT | \
> > +                                              XTP_LN_TX_LCTXCP1_SW3_
> > PRE0_DEFAULT)
> > +
> > +struct mtk_dp_phy {
> > +     struct regmap *regs;
> > +};
> > +
> > +static int mtk_dp_phy_init(struct phy *phy)
> > +{
> > +     struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> > +     u32 driving_params[] = {
> > +             DRIVING_PARAM_3_DEFAULT,
> > +             DRIVING_PARAM_4_DEFAULT,
> > +             DRIVING_PARAM_5_DEFAULT,
> > +             DRIVING_PARAM_6_DEFAULT,
> > +             DRIVING_PARAM_7_DEFAULT,
> > +             DRIVING_PARAM_8_DEFAULT
> > +     };
> > +
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +     regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
> > +                       driving_params, ARRAY_SIZE(driving_params));
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_phy_configure(struct phy *phy, union
> > phy_configure_opts *opts)
> > +{
> > +     struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> > +     u32 val;
> > +
> > +     if (opts->dp.set_rate) {
> > +             switch (opts->dp.link_rate) {
> > +             default:
> > +                     dev_err(&phy->dev,
> > +                             "Implementation error, unknown linkrate
> > %x\n",
> > +                             opts->dp.link_rate);
> > +                     return -EINVAL;
> > +             case 1620:
> > +                     val = BIT_RATE_RBR;
> > +                     break;
> > +             case 2700:
> > +                     val = BIT_RATE_HBR;
> > +                     break;
> > +             case 5400:
> > +                     val = BIT_RATE_HBR2;
> > +                     break;
> > +             case 8100:
> > +                     val = BIT_RATE_HBR3;
> > +                     break;
> > +             }
> > +             regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE,
> > val);
> > +     }
> > +
> > +     regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
> > +                        TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN :
> > 0);
> > +
> > +     return 0;
> > +}
> > +
> > +static int mtk_dp_phy_reset(struct phy *phy)
> > +{
> > +     struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> > +
> > +     regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> > +                        DP_GLB_SW_RST_PHYD, 0);
> > +     usleep_range(50, 200);
> > +     regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
> > +                        DP_GLB_SW_RST_PHYD, 1);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct phy_ops mtk_dp_phy_dev_ops = {
> > +     .init = mtk_dp_phy_init,
> > +     .configure = mtk_dp_phy_configure,
> > +     .reset = mtk_dp_phy_reset,
> > +     .owner = THIS_MODULE,
> > +};
> > +
> > +static int mtk_dp_phy_probe(struct platform_device *pdev)
> > +{
> > +     struct device *dev = &pdev->dev;
> > +     struct mtk_dp_phy *dp_phy;
> > +     struct phy *phy;
> > +
> > +     dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
> > +     if (!dp_phy)
> > +             return -ENOMEM;
> > +
> > +     dp_phy->regs = *(struct regmap **)dev->platform_data;
> > +     if (!dp_phy->regs) {
> > +             dev_err(dev, "No data passed, requires struct
> > regmap**\n");
> > +             return -EINVAL;
> > +     }
> Why not get it from dts?
>
No dt bindings, this is passed from the dp driver (which triggers the
probe of the phy btw).

> > +
> > +     phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
> > +     if (IS_ERR(phy)) {
> > +             dev_err(dev, "Failed to create DP PHY: %ld\n",
> > PTR_ERR(phy));
> > +             return PTR_ERR(phy);
> > +     }
> > +     phy_set_drvdata(phy, dp_phy);
> > +
> > +     // Set device data to the phy so that mtk-dp can get it easily
> use /* */
> > +     dev_set_drvdata(dev, phy);
> > +
> > +     return 0;
> > +}
> > +
> > +struct platform_driver mtk_dp_phy_driver = {
> > +     .probe = mtk_dp_phy_probe,
> > +     .driver = {
> > +             .name = "mediatek-dp-phy",
> > +     },
> > +};
> > +module_platform_driver(mtk_dp_phy_driver);
> > +
> > +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> > +MODULE_DESCRIPTION("MediaTek DP PHY Driver");
> > +MODULE_LICENSE("GPL v2");

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-11-24 13:45           ` Guillaume Ranquet
  (?)
  (?)
@ 2021-11-25 14:30             ` Maxime Ripard
  -1 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-11-25 14:30 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Chun-Kuang Hu, David Airlie, Thomas Zimmermann, linux-kernel,
	dri-devel, Markus Schneider-Pargmann, linux-mediatek,
	Matthias Brugger, linux-arm-kernel, kernel test robot

[-- Attachment #1: Type: text/plain, Size: 20365 bytes --]

On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> Hi,
> Thanks for all your input, really appreciated.
> 
> Quoting Maxime Ripard (2021-11-16 15:51:12)
> > Hi,
> >
> > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > The driver creates a child device for the phy. The child device will
> > > > > never exist without the parent being active. As they are sharing a
> > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > both can work with the same register range. The phy driver sets device
> > > > > data that is read by the parent to get the phy device that can be used
> > > > > to control the phy properties.
> > > >
> > > > If the PHY is in the same register space than the DP controller, why do
> > > > you need a separate PHY driver in the first place?
> > >
> > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > >
> > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> >
> > It's a bit of a circular argument though :)
> >
> > It's a separate phy driver because it needs to go through another
> > maintainer's tree, but it needs to go through another maintainer's tree
> > because it's a separate phy driver.
> >
> > It doesn't explain why it needs to be a separate phy driver? Why can't
> > the phy setup be done directly in the DP driver, if it's essentially a
> > single device?
> >
> > That being said, usually what those kind of questions mean is that
> > you're missing a comment or something in the commit log to provide that
> > context in the first place, so it would be great to add that context
> > here.
> >
> > And it will avoid the situation we're now in where multiple reviewers
> > ask the same questions over and over again :)
> >
> At first I didn't understand your reply, then I realized I gave you
> the wrong link...
> my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> 
> The driver and phy were a single driver until v2 of this patch series
> and the phy setup
> was done directly in the driver (single driver, single C file).
> Here's the relevant link to the discussion between Chun-Kuang and Markus
> 
> https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> 
> I'll try to find a way to make it clearer for v7.

OK, it makes sense then :)

There's something weird though: the devices definitely look like they're
in a separate register range, yet you mention a regmap to handle the
shared register range. That range doesn't seem described anywhere in the
device tree though? What is it for?

> > > > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > > > +                                     struct drm_bridge_state *old_state)
> > > > > +{
> > > > > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > > > +     struct drm_connector *conn;
> > > > > +     struct drm_connector_state *conn_state;
> > > > > +     struct drm_crtc *crtc;
> > > > > +     struct drm_crtc_state *crtc_state;
> > > > > +     int ret = 0;
> > > > > +     int i;
> > > > > +
> > > > > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > > > +                                                     bridge->encoder);
> > > > > +     if (!conn) {
> > > > > +             drm_err(mtk_dp->drm_dev,
> > > > > +                     "Can't enable bridge as connector is missing\n");
> > > > > +             return;
> > > > > +     }
> > > > > +
> > > > > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > > >
> > > > This should be protected by a mutex (just like any resource shared
> > > > between KMS and ALSA)
> > >
> > > Ok.
> >
> > I forgot to ask (even though checkpatch does mention it iirc), but since
> > you have multiple mutex it would be nice to have a comment for each
> > mutex stating exactly what it protects, and against what.
> >
> > It's hard otherwise to remember or figure out what the locks are here
> > for.
> >
> > > > > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > > > > +     if (ret)
> > > > > +             return ret;
> > > >
> > > > pdata?
> > > >
> > > can you elaborate?
> >
> > Sorry, yeah, pdata is usually the abbreviation used in linux for the
> > platform_data mechanism, but you're using the DT to retrieve your
> > resources (and platform_data usually don't involve any parsing), so the
> > name is odd.
> >
> > > > > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > index 384074f69111b..e6e88e3cd811d 100644
> > > > > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> > > > >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> > > > >  };
> > > > >
> > > > > +enum TVDPLL_CLK {
> > > > > +     TVDPLL_PLL = 0,
> > > > > +     TVDPLL_D2 = 2,
> > > > > +     TVDPLL_D4 = 4,
> > > > > +     TVDPLL_D8 = 8,
> > > > > +     TVDPLL_D16 = 16,
> > > > > +};
> > > > > +
> > > > >  struct mtk_dpi {
> > > > >       struct drm_encoder encoder;
> > > > >       struct drm_bridge bridge;
> > > > > @@ -71,8 +79,10 @@ struct mtk_dpi {
> > > > >       void __iomem *regs;
> > > > >       struct device *dev;
> > > > >       struct clk *engine_clk;
> > > > > +     struct clk *dpi_ck_cg;
> > > > >       struct clk *pixel_clk;
> > > > >       struct clk *tvd_clk;
> > > > > +     struct clk *pclk_src[5];
> > > > >       int irq;
> > > > >       struct drm_display_mode mode;
> > > > >       const struct mtk_dpi_conf *conf;
> > > > > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> > > > >       u32 hvsize_mask;
> > > > >       u32 channel_swap_shift;
> > > > >       u32 yuv422_en_bit;
> > > > > +     u32 csc_enable_bit;
> > > > >       const struct mtk_dpi_yc_limit *limit;
> > > > >  };
> > > > >
> > > > > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> > > > >
> > > > >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> > > > >  {
> > > > > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > > > > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> > > > >  }
> > > > >
> > > > >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > > > > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> > > > >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> > > > >  }
> > > > >
> > > > > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > > > > +{
> > > > > +     u32 matrix_sel = 0;
> > > > > +
> > > > > +     switch (format) {
> > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > > > > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > > > > +             if (dpi->mode.hdisplay <= 720)
> > > > > +                     matrix_sel = 0x2;
> > > > > +             break;
> > > > > +     default:
> > > > > +             break;
> > > > > +     }
> > > > > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > > > > +}
> > > > > +
> > > > >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> > > > >                                       enum mtk_dpi_out_color_format format)
> > > > >  {
> > > > >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> > > > >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> > > > >               mtk_dpi_config_yuv422_enable(dpi, false);
> > > > > -             if (dpi->conf->csc_support)
> > > > > +             if (dpi->conf->csc_support) {
> > > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > > +             }
> > > > >               if (dpi->conf->swap_input_support)
> > > > >                       mtk_dpi_config_swap_input(dpi, false);
> > > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> > > > >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> > > > >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> > > > >               mtk_dpi_config_yuv422_enable(dpi, true);
> > > > > -             if (dpi->conf->csc_support)
> > > > > +             if (dpi->conf->csc_support) {
> > > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > > +             }
> > > > >               if (dpi->conf->swap_input_support)
> > > > >                       mtk_dpi_config_swap_input(dpi, true);
> > > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > > > > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> > > > >       mtk_dpi_disable(dpi);
> > > > >       clk_disable_unprepare(dpi->pixel_clk);
> > > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > > +     clk_disable_unprepare(dpi->tvd_clk);
> > > > >  }
> > > > >
> > > > >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > >       if (++dpi->refcount != 1)
> > > > >               return 0;
> > > > >
> > > > > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > > > > +     if (ret) {
> > > > > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > > > > +             goto err_pixel;
> > > > > +     }
> > > > > +
> > > > >       ret = clk_prepare_enable(dpi->engine_clk);
> > > > >       if (ret) {
> > > > >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> > > > >               goto err_refcount;
> > > > >       }
> > > > >
> > > > > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > > > > +     if (ret) {
> > > > > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > > > > +             goto err_ck_cg;
> > > > > +     }
> > > > > +
> > > > >       ret = clk_prepare_enable(dpi->pixel_clk);
> > > > >       if (ret) {
> > > > >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > > > > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > >       if (dpi->pinctrl && dpi->pins_dpi)
> > > > >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> > > > >
> > > > > -     mtk_dpi_enable(dpi);
> > > > >       return 0;
> > > > >
> > > > >  err_pixel:
> > > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > > +err_ck_cg:
> > > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > >  err_refcount:
> > > > >       dpi->refcount--;
> > > > > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > > >       pll_rate = clk_get_rate(dpi->tvd_clk);
> > > > >
> > > > >       vm.pixelclock = pll_rate / factor;
> > > > > -     if (dpi->conf->is_dpintf)
> > > > > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > > > > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > > +     if (dpi->conf->is_dpintf) {
> > > > > +             if (factor == 1)
> > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > > +             else if (factor == 2)
> > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > > > > +             else if (factor == 4)
> > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > > > > +             else
> > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> > > > >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> > > > >       else
> > > > > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > > >       }
> > > > >       mtk_dpi_sw_reset(dpi, false);
> > > > >
> > > > > +     mtk_dpi_enable(dpi);
> > > > > +
> > > > >       return 0;
> > > > >  }
> > > > >
> > > > > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> > > > >       u32 *input_fmts;
> > > > >
> > > > >       *num_input_fmts = 0;
> > > > > -
> > > > >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> > > > >                            GFP_KERNEL);
> > > > >       if (!input_fmts)
> > > > > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > > >               if (dpi->conf->num_output_fmts)
> > > > >                       out_bus_format = dpi->conf->output_fmts[0];
> > > > >
> > > > > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > >               bridge_state->input_bus_cfg.format,
> > > > >               bridge_state->output_bus_cfg.format);
> > > > >
> > > > > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > > >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> > > > >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> > > > >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > > > > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > > > > +     else
> > > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > >
> > > > >       return 0;
> > > > >  }
> > > > > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> > > > >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> > > > >  };
> > > > >
> > > > > +static const u32 mt8195_output_fmts[] = {
> > > > > +     MEDIA_BUS_FMT_RGB888_1X24,
> > > > > +     MEDIA_BUS_FMT_YUV8_1X24,
> > > > > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > > > > +};
> > > > > +
> > > > >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> > > > >       .c_bottom = 0x0010,
> > > > >       .c_top = 0x0FE0,
> > > > > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> > > > >       .hvsize_mask = HSIZE_MASK,
> > > > >       .channel_swap_shift = CH_SWAP,
> > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > >       .limit = &mtk_dpi_limit,
> > > > >  };
> > > > >
> > > > > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> > > > >       .hvsize_mask = HSIZE_MASK,
> > > > >       .channel_swap_shift = CH_SWAP,
> > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > >       .limit = &mtk_dpi_limit,
> > > > >  };
> > > > >
> > > > > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> > > > >       .hvsize_mask = HSIZE_MASK,
> > > > >       .channel_swap_shift = CH_SWAP,
> > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > >       .limit = &mtk_dpi_limit,
> > > > >  };
> > > > >
> > > > > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> > > > >       .hvsize_mask = HSIZE_MASK,
> > > > >       .channel_swap_shift = CH_SWAP,
> > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > >       .limit = &mtk_dpi_limit,
> > > > >  };
> > > > >
> > > > >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> > > > >       .cal_factor = mt8195_dpintf_calculate_factor,
> > > > > -     .output_fmts = mt8173_output_fmts,
> > > > > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > > > > +     .output_fmts = mt8195_output_fmts,
> > > > > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> > > > >       .is_dpintf = true,
> > > > > +     .csc_support = true,
> > > > >       .dimension_mask = DPINTF_HPW_MASK,
> > > > >       .hvsize_mask = DPINTF_HSIZE_MASK,
> > > > >       .channel_swap_shift = DPINTF_CH_SWAP,
> > > > >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > > > > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> > > > >       .limit = &mtk_dpintf_limit,
> > > > >  };
> > > > >
> > > > > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > > >               return ret;
> > > > >       }
> > > > >
> > > > > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > > > > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > > > > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > > > > +             if (ret != -EPROBE_DEFER)
> > > > > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > > > > +                             ret);
> > > > > +
> > > > > +             return ret;
> > > > > +     }
> > > > > +
> > > > >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> > > > >       if (IS_ERR(dpi->pixel_clk)) {
> > > > >               ret = PTR_ERR(dpi->pixel_clk);
> > > > > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > > >               return ret;
> > > > >       }
> > > > >
> > > > > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > > > > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > > > > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > > > > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > > > > +
> > > > >       dpi->irq = platform_get_irq(pdev, 0);
> > > > >       if (dpi->irq <= 0)
> > > > >               return -EINVAL;
> > > >
> > > > All those changes look unrelated as well?
> > > >
> > > Do you mean all the changes in mtk_dpi.c ? They are in support of
> > > enabling the mt8195 dpintf driver... so, not sure they are unlreated?
> >
> > I don't know that driver at all, so it's probably related if you say so
> > :)
> >
> > The DPI interface seems to be a panel interface, so it's a bit weird to
> > me why you would need to change things in the panel interface while
> > claiming to support a DP output.
> >
> > Especially since it looks like you have some support for CSC, and
> > support for additional output formats?
> >
> > I guess the DPI interface feeds the DP that essentially acts as a RGB ->
> > DP bridge?
> >
> > If so, then the part adding support for CSC and additional formats
> > should be split into a separate patch, and it should be mentioned in the
> > commit log
> >
> > Maxime
> 
> I see what you mean, I'll split the patches functionally...
> I'm discovering the gpu/drm tree, so It may take me a while to get it right :D

It's not really yet another weird drm rule :)

Splitting your patches is valuable for a bunch of reasons that apply to
basically every tree:

  - It's easier to review (and maintainers like that). It's also easier
    to justify in a commit log, so it leads to a better history over the
    long term.

  - It's also easier to merge since you can merge smaller chunks when
    they are ready, even if the entire set is not. Smaller chunks also
    mean it's easier to apply (for the maintainer) and rebase (for you).

  - Finally, and possibly the strongest technical argument, it's much
    more helpful when you're bisecting. Taking the above patch as an
    example, let's assume you introduced a bug in some RGB panel with
    the colors being off due to the CSC changes. If you run git bisect,
    you'll end up with that big patch affecting multiple things and it
    would still be difficult to figure out what's wrong exactly. If it
    was split, you would end up only on the changeset that introduces
    the regression, which is very likely to be much easier to analyze.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-25 14:30             ` Maxime Ripard
  0 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-11-25 14:30 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 20365 bytes --]

On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> Hi,
> Thanks for all your input, really appreciated.
> 
> Quoting Maxime Ripard (2021-11-16 15:51:12)
> > Hi,
> >
> > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > The driver creates a child device for the phy. The child device will
> > > > > never exist without the parent being active. As they are sharing a
> > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > both can work with the same register range. The phy driver sets device
> > > > > data that is read by the parent to get the phy device that can be used
> > > > > to control the phy properties.
> > > >
> > > > If the PHY is in the same register space than the DP controller, why do
> > > > you need a separate PHY driver in the first place?
> > >
> > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > >
> > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> >
> > It's a bit of a circular argument though :)
> >
> > It's a separate phy driver because it needs to go through another
> > maintainer's tree, but it needs to go through another maintainer's tree
> > because it's a separate phy driver.
> >
> > It doesn't explain why it needs to be a separate phy driver? Why can't
> > the phy setup be done directly in the DP driver, if it's essentially a
> > single device?
> >
> > That being said, usually what those kind of questions mean is that
> > you're missing a comment or something in the commit log to provide that
> > context in the first place, so it would be great to add that context
> > here.
> >
> > And it will avoid the situation we're now in where multiple reviewers
> > ask the same questions over and over again :)
> >
> At first I didn't understand your reply, then I realized I gave you
> the wrong link...
> my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> 
> The driver and phy were a single driver until v2 of this patch series
> and the phy setup
> was done directly in the driver (single driver, single C file).
> Here's the relevant link to the discussion between Chun-Kuang and Markus
> 
> https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> 
> I'll try to find a way to make it clearer for v7.

OK, it makes sense then :)

There's something weird though: the devices definitely look like they're
in a separate register range, yet you mention a regmap to handle the
shared register range. That range doesn't seem described anywhere in the
device tree though? What is it for?

> > > > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > > > +                                     struct drm_bridge_state *old_state)
> > > > > +{
> > > > > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > > > +     struct drm_connector *conn;
> > > > > +     struct drm_connector_state *conn_state;
> > > > > +     struct drm_crtc *crtc;
> > > > > +     struct drm_crtc_state *crtc_state;
> > > > > +     int ret = 0;
> > > > > +     int i;
> > > > > +
> > > > > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > > > +                                                     bridge->encoder);
> > > > > +     if (!conn) {
> > > > > +             drm_err(mtk_dp->drm_dev,
> > > > > +                     "Can't enable bridge as connector is missing\n");
> > > > > +             return;
> > > > > +     }
> > > > > +
> > > > > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > > >
> > > > This should be protected by a mutex (just like any resource shared
> > > > between KMS and ALSA)
> > >
> > > Ok.
> >
> > I forgot to ask (even though checkpatch does mention it iirc), but since
> > you have multiple mutex it would be nice to have a comment for each
> > mutex stating exactly what it protects, and against what.
> >
> > It's hard otherwise to remember or figure out what the locks are here
> > for.
> >
> > > > > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > > > > +     if (ret)
> > > > > +             return ret;
> > > >
> > > > pdata?
> > > >
> > > can you elaborate?
> >
> > Sorry, yeah, pdata is usually the abbreviation used in linux for the
> > platform_data mechanism, but you're using the DT to retrieve your
> > resources (and platform_data usually don't involve any parsing), so the
> > name is odd.
> >
> > > > > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > index 384074f69111b..e6e88e3cd811d 100644
> > > > > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> > > > >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> > > > >  };
> > > > >
> > > > > +enum TVDPLL_CLK {
> > > > > +     TVDPLL_PLL = 0,
> > > > > +     TVDPLL_D2 = 2,
> > > > > +     TVDPLL_D4 = 4,
> > > > > +     TVDPLL_D8 = 8,
> > > > > +     TVDPLL_D16 = 16,
> > > > > +};
> > > > > +
> > > > >  struct mtk_dpi {
> > > > >       struct drm_encoder encoder;
> > > > >       struct drm_bridge bridge;
> > > > > @@ -71,8 +79,10 @@ struct mtk_dpi {
> > > > >       void __iomem *regs;
> > > > >       struct device *dev;
> > > > >       struct clk *engine_clk;
> > > > > +     struct clk *dpi_ck_cg;
> > > > >       struct clk *pixel_clk;
> > > > >       struct clk *tvd_clk;
> > > > > +     struct clk *pclk_src[5];
> > > > >       int irq;
> > > > >       struct drm_display_mode mode;
> > > > >       const struct mtk_dpi_conf *conf;
> > > > > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> > > > >       u32 hvsize_mask;
> > > > >       u32 channel_swap_shift;
> > > > >       u32 yuv422_en_bit;
> > > > > +     u32 csc_enable_bit;
> > > > >       const struct mtk_dpi_yc_limit *limit;
> > > > >  };
> > > > >
> > > > > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> > > > >
> > > > >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> > > > >  {
> > > > > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > > > > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> > > > >  }
> > > > >
> > > > >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > > > > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> > > > >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> > > > >  }
> > > > >
> > > > > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > > > > +{
> > > > > +     u32 matrix_sel = 0;
> > > > > +
> > > > > +     switch (format) {
> > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > > > > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > > > > +             if (dpi->mode.hdisplay <= 720)
> > > > > +                     matrix_sel = 0x2;
> > > > > +             break;
> > > > > +     default:
> > > > > +             break;
> > > > > +     }
> > > > > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > > > > +}
> > > > > +
> > > > >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> > > > >                                       enum mtk_dpi_out_color_format format)
> > > > >  {
> > > > >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> > > > >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> > > > >               mtk_dpi_config_yuv422_enable(dpi, false);
> > > > > -             if (dpi->conf->csc_support)
> > > > > +             if (dpi->conf->csc_support) {
> > > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > > +             }
> > > > >               if (dpi->conf->swap_input_support)
> > > > >                       mtk_dpi_config_swap_input(dpi, false);
> > > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> > > > >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> > > > >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> > > > >               mtk_dpi_config_yuv422_enable(dpi, true);
> > > > > -             if (dpi->conf->csc_support)
> > > > > +             if (dpi->conf->csc_support) {
> > > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > > +             }
> > > > >               if (dpi->conf->swap_input_support)
> > > > >                       mtk_dpi_config_swap_input(dpi, true);
> > > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > > > > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> > > > >       mtk_dpi_disable(dpi);
> > > > >       clk_disable_unprepare(dpi->pixel_clk);
> > > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > > +     clk_disable_unprepare(dpi->tvd_clk);
> > > > >  }
> > > > >
> > > > >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > >       if (++dpi->refcount != 1)
> > > > >               return 0;
> > > > >
> > > > > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > > > > +     if (ret) {
> > > > > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > > > > +             goto err_pixel;
> > > > > +     }
> > > > > +
> > > > >       ret = clk_prepare_enable(dpi->engine_clk);
> > > > >       if (ret) {
> > > > >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> > > > >               goto err_refcount;
> > > > >       }
> > > > >
> > > > > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > > > > +     if (ret) {
> > > > > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > > > > +             goto err_ck_cg;
> > > > > +     }
> > > > > +
> > > > >       ret = clk_prepare_enable(dpi->pixel_clk);
> > > > >       if (ret) {
> > > > >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > > > > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > >       if (dpi->pinctrl && dpi->pins_dpi)
> > > > >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> > > > >
> > > > > -     mtk_dpi_enable(dpi);
> > > > >       return 0;
> > > > >
> > > > >  err_pixel:
> > > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > > +err_ck_cg:
> > > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > >  err_refcount:
> > > > >       dpi->refcount--;
> > > > > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > > >       pll_rate = clk_get_rate(dpi->tvd_clk);
> > > > >
> > > > >       vm.pixelclock = pll_rate / factor;
> > > > > -     if (dpi->conf->is_dpintf)
> > > > > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > > > > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > > +     if (dpi->conf->is_dpintf) {
> > > > > +             if (factor == 1)
> > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > > +             else if (factor == 2)
> > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > > > > +             else if (factor == 4)
> > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > > > > +             else
> > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> > > > >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> > > > >       else
> > > > > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > > >       }
> > > > >       mtk_dpi_sw_reset(dpi, false);
> > > > >
> > > > > +     mtk_dpi_enable(dpi);
> > > > > +
> > > > >       return 0;
> > > > >  }
> > > > >
> > > > > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> > > > >       u32 *input_fmts;
> > > > >
> > > > >       *num_input_fmts = 0;
> > > > > -
> > > > >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> > > > >                            GFP_KERNEL);
> > > > >       if (!input_fmts)
> > > > > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > > >               if (dpi->conf->num_output_fmts)
> > > > >                       out_bus_format = dpi->conf->output_fmts[0];
> > > > >
> > > > > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > >               bridge_state->input_bus_cfg.format,
> > > > >               bridge_state->output_bus_cfg.format);
> > > > >
> > > > > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > > >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> > > > >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> > > > >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > > > > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > > > > +     else
> > > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > >
> > > > >       return 0;
> > > > >  }
> > > > > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> > > > >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> > > > >  };
> > > > >
> > > > > +static const u32 mt8195_output_fmts[] = {
> > > > > +     MEDIA_BUS_FMT_RGB888_1X24,
> > > > > +     MEDIA_BUS_FMT_YUV8_1X24,
> > > > > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > > > > +};
> > > > > +
> > > > >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> > > > >       .c_bottom = 0x0010,
> > > > >       .c_top = 0x0FE0,
> > > > > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> > > > >       .hvsize_mask = HSIZE_MASK,
> > > > >       .channel_swap_shift = CH_SWAP,
> > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > >       .limit = &mtk_dpi_limit,
> > > > >  };
> > > > >
> > > > > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> > > > >       .hvsize_mask = HSIZE_MASK,
> > > > >       .channel_swap_shift = CH_SWAP,
> > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > >       .limit = &mtk_dpi_limit,
> > > > >  };
> > > > >
> > > > > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> > > > >       .hvsize_mask = HSIZE_MASK,
> > > > >       .channel_swap_shift = CH_SWAP,
> > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > >       .limit = &mtk_dpi_limit,
> > > > >  };
> > > > >
> > > > > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> > > > >       .hvsize_mask = HSIZE_MASK,
> > > > >       .channel_swap_shift = CH_SWAP,
> > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > >       .limit = &mtk_dpi_limit,
> > > > >  };
> > > > >
> > > > >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> > > > >       .cal_factor = mt8195_dpintf_calculate_factor,
> > > > > -     .output_fmts = mt8173_output_fmts,
> > > > > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > > > > +     .output_fmts = mt8195_output_fmts,
> > > > > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> > > > >       .is_dpintf = true,
> > > > > +     .csc_support = true,
> > > > >       .dimension_mask = DPINTF_HPW_MASK,
> > > > >       .hvsize_mask = DPINTF_HSIZE_MASK,
> > > > >       .channel_swap_shift = DPINTF_CH_SWAP,
> > > > >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > > > > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> > > > >       .limit = &mtk_dpintf_limit,
> > > > >  };
> > > > >
> > > > > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > > >               return ret;
> > > > >       }
> > > > >
> > > > > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > > > > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > > > > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > > > > +             if (ret != -EPROBE_DEFER)
> > > > > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > > > > +                             ret);
> > > > > +
> > > > > +             return ret;
> > > > > +     }
> > > > > +
> > > > >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> > > > >       if (IS_ERR(dpi->pixel_clk)) {
> > > > >               ret = PTR_ERR(dpi->pixel_clk);
> > > > > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > > >               return ret;
> > > > >       }
> > > > >
> > > > > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > > > > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > > > > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > > > > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > > > > +
> > > > >       dpi->irq = platform_get_irq(pdev, 0);
> > > > >       if (dpi->irq <= 0)
> > > > >               return -EINVAL;
> > > >
> > > > All those changes look unrelated as well?
> > > >
> > > Do you mean all the changes in mtk_dpi.c ? They are in support of
> > > enabling the mt8195 dpintf driver... so, not sure they are unlreated?
> >
> > I don't know that driver at all, so it's probably related if you say so
> > :)
> >
> > The DPI interface seems to be a panel interface, so it's a bit weird to
> > me why you would need to change things in the panel interface while
> > claiming to support a DP output.
> >
> > Especially since it looks like you have some support for CSC, and
> > support for additional output formats?
> >
> > I guess the DPI interface feeds the DP that essentially acts as a RGB ->
> > DP bridge?
> >
> > If so, then the part adding support for CSC and additional formats
> > should be split into a separate patch, and it should be mentioned in the
> > commit log
> >
> > Maxime
> 
> I see what you mean, I'll split the patches functionally...
> I'm discovering the gpu/drm tree, so It may take me a while to get it right :D

It's not really yet another weird drm rule :)

Splitting your patches is valuable for a bunch of reasons that apply to
basically every tree:

  - It's easier to review (and maintainers like that). It's also easier
    to justify in a commit log, so it leads to a better history over the
    long term.

  - It's also easier to merge since you can merge smaller chunks when
    they are ready, even if the entire set is not. Smaller chunks also
    mean it's easier to apply (for the maintainer) and rebase (for you).

  - Finally, and possibly the strongest technical argument, it's much
    more helpful when you're bisecting. Taking the above patch as an
    example, let's assume you introduced a bug in some RGB panel with
    the colors being off due to the CSC changes. If you run git bisect,
    you'll end up with that big patch affecting multiple things and it
    would still be difficult to figure out what's wrong exactly. If it
    was split, you would end up only on the changeset that introduces
    the regression, which is very likely to be much easier to analyze.

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 170 bytes --]

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-25 14:30             ` Maxime Ripard
  0 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-11-25 14:30 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 20365 bytes --]

On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> Hi,
> Thanks for all your input, really appreciated.
> 
> Quoting Maxime Ripard (2021-11-16 15:51:12)
> > Hi,
> >
> > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > The driver creates a child device for the phy. The child device will
> > > > > never exist without the parent being active. As they are sharing a
> > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > both can work with the same register range. The phy driver sets device
> > > > > data that is read by the parent to get the phy device that can be used
> > > > > to control the phy properties.
> > > >
> > > > If the PHY is in the same register space than the DP controller, why do
> > > > you need a separate PHY driver in the first place?
> > >
> > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > >
> > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> >
> > It's a bit of a circular argument though :)
> >
> > It's a separate phy driver because it needs to go through another
> > maintainer's tree, but it needs to go through another maintainer's tree
> > because it's a separate phy driver.
> >
> > It doesn't explain why it needs to be a separate phy driver? Why can't
> > the phy setup be done directly in the DP driver, if it's essentially a
> > single device?
> >
> > That being said, usually what those kind of questions mean is that
> > you're missing a comment or something in the commit log to provide that
> > context in the first place, so it would be great to add that context
> > here.
> >
> > And it will avoid the situation we're now in where multiple reviewers
> > ask the same questions over and over again :)
> >
> At first I didn't understand your reply, then I realized I gave you
> the wrong link...
> my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> 
> The driver and phy were a single driver until v2 of this patch series
> and the phy setup
> was done directly in the driver (single driver, single C file).
> Here's the relevant link to the discussion between Chun-Kuang and Markus
> 
> https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> 
> I'll try to find a way to make it clearer for v7.

OK, it makes sense then :)

There's something weird though: the devices definitely look like they're
in a separate register range, yet you mention a regmap to handle the
shared register range. That range doesn't seem described anywhere in the
device tree though? What is it for?

> > > > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > > > +                                     struct drm_bridge_state *old_state)
> > > > > +{
> > > > > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > > > +     struct drm_connector *conn;
> > > > > +     struct drm_connector_state *conn_state;
> > > > > +     struct drm_crtc *crtc;
> > > > > +     struct drm_crtc_state *crtc_state;
> > > > > +     int ret = 0;
> > > > > +     int i;
> > > > > +
> > > > > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > > > +                                                     bridge->encoder);
> > > > > +     if (!conn) {
> > > > > +             drm_err(mtk_dp->drm_dev,
> > > > > +                     "Can't enable bridge as connector is missing\n");
> > > > > +             return;
> > > > > +     }
> > > > > +
> > > > > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > > >
> > > > This should be protected by a mutex (just like any resource shared
> > > > between KMS and ALSA)
> > >
> > > Ok.
> >
> > I forgot to ask (even though checkpatch does mention it iirc), but since
> > you have multiple mutex it would be nice to have a comment for each
> > mutex stating exactly what it protects, and against what.
> >
> > It's hard otherwise to remember or figure out what the locks are here
> > for.
> >
> > > > > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > > > > +     if (ret)
> > > > > +             return ret;
> > > >
> > > > pdata?
> > > >
> > > can you elaborate?
> >
> > Sorry, yeah, pdata is usually the abbreviation used in linux for the
> > platform_data mechanism, but you're using the DT to retrieve your
> > resources (and platform_data usually don't involve any parsing), so the
> > name is odd.
> >
> > > > > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > index 384074f69111b..e6e88e3cd811d 100644
> > > > > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> > > > >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> > > > >  };
> > > > >
> > > > > +enum TVDPLL_CLK {
> > > > > +     TVDPLL_PLL = 0,
> > > > > +     TVDPLL_D2 = 2,
> > > > > +     TVDPLL_D4 = 4,
> > > > > +     TVDPLL_D8 = 8,
> > > > > +     TVDPLL_D16 = 16,
> > > > > +};
> > > > > +
> > > > >  struct mtk_dpi {
> > > > >       struct drm_encoder encoder;
> > > > >       struct drm_bridge bridge;
> > > > > @@ -71,8 +79,10 @@ struct mtk_dpi {
> > > > >       void __iomem *regs;
> > > > >       struct device *dev;
> > > > >       struct clk *engine_clk;
> > > > > +     struct clk *dpi_ck_cg;
> > > > >       struct clk *pixel_clk;
> > > > >       struct clk *tvd_clk;
> > > > > +     struct clk *pclk_src[5];
> > > > >       int irq;
> > > > >       struct drm_display_mode mode;
> > > > >       const struct mtk_dpi_conf *conf;
> > > > > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> > > > >       u32 hvsize_mask;
> > > > >       u32 channel_swap_shift;
> > > > >       u32 yuv422_en_bit;
> > > > > +     u32 csc_enable_bit;
> > > > >       const struct mtk_dpi_yc_limit *limit;
> > > > >  };
> > > > >
> > > > > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> > > > >
> > > > >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> > > > >  {
> > > > > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > > > > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> > > > >  }
> > > > >
> > > > >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > > > > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> > > > >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> > > > >  }
> > > > >
> > > > > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > > > > +{
> > > > > +     u32 matrix_sel = 0;
> > > > > +
> > > > > +     switch (format) {
> > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > > > > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > > > > +             if (dpi->mode.hdisplay <= 720)
> > > > > +                     matrix_sel = 0x2;
> > > > > +             break;
> > > > > +     default:
> > > > > +             break;
> > > > > +     }
> > > > > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > > > > +}
> > > > > +
> > > > >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> > > > >                                       enum mtk_dpi_out_color_format format)
> > > > >  {
> > > > >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> > > > >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> > > > >               mtk_dpi_config_yuv422_enable(dpi, false);
> > > > > -             if (dpi->conf->csc_support)
> > > > > +             if (dpi->conf->csc_support) {
> > > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > > +             }
> > > > >               if (dpi->conf->swap_input_support)
> > > > >                       mtk_dpi_config_swap_input(dpi, false);
> > > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> > > > >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> > > > >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> > > > >               mtk_dpi_config_yuv422_enable(dpi, true);
> > > > > -             if (dpi->conf->csc_support)
> > > > > +             if (dpi->conf->csc_support) {
> > > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > > +             }
> > > > >               if (dpi->conf->swap_input_support)
> > > > >                       mtk_dpi_config_swap_input(dpi, true);
> > > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > > > > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> > > > >       mtk_dpi_disable(dpi);
> > > > >       clk_disable_unprepare(dpi->pixel_clk);
> > > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > > +     clk_disable_unprepare(dpi->tvd_clk);
> > > > >  }
> > > > >
> > > > >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > >       if (++dpi->refcount != 1)
> > > > >               return 0;
> > > > >
> > > > > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > > > > +     if (ret) {
> > > > > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > > > > +             goto err_pixel;
> > > > > +     }
> > > > > +
> > > > >       ret = clk_prepare_enable(dpi->engine_clk);
> > > > >       if (ret) {
> > > > >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> > > > >               goto err_refcount;
> > > > >       }
> > > > >
> > > > > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > > > > +     if (ret) {
> > > > > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > > > > +             goto err_ck_cg;
> > > > > +     }
> > > > > +
> > > > >       ret = clk_prepare_enable(dpi->pixel_clk);
> > > > >       if (ret) {
> > > > >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > > > > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > >       if (dpi->pinctrl && dpi->pins_dpi)
> > > > >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> > > > >
> > > > > -     mtk_dpi_enable(dpi);
> > > > >       return 0;
> > > > >
> > > > >  err_pixel:
> > > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > > +err_ck_cg:
> > > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > >  err_refcount:
> > > > >       dpi->refcount--;
> > > > > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > > >       pll_rate = clk_get_rate(dpi->tvd_clk);
> > > > >
> > > > >       vm.pixelclock = pll_rate / factor;
> > > > > -     if (dpi->conf->is_dpintf)
> > > > > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > > > > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > > +     if (dpi->conf->is_dpintf) {
> > > > > +             if (factor == 1)
> > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > > +             else if (factor == 2)
> > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > > > > +             else if (factor == 4)
> > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > > > > +             else
> > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> > > > >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> > > > >       else
> > > > > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > > >       }
> > > > >       mtk_dpi_sw_reset(dpi, false);
> > > > >
> > > > > +     mtk_dpi_enable(dpi);
> > > > > +
> > > > >       return 0;
> > > > >  }
> > > > >
> > > > > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> > > > >       u32 *input_fmts;
> > > > >
> > > > >       *num_input_fmts = 0;
> > > > > -
> > > > >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> > > > >                            GFP_KERNEL);
> > > > >       if (!input_fmts)
> > > > > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > > >               if (dpi->conf->num_output_fmts)
> > > > >                       out_bus_format = dpi->conf->output_fmts[0];
> > > > >
> > > > > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > >               bridge_state->input_bus_cfg.format,
> > > > >               bridge_state->output_bus_cfg.format);
> > > > >
> > > > > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > > >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> > > > >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> > > > >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > > > > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > > > > +     else
> > > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > >
> > > > >       return 0;
> > > > >  }
> > > > > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> > > > >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> > > > >  };
> > > > >
> > > > > +static const u32 mt8195_output_fmts[] = {
> > > > > +     MEDIA_BUS_FMT_RGB888_1X24,
> > > > > +     MEDIA_BUS_FMT_YUV8_1X24,
> > > > > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > > > > +};
> > > > > +
> > > > >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> > > > >       .c_bottom = 0x0010,
> > > > >       .c_top = 0x0FE0,
> > > > > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> > > > >       .hvsize_mask = HSIZE_MASK,
> > > > >       .channel_swap_shift = CH_SWAP,
> > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > >       .limit = &mtk_dpi_limit,
> > > > >  };
> > > > >
> > > > > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> > > > >       .hvsize_mask = HSIZE_MASK,
> > > > >       .channel_swap_shift = CH_SWAP,
> > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > >       .limit = &mtk_dpi_limit,
> > > > >  };
> > > > >
> > > > > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> > > > >       .hvsize_mask = HSIZE_MASK,
> > > > >       .channel_swap_shift = CH_SWAP,
> > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > >       .limit = &mtk_dpi_limit,
> > > > >  };
> > > > >
> > > > > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> > > > >       .hvsize_mask = HSIZE_MASK,
> > > > >       .channel_swap_shift = CH_SWAP,
> > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > >       .limit = &mtk_dpi_limit,
> > > > >  };
> > > > >
> > > > >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> > > > >       .cal_factor = mt8195_dpintf_calculate_factor,
> > > > > -     .output_fmts = mt8173_output_fmts,
> > > > > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > > > > +     .output_fmts = mt8195_output_fmts,
> > > > > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> > > > >       .is_dpintf = true,
> > > > > +     .csc_support = true,
> > > > >       .dimension_mask = DPINTF_HPW_MASK,
> > > > >       .hvsize_mask = DPINTF_HSIZE_MASK,
> > > > >       .channel_swap_shift = DPINTF_CH_SWAP,
> > > > >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > > > > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> > > > >       .limit = &mtk_dpintf_limit,
> > > > >  };
> > > > >
> > > > > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > > >               return ret;
> > > > >       }
> > > > >
> > > > > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > > > > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > > > > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > > > > +             if (ret != -EPROBE_DEFER)
> > > > > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > > > > +                             ret);
> > > > > +
> > > > > +             return ret;
> > > > > +     }
> > > > > +
> > > > >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> > > > >       if (IS_ERR(dpi->pixel_clk)) {
> > > > >               ret = PTR_ERR(dpi->pixel_clk);
> > > > > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > > >               return ret;
> > > > >       }
> > > > >
> > > > > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > > > > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > > > > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > > > > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > > > > +
> > > > >       dpi->irq = platform_get_irq(pdev, 0);
> > > > >       if (dpi->irq <= 0)
> > > > >               return -EINVAL;
> > > >
> > > > All those changes look unrelated as well?
> > > >
> > > Do you mean all the changes in mtk_dpi.c ? They are in support of
> > > enabling the mt8195 dpintf driver... so, not sure they are unlreated?
> >
> > I don't know that driver at all, so it's probably related if you say so
> > :)
> >
> > The DPI interface seems to be a panel interface, so it's a bit weird to
> > me why you would need to change things in the panel interface while
> > claiming to support a DP output.
> >
> > Especially since it looks like you have some support for CSC, and
> > support for additional output formats?
> >
> > I guess the DPI interface feeds the DP that essentially acts as a RGB ->
> > DP bridge?
> >
> > If so, then the part adding support for CSC and additional formats
> > should be split into a separate patch, and it should be mentioned in the
> > commit log
> >
> > Maxime
> 
> I see what you mean, I'll split the patches functionally...
> I'm discovering the gpu/drm tree, so It may take me a while to get it right :D

It's not really yet another weird drm rule :)

Splitting your patches is valuable for a bunch of reasons that apply to
basically every tree:

  - It's easier to review (and maintainers like that). It's also easier
    to justify in a commit log, so it leads to a better history over the
    long term.

  - It's also easier to merge since you can merge smaller chunks when
    they are ready, even if the entire set is not. Smaller chunks also
    mean it's easier to apply (for the maintainer) and rebase (for you).

  - Finally, and possibly the strongest technical argument, it's much
    more helpful when you're bisecting. Taking the above patch as an
    example, let's assume you introduced a bug in some RGB panel with
    the colors being off due to the CSC changes. If you run git bisect,
    you'll end up with that big patch affecting multiple things and it
    would still be difficult to figure out what's wrong exactly. If it
    was split, you would end up only on the changeset that introduces
    the regression, which is very likely to be much easier to analyze.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-25 14:30             ` Maxime Ripard
  0 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-11-25 14:30 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 20365 bytes --]

On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> Hi,
> Thanks for all your input, really appreciated.
> 
> Quoting Maxime Ripard (2021-11-16 15:51:12)
> > Hi,
> >
> > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > The driver creates a child device for the phy. The child device will
> > > > > never exist without the parent being active. As they are sharing a
> > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > both can work with the same register range. The phy driver sets device
> > > > > data that is read by the parent to get the phy device that can be used
> > > > > to control the phy properties.
> > > >
> > > > If the PHY is in the same register space than the DP controller, why do
> > > > you need a separate PHY driver in the first place?
> > >
> > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > >
> > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> >
> > It's a bit of a circular argument though :)
> >
> > It's a separate phy driver because it needs to go through another
> > maintainer's tree, but it needs to go through another maintainer's tree
> > because it's a separate phy driver.
> >
> > It doesn't explain why it needs to be a separate phy driver? Why can't
> > the phy setup be done directly in the DP driver, if it's essentially a
> > single device?
> >
> > That being said, usually what those kind of questions mean is that
> > you're missing a comment or something in the commit log to provide that
> > context in the first place, so it would be great to add that context
> > here.
> >
> > And it will avoid the situation we're now in where multiple reviewers
> > ask the same questions over and over again :)
> >
> At first I didn't understand your reply, then I realized I gave you
> the wrong link...
> my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> 
> The driver and phy were a single driver until v2 of this patch series
> and the phy setup
> was done directly in the driver (single driver, single C file).
> Here's the relevant link to the discussion between Chun-Kuang and Markus
> 
> https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> 
> I'll try to find a way to make it clearer for v7.

OK, it makes sense then :)

There's something weird though: the devices definitely look like they're
in a separate register range, yet you mention a regmap to handle the
shared register range. That range doesn't seem described anywhere in the
device tree though? What is it for?

> > > > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > > > +                                     struct drm_bridge_state *old_state)
> > > > > +{
> > > > > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > > > +     struct drm_connector *conn;
> > > > > +     struct drm_connector_state *conn_state;
> > > > > +     struct drm_crtc *crtc;
> > > > > +     struct drm_crtc_state *crtc_state;
> > > > > +     int ret = 0;
> > > > > +     int i;
> > > > > +
> > > > > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > > > +                                                     bridge->encoder);
> > > > > +     if (!conn) {
> > > > > +             drm_err(mtk_dp->drm_dev,
> > > > > +                     "Can't enable bridge as connector is missing\n");
> > > > > +             return;
> > > > > +     }
> > > > > +
> > > > > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > > >
> > > > This should be protected by a mutex (just like any resource shared
> > > > between KMS and ALSA)
> > >
> > > Ok.
> >
> > I forgot to ask (even though checkpatch does mention it iirc), but since
> > you have multiple mutex it would be nice to have a comment for each
> > mutex stating exactly what it protects, and against what.
> >
> > It's hard otherwise to remember or figure out what the locks are here
> > for.
> >
> > > > > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > > > > +     if (ret)
> > > > > +             return ret;
> > > >
> > > > pdata?
> > > >
> > > can you elaborate?
> >
> > Sorry, yeah, pdata is usually the abbreviation used in linux for the
> > platform_data mechanism, but you're using the DT to retrieve your
> > resources (and platform_data usually don't involve any parsing), so the
> > name is odd.
> >
> > > > > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > index 384074f69111b..e6e88e3cd811d 100644
> > > > > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> > > > >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> > > > >  };
> > > > >
> > > > > +enum TVDPLL_CLK {
> > > > > +     TVDPLL_PLL = 0,
> > > > > +     TVDPLL_D2 = 2,
> > > > > +     TVDPLL_D4 = 4,
> > > > > +     TVDPLL_D8 = 8,
> > > > > +     TVDPLL_D16 = 16,
> > > > > +};
> > > > > +
> > > > >  struct mtk_dpi {
> > > > >       struct drm_encoder encoder;
> > > > >       struct drm_bridge bridge;
> > > > > @@ -71,8 +79,10 @@ struct mtk_dpi {
> > > > >       void __iomem *regs;
> > > > >       struct device *dev;
> > > > >       struct clk *engine_clk;
> > > > > +     struct clk *dpi_ck_cg;
> > > > >       struct clk *pixel_clk;
> > > > >       struct clk *tvd_clk;
> > > > > +     struct clk *pclk_src[5];
> > > > >       int irq;
> > > > >       struct drm_display_mode mode;
> > > > >       const struct mtk_dpi_conf *conf;
> > > > > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> > > > >       u32 hvsize_mask;
> > > > >       u32 channel_swap_shift;
> > > > >       u32 yuv422_en_bit;
> > > > > +     u32 csc_enable_bit;
> > > > >       const struct mtk_dpi_yc_limit *limit;
> > > > >  };
> > > > >
> > > > > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> > > > >
> > > > >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> > > > >  {
> > > > > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > > > > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> > > > >  }
> > > > >
> > > > >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > > > > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> > > > >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> > > > >  }
> > > > >
> > > > > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > > > > +{
> > > > > +     u32 matrix_sel = 0;
> > > > > +
> > > > > +     switch (format) {
> > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > > > > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > > > > +             if (dpi->mode.hdisplay <= 720)
> > > > > +                     matrix_sel = 0x2;
> > > > > +             break;
> > > > > +     default:
> > > > > +             break;
> > > > > +     }
> > > > > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > > > > +}
> > > > > +
> > > > >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> > > > >                                       enum mtk_dpi_out_color_format format)
> > > > >  {
> > > > >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> > > > >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> > > > >               mtk_dpi_config_yuv422_enable(dpi, false);
> > > > > -             if (dpi->conf->csc_support)
> > > > > +             if (dpi->conf->csc_support) {
> > > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > > +             }
> > > > >               if (dpi->conf->swap_input_support)
> > > > >                       mtk_dpi_config_swap_input(dpi, false);
> > > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> > > > >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> > > > >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> > > > >               mtk_dpi_config_yuv422_enable(dpi, true);
> > > > > -             if (dpi->conf->csc_support)
> > > > > +             if (dpi->conf->csc_support) {
> > > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > > +             }
> > > > >               if (dpi->conf->swap_input_support)
> > > > >                       mtk_dpi_config_swap_input(dpi, true);
> > > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > > > > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> > > > >       mtk_dpi_disable(dpi);
> > > > >       clk_disable_unprepare(dpi->pixel_clk);
> > > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > > +     clk_disable_unprepare(dpi->tvd_clk);
> > > > >  }
> > > > >
> > > > >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > >       if (++dpi->refcount != 1)
> > > > >               return 0;
> > > > >
> > > > > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > > > > +     if (ret) {
> > > > > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > > > > +             goto err_pixel;
> > > > > +     }
> > > > > +
> > > > >       ret = clk_prepare_enable(dpi->engine_clk);
> > > > >       if (ret) {
> > > > >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> > > > >               goto err_refcount;
> > > > >       }
> > > > >
> > > > > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > > > > +     if (ret) {
> > > > > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > > > > +             goto err_ck_cg;
> > > > > +     }
> > > > > +
> > > > >       ret = clk_prepare_enable(dpi->pixel_clk);
> > > > >       if (ret) {
> > > > >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > > > > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > >       if (dpi->pinctrl && dpi->pins_dpi)
> > > > >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> > > > >
> > > > > -     mtk_dpi_enable(dpi);
> > > > >       return 0;
> > > > >
> > > > >  err_pixel:
> > > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > > +err_ck_cg:
> > > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > >  err_refcount:
> > > > >       dpi->refcount--;
> > > > > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > > >       pll_rate = clk_get_rate(dpi->tvd_clk);
> > > > >
> > > > >       vm.pixelclock = pll_rate / factor;
> > > > > -     if (dpi->conf->is_dpintf)
> > > > > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > > > > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > > +     if (dpi->conf->is_dpintf) {
> > > > > +             if (factor == 1)
> > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > > +             else if (factor == 2)
> > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > > > > +             else if (factor == 4)
> > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > > > > +             else
> > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> > > > >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> > > > >       else
> > > > > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > > >       }
> > > > >       mtk_dpi_sw_reset(dpi, false);
> > > > >
> > > > > +     mtk_dpi_enable(dpi);
> > > > > +
> > > > >       return 0;
> > > > >  }
> > > > >
> > > > > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> > > > >       u32 *input_fmts;
> > > > >
> > > > >       *num_input_fmts = 0;
> > > > > -
> > > > >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> > > > >                            GFP_KERNEL);
> > > > >       if (!input_fmts)
> > > > > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > > >               if (dpi->conf->num_output_fmts)
> > > > >                       out_bus_format = dpi->conf->output_fmts[0];
> > > > >
> > > > > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > >               bridge_state->input_bus_cfg.format,
> > > > >               bridge_state->output_bus_cfg.format);
> > > > >
> > > > > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > > >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> > > > >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> > > > >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > > > > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > > > > +     else
> > > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > >
> > > > >       return 0;
> > > > >  }
> > > > > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> > > > >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> > > > >  };
> > > > >
> > > > > +static const u32 mt8195_output_fmts[] = {
> > > > > +     MEDIA_BUS_FMT_RGB888_1X24,
> > > > > +     MEDIA_BUS_FMT_YUV8_1X24,
> > > > > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > > > > +};
> > > > > +
> > > > >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> > > > >       .c_bottom = 0x0010,
> > > > >       .c_top = 0x0FE0,
> > > > > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> > > > >       .hvsize_mask = HSIZE_MASK,
> > > > >       .channel_swap_shift = CH_SWAP,
> > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > >       .limit = &mtk_dpi_limit,
> > > > >  };
> > > > >
> > > > > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> > > > >       .hvsize_mask = HSIZE_MASK,
> > > > >       .channel_swap_shift = CH_SWAP,
> > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > >       .limit = &mtk_dpi_limit,
> > > > >  };
> > > > >
> > > > > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> > > > >       .hvsize_mask = HSIZE_MASK,
> > > > >       .channel_swap_shift = CH_SWAP,
> > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > >       .limit = &mtk_dpi_limit,
> > > > >  };
> > > > >
> > > > > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> > > > >       .hvsize_mask = HSIZE_MASK,
> > > > >       .channel_swap_shift = CH_SWAP,
> > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > >       .limit = &mtk_dpi_limit,
> > > > >  };
> > > > >
> > > > >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> > > > >       .cal_factor = mt8195_dpintf_calculate_factor,
> > > > > -     .output_fmts = mt8173_output_fmts,
> > > > > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > > > > +     .output_fmts = mt8195_output_fmts,
> > > > > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> > > > >       .is_dpintf = true,
> > > > > +     .csc_support = true,
> > > > >       .dimension_mask = DPINTF_HPW_MASK,
> > > > >       .hvsize_mask = DPINTF_HSIZE_MASK,
> > > > >       .channel_swap_shift = DPINTF_CH_SWAP,
> > > > >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > > > > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> > > > >       .limit = &mtk_dpintf_limit,
> > > > >  };
> > > > >
> > > > > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > > >               return ret;
> > > > >       }
> > > > >
> > > > > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > > > > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > > > > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > > > > +             if (ret != -EPROBE_DEFER)
> > > > > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > > > > +                             ret);
> > > > > +
> > > > > +             return ret;
> > > > > +     }
> > > > > +
> > > > >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> > > > >       if (IS_ERR(dpi->pixel_clk)) {
> > > > >               ret = PTR_ERR(dpi->pixel_clk);
> > > > > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > > >               return ret;
> > > > >       }
> > > > >
> > > > > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > > > > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > > > > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > > > > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > > > > +
> > > > >       dpi->irq = platform_get_irq(pdev, 0);
> > > > >       if (dpi->irq <= 0)
> > > > >               return -EINVAL;
> > > >
> > > > All those changes look unrelated as well?
> > > >
> > > Do you mean all the changes in mtk_dpi.c ? They are in support of
> > > enabling the mt8195 dpintf driver... so, not sure they are unlreated?
> >
> > I don't know that driver at all, so it's probably related if you say so
> > :)
> >
> > The DPI interface seems to be a panel interface, so it's a bit weird to
> > me why you would need to change things in the panel interface while
> > claiming to support a DP output.
> >
> > Especially since it looks like you have some support for CSC, and
> > support for additional output formats?
> >
> > I guess the DPI interface feeds the DP that essentially acts as a RGB ->
> > DP bridge?
> >
> > If so, then the part adding support for CSC and additional formats
> > should be split into a separate patch, and it should be mentioned in the
> > commit log
> >
> > Maxime
> 
> I see what you mean, I'll split the patches functionally...
> I'm discovering the gpu/drm tree, so It may take me a while to get it right :D

It's not really yet another weird drm rule :)

Splitting your patches is valuable for a bunch of reasons that apply to
basically every tree:

  - It's easier to review (and maintainers like that). It's also easier
    to justify in a commit log, so it leads to a better history over the
    long term.

  - It's also easier to merge since you can merge smaller chunks when
    they are ready, even if the entire set is not. Smaller chunks also
    mean it's easier to apply (for the maintainer) and rebase (for you).

  - Finally, and possibly the strongest technical argument, it's much
    more helpful when you're bisecting. Taking the above patch as an
    example, let's assume you introduced a bug in some RGB panel with
    the colors being off due to the CSC changes. If you run git bisect,
    you'll end up with that big patch affecting multiple things and it
    would still be difficult to figure out what's wrong exactly. If it
    was split, you would end up only on the changeset that introduces
    the regression, which is very likely to be much easier to analyze.

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-11-10 13:06   ` Guillaume Ranquet
  (?)
  (?)
@ 2021-11-25 15:27     ` Chun-Kuang Hu
  -1 siblings, 0 replies; 122+ messages in thread
From: Chun-Kuang Hu @ 2021-11-25 15:27 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Chun-Kuang Hu, Thomas Zimmermann, David Airlie, linux-kernel,
	Markus Schneider-Pargmann,
	moderated list:ARM/Mediatek SoC support, DRI Development,
	Matthias Brugger, Linux ARM, kernel test robot

Hi, Guillaume:

This is a big patch, so I give some comment first.

Guillaume Ranquet <granquet@baylibre.com> 於 2021年11月10日 週三 下午9:06寫道:
>
> From: Markus Schneider-Pargmann <msp@baylibre.com>
>
> This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> according phy driver mediatek-dp-phy.
>
> It supports both functional units on the mt8195, the embedded
> DisplayPort as well as the external DisplayPort units. It offers
> hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> to 4 lanes.
>
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.
>
> This driver is based on an initial version by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
>
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reported-by: kernel test robot <lkp@intel.com>
> ---
>  drivers/gpu/drm/drm_edid.c              |    2 +-

Separate this to another patch.

>  drivers/gpu/drm/mediatek/Kconfig        |    7 +
>  drivers/gpu/drm/mediatek/Makefile       |    2 +
>  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
>  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-

Ditto.

>  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +

Ditto.

>  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +

Ditto

>  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +

Ditto

>  9 files changed, 3799 insertions(+), 13 deletions(-)
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
>
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 500279a82167a..bfd98b50ceb5b 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
>          * modes and forbids YCRCB422 support for all video modes per
>          * HDMI 1.3 spec.
>          */
> -       info->color_formats = DRM_COLOR_FORMAT_RGB444;
> +       info->color_formats |= DRM_COLOR_FORMAT_RGB444;
>
>         /* YCRCB444 is optional according to spec. */
>         if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
> diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> index 2976d21e9a34a..029b94c716131 100644
> --- a/drivers/gpu/drm/mediatek/Kconfig
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
>         select PHY_MTK_HDMI
>         help
>           DRM/KMS HDMI driver for Mediatek SoCs
> +
> +config MTK_DPTX_SUPPORT
> +       tristate "DRM DPTX Support for Mediatek SoCs"
> +       depends on DRM_MEDIATEK
> +       select PHY_MTK_DP
> +       help
> +         DRM/KMS Display Port driver for Mediatek SoCs.
> diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> index 29098d7c8307c..d86a6406055e6 100644
> --- a/drivers/gpu/drm/mediatek/Makefile
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
>                           mtk_hdmi_ddc.o
>
>  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> +
> +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> new file mode 100644
> index 0000000000000..83087219d5a5e
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -0,0 +1,3094 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019 MediaTek Inc.
> + * Copyright (c) 2021 BayLibre
> + */
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_dp_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <linux/arm-smccc.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <sound/hdmi-codec.h>
> +#include <video/videomode.h>
> +
> +#include "mtk_dp_reg.h"
> +
> +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> +
> +#define MTK_DP_MAX_LANES 4
> +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> +
> +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> +
> +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> +
> +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> +
> +#define MTK_DP_DP_VERSION_11 0x11
> +
> +enum mtk_dp_state {
> +       MTK_DP_STATE_INITIAL,
> +       MTK_DP_STATE_IDLE,
> +       MTK_DP_STATE_PREPARE,
> +       MTK_DP_STATE_NORMAL,
> +};
> +
> +enum mtk_dp_train_state {
> +       MTK_DP_TRAIN_STATE_STARTUP = 0,
> +       MTK_DP_TRAIN_STATE_CHECKCAP,
> +       MTK_DP_TRAIN_STATE_CHECKEDID,
> +       MTK_DP_TRAIN_STATE_TRAINING_PRE,
> +       MTK_DP_TRAIN_STATE_TRAINING,
> +       MTK_DP_TRAIN_STATE_CHECKTIMING,
> +       MTK_DP_TRAIN_STATE_NORMAL,
> +       MTK_DP_TRAIN_STATE_POWERSAVE,
> +       MTK_DP_TRAIN_STATE_DPIDLE,
> +};
> +
> +struct mtk_dp_timings {
> +       struct videomode vm;
> +
> +       u16 htotal;
> +       u16 vtotal;
> +       u8 frame_rate;
> +       u32 pix_rate_khz;
> +};
> +
> +struct mtk_dp_train_info {
> +       bool tps3;
> +       bool tps4;
> +       bool sink_ssc;
> +       bool cable_plugged_in;
> +       bool cable_state_change;
> +       bool cr_done;
> +       bool eq_done;
> +
> +       // link_rate is in multiple of 0.27Gbps
> +       int link_rate;
> +       int lane_count;
> +
> +       int irq_status;
> +       int check_cap_count;
> +};
> +
> +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> +enum mtk_dp_color_format {
> +       MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> +       MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> +       MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> +       MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> +       MTK_DP_COLOR_FORMAT_YONLY = 4,
> +       MTK_DP_COLOR_FORMAT_RAW = 5,
> +       MTK_DP_COLOR_FORMAT_RESERVED = 6,
> +       MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> +       MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> +};
> +
> +// Multiple of 0.27Gbps
> +enum mtk_dp_linkrate {
> +       MTK_DP_LINKRATE_RBR = 0x6,
> +       MTK_DP_LINKRATE_HBR = 0xA,
> +       MTK_DP_LINKRATE_HBR2 = 0x14,
> +       MTK_DP_LINKRATE_HBR25 = 0x19,
> +       MTK_DP_LINKRATE_HBR3 = 0x1E,
> +};
> +
> +// Same values as used for DP Spec MISC0 bits 5,6,7
> +enum mtk_dp_color_depth {
> +       MTK_DP_COLOR_DEPTH_6BIT = 0,
> +       MTK_DP_COLOR_DEPTH_8BIT = 1,
> +       MTK_DP_COLOR_DEPTH_10BIT = 2,
> +       MTK_DP_COLOR_DEPTH_12BIT = 3,
> +       MTK_DP_COLOR_DEPTH_16BIT = 4,
> +       MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> +};
> +
> +struct mtk_dp_audio_cfg {
> +       int sample_rate;
> +       int word_length_bits;
> +       int channels;
> +};
> +
> +struct mtk_dp_info {
> +       enum mtk_dp_color_depth depth;
> +       enum mtk_dp_color_format format;
> +       struct mtk_dp_audio_cfg audio_caps;
> +       struct mtk_dp_timings timings;
> +};
> +
> +struct dp_cal_data {
> +       unsigned int glb_bias_trim;
> +       unsigned int clktx_impse;
> +
> +       unsigned int ln0_tx_impsel_pmos;
> +       unsigned int ln0_tx_impsel_nmos;
> +       unsigned int ln1_tx_impsel_pmos;
> +       unsigned int ln1_tx_impsel_nmos;
> +       unsigned int ln2_tx_impsel_pmos;
> +       unsigned int ln2_tx_impsel_nmos;
> +       unsigned int ln3_tx_impsel_pmos;
> +       unsigned int ln3_tx_impsel_nmos;
> +};
> +
> +struct mtk_dp {
> +       struct device *dev;
> +       struct platform_device *phy_dev;
> +       struct phy *phy;
> +       struct dp_cal_data cal_data;
> +
> +       struct drm_device *drm_dev;
> +       struct drm_bridge bridge;
> +       struct drm_bridge *next_bridge;
> +       struct drm_dp_aux aux;
> +
> +       struct mutex edid_lock;
> +       struct edid *edid;
> +
> +       u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> +
> +       struct mtk_dp_info info;
> +       enum mtk_dp_state state;
> +
> +       struct mtk_dp_train_info train_info;
> +       enum mtk_dp_train_state train_state;
> +       unsigned int input_fmt;
> +
> +       struct regmap *regs;
> +       struct clk *dp_tx_clk;
> +
> +       bool enabled;
> +       bool audio_enable;
> +
> +       bool has_fec;
> +       struct mutex dp_lock;
> +
> +       struct mutex update_plugged_status_lock;
> +
> +       hdmi_codec_plugged_cb plugged_cb;
> +       struct device *codec_dev;
> +       u8 connector_eld[MAX_ELD_BYTES];
> +};
> +
> +enum mtk_dp_sdp_type {
> +       MTK_DP_SDP_NONE = 0x00,
> +       MTK_DP_SDP_ACM = 0x01,
> +       MTK_DP_SDP_ISRC = 0x02,
> +       MTK_DP_SDP_AVI = 0x03,
> +       MTK_DP_SDP_AUI = 0x04,
> +       MTK_DP_SDP_SPD = 0x05,
> +       MTK_DP_SDP_MPEG = 0x06,
> +       MTK_DP_SDP_NTSC = 0x07,
> +       MTK_DP_SDP_VSP = 0x08,
> +       MTK_DP_SDP_VSC = 0x09,
> +       MTK_DP_SDP_EXT = 0x0A,
> +       MTK_DP_SDP_PPS0 = 0x0B,
> +       MTK_DP_SDP_PPS1 = 0x0C,
> +       MTK_DP_SDP_PPS2 = 0x0D,
> +       MTK_DP_SDP_PPS3 = 0x0E,
> +       MTK_DP_SDP_DRM = 0x10,
> +       MTK_DP_SDP_MAX_NUM
> +};
> +
> +struct mtk_dp_sdp_packet {
> +       enum mtk_dp_sdp_type type;
> +       struct dp_sdp sdp;
> +};
> +
> +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> +union mtk_dp_audio_channel_status {
> +       struct {
> +               u8 rev : 1;
> +               u8 is_lpcm : 1;
> +               u8 copy_right : 1;
> +               u8 additional_format_info : 3;
> +               u8 channel_status_mode : 2;
> +               u8 category_code;
> +               u8 src_num : 4;
> +               u8 channel_num : 4;
> +               u8 sampling_freq : 4;
> +               u8 clk_accuracy : 2;
> +               u8 rev2 : 2;
> +               u8 word_len : 4;
> +               u8 original_sampling_freq : 4;
> +       } iec;
> +
> +       u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> +};
> +
> +static struct regmap_config mtk_dp_regmap_config = {
> +       .reg_bits = 32,
> +       .val_bits = 32,
> +       .reg_stride = 4,
> +       .max_register = SEC_OFFSET + 0x90,
> +       .name = "mtk-dp-registers",
> +};
> +
> +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)

Separate edp and displayport into two patches. For example, the first
patch support edp only, and the second patch add displayport function.

> +{
> +       return mtk_dp->next_bridge != NULL;
> +}
> +

[snip]

> +
> +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> +                                   struct drm_connector *connector)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       bool enabled = mtk_dp->enabled;
> +       struct edid *new_edid = NULL;
> +
> +       if (!enabled)
> +               drm_bridge_chain_pre_enable(bridge);

In mtk_hdmi_bridge_get_edid(), there does not check the power. Why in
this function need this?
Does mtk hdmi driver has a bug?

> +
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> +       usleep_range(2000, 5000);
> +
> +       if (mtk_dp_plug_state(mtk_dp))
> +               new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> +
> +       if (!enabled)
> +               drm_bridge_chain_post_disable(bridge);
> +
> +       mutex_lock(&mtk_dp->edid_lock);
> +       kfree(mtk_dp->edid);
> +       mtk_dp->edid = NULL;
> +
> +       if (!new_edid) {
> +               mutex_unlock(&mtk_dp->edid_lock);
> +               return NULL;
> +       }
> +
> +       mtk_dp->edid = drm_edid_duplicate(new_edid);
> +       mutex_unlock(&mtk_dp->edid_lock);
> +
> +       return new_edid;
> +}
> +
> +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> +                                  struct drm_dp_aux_msg *msg)
> +{
> +       ssize_t err = -EAGAIN;
> +       struct mtk_dp *mtk_dp;
> +       bool is_read;
> +       u8 request;
> +       size_t accessed_bytes = 0;
> +       int retry = 3, ret = 0;
> +
> +       mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> +
> +       if (!mtk_dp->train_info.cable_plugged_in ||
> +           mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> +               mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> +               err = -EAGAIN;
> +               goto err;
> +       }
> +
> +       switch (msg->request) {
> +       case DP_AUX_I2C_MOT:
> +       case DP_AUX_I2C_WRITE:
> +       case DP_AUX_NATIVE_WRITE:
> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> +               request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> +               is_read = false;
> +               break;
> +       case DP_AUX_I2C_READ:
> +       case DP_AUX_NATIVE_READ:
> +       case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> +               request = msg->request;
> +               is_read = true;
> +               break;
> +       default:
> +               drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> +                       msg->request);
> +               err = -EINVAL;
> +               goto err;

Directly return here.

> +       }
> +
> +       if (msg->size == 0) {
> +               mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> +                                      msg->address + accessed_bytes,
> +                                      msg->buffer + accessed_bytes, 0);
> +       } else {
> +               while (accessed_bytes < msg->size) {
> +                       size_t to_access =
> +                               min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> +                                     msg->size - accessed_bytes);
> +                       retry = 3;
> +                       while (retry--) {
> +                               ret = mtk_dp_aux_do_transfer(
> +                                       mtk_dp, is_read, request,
> +                                       msg->address + accessed_bytes,
> +                                       msg->buffer + accessed_bytes,
> +                                       to_access);
> +                               if (ret == 0)
> +                                       break;
> +                               udelay(50);
> +                       }
> +                       if (!retry && ret) {
> +                               drm_info(mtk_dp->drm_dev,
> +                                        "Failed to do AUX transfer: %d\n",
> +                                        ret);
> +                               break;
> +                       }
> +                       accessed_bytes += to_access;
> +               }
> +       }
> +err:
> +       if (!ret) {
> +               msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +               ret = msg->size;
> +       } else {
> +               msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> +               return err;
> +       }
> +
> +       msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +       return msg->size;
> +}
> +
> +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> +{
> +       drm_dp_aux_init(&mtk_dp->aux);
> +       mtk_dp->aux.name = "aux_mtk_dp";
> +       mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> +}
> +
> +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> +{
> +       mutex_lock(&mtk_dp->dp_lock);
> +
> +       mtk_dp_hwirq_enable(mtk_dp, false);
> +       mtk_dp_power_disable(mtk_dp);
> +       phy_exit(mtk_dp->phy);
> +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> +
> +       mutex_unlock(&mtk_dp->dp_lock);
> +}
> +
> +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> +{
> +       int ret = 0;
> +
> +       mutex_lock(&mtk_dp->dp_lock);
> +
> +       ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> +       if (ret < 0) {
> +               dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> +               goto err;
> +       }
> +       ret = phy_init(mtk_dp->phy);
> +       if (ret) {
> +               dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> +               goto err_phy_init;
> +       }
> +       ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> +       if (ret) {
> +               dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> +               goto err_phy_config;
> +       }
> +
> +       mtk_dp_init_port(mtk_dp);
> +       mtk_dp_power_enable(mtk_dp);
> +       mtk_dp_hwirq_enable(mtk_dp, true);
> +
> +err_phy_config:
> +       phy_exit(mtk_dp->phy);
> +err_phy_init:
> +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> +err:
> +       mutex_unlock(&mtk_dp->dp_lock);
> +       return ret;
> +}
> +
> +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> +                               enum drm_bridge_attach_flags flags)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       int ret;
> +
> +       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> +               dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> +               return -EINVAL;
> +       }
> +
> +       ret = mtk_dp_poweron(mtk_dp);

Move the power on to mtk_dp_bridge_atomic_enable().

> +       if (ret)
> +               return ret;
> +
> +       if (mtk_dp->next_bridge) {
> +               ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> +                                       &mtk_dp->bridge, flags);
> +               if (ret) {
> +                       drm_warn(mtk_dp->drm_dev,
> +                                "Failed to attach external bridge: %d\n", ret);
> +                       goto err_bridge_attach;
> +               }
> +       }
> +
> +       mtk_dp->drm_dev = bridge->dev;
> +
> +       return 0;
> +
> +err_bridge_attach:
> +       mtk_dp_poweroff(mtk_dp);
> +       return ret;
> +}
> +
> +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +       mtk_dp->drm_dev = NULL;
> +
> +       mtk_dp_poweroff(mtk_dp);
> +}
> +
> +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> +                                        struct drm_bridge_state *old_state)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +       mtk_dp_video_mute(mtk_dp, true);
> +       mtk_dp_audio_mute(mtk_dp, true);
> +       mtk_dp->state = MTK_DP_STATE_IDLE;
> +       mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> +
> +       mtk_dp->enabled = false;
> +       msleep(100);
> +}
> +
> +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> +                                         struct drm_display_mode *mode)
> +{
> +       struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> +
> +       drm_display_mode_to_videomode(mode, &timings->vm);
> +       timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> +       timings->htotal = mode->htotal;
> +       timings->vtotal = mode->vtotal;
> +}
> +
> +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> +                                       struct drm_bridge_state *old_state)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       struct drm_connector *conn;
> +       struct drm_connector_state *conn_state;
> +       struct drm_crtc *crtc;
> +       struct drm_crtc_state *crtc_state;
> +       int ret = 0;
> +       int i;
> +
> +       conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> +                                                       bridge->encoder);

mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
                                                       bridge->encoder);

> +       if (!conn) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector is missing\n");
> +               return;
> +       }
> +
> +       memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> +
> +       conn_state =
> +               drm_atomic_get_new_connector_state(old_state->base.state, conn);
> +       if (!conn_state) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector state is missing\n");
> +               return;
> +       }
> +
> +       crtc = conn_state->crtc;
> +       if (!crtc) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector state doesn't have a crtc\n");
> +               return;
> +       }
> +
> +       crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> +       if (!crtc_state) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as crtc state is missing\n");
> +               return;
> +       }
> +
> +       mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);

Refer to mtk_hdmi_bridge_atomic_enable() for getting the mode.

> +       if (!mtk_dp_parse_capabilities(mtk_dp)) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as nothing is plugged in\n");
> +               return;
> +       }
> +
> +       //training

Run checkpatch first.

Regards,
Chun-Kuang.

> +       for (i = 0; i < 50; i++) {
> +               ret = mtk_dp_train_handler(mtk_dp);
> +               if (ret) {
> +                       drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> +                               ret);
> +                       return;
> +               }
> +
> +               ret = mtk_dp_state_handler(mtk_dp);
> +               if (ret) {
> +                       drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> +                               ret);
> +                       return;
> +               }
> +       }
> +
> +       mtk_dp->enabled = true;
> +       mtk_dp_update_plugged_status(mtk_dp);
> +}
> +



> --
> 2.32.0
>

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-25 15:27     ` Chun-Kuang Hu
  0 siblings, 0 replies; 122+ messages in thread
From: Chun-Kuang Hu @ 2021-11-25 15:27 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Chun-Kuang Hu, Philipp Zabel,
	Matthias Brugger, Markus Schneider-Pargmann, kernel test robot,
	linux-kernel, DRI Development,
	moderated list:ARM/Mediatek SoC support, Linux ARM

Hi, Guillaume:

This is a big patch, so I give some comment first.

Guillaume Ranquet <granquet@baylibre.com> 於 2021年11月10日 週三 下午9:06寫道:
>
> From: Markus Schneider-Pargmann <msp@baylibre.com>
>
> This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> according phy driver mediatek-dp-phy.
>
> It supports both functional units on the mt8195, the embedded
> DisplayPort as well as the external DisplayPort units. It offers
> hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> to 4 lanes.
>
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.
>
> This driver is based on an initial version by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
>
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reported-by: kernel test robot <lkp@intel.com>
> ---
>  drivers/gpu/drm/drm_edid.c              |    2 +-

Separate this to another patch.

>  drivers/gpu/drm/mediatek/Kconfig        |    7 +
>  drivers/gpu/drm/mediatek/Makefile       |    2 +
>  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
>  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-

Ditto.

>  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +

Ditto.

>  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +

Ditto

>  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +

Ditto

>  9 files changed, 3799 insertions(+), 13 deletions(-)
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
>
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 500279a82167a..bfd98b50ceb5b 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
>          * modes and forbids YCRCB422 support for all video modes per
>          * HDMI 1.3 spec.
>          */
> -       info->color_formats = DRM_COLOR_FORMAT_RGB444;
> +       info->color_formats |= DRM_COLOR_FORMAT_RGB444;
>
>         /* YCRCB444 is optional according to spec. */
>         if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
> diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> index 2976d21e9a34a..029b94c716131 100644
> --- a/drivers/gpu/drm/mediatek/Kconfig
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
>         select PHY_MTK_HDMI
>         help
>           DRM/KMS HDMI driver for Mediatek SoCs
> +
> +config MTK_DPTX_SUPPORT
> +       tristate "DRM DPTX Support for Mediatek SoCs"
> +       depends on DRM_MEDIATEK
> +       select PHY_MTK_DP
> +       help
> +         DRM/KMS Display Port driver for Mediatek SoCs.
> diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> index 29098d7c8307c..d86a6406055e6 100644
> --- a/drivers/gpu/drm/mediatek/Makefile
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
>                           mtk_hdmi_ddc.o
>
>  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> +
> +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> new file mode 100644
> index 0000000000000..83087219d5a5e
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -0,0 +1,3094 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019 MediaTek Inc.
> + * Copyright (c) 2021 BayLibre
> + */
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_dp_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <linux/arm-smccc.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <sound/hdmi-codec.h>
> +#include <video/videomode.h>
> +
> +#include "mtk_dp_reg.h"
> +
> +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> +
> +#define MTK_DP_MAX_LANES 4
> +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> +
> +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> +
> +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> +
> +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> +
> +#define MTK_DP_DP_VERSION_11 0x11
> +
> +enum mtk_dp_state {
> +       MTK_DP_STATE_INITIAL,
> +       MTK_DP_STATE_IDLE,
> +       MTK_DP_STATE_PREPARE,
> +       MTK_DP_STATE_NORMAL,
> +};
> +
> +enum mtk_dp_train_state {
> +       MTK_DP_TRAIN_STATE_STARTUP = 0,
> +       MTK_DP_TRAIN_STATE_CHECKCAP,
> +       MTK_DP_TRAIN_STATE_CHECKEDID,
> +       MTK_DP_TRAIN_STATE_TRAINING_PRE,
> +       MTK_DP_TRAIN_STATE_TRAINING,
> +       MTK_DP_TRAIN_STATE_CHECKTIMING,
> +       MTK_DP_TRAIN_STATE_NORMAL,
> +       MTK_DP_TRAIN_STATE_POWERSAVE,
> +       MTK_DP_TRAIN_STATE_DPIDLE,
> +};
> +
> +struct mtk_dp_timings {
> +       struct videomode vm;
> +
> +       u16 htotal;
> +       u16 vtotal;
> +       u8 frame_rate;
> +       u32 pix_rate_khz;
> +};
> +
> +struct mtk_dp_train_info {
> +       bool tps3;
> +       bool tps4;
> +       bool sink_ssc;
> +       bool cable_plugged_in;
> +       bool cable_state_change;
> +       bool cr_done;
> +       bool eq_done;
> +
> +       // link_rate is in multiple of 0.27Gbps
> +       int link_rate;
> +       int lane_count;
> +
> +       int irq_status;
> +       int check_cap_count;
> +};
> +
> +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> +enum mtk_dp_color_format {
> +       MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> +       MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> +       MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> +       MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> +       MTK_DP_COLOR_FORMAT_YONLY = 4,
> +       MTK_DP_COLOR_FORMAT_RAW = 5,
> +       MTK_DP_COLOR_FORMAT_RESERVED = 6,
> +       MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> +       MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> +};
> +
> +// Multiple of 0.27Gbps
> +enum mtk_dp_linkrate {
> +       MTK_DP_LINKRATE_RBR = 0x6,
> +       MTK_DP_LINKRATE_HBR = 0xA,
> +       MTK_DP_LINKRATE_HBR2 = 0x14,
> +       MTK_DP_LINKRATE_HBR25 = 0x19,
> +       MTK_DP_LINKRATE_HBR3 = 0x1E,
> +};
> +
> +// Same values as used for DP Spec MISC0 bits 5,6,7
> +enum mtk_dp_color_depth {
> +       MTK_DP_COLOR_DEPTH_6BIT = 0,
> +       MTK_DP_COLOR_DEPTH_8BIT = 1,
> +       MTK_DP_COLOR_DEPTH_10BIT = 2,
> +       MTK_DP_COLOR_DEPTH_12BIT = 3,
> +       MTK_DP_COLOR_DEPTH_16BIT = 4,
> +       MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> +};
> +
> +struct mtk_dp_audio_cfg {
> +       int sample_rate;
> +       int word_length_bits;
> +       int channels;
> +};
> +
> +struct mtk_dp_info {
> +       enum mtk_dp_color_depth depth;
> +       enum mtk_dp_color_format format;
> +       struct mtk_dp_audio_cfg audio_caps;
> +       struct mtk_dp_timings timings;
> +};
> +
> +struct dp_cal_data {
> +       unsigned int glb_bias_trim;
> +       unsigned int clktx_impse;
> +
> +       unsigned int ln0_tx_impsel_pmos;
> +       unsigned int ln0_tx_impsel_nmos;
> +       unsigned int ln1_tx_impsel_pmos;
> +       unsigned int ln1_tx_impsel_nmos;
> +       unsigned int ln2_tx_impsel_pmos;
> +       unsigned int ln2_tx_impsel_nmos;
> +       unsigned int ln3_tx_impsel_pmos;
> +       unsigned int ln3_tx_impsel_nmos;
> +};
> +
> +struct mtk_dp {
> +       struct device *dev;
> +       struct platform_device *phy_dev;
> +       struct phy *phy;
> +       struct dp_cal_data cal_data;
> +
> +       struct drm_device *drm_dev;
> +       struct drm_bridge bridge;
> +       struct drm_bridge *next_bridge;
> +       struct drm_dp_aux aux;
> +
> +       struct mutex edid_lock;
> +       struct edid *edid;
> +
> +       u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> +
> +       struct mtk_dp_info info;
> +       enum mtk_dp_state state;
> +
> +       struct mtk_dp_train_info train_info;
> +       enum mtk_dp_train_state train_state;
> +       unsigned int input_fmt;
> +
> +       struct regmap *regs;
> +       struct clk *dp_tx_clk;
> +
> +       bool enabled;
> +       bool audio_enable;
> +
> +       bool has_fec;
> +       struct mutex dp_lock;
> +
> +       struct mutex update_plugged_status_lock;
> +
> +       hdmi_codec_plugged_cb plugged_cb;
> +       struct device *codec_dev;
> +       u8 connector_eld[MAX_ELD_BYTES];
> +};
> +
> +enum mtk_dp_sdp_type {
> +       MTK_DP_SDP_NONE = 0x00,
> +       MTK_DP_SDP_ACM = 0x01,
> +       MTK_DP_SDP_ISRC = 0x02,
> +       MTK_DP_SDP_AVI = 0x03,
> +       MTK_DP_SDP_AUI = 0x04,
> +       MTK_DP_SDP_SPD = 0x05,
> +       MTK_DP_SDP_MPEG = 0x06,
> +       MTK_DP_SDP_NTSC = 0x07,
> +       MTK_DP_SDP_VSP = 0x08,
> +       MTK_DP_SDP_VSC = 0x09,
> +       MTK_DP_SDP_EXT = 0x0A,
> +       MTK_DP_SDP_PPS0 = 0x0B,
> +       MTK_DP_SDP_PPS1 = 0x0C,
> +       MTK_DP_SDP_PPS2 = 0x0D,
> +       MTK_DP_SDP_PPS3 = 0x0E,
> +       MTK_DP_SDP_DRM = 0x10,
> +       MTK_DP_SDP_MAX_NUM
> +};
> +
> +struct mtk_dp_sdp_packet {
> +       enum mtk_dp_sdp_type type;
> +       struct dp_sdp sdp;
> +};
> +
> +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> +union mtk_dp_audio_channel_status {
> +       struct {
> +               u8 rev : 1;
> +               u8 is_lpcm : 1;
> +               u8 copy_right : 1;
> +               u8 additional_format_info : 3;
> +               u8 channel_status_mode : 2;
> +               u8 category_code;
> +               u8 src_num : 4;
> +               u8 channel_num : 4;
> +               u8 sampling_freq : 4;
> +               u8 clk_accuracy : 2;
> +               u8 rev2 : 2;
> +               u8 word_len : 4;
> +               u8 original_sampling_freq : 4;
> +       } iec;
> +
> +       u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> +};
> +
> +static struct regmap_config mtk_dp_regmap_config = {
> +       .reg_bits = 32,
> +       .val_bits = 32,
> +       .reg_stride = 4,
> +       .max_register = SEC_OFFSET + 0x90,
> +       .name = "mtk-dp-registers",
> +};
> +
> +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)

Separate edp and displayport into two patches. For example, the first
patch support edp only, and the second patch add displayport function.

> +{
> +       return mtk_dp->next_bridge != NULL;
> +}
> +

[snip]

> +
> +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> +                                   struct drm_connector *connector)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       bool enabled = mtk_dp->enabled;
> +       struct edid *new_edid = NULL;
> +
> +       if (!enabled)
> +               drm_bridge_chain_pre_enable(bridge);

In mtk_hdmi_bridge_get_edid(), there does not check the power. Why in
this function need this?
Does mtk hdmi driver has a bug?

> +
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> +       usleep_range(2000, 5000);
> +
> +       if (mtk_dp_plug_state(mtk_dp))
> +               new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> +
> +       if (!enabled)
> +               drm_bridge_chain_post_disable(bridge);
> +
> +       mutex_lock(&mtk_dp->edid_lock);
> +       kfree(mtk_dp->edid);
> +       mtk_dp->edid = NULL;
> +
> +       if (!new_edid) {
> +               mutex_unlock(&mtk_dp->edid_lock);
> +               return NULL;
> +       }
> +
> +       mtk_dp->edid = drm_edid_duplicate(new_edid);
> +       mutex_unlock(&mtk_dp->edid_lock);
> +
> +       return new_edid;
> +}
> +
> +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> +                                  struct drm_dp_aux_msg *msg)
> +{
> +       ssize_t err = -EAGAIN;
> +       struct mtk_dp *mtk_dp;
> +       bool is_read;
> +       u8 request;
> +       size_t accessed_bytes = 0;
> +       int retry = 3, ret = 0;
> +
> +       mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> +
> +       if (!mtk_dp->train_info.cable_plugged_in ||
> +           mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> +               mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> +               err = -EAGAIN;
> +               goto err;
> +       }
> +
> +       switch (msg->request) {
> +       case DP_AUX_I2C_MOT:
> +       case DP_AUX_I2C_WRITE:
> +       case DP_AUX_NATIVE_WRITE:
> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> +               request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> +               is_read = false;
> +               break;
> +       case DP_AUX_I2C_READ:
> +       case DP_AUX_NATIVE_READ:
> +       case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> +               request = msg->request;
> +               is_read = true;
> +               break;
> +       default:
> +               drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> +                       msg->request);
> +               err = -EINVAL;
> +               goto err;

Directly return here.

> +       }
> +
> +       if (msg->size == 0) {
> +               mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> +                                      msg->address + accessed_bytes,
> +                                      msg->buffer + accessed_bytes, 0);
> +       } else {
> +               while (accessed_bytes < msg->size) {
> +                       size_t to_access =
> +                               min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> +                                     msg->size - accessed_bytes);
> +                       retry = 3;
> +                       while (retry--) {
> +                               ret = mtk_dp_aux_do_transfer(
> +                                       mtk_dp, is_read, request,
> +                                       msg->address + accessed_bytes,
> +                                       msg->buffer + accessed_bytes,
> +                                       to_access);
> +                               if (ret == 0)
> +                                       break;
> +                               udelay(50);
> +                       }
> +                       if (!retry && ret) {
> +                               drm_info(mtk_dp->drm_dev,
> +                                        "Failed to do AUX transfer: %d\n",
> +                                        ret);
> +                               break;
> +                       }
> +                       accessed_bytes += to_access;
> +               }
> +       }
> +err:
> +       if (!ret) {
> +               msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +               ret = msg->size;
> +       } else {
> +               msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> +               return err;
> +       }
> +
> +       msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +       return msg->size;
> +}
> +
> +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> +{
> +       drm_dp_aux_init(&mtk_dp->aux);
> +       mtk_dp->aux.name = "aux_mtk_dp";
> +       mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> +}
> +
> +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> +{
> +       mutex_lock(&mtk_dp->dp_lock);
> +
> +       mtk_dp_hwirq_enable(mtk_dp, false);
> +       mtk_dp_power_disable(mtk_dp);
> +       phy_exit(mtk_dp->phy);
> +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> +
> +       mutex_unlock(&mtk_dp->dp_lock);
> +}
> +
> +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> +{
> +       int ret = 0;
> +
> +       mutex_lock(&mtk_dp->dp_lock);
> +
> +       ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> +       if (ret < 0) {
> +               dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> +               goto err;
> +       }
> +       ret = phy_init(mtk_dp->phy);
> +       if (ret) {
> +               dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> +               goto err_phy_init;
> +       }
> +       ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> +       if (ret) {
> +               dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> +               goto err_phy_config;
> +       }
> +
> +       mtk_dp_init_port(mtk_dp);
> +       mtk_dp_power_enable(mtk_dp);
> +       mtk_dp_hwirq_enable(mtk_dp, true);
> +
> +err_phy_config:
> +       phy_exit(mtk_dp->phy);
> +err_phy_init:
> +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> +err:
> +       mutex_unlock(&mtk_dp->dp_lock);
> +       return ret;
> +}
> +
> +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> +                               enum drm_bridge_attach_flags flags)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       int ret;
> +
> +       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> +               dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> +               return -EINVAL;
> +       }
> +
> +       ret = mtk_dp_poweron(mtk_dp);

Move the power on to mtk_dp_bridge_atomic_enable().

> +       if (ret)
> +               return ret;
> +
> +       if (mtk_dp->next_bridge) {
> +               ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> +                                       &mtk_dp->bridge, flags);
> +               if (ret) {
> +                       drm_warn(mtk_dp->drm_dev,
> +                                "Failed to attach external bridge: %d\n", ret);
> +                       goto err_bridge_attach;
> +               }
> +       }
> +
> +       mtk_dp->drm_dev = bridge->dev;
> +
> +       return 0;
> +
> +err_bridge_attach:
> +       mtk_dp_poweroff(mtk_dp);
> +       return ret;
> +}
> +
> +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +       mtk_dp->drm_dev = NULL;
> +
> +       mtk_dp_poweroff(mtk_dp);
> +}
> +
> +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> +                                        struct drm_bridge_state *old_state)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +       mtk_dp_video_mute(mtk_dp, true);
> +       mtk_dp_audio_mute(mtk_dp, true);
> +       mtk_dp->state = MTK_DP_STATE_IDLE;
> +       mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> +
> +       mtk_dp->enabled = false;
> +       msleep(100);
> +}
> +
> +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> +                                         struct drm_display_mode *mode)
> +{
> +       struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> +
> +       drm_display_mode_to_videomode(mode, &timings->vm);
> +       timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> +       timings->htotal = mode->htotal;
> +       timings->vtotal = mode->vtotal;
> +}
> +
> +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> +                                       struct drm_bridge_state *old_state)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       struct drm_connector *conn;
> +       struct drm_connector_state *conn_state;
> +       struct drm_crtc *crtc;
> +       struct drm_crtc_state *crtc_state;
> +       int ret = 0;
> +       int i;
> +
> +       conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> +                                                       bridge->encoder);

mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
                                                       bridge->encoder);

> +       if (!conn) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector is missing\n");
> +               return;
> +       }
> +
> +       memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> +
> +       conn_state =
> +               drm_atomic_get_new_connector_state(old_state->base.state, conn);
> +       if (!conn_state) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector state is missing\n");
> +               return;
> +       }
> +
> +       crtc = conn_state->crtc;
> +       if (!crtc) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector state doesn't have a crtc\n");
> +               return;
> +       }
> +
> +       crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> +       if (!crtc_state) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as crtc state is missing\n");
> +               return;
> +       }
> +
> +       mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);

Refer to mtk_hdmi_bridge_atomic_enable() for getting the mode.

> +       if (!mtk_dp_parse_capabilities(mtk_dp)) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as nothing is plugged in\n");
> +               return;
> +       }
> +
> +       //training

Run checkpatch first.

Regards,
Chun-Kuang.

> +       for (i = 0; i < 50; i++) {
> +               ret = mtk_dp_train_handler(mtk_dp);
> +               if (ret) {
> +                       drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> +                               ret);
> +                       return;
> +               }
> +
> +               ret = mtk_dp_state_handler(mtk_dp);
> +               if (ret) {
> +                       drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> +                               ret);
> +                       return;
> +               }
> +       }
> +
> +       mtk_dp->enabled = true;
> +       mtk_dp_update_plugged_status(mtk_dp);
> +}
> +



> --
> 2.32.0
>

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-25 15:27     ` Chun-Kuang Hu
  0 siblings, 0 replies; 122+ messages in thread
From: Chun-Kuang Hu @ 2021-11-25 15:27 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Chun-Kuang Hu, Philipp Zabel,
	Matthias Brugger, Markus Schneider-Pargmann, kernel test robot,
	linux-kernel, DRI Development,
	moderated list:ARM/Mediatek SoC support, Linux ARM

Hi, Guillaume:

This is a big patch, so I give some comment first.

Guillaume Ranquet <granquet@baylibre.com> 於 2021年11月10日 週三 下午9:06寫道:
>
> From: Markus Schneider-Pargmann <msp@baylibre.com>
>
> This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> according phy driver mediatek-dp-phy.
>
> It supports both functional units on the mt8195, the embedded
> DisplayPort as well as the external DisplayPort units. It offers
> hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> to 4 lanes.
>
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.
>
> This driver is based on an initial version by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
>
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reported-by: kernel test robot <lkp@intel.com>
> ---
>  drivers/gpu/drm/drm_edid.c              |    2 +-

Separate this to another patch.

>  drivers/gpu/drm/mediatek/Kconfig        |    7 +
>  drivers/gpu/drm/mediatek/Makefile       |    2 +
>  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
>  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-

Ditto.

>  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +

Ditto.

>  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +

Ditto

>  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +

Ditto

>  9 files changed, 3799 insertions(+), 13 deletions(-)
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
>
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 500279a82167a..bfd98b50ceb5b 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
>          * modes and forbids YCRCB422 support for all video modes per
>          * HDMI 1.3 spec.
>          */
> -       info->color_formats = DRM_COLOR_FORMAT_RGB444;
> +       info->color_formats |= DRM_COLOR_FORMAT_RGB444;
>
>         /* YCRCB444 is optional according to spec. */
>         if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
> diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> index 2976d21e9a34a..029b94c716131 100644
> --- a/drivers/gpu/drm/mediatek/Kconfig
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
>         select PHY_MTK_HDMI
>         help
>           DRM/KMS HDMI driver for Mediatek SoCs
> +
> +config MTK_DPTX_SUPPORT
> +       tristate "DRM DPTX Support for Mediatek SoCs"
> +       depends on DRM_MEDIATEK
> +       select PHY_MTK_DP
> +       help
> +         DRM/KMS Display Port driver for Mediatek SoCs.
> diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> index 29098d7c8307c..d86a6406055e6 100644
> --- a/drivers/gpu/drm/mediatek/Makefile
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
>                           mtk_hdmi_ddc.o
>
>  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> +
> +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> new file mode 100644
> index 0000000000000..83087219d5a5e
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -0,0 +1,3094 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019 MediaTek Inc.
> + * Copyright (c) 2021 BayLibre
> + */
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_dp_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <linux/arm-smccc.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <sound/hdmi-codec.h>
> +#include <video/videomode.h>
> +
> +#include "mtk_dp_reg.h"
> +
> +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> +
> +#define MTK_DP_MAX_LANES 4
> +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> +
> +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> +
> +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> +
> +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> +
> +#define MTK_DP_DP_VERSION_11 0x11
> +
> +enum mtk_dp_state {
> +       MTK_DP_STATE_INITIAL,
> +       MTK_DP_STATE_IDLE,
> +       MTK_DP_STATE_PREPARE,
> +       MTK_DP_STATE_NORMAL,
> +};
> +
> +enum mtk_dp_train_state {
> +       MTK_DP_TRAIN_STATE_STARTUP = 0,
> +       MTK_DP_TRAIN_STATE_CHECKCAP,
> +       MTK_DP_TRAIN_STATE_CHECKEDID,
> +       MTK_DP_TRAIN_STATE_TRAINING_PRE,
> +       MTK_DP_TRAIN_STATE_TRAINING,
> +       MTK_DP_TRAIN_STATE_CHECKTIMING,
> +       MTK_DP_TRAIN_STATE_NORMAL,
> +       MTK_DP_TRAIN_STATE_POWERSAVE,
> +       MTK_DP_TRAIN_STATE_DPIDLE,
> +};
> +
> +struct mtk_dp_timings {
> +       struct videomode vm;
> +
> +       u16 htotal;
> +       u16 vtotal;
> +       u8 frame_rate;
> +       u32 pix_rate_khz;
> +};
> +
> +struct mtk_dp_train_info {
> +       bool tps3;
> +       bool tps4;
> +       bool sink_ssc;
> +       bool cable_plugged_in;
> +       bool cable_state_change;
> +       bool cr_done;
> +       bool eq_done;
> +
> +       // link_rate is in multiple of 0.27Gbps
> +       int link_rate;
> +       int lane_count;
> +
> +       int irq_status;
> +       int check_cap_count;
> +};
> +
> +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> +enum mtk_dp_color_format {
> +       MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> +       MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> +       MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> +       MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> +       MTK_DP_COLOR_FORMAT_YONLY = 4,
> +       MTK_DP_COLOR_FORMAT_RAW = 5,
> +       MTK_DP_COLOR_FORMAT_RESERVED = 6,
> +       MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> +       MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> +};
> +
> +// Multiple of 0.27Gbps
> +enum mtk_dp_linkrate {
> +       MTK_DP_LINKRATE_RBR = 0x6,
> +       MTK_DP_LINKRATE_HBR = 0xA,
> +       MTK_DP_LINKRATE_HBR2 = 0x14,
> +       MTK_DP_LINKRATE_HBR25 = 0x19,
> +       MTK_DP_LINKRATE_HBR3 = 0x1E,
> +};
> +
> +// Same values as used for DP Spec MISC0 bits 5,6,7
> +enum mtk_dp_color_depth {
> +       MTK_DP_COLOR_DEPTH_6BIT = 0,
> +       MTK_DP_COLOR_DEPTH_8BIT = 1,
> +       MTK_DP_COLOR_DEPTH_10BIT = 2,
> +       MTK_DP_COLOR_DEPTH_12BIT = 3,
> +       MTK_DP_COLOR_DEPTH_16BIT = 4,
> +       MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> +};
> +
> +struct mtk_dp_audio_cfg {
> +       int sample_rate;
> +       int word_length_bits;
> +       int channels;
> +};
> +
> +struct mtk_dp_info {
> +       enum mtk_dp_color_depth depth;
> +       enum mtk_dp_color_format format;
> +       struct mtk_dp_audio_cfg audio_caps;
> +       struct mtk_dp_timings timings;
> +};
> +
> +struct dp_cal_data {
> +       unsigned int glb_bias_trim;
> +       unsigned int clktx_impse;
> +
> +       unsigned int ln0_tx_impsel_pmos;
> +       unsigned int ln0_tx_impsel_nmos;
> +       unsigned int ln1_tx_impsel_pmos;
> +       unsigned int ln1_tx_impsel_nmos;
> +       unsigned int ln2_tx_impsel_pmos;
> +       unsigned int ln2_tx_impsel_nmos;
> +       unsigned int ln3_tx_impsel_pmos;
> +       unsigned int ln3_tx_impsel_nmos;
> +};
> +
> +struct mtk_dp {
> +       struct device *dev;
> +       struct platform_device *phy_dev;
> +       struct phy *phy;
> +       struct dp_cal_data cal_data;
> +
> +       struct drm_device *drm_dev;
> +       struct drm_bridge bridge;
> +       struct drm_bridge *next_bridge;
> +       struct drm_dp_aux aux;
> +
> +       struct mutex edid_lock;
> +       struct edid *edid;
> +
> +       u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> +
> +       struct mtk_dp_info info;
> +       enum mtk_dp_state state;
> +
> +       struct mtk_dp_train_info train_info;
> +       enum mtk_dp_train_state train_state;
> +       unsigned int input_fmt;
> +
> +       struct regmap *regs;
> +       struct clk *dp_tx_clk;
> +
> +       bool enabled;
> +       bool audio_enable;
> +
> +       bool has_fec;
> +       struct mutex dp_lock;
> +
> +       struct mutex update_plugged_status_lock;
> +
> +       hdmi_codec_plugged_cb plugged_cb;
> +       struct device *codec_dev;
> +       u8 connector_eld[MAX_ELD_BYTES];
> +};
> +
> +enum mtk_dp_sdp_type {
> +       MTK_DP_SDP_NONE = 0x00,
> +       MTK_DP_SDP_ACM = 0x01,
> +       MTK_DP_SDP_ISRC = 0x02,
> +       MTK_DP_SDP_AVI = 0x03,
> +       MTK_DP_SDP_AUI = 0x04,
> +       MTK_DP_SDP_SPD = 0x05,
> +       MTK_DP_SDP_MPEG = 0x06,
> +       MTK_DP_SDP_NTSC = 0x07,
> +       MTK_DP_SDP_VSP = 0x08,
> +       MTK_DP_SDP_VSC = 0x09,
> +       MTK_DP_SDP_EXT = 0x0A,
> +       MTK_DP_SDP_PPS0 = 0x0B,
> +       MTK_DP_SDP_PPS1 = 0x0C,
> +       MTK_DP_SDP_PPS2 = 0x0D,
> +       MTK_DP_SDP_PPS3 = 0x0E,
> +       MTK_DP_SDP_DRM = 0x10,
> +       MTK_DP_SDP_MAX_NUM
> +};
> +
> +struct mtk_dp_sdp_packet {
> +       enum mtk_dp_sdp_type type;
> +       struct dp_sdp sdp;
> +};
> +
> +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> +union mtk_dp_audio_channel_status {
> +       struct {
> +               u8 rev : 1;
> +               u8 is_lpcm : 1;
> +               u8 copy_right : 1;
> +               u8 additional_format_info : 3;
> +               u8 channel_status_mode : 2;
> +               u8 category_code;
> +               u8 src_num : 4;
> +               u8 channel_num : 4;
> +               u8 sampling_freq : 4;
> +               u8 clk_accuracy : 2;
> +               u8 rev2 : 2;
> +               u8 word_len : 4;
> +               u8 original_sampling_freq : 4;
> +       } iec;
> +
> +       u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> +};
> +
> +static struct regmap_config mtk_dp_regmap_config = {
> +       .reg_bits = 32,
> +       .val_bits = 32,
> +       .reg_stride = 4,
> +       .max_register = SEC_OFFSET + 0x90,
> +       .name = "mtk-dp-registers",
> +};
> +
> +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)

Separate edp and displayport into two patches. For example, the first
patch support edp only, and the second patch add displayport function.

> +{
> +       return mtk_dp->next_bridge != NULL;
> +}
> +

[snip]

> +
> +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> +                                   struct drm_connector *connector)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       bool enabled = mtk_dp->enabled;
> +       struct edid *new_edid = NULL;
> +
> +       if (!enabled)
> +               drm_bridge_chain_pre_enable(bridge);

In mtk_hdmi_bridge_get_edid(), there does not check the power. Why in
this function need this?
Does mtk hdmi driver has a bug?

> +
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> +       usleep_range(2000, 5000);
> +
> +       if (mtk_dp_plug_state(mtk_dp))
> +               new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> +
> +       if (!enabled)
> +               drm_bridge_chain_post_disable(bridge);
> +
> +       mutex_lock(&mtk_dp->edid_lock);
> +       kfree(mtk_dp->edid);
> +       mtk_dp->edid = NULL;
> +
> +       if (!new_edid) {
> +               mutex_unlock(&mtk_dp->edid_lock);
> +               return NULL;
> +       }
> +
> +       mtk_dp->edid = drm_edid_duplicate(new_edid);
> +       mutex_unlock(&mtk_dp->edid_lock);
> +
> +       return new_edid;
> +}
> +
> +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> +                                  struct drm_dp_aux_msg *msg)
> +{
> +       ssize_t err = -EAGAIN;
> +       struct mtk_dp *mtk_dp;
> +       bool is_read;
> +       u8 request;
> +       size_t accessed_bytes = 0;
> +       int retry = 3, ret = 0;
> +
> +       mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> +
> +       if (!mtk_dp->train_info.cable_plugged_in ||
> +           mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> +               mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> +               err = -EAGAIN;
> +               goto err;
> +       }
> +
> +       switch (msg->request) {
> +       case DP_AUX_I2C_MOT:
> +       case DP_AUX_I2C_WRITE:
> +       case DP_AUX_NATIVE_WRITE:
> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> +               request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> +               is_read = false;
> +               break;
> +       case DP_AUX_I2C_READ:
> +       case DP_AUX_NATIVE_READ:
> +       case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> +               request = msg->request;
> +               is_read = true;
> +               break;
> +       default:
> +               drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> +                       msg->request);
> +               err = -EINVAL;
> +               goto err;

Directly return here.

> +       }
> +
> +       if (msg->size == 0) {
> +               mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> +                                      msg->address + accessed_bytes,
> +                                      msg->buffer + accessed_bytes, 0);
> +       } else {
> +               while (accessed_bytes < msg->size) {
> +                       size_t to_access =
> +                               min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> +                                     msg->size - accessed_bytes);
> +                       retry = 3;
> +                       while (retry--) {
> +                               ret = mtk_dp_aux_do_transfer(
> +                                       mtk_dp, is_read, request,
> +                                       msg->address + accessed_bytes,
> +                                       msg->buffer + accessed_bytes,
> +                                       to_access);
> +                               if (ret == 0)
> +                                       break;
> +                               udelay(50);
> +                       }
> +                       if (!retry && ret) {
> +                               drm_info(mtk_dp->drm_dev,
> +                                        "Failed to do AUX transfer: %d\n",
> +                                        ret);
> +                               break;
> +                       }
> +                       accessed_bytes += to_access;
> +               }
> +       }
> +err:
> +       if (!ret) {
> +               msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +               ret = msg->size;
> +       } else {
> +               msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> +               return err;
> +       }
> +
> +       msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +       return msg->size;
> +}
> +
> +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> +{
> +       drm_dp_aux_init(&mtk_dp->aux);
> +       mtk_dp->aux.name = "aux_mtk_dp";
> +       mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> +}
> +
> +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> +{
> +       mutex_lock(&mtk_dp->dp_lock);
> +
> +       mtk_dp_hwirq_enable(mtk_dp, false);
> +       mtk_dp_power_disable(mtk_dp);
> +       phy_exit(mtk_dp->phy);
> +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> +
> +       mutex_unlock(&mtk_dp->dp_lock);
> +}
> +
> +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> +{
> +       int ret = 0;
> +
> +       mutex_lock(&mtk_dp->dp_lock);
> +
> +       ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> +       if (ret < 0) {
> +               dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> +               goto err;
> +       }
> +       ret = phy_init(mtk_dp->phy);
> +       if (ret) {
> +               dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> +               goto err_phy_init;
> +       }
> +       ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> +       if (ret) {
> +               dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> +               goto err_phy_config;
> +       }
> +
> +       mtk_dp_init_port(mtk_dp);
> +       mtk_dp_power_enable(mtk_dp);
> +       mtk_dp_hwirq_enable(mtk_dp, true);
> +
> +err_phy_config:
> +       phy_exit(mtk_dp->phy);
> +err_phy_init:
> +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> +err:
> +       mutex_unlock(&mtk_dp->dp_lock);
> +       return ret;
> +}
> +
> +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> +                               enum drm_bridge_attach_flags flags)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       int ret;
> +
> +       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> +               dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> +               return -EINVAL;
> +       }
> +
> +       ret = mtk_dp_poweron(mtk_dp);

Move the power on to mtk_dp_bridge_atomic_enable().

> +       if (ret)
> +               return ret;
> +
> +       if (mtk_dp->next_bridge) {
> +               ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> +                                       &mtk_dp->bridge, flags);
> +               if (ret) {
> +                       drm_warn(mtk_dp->drm_dev,
> +                                "Failed to attach external bridge: %d\n", ret);
> +                       goto err_bridge_attach;
> +               }
> +       }
> +
> +       mtk_dp->drm_dev = bridge->dev;
> +
> +       return 0;
> +
> +err_bridge_attach:
> +       mtk_dp_poweroff(mtk_dp);
> +       return ret;
> +}
> +
> +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +       mtk_dp->drm_dev = NULL;
> +
> +       mtk_dp_poweroff(mtk_dp);
> +}
> +
> +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> +                                        struct drm_bridge_state *old_state)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +       mtk_dp_video_mute(mtk_dp, true);
> +       mtk_dp_audio_mute(mtk_dp, true);
> +       mtk_dp->state = MTK_DP_STATE_IDLE;
> +       mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> +
> +       mtk_dp->enabled = false;
> +       msleep(100);
> +}
> +
> +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> +                                         struct drm_display_mode *mode)
> +{
> +       struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> +
> +       drm_display_mode_to_videomode(mode, &timings->vm);
> +       timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> +       timings->htotal = mode->htotal;
> +       timings->vtotal = mode->vtotal;
> +}
> +
> +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> +                                       struct drm_bridge_state *old_state)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       struct drm_connector *conn;
> +       struct drm_connector_state *conn_state;
> +       struct drm_crtc *crtc;
> +       struct drm_crtc_state *crtc_state;
> +       int ret = 0;
> +       int i;
> +
> +       conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> +                                                       bridge->encoder);

mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
                                                       bridge->encoder);

> +       if (!conn) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector is missing\n");
> +               return;
> +       }
> +
> +       memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> +
> +       conn_state =
> +               drm_atomic_get_new_connector_state(old_state->base.state, conn);
> +       if (!conn_state) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector state is missing\n");
> +               return;
> +       }
> +
> +       crtc = conn_state->crtc;
> +       if (!crtc) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector state doesn't have a crtc\n");
> +               return;
> +       }
> +
> +       crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> +       if (!crtc_state) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as crtc state is missing\n");
> +               return;
> +       }
> +
> +       mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);

Refer to mtk_hdmi_bridge_atomic_enable() for getting the mode.

> +       if (!mtk_dp_parse_capabilities(mtk_dp)) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as nothing is plugged in\n");
> +               return;
> +       }
> +
> +       //training

Run checkpatch first.

Regards,
Chun-Kuang.

> +       for (i = 0; i < 50; i++) {
> +               ret = mtk_dp_train_handler(mtk_dp);
> +               if (ret) {
> +                       drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> +                               ret);
> +                       return;
> +               }
> +
> +               ret = mtk_dp_state_handler(mtk_dp);
> +               if (ret) {
> +                       drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> +                               ret);
> +                       return;
> +               }
> +       }
> +
> +       mtk_dp->enabled = true;
> +       mtk_dp_update_plugged_status(mtk_dp);
> +}
> +



> --
> 2.32.0
>

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-11-25 15:27     ` Chun-Kuang Hu
  0 siblings, 0 replies; 122+ messages in thread
From: Chun-Kuang Hu @ 2021-11-25 15:27 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Chun-Kuang Hu, Philipp Zabel,
	Matthias Brugger, Markus Schneider-Pargmann, kernel test robot,
	linux-kernel, DRI Development,
	moderated list:ARM/Mediatek SoC support, Linux ARM

Hi, Guillaume:

This is a big patch, so I give some comment first.

Guillaume Ranquet <granquet@baylibre.com> 於 2021年11月10日 週三 下午9:06寫道:
>
> From: Markus Schneider-Pargmann <msp@baylibre.com>
>
> This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> according phy driver mediatek-dp-phy.
>
> It supports both functional units on the mt8195, the embedded
> DisplayPort as well as the external DisplayPort units. It offers
> hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> to 4 lanes.
>
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.
>
> This driver is based on an initial version by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
>
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reported-by: kernel test robot <lkp@intel.com>
> ---
>  drivers/gpu/drm/drm_edid.c              |    2 +-

Separate this to another patch.

>  drivers/gpu/drm/mediatek/Kconfig        |    7 +
>  drivers/gpu/drm/mediatek/Makefile       |    2 +
>  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
>  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-

Ditto.

>  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +

Ditto.

>  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +

Ditto

>  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +

Ditto

>  9 files changed, 3799 insertions(+), 13 deletions(-)
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
>
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 500279a82167a..bfd98b50ceb5b 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
>          * modes and forbids YCRCB422 support for all video modes per
>          * HDMI 1.3 spec.
>          */
> -       info->color_formats = DRM_COLOR_FORMAT_RGB444;
> +       info->color_formats |= DRM_COLOR_FORMAT_RGB444;
>
>         /* YCRCB444 is optional according to spec. */
>         if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
> diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> index 2976d21e9a34a..029b94c716131 100644
> --- a/drivers/gpu/drm/mediatek/Kconfig
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
>         select PHY_MTK_HDMI
>         help
>           DRM/KMS HDMI driver for Mediatek SoCs
> +
> +config MTK_DPTX_SUPPORT
> +       tristate "DRM DPTX Support for Mediatek SoCs"
> +       depends on DRM_MEDIATEK
> +       select PHY_MTK_DP
> +       help
> +         DRM/KMS Display Port driver for Mediatek SoCs.
> diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> index 29098d7c8307c..d86a6406055e6 100644
> --- a/drivers/gpu/drm/mediatek/Makefile
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
>                           mtk_hdmi_ddc.o
>
>  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> +
> +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> new file mode 100644
> index 0000000000000..83087219d5a5e
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -0,0 +1,3094 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019 MediaTek Inc.
> + * Copyright (c) 2021 BayLibre
> + */
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_dp_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <linux/arm-smccc.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <sound/hdmi-codec.h>
> +#include <video/videomode.h>
> +
> +#include "mtk_dp_reg.h"
> +
> +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> +
> +#define MTK_DP_MAX_LANES 4
> +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> +
> +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> +
> +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> +
> +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> +
> +#define MTK_DP_DP_VERSION_11 0x11
> +
> +enum mtk_dp_state {
> +       MTK_DP_STATE_INITIAL,
> +       MTK_DP_STATE_IDLE,
> +       MTK_DP_STATE_PREPARE,
> +       MTK_DP_STATE_NORMAL,
> +};
> +
> +enum mtk_dp_train_state {
> +       MTK_DP_TRAIN_STATE_STARTUP = 0,
> +       MTK_DP_TRAIN_STATE_CHECKCAP,
> +       MTK_DP_TRAIN_STATE_CHECKEDID,
> +       MTK_DP_TRAIN_STATE_TRAINING_PRE,
> +       MTK_DP_TRAIN_STATE_TRAINING,
> +       MTK_DP_TRAIN_STATE_CHECKTIMING,
> +       MTK_DP_TRAIN_STATE_NORMAL,
> +       MTK_DP_TRAIN_STATE_POWERSAVE,
> +       MTK_DP_TRAIN_STATE_DPIDLE,
> +};
> +
> +struct mtk_dp_timings {
> +       struct videomode vm;
> +
> +       u16 htotal;
> +       u16 vtotal;
> +       u8 frame_rate;
> +       u32 pix_rate_khz;
> +};
> +
> +struct mtk_dp_train_info {
> +       bool tps3;
> +       bool tps4;
> +       bool sink_ssc;
> +       bool cable_plugged_in;
> +       bool cable_state_change;
> +       bool cr_done;
> +       bool eq_done;
> +
> +       // link_rate is in multiple of 0.27Gbps
> +       int link_rate;
> +       int lane_count;
> +
> +       int irq_status;
> +       int check_cap_count;
> +};
> +
> +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> +enum mtk_dp_color_format {
> +       MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> +       MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> +       MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> +       MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> +       MTK_DP_COLOR_FORMAT_YONLY = 4,
> +       MTK_DP_COLOR_FORMAT_RAW = 5,
> +       MTK_DP_COLOR_FORMAT_RESERVED = 6,
> +       MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> +       MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> +};
> +
> +// Multiple of 0.27Gbps
> +enum mtk_dp_linkrate {
> +       MTK_DP_LINKRATE_RBR = 0x6,
> +       MTK_DP_LINKRATE_HBR = 0xA,
> +       MTK_DP_LINKRATE_HBR2 = 0x14,
> +       MTK_DP_LINKRATE_HBR25 = 0x19,
> +       MTK_DP_LINKRATE_HBR3 = 0x1E,
> +};
> +
> +// Same values as used for DP Spec MISC0 bits 5,6,7
> +enum mtk_dp_color_depth {
> +       MTK_DP_COLOR_DEPTH_6BIT = 0,
> +       MTK_DP_COLOR_DEPTH_8BIT = 1,
> +       MTK_DP_COLOR_DEPTH_10BIT = 2,
> +       MTK_DP_COLOR_DEPTH_12BIT = 3,
> +       MTK_DP_COLOR_DEPTH_16BIT = 4,
> +       MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> +};
> +
> +struct mtk_dp_audio_cfg {
> +       int sample_rate;
> +       int word_length_bits;
> +       int channels;
> +};
> +
> +struct mtk_dp_info {
> +       enum mtk_dp_color_depth depth;
> +       enum mtk_dp_color_format format;
> +       struct mtk_dp_audio_cfg audio_caps;
> +       struct mtk_dp_timings timings;
> +};
> +
> +struct dp_cal_data {
> +       unsigned int glb_bias_trim;
> +       unsigned int clktx_impse;
> +
> +       unsigned int ln0_tx_impsel_pmos;
> +       unsigned int ln0_tx_impsel_nmos;
> +       unsigned int ln1_tx_impsel_pmos;
> +       unsigned int ln1_tx_impsel_nmos;
> +       unsigned int ln2_tx_impsel_pmos;
> +       unsigned int ln2_tx_impsel_nmos;
> +       unsigned int ln3_tx_impsel_pmos;
> +       unsigned int ln3_tx_impsel_nmos;
> +};
> +
> +struct mtk_dp {
> +       struct device *dev;
> +       struct platform_device *phy_dev;
> +       struct phy *phy;
> +       struct dp_cal_data cal_data;
> +
> +       struct drm_device *drm_dev;
> +       struct drm_bridge bridge;
> +       struct drm_bridge *next_bridge;
> +       struct drm_dp_aux aux;
> +
> +       struct mutex edid_lock;
> +       struct edid *edid;
> +
> +       u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> +
> +       struct mtk_dp_info info;
> +       enum mtk_dp_state state;
> +
> +       struct mtk_dp_train_info train_info;
> +       enum mtk_dp_train_state train_state;
> +       unsigned int input_fmt;
> +
> +       struct regmap *regs;
> +       struct clk *dp_tx_clk;
> +
> +       bool enabled;
> +       bool audio_enable;
> +
> +       bool has_fec;
> +       struct mutex dp_lock;
> +
> +       struct mutex update_plugged_status_lock;
> +
> +       hdmi_codec_plugged_cb plugged_cb;
> +       struct device *codec_dev;
> +       u8 connector_eld[MAX_ELD_BYTES];
> +};
> +
> +enum mtk_dp_sdp_type {
> +       MTK_DP_SDP_NONE = 0x00,
> +       MTK_DP_SDP_ACM = 0x01,
> +       MTK_DP_SDP_ISRC = 0x02,
> +       MTK_DP_SDP_AVI = 0x03,
> +       MTK_DP_SDP_AUI = 0x04,
> +       MTK_DP_SDP_SPD = 0x05,
> +       MTK_DP_SDP_MPEG = 0x06,
> +       MTK_DP_SDP_NTSC = 0x07,
> +       MTK_DP_SDP_VSP = 0x08,
> +       MTK_DP_SDP_VSC = 0x09,
> +       MTK_DP_SDP_EXT = 0x0A,
> +       MTK_DP_SDP_PPS0 = 0x0B,
> +       MTK_DP_SDP_PPS1 = 0x0C,
> +       MTK_DP_SDP_PPS2 = 0x0D,
> +       MTK_DP_SDP_PPS3 = 0x0E,
> +       MTK_DP_SDP_DRM = 0x10,
> +       MTK_DP_SDP_MAX_NUM
> +};
> +
> +struct mtk_dp_sdp_packet {
> +       enum mtk_dp_sdp_type type;
> +       struct dp_sdp sdp;
> +};
> +
> +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> +union mtk_dp_audio_channel_status {
> +       struct {
> +               u8 rev : 1;
> +               u8 is_lpcm : 1;
> +               u8 copy_right : 1;
> +               u8 additional_format_info : 3;
> +               u8 channel_status_mode : 2;
> +               u8 category_code;
> +               u8 src_num : 4;
> +               u8 channel_num : 4;
> +               u8 sampling_freq : 4;
> +               u8 clk_accuracy : 2;
> +               u8 rev2 : 2;
> +               u8 word_len : 4;
> +               u8 original_sampling_freq : 4;
> +       } iec;
> +
> +       u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> +};
> +
> +static struct regmap_config mtk_dp_regmap_config = {
> +       .reg_bits = 32,
> +       .val_bits = 32,
> +       .reg_stride = 4,
> +       .max_register = SEC_OFFSET + 0x90,
> +       .name = "mtk-dp-registers",
> +};
> +
> +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)

Separate edp and displayport into two patches. For example, the first
patch support edp only, and the second patch add displayport function.

> +{
> +       return mtk_dp->next_bridge != NULL;
> +}
> +

[snip]

> +
> +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> +                                   struct drm_connector *connector)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       bool enabled = mtk_dp->enabled;
> +       struct edid *new_edid = NULL;
> +
> +       if (!enabled)
> +               drm_bridge_chain_pre_enable(bridge);

In mtk_hdmi_bridge_get_edid(), there does not check the power. Why in
this function need this?
Does mtk hdmi driver has a bug?

> +
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> +       usleep_range(2000, 5000);
> +
> +       if (mtk_dp_plug_state(mtk_dp))
> +               new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> +
> +       if (!enabled)
> +               drm_bridge_chain_post_disable(bridge);
> +
> +       mutex_lock(&mtk_dp->edid_lock);
> +       kfree(mtk_dp->edid);
> +       mtk_dp->edid = NULL;
> +
> +       if (!new_edid) {
> +               mutex_unlock(&mtk_dp->edid_lock);
> +               return NULL;
> +       }
> +
> +       mtk_dp->edid = drm_edid_duplicate(new_edid);
> +       mutex_unlock(&mtk_dp->edid_lock);
> +
> +       return new_edid;
> +}
> +
> +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> +                                  struct drm_dp_aux_msg *msg)
> +{
> +       ssize_t err = -EAGAIN;
> +       struct mtk_dp *mtk_dp;
> +       bool is_read;
> +       u8 request;
> +       size_t accessed_bytes = 0;
> +       int retry = 3, ret = 0;
> +
> +       mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> +
> +       if (!mtk_dp->train_info.cable_plugged_in ||
> +           mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> +               mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> +               err = -EAGAIN;
> +               goto err;
> +       }
> +
> +       switch (msg->request) {
> +       case DP_AUX_I2C_MOT:
> +       case DP_AUX_I2C_WRITE:
> +       case DP_AUX_NATIVE_WRITE:
> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> +               request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> +               is_read = false;
> +               break;
> +       case DP_AUX_I2C_READ:
> +       case DP_AUX_NATIVE_READ:
> +       case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> +               request = msg->request;
> +               is_read = true;
> +               break;
> +       default:
> +               drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> +                       msg->request);
> +               err = -EINVAL;
> +               goto err;

Directly return here.

> +       }
> +
> +       if (msg->size == 0) {
> +               mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> +                                      msg->address + accessed_bytes,
> +                                      msg->buffer + accessed_bytes, 0);
> +       } else {
> +               while (accessed_bytes < msg->size) {
> +                       size_t to_access =
> +                               min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> +                                     msg->size - accessed_bytes);
> +                       retry = 3;
> +                       while (retry--) {
> +                               ret = mtk_dp_aux_do_transfer(
> +                                       mtk_dp, is_read, request,
> +                                       msg->address + accessed_bytes,
> +                                       msg->buffer + accessed_bytes,
> +                                       to_access);
> +                               if (ret == 0)
> +                                       break;
> +                               udelay(50);
> +                       }
> +                       if (!retry && ret) {
> +                               drm_info(mtk_dp->drm_dev,
> +                                        "Failed to do AUX transfer: %d\n",
> +                                        ret);
> +                               break;
> +                       }
> +                       accessed_bytes += to_access;
> +               }
> +       }
> +err:
> +       if (!ret) {
> +               msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +               ret = msg->size;
> +       } else {
> +               msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> +               return err;
> +       }
> +
> +       msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +       return msg->size;
> +}
> +
> +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> +{
> +       drm_dp_aux_init(&mtk_dp->aux);
> +       mtk_dp->aux.name = "aux_mtk_dp";
> +       mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> +}
> +
> +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> +{
> +       mutex_lock(&mtk_dp->dp_lock);
> +
> +       mtk_dp_hwirq_enable(mtk_dp, false);
> +       mtk_dp_power_disable(mtk_dp);
> +       phy_exit(mtk_dp->phy);
> +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> +
> +       mutex_unlock(&mtk_dp->dp_lock);
> +}
> +
> +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> +{
> +       int ret = 0;
> +
> +       mutex_lock(&mtk_dp->dp_lock);
> +
> +       ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> +       if (ret < 0) {
> +               dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> +               goto err;
> +       }
> +       ret = phy_init(mtk_dp->phy);
> +       if (ret) {
> +               dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> +               goto err_phy_init;
> +       }
> +       ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> +       if (ret) {
> +               dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> +               goto err_phy_config;
> +       }
> +
> +       mtk_dp_init_port(mtk_dp);
> +       mtk_dp_power_enable(mtk_dp);
> +       mtk_dp_hwirq_enable(mtk_dp, true);
> +
> +err_phy_config:
> +       phy_exit(mtk_dp->phy);
> +err_phy_init:
> +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> +err:
> +       mutex_unlock(&mtk_dp->dp_lock);
> +       return ret;
> +}
> +
> +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> +                               enum drm_bridge_attach_flags flags)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       int ret;
> +
> +       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> +               dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> +               return -EINVAL;
> +       }
> +
> +       ret = mtk_dp_poweron(mtk_dp);

Move the power on to mtk_dp_bridge_atomic_enable().

> +       if (ret)
> +               return ret;
> +
> +       if (mtk_dp->next_bridge) {
> +               ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> +                                       &mtk_dp->bridge, flags);
> +               if (ret) {
> +                       drm_warn(mtk_dp->drm_dev,
> +                                "Failed to attach external bridge: %d\n", ret);
> +                       goto err_bridge_attach;
> +               }
> +       }
> +
> +       mtk_dp->drm_dev = bridge->dev;
> +
> +       return 0;
> +
> +err_bridge_attach:
> +       mtk_dp_poweroff(mtk_dp);
> +       return ret;
> +}
> +
> +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +       mtk_dp->drm_dev = NULL;
> +
> +       mtk_dp_poweroff(mtk_dp);
> +}
> +
> +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> +                                        struct drm_bridge_state *old_state)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +       mtk_dp_video_mute(mtk_dp, true);
> +       mtk_dp_audio_mute(mtk_dp, true);
> +       mtk_dp->state = MTK_DP_STATE_IDLE;
> +       mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> +
> +       mtk_dp->enabled = false;
> +       msleep(100);
> +}
> +
> +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> +                                         struct drm_display_mode *mode)
> +{
> +       struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> +
> +       drm_display_mode_to_videomode(mode, &timings->vm);
> +       timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> +       timings->htotal = mode->htotal;
> +       timings->vtotal = mode->vtotal;
> +}
> +
> +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> +                                       struct drm_bridge_state *old_state)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       struct drm_connector *conn;
> +       struct drm_connector_state *conn_state;
> +       struct drm_crtc *crtc;
> +       struct drm_crtc_state *crtc_state;
> +       int ret = 0;
> +       int i;
> +
> +       conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> +                                                       bridge->encoder);

mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
                                                       bridge->encoder);

> +       if (!conn) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector is missing\n");
> +               return;
> +       }
> +
> +       memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> +
> +       conn_state =
> +               drm_atomic_get_new_connector_state(old_state->base.state, conn);
> +       if (!conn_state) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector state is missing\n");
> +               return;
> +       }
> +
> +       crtc = conn_state->crtc;
> +       if (!crtc) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector state doesn't have a crtc\n");
> +               return;
> +       }
> +
> +       crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> +       if (!crtc_state) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as crtc state is missing\n");
> +               return;
> +       }
> +
> +       mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);

Refer to mtk_hdmi_bridge_atomic_enable() for getting the mode.

> +       if (!mtk_dp_parse_capabilities(mtk_dp)) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as nothing is plugged in\n");
> +               return;
> +       }
> +
> +       //training

Run checkpatch first.

Regards,
Chun-Kuang.

> +       for (i = 0; i < 50; i++) {
> +               ret = mtk_dp_train_handler(mtk_dp);
> +               if (ret) {
> +                       drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> +                               ret);
> +                       return;
> +               }
> +
> +               ret = mtk_dp_state_handler(mtk_dp);
> +               if (ret) {
> +                       drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> +                               ret);
> +                       return;
> +               }
> +       }
> +
> +       mtk_dp->enabled = true;
> +       mtk_dp_update_plugged_status(mtk_dp);
> +}
> +



> --
> 2.32.0
>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 5/7] drm/mediatek: dpi: Add dpintf support
  2021-11-10 13:06   ` Guillaume Ranquet
  (?)
  (?)
@ 2021-11-26 10:22     ` AngeloGioacchino Del Regno
  -1 siblings, 0 replies; 122+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-11-26 10:22 UTC (permalink / raw)
  To: Guillaume Ranquet, Chun-Kuang Hu, Philipp Zabel, David Airlie,
	Daniel Vetter, Matthias Brugger
  Cc: linux-kernel, linux-mediatek, linux-arm-kernel, dri-devel,
	Markus Schneider-Pargmann

Il 10/11/21 14:06, Guillaume Ranquet ha scritto:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> dpintf is the displayport interface hardware unit. This unit is similar
> to dpi and can reuse most of the code.
> 
> This patch adds support for mt8195-dpintf to this dpi driver. Main
> differences are:
>   - Some features/functional components are not available for dpintf
>     which are now excluded from code execution once is_dpintf is set
>   - dpintf can and needs to choose between different clockdividers based
>     on the clockspeed. This is done by choosing a different clock parent.
>   - There are two additional clocks that need to be managed. These are
>     only set for dpintf and will be set to NULL if not supplied. The
>     clk_* calls handle these as normal clocks then.
>   - Some register contents differ slightly between the two components. To
>     work around this I added register bits/masks with a DPINTF_ prefix
>     and use them where different.
> 
> Based on a separate driver for dpintf created by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>

Hello Guillaume, Markus

Strictly speaking about functionality, the entire series is totally fine,
however, I cannot give you a R-b on patches 6 and 7, since this code should
*really* make use of phy_get(), like suggested by Vinod.

In any case, for this patch:

Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>

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

* Re: [PATCH v6 5/7] drm/mediatek: dpi: Add dpintf support
@ 2021-11-26 10:22     ` AngeloGioacchino Del Regno
  0 siblings, 0 replies; 122+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-11-26 10:22 UTC (permalink / raw)
  To: Guillaume Ranquet, Chun-Kuang Hu, Philipp Zabel, David Airlie,
	Daniel Vetter, Matthias Brugger
  Cc: Markus Schneider-Pargmann, dri-devel, linux-mediatek,
	linux-arm-kernel, linux-kernel

Il 10/11/21 14:06, Guillaume Ranquet ha scritto:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> dpintf is the displayport interface hardware unit. This unit is similar
> to dpi and can reuse most of the code.
> 
> This patch adds support for mt8195-dpintf to this dpi driver. Main
> differences are:
>   - Some features/functional components are not available for dpintf
>     which are now excluded from code execution once is_dpintf is set
>   - dpintf can and needs to choose between different clockdividers based
>     on the clockspeed. This is done by choosing a different clock parent.
>   - There are two additional clocks that need to be managed. These are
>     only set for dpintf and will be set to NULL if not supplied. The
>     clk_* calls handle these as normal clocks then.
>   - Some register contents differ slightly between the two components. To
>     work around this I added register bits/masks with a DPINTF_ prefix
>     and use them where different.
> 
> Based on a separate driver for dpintf created by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>

Hello Guillaume, Markus

Strictly speaking about functionality, the entire series is totally fine,
however, I cannot give you a R-b on patches 6 and 7, since this code should
*really* make use of phy_get(), like suggested by Vinod.

In any case, for this patch:

Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 5/7] drm/mediatek: dpi: Add dpintf support
@ 2021-11-26 10:22     ` AngeloGioacchino Del Regno
  0 siblings, 0 replies; 122+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-11-26 10:22 UTC (permalink / raw)
  To: Guillaume Ranquet, Chun-Kuang Hu, Philipp Zabel, David Airlie,
	Daniel Vetter, Matthias Brugger
  Cc: Markus Schneider-Pargmann, dri-devel, linux-mediatek,
	linux-arm-kernel, linux-kernel

Il 10/11/21 14:06, Guillaume Ranquet ha scritto:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> dpintf is the displayport interface hardware unit. This unit is similar
> to dpi and can reuse most of the code.
> 
> This patch adds support for mt8195-dpintf to this dpi driver. Main
> differences are:
>   - Some features/functional components are not available for dpintf
>     which are now excluded from code execution once is_dpintf is set
>   - dpintf can and needs to choose between different clockdividers based
>     on the clockspeed. This is done by choosing a different clock parent.
>   - There are two additional clocks that need to be managed. These are
>     only set for dpintf and will be set to NULL if not supplied. The
>     clk_* calls handle these as normal clocks then.
>   - Some register contents differ slightly between the two components. To
>     work around this I added register bits/masks with a DPINTF_ prefix
>     and use them where different.
> 
> Based on a separate driver for dpintf created by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>

Hello Guillaume, Markus

Strictly speaking about functionality, the entire series is totally fine,
however, I cannot give you a R-b on patches 6 and 7, since this code should
*really* make use of phy_get(), like suggested by Vinod.

In any case, for this patch:

Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>

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

* Re: [PATCH v6 5/7] drm/mediatek: dpi: Add dpintf support
@ 2021-11-26 10:22     ` AngeloGioacchino Del Regno
  0 siblings, 0 replies; 122+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-11-26 10:22 UTC (permalink / raw)
  To: Guillaume Ranquet, Chun-Kuang Hu, Philipp Zabel, David Airlie,
	Daniel Vetter, Matthias Brugger
  Cc: Markus Schneider-Pargmann, dri-devel, linux-mediatek,
	linux-arm-kernel, linux-kernel

Il 10/11/21 14:06, Guillaume Ranquet ha scritto:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> dpintf is the displayport interface hardware unit. This unit is similar
> to dpi and can reuse most of the code.
> 
> This patch adds support for mt8195-dpintf to this dpi driver. Main
> differences are:
>   - Some features/functional components are not available for dpintf
>     which are now excluded from code execution once is_dpintf is set
>   - dpintf can and needs to choose between different clockdividers based
>     on the clockspeed. This is done by choosing a different clock parent.
>   - There are two additional clocks that need to be managed. These are
>     only set for dpintf and will be set to NULL if not supplied. The
>     clk_* calls handle these as normal clocks then.
>   - Some register contents differ slightly between the two components. To
>     work around this I added register bits/masks with a DPINTF_ prefix
>     and use them where different.
> 
> Based on a separate driver for dpintf created by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>

Hello Guillaume, Markus

Strictly speaking about functionality, the entire series is totally fine,
however, I cannot give you a R-b on patches 6 and 7, since this code should
*really* make use of phy_get(), like suggested by Vinod.

In any case, for this patch:

Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 5/7] drm/mediatek: dpi: Add dpintf support
  2021-11-10 13:06   ` Guillaume Ranquet
  (?)
  (?)
@ 2021-11-26 10:23     ` AngeloGioacchino Del Regno
  -1 siblings, 0 replies; 122+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-11-26 10:23 UTC (permalink / raw)
  To: Guillaume Ranquet, Chun-Kuang Hu, Philipp Zabel, David Airlie,
	Daniel Vetter, Matthias Brugger
  Cc: linux-kernel, linux-mediatek, linux-arm-kernel, dri-devel,
	Markus Schneider-Pargmann

Il 10/11/21 14:06, Guillaume Ranquet ha scritto:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> dpintf is the displayport interface hardware unit. This unit is similar
> to dpi and can reuse most of the code.
> 
> This patch adds support for mt8195-dpintf to this dpi driver. Main
> differences are:
>   - Some features/functional components are not available for dpintf
>     which are now excluded from code execution once is_dpintf is set
>   - dpintf can and needs to choose between different clockdividers based
>     on the clockspeed. This is done by choosing a different clock parent.
>   - There are two additional clocks that need to be managed. These are
>     only set for dpintf and will be set to NULL if not supplied. The
>     clk_* calls handle these as normal clocks then.
>   - Some register contents differ slightly between the two components. To
>     work around this I added register bits/masks with a DPINTF_ prefix
>     and use them where different.
> 
> Based on a separate driver for dpintf created by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>


Hello Guillaume, Markus



Strictly speaking about functionality, the entire series is totally fine,

however, I cannot give you a R-b on patches 6 and 7, since this code should

*really* make use of phy_get(), like suggested by Vinod.



In any case, for this patch:



Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>


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

* Re: [PATCH v6 5/7] drm/mediatek: dpi: Add dpintf support
@ 2021-11-26 10:23     ` AngeloGioacchino Del Regno
  0 siblings, 0 replies; 122+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-11-26 10:23 UTC (permalink / raw)
  To: Guillaume Ranquet, Chun-Kuang Hu, Philipp Zabel, David Airlie,
	Daniel Vetter, Matthias Brugger
  Cc: Markus Schneider-Pargmann, dri-devel, linux-mediatek,
	linux-arm-kernel, linux-kernel

Il 10/11/21 14:06, Guillaume Ranquet ha scritto:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> dpintf is the displayport interface hardware unit. This unit is similar
> to dpi and can reuse most of the code.
> 
> This patch adds support for mt8195-dpintf to this dpi driver. Main
> differences are:
>   - Some features/functional components are not available for dpintf
>     which are now excluded from code execution once is_dpintf is set
>   - dpintf can and needs to choose between different clockdividers based
>     on the clockspeed. This is done by choosing a different clock parent.
>   - There are two additional clocks that need to be managed. These are
>     only set for dpintf and will be set to NULL if not supplied. The
>     clk_* calls handle these as normal clocks then.
>   - Some register contents differ slightly between the two components. To
>     work around this I added register bits/masks with a DPINTF_ prefix
>     and use them where different.
> 
> Based on a separate driver for dpintf created by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>


Hello Guillaume, Markus



Strictly speaking about functionality, the entire series is totally fine,

however, I cannot give you a R-b on patches 6 and 7, since this code should

*really* make use of phy_get(), like suggested by Vinod.



In any case, for this patch:



Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 5/7] drm/mediatek: dpi: Add dpintf support
@ 2021-11-26 10:23     ` AngeloGioacchino Del Regno
  0 siblings, 0 replies; 122+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-11-26 10:23 UTC (permalink / raw)
  To: Guillaume Ranquet, Chun-Kuang Hu, Philipp Zabel, David Airlie,
	Daniel Vetter, Matthias Brugger
  Cc: Markus Schneider-Pargmann, dri-devel, linux-mediatek,
	linux-arm-kernel, linux-kernel

Il 10/11/21 14:06, Guillaume Ranquet ha scritto:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> dpintf is the displayport interface hardware unit. This unit is similar
> to dpi and can reuse most of the code.
> 
> This patch adds support for mt8195-dpintf to this dpi driver. Main
> differences are:
>   - Some features/functional components are not available for dpintf
>     which are now excluded from code execution once is_dpintf is set
>   - dpintf can and needs to choose between different clockdividers based
>     on the clockspeed. This is done by choosing a different clock parent.
>   - There are two additional clocks that need to be managed. These are
>     only set for dpintf and will be set to NULL if not supplied. The
>     clk_* calls handle these as normal clocks then.
>   - Some register contents differ slightly between the two components. To
>     work around this I added register bits/masks with a DPINTF_ prefix
>     and use them where different.
> 
> Based on a separate driver for dpintf created by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>


Hello Guillaume, Markus



Strictly speaking about functionality, the entire series is totally fine,

however, I cannot give you a R-b on patches 6 and 7, since this code should

*really* make use of phy_get(), like suggested by Vinod.



In any case, for this patch:



Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>


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

* Re: [PATCH v6 5/7] drm/mediatek: dpi: Add dpintf support
@ 2021-11-26 10:23     ` AngeloGioacchino Del Regno
  0 siblings, 0 replies; 122+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-11-26 10:23 UTC (permalink / raw)
  To: Guillaume Ranquet, Chun-Kuang Hu, Philipp Zabel, David Airlie,
	Daniel Vetter, Matthias Brugger
  Cc: Markus Schneider-Pargmann, dri-devel, linux-mediatek,
	linux-arm-kernel, linux-kernel

Il 10/11/21 14:06, Guillaume Ranquet ha scritto:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> dpintf is the displayport interface hardware unit. This unit is similar
> to dpi and can reuse most of the code.
> 
> This patch adds support for mt8195-dpintf to this dpi driver. Main
> differences are:
>   - Some features/functional components are not available for dpintf
>     which are now excluded from code execution once is_dpintf is set
>   - dpintf can and needs to choose between different clockdividers based
>     on the clockspeed. This is done by choosing a different clock parent.
>   - There are two additional clocks that need to be managed. These are
>     only set for dpintf and will be set to NULL if not supplied. The
>     clk_* calls handle these as normal clocks then.
>   - Some register contents differ slightly between the two components. To
>     work around this I added register bits/masks with a DPINTF_ prefix
>     and use them where different.
> 
> Based on a separate driver for dpintf created by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>


Hello Guillaume, Markus



Strictly speaking about functionality, the entire series is totally fine,

however, I cannot give you a R-b on patches 6 and 7, since this code should

*really* make use of phy_get(), like suggested by Vinod.



In any case, for this patch:



Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-11-25 14:30             ` Maxime Ripard
  (?)
  (?)
@ 2021-12-02 14:48               ` Guillaume Ranquet
  -1 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-02 14:48 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Hi,

Quoting Maxime Ripard (2021-11-25 15:30:34)
> On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > Hi,
> > Thanks for all your input, really appreciated.
> >
> > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > Hi,
> > >
> > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > The driver creates a child device for the phy. The child device will
> > > > > > never exist without the parent being active. As they are sharing a
> > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > both can work with the same register range. The phy driver sets device
> > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > to control the phy properties.
> > > > >
> > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > you need a separate PHY driver in the first place?
> > > >
> > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > >
> > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > >
> > > It's a bit of a circular argument though :)
> > >
> > > It's a separate phy driver because it needs to go through another
> > > maintainer's tree, but it needs to go through another maintainer's tree
> > > because it's a separate phy driver.
> > >
> > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > the phy setup be done directly in the DP driver, if it's essentially a
> > > single device?
> > >
> > > That being said, usually what those kind of questions mean is that
> > > you're missing a comment or something in the commit log to provide that
> > > context in the first place, so it would be great to add that context
> > > here.
> > >
> > > And it will avoid the situation we're now in where multiple reviewers
> > > ask the same questions over and over again :)
> > >
> > At first I didn't understand your reply, then I realized I gave you
> > the wrong link...
> > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> >
> > The driver and phy were a single driver until v2 of this patch series
> > and the phy setup
> > was done directly in the driver (single driver, single C file).
> > Here's the relevant link to the discussion between Chun-Kuang and Markus
> >
> > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> >
> > I'll try to find a way to make it clearer for v7.
>
> OK, it makes sense then :)
>
> There's something weird though: the devices definitely look like they're
> in a separate register range, yet you mention a regmap to handle the
> shared register range. That range doesn't seem described anywhere in the
> device tree though? What is it for?
>

My understanding is that 0x1000 to 0x1fff controls the phy functionalities and
 0x2000 to 0x4fff controls "non-phy" functionalities.
And you are right, there's no description of that in the device tree whatsoever.
The ranges are in the same actual device and thus it has been decided
to not have
dt-bindings for the phy device.

The phy driver is a child of the DP driver that we register using
platform_device_register_data() and we pass along the same regmap as the DP
driver in its platform data.

I'm not sure I've understood and fully replied to the question...

> > > > > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > > > > +                                     struct drm_bridge_state *old_state)
> > > > > > +{
> > > > > > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > > > > +     struct drm_connector *conn;
> > > > > > +     struct drm_connector_state *conn_state;
> > > > > > +     struct drm_crtc *crtc;
> > > > > > +     struct drm_crtc_state *crtc_state;
> > > > > > +     int ret = 0;
> > > > > > +     int i;
> > > > > > +
> > > > > > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > > > > +                                                     bridge->encoder);
> > > > > > +     if (!conn) {
> > > > > > +             drm_err(mtk_dp->drm_dev,
> > > > > > +                     "Can't enable bridge as connector is missing\n");
> > > > > > +             return;
> > > > > > +     }
> > > > > > +
> > > > > > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > > > >
> > > > > This should be protected by a mutex (just like any resource shared
> > > > > between KMS and ALSA)
> > > >
> > > > Ok.
> > >
> > > I forgot to ask (even though checkpatch does mention it iirc), but since
> > > you have multiple mutex it would be nice to have a comment for each
> > > mutex stating exactly what it protects, and against what.
> > >
> > > It's hard otherwise to remember or figure out what the locks are here
> > > for.
> > >
> > > > > > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > > > > > +     if (ret)
> > > > > > +             return ret;
> > > > >
> > > > > pdata?
> > > > >
> > > > can you elaborate?
> > >
> > > Sorry, yeah, pdata is usually the abbreviation used in linux for the
> > > platform_data mechanism, but you're using the DT to retrieve your
> > > resources (and platform_data usually don't involve any parsing), so the
> > > name is odd.
> > >
> > > > > > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > > index 384074f69111b..e6e88e3cd811d 100644
> > > > > > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> > > > > >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> > > > > >  };
> > > > > >
> > > > > > +enum TVDPLL_CLK {
> > > > > > +     TVDPLL_PLL = 0,
> > > > > > +     TVDPLL_D2 = 2,
> > > > > > +     TVDPLL_D4 = 4,
> > > > > > +     TVDPLL_D8 = 8,
> > > > > > +     TVDPLL_D16 = 16,
> > > > > > +};
> > > > > > +
> > > > > >  struct mtk_dpi {
> > > > > >       struct drm_encoder encoder;
> > > > > >       struct drm_bridge bridge;
> > > > > > @@ -71,8 +79,10 @@ struct mtk_dpi {
> > > > > >       void __iomem *regs;
> > > > > >       struct device *dev;
> > > > > >       struct clk *engine_clk;
> > > > > > +     struct clk *dpi_ck_cg;
> > > > > >       struct clk *pixel_clk;
> > > > > >       struct clk *tvd_clk;
> > > > > > +     struct clk *pclk_src[5];
> > > > > >       int irq;
> > > > > >       struct drm_display_mode mode;
> > > > > >       const struct mtk_dpi_conf *conf;
> > > > > > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> > > > > >       u32 hvsize_mask;
> > > > > >       u32 channel_swap_shift;
> > > > > >       u32 yuv422_en_bit;
> > > > > > +     u32 csc_enable_bit;
> > > > > >       const struct mtk_dpi_yc_limit *limit;
> > > > > >  };
> > > > > >
> > > > > > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> > > > > >
> > > > > >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> > > > > >  {
> > > > > > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > > > > > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> > > > > >  }
> > > > > >
> > > > > >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > > > > > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> > > > > >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> > > > > >  }
> > > > > >
> > > > > > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > > > > > +{
> > > > > > +     u32 matrix_sel = 0;
> > > > > > +
> > > > > > +     switch (format) {
> > > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > > > > > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > > > > > +             if (dpi->mode.hdisplay <= 720)
> > > > > > +                     matrix_sel = 0x2;
> > > > > > +             break;
> > > > > > +     default:
> > > > > > +             break;
> > > > > > +     }
> > > > > > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > > > > > +}
> > > > > > +
> > > > > >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> > > > > >                                       enum mtk_dpi_out_color_format format)
> > > > > >  {
> > > > > >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> > > > > >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> > > > > >               mtk_dpi_config_yuv422_enable(dpi, false);
> > > > > > -             if (dpi->conf->csc_support)
> > > > > > +             if (dpi->conf->csc_support) {
> > > > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > > > +             }
> > > > > >               if (dpi->conf->swap_input_support)
> > > > > >                       mtk_dpi_config_swap_input(dpi, false);
> > > > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> > > > > >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> > > > > >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> > > > > >               mtk_dpi_config_yuv422_enable(dpi, true);
> > > > > > -             if (dpi->conf->csc_support)
> > > > > > +             if (dpi->conf->csc_support) {
> > > > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > > > +             }
> > > > > >               if (dpi->conf->swap_input_support)
> > > > > >                       mtk_dpi_config_swap_input(dpi, true);
> > > > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > > > > > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> > > > > >       mtk_dpi_disable(dpi);
> > > > > >       clk_disable_unprepare(dpi->pixel_clk);
> > > > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > > > +     clk_disable_unprepare(dpi->tvd_clk);
> > > > > >  }
> > > > > >
> > > > > >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > > > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > > >       if (++dpi->refcount != 1)
> > > > > >               return 0;
> > > > > >
> > > > > > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > > > > > +     if (ret) {
> > > > > > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > > > > > +             goto err_pixel;
> > > > > > +     }
> > > > > > +
> > > > > >       ret = clk_prepare_enable(dpi->engine_clk);
> > > > > >       if (ret) {
> > > > > >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> > > > > >               goto err_refcount;
> > > > > >       }
> > > > > >
> > > > > > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > > > > > +     if (ret) {
> > > > > > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > > > > > +             goto err_ck_cg;
> > > > > > +     }
> > > > > > +
> > > > > >       ret = clk_prepare_enable(dpi->pixel_clk);
> > > > > >       if (ret) {
> > > > > >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > > > > > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > > >       if (dpi->pinctrl && dpi->pins_dpi)
> > > > > >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> > > > > >
> > > > > > -     mtk_dpi_enable(dpi);
> > > > > >       return 0;
> > > > > >
> > > > > >  err_pixel:
> > > > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > > > +err_ck_cg:
> > > > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > > >  err_refcount:
> > > > > >       dpi->refcount--;
> > > > > > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > > > >       pll_rate = clk_get_rate(dpi->tvd_clk);
> > > > > >
> > > > > >       vm.pixelclock = pll_rate / factor;
> > > > > > -     if (dpi->conf->is_dpintf)
> > > > > > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > > > > > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > > > +     if (dpi->conf->is_dpintf) {
> > > > > > +             if (factor == 1)
> > > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > > > +             else if (factor == 2)
> > > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > > > > > +             else if (factor == 4)
> > > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > > > > > +             else
> > > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > > > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > > >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> > > > > >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> > > > > >       else
> > > > > > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > > > >       }
> > > > > >       mtk_dpi_sw_reset(dpi, false);
> > > > > >
> > > > > > +     mtk_dpi_enable(dpi);
> > > > > > +
> > > > > >       return 0;
> > > > > >  }
> > > > > >
> > > > > > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> > > > > >       u32 *input_fmts;
> > > > > >
> > > > > >       *num_input_fmts = 0;
> > > > > > -
> > > > > >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> > > > > >                            GFP_KERNEL);
> > > > > >       if (!input_fmts)
> > > > > > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > > > >               if (dpi->conf->num_output_fmts)
> > > > > >                       out_bus_format = dpi->conf->output_fmts[0];
> > > > > >
> > > > > > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > > > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > > >               bridge_state->input_bus_cfg.format,
> > > > > >               bridge_state->output_bus_cfg.format);
> > > > > >
> > > > > > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > > > >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> > > > > >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> > > > > >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > > > > > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > > > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > > > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > > > > > +     else
> > > > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > > >
> > > > > >       return 0;
> > > > > >  }
> > > > > > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> > > > > >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> > > > > >  };
> > > > > >
> > > > > > +static const u32 mt8195_output_fmts[] = {
> > > > > > +     MEDIA_BUS_FMT_RGB888_1X24,
> > > > > > +     MEDIA_BUS_FMT_YUV8_1X24,
> > > > > > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > > > > > +};
> > > > > > +
> > > > > >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> > > > > >       .c_bottom = 0x0010,
> > > > > >       .c_top = 0x0FE0,
> > > > > > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> > > > > >       .hvsize_mask = HSIZE_MASK,
> > > > > >       .channel_swap_shift = CH_SWAP,
> > > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > > >       .limit = &mtk_dpi_limit,
> > > > > >  };
> > > > > >
> > > > > > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> > > > > >       .hvsize_mask = HSIZE_MASK,
> > > > > >       .channel_swap_shift = CH_SWAP,
> > > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > > >       .limit = &mtk_dpi_limit,
> > > > > >  };
> > > > > >
> > > > > > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> > > > > >       .hvsize_mask = HSIZE_MASK,
> > > > > >       .channel_swap_shift = CH_SWAP,
> > > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > > >       .limit = &mtk_dpi_limit,
> > > > > >  };
> > > > > >
> > > > > > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> > > > > >       .hvsize_mask = HSIZE_MASK,
> > > > > >       .channel_swap_shift = CH_SWAP,
> > > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > > >       .limit = &mtk_dpi_limit,
> > > > > >  };
> > > > > >
> > > > > >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> > > > > >       .cal_factor = mt8195_dpintf_calculate_factor,
> > > > > > -     .output_fmts = mt8173_output_fmts,
> > > > > > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > > > > > +     .output_fmts = mt8195_output_fmts,
> > > > > > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> > > > > >       .is_dpintf = true,
> > > > > > +     .csc_support = true,
> > > > > >       .dimension_mask = DPINTF_HPW_MASK,
> > > > > >       .hvsize_mask = DPINTF_HSIZE_MASK,
> > > > > >       .channel_swap_shift = DPINTF_CH_SWAP,
> > > > > >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > > > > > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> > > > > >       .limit = &mtk_dpintf_limit,
> > > > > >  };
> > > > > >
> > > > > > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > > > >               return ret;
> > > > > >       }
> > > > > >
> > > > > > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > > > > > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > > > > > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > > > > > +             if (ret != -EPROBE_DEFER)
> > > > > > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > > > > > +                             ret);
> > > > > > +
> > > > > > +             return ret;
> > > > > > +     }
> > > > > > +
> > > > > >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> > > > > >       if (IS_ERR(dpi->pixel_clk)) {
> > > > > >               ret = PTR_ERR(dpi->pixel_clk);
> > > > > > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > > > >               return ret;
> > > > > >       }
> > > > > >
> > > > > > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > > > > > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > > > > > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > > > > > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > > > > > +
> > > > > >       dpi->irq = platform_get_irq(pdev, 0);
> > > > > >       if (dpi->irq <= 0)
> > > > > >               return -EINVAL;
> > > > >
> > > > > All those changes look unrelated as well?
> > > > >
> > > > Do you mean all the changes in mtk_dpi.c ? They are in support of
> > > > enabling the mt8195 dpintf driver... so, not sure they are unlreated?
> > >
> > > I don't know that driver at all, so it's probably related if you say so
> > > :)
> > >
> > > The DPI interface seems to be a panel interface, so it's a bit weird to
> > > me why you would need to change things in the panel interface while
> > > claiming to support a DP output.
> > >
> > > Especially since it looks like you have some support for CSC, and
> > > support for additional output formats?
> > >
> > > I guess the DPI interface feeds the DP that essentially acts as a RGB ->
> > > DP bridge?
> > >
> > > If so, then the part adding support for CSC and additional formats
> > > should be split into a separate patch, and it should be mentioned in the
> > > commit log
> > >
> > > Maxime
> >
> > I see what you mean, I'll split the patches functionally...
> > I'm discovering the gpu/drm tree, so It may take me a while to get it right :D
>
> It's not really yet another weird drm rule :)
>
> Splitting your patches is valuable for a bunch of reasons that apply to
> basically every tree:
>
>   - It's easier to review (and maintainers like that). It's also easier
>     to justify in a commit log, so it leads to a better history over the
>     long term.
>
>   - It's also easier to merge since you can merge smaller chunks when
>     they are ready, even if the entire set is not. Smaller chunks also
>     mean it's easier to apply (for the maintainer) and rebase (for you).
>
>   - Finally, and possibly the strongest technical argument, it's much
>     more helpful when you're bisecting. Taking the above patch as an
>     example, let's assume you introduced a bug in some RGB panel with
>     the colors being off due to the CSC changes. If you run git bisect,
>     you'll end up with that big patch affecting multiple things and it
>     would still be difficult to figure out what's wrong exactly. If it
>     was split, you would end up only on the changeset that introduces
>     the regression, which is very likely to be much easier to analyze.
>
> Maxime

Thx,
Guillaume.

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-02 14:48               ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-02 14:48 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Chun-Kuang Hu, David Airlie, Thomas Zimmermann, linux-kernel,
	dri-devel, Markus Schneider-Pargmann, linux-mediatek,
	Matthias Brugger, linux-arm-kernel, kernel test robot

Hi,

Quoting Maxime Ripard (2021-11-25 15:30:34)
> On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > Hi,
> > Thanks for all your input, really appreciated.
> >
> > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > Hi,
> > >
> > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > The driver creates a child device for the phy. The child device will
> > > > > > never exist without the parent being active. As they are sharing a
> > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > both can work with the same register range. The phy driver sets device
> > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > to control the phy properties.
> > > > >
> > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > you need a separate PHY driver in the first place?
> > > >
> > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > >
> > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > >
> > > It's a bit of a circular argument though :)
> > >
> > > It's a separate phy driver because it needs to go through another
> > > maintainer's tree, but it needs to go through another maintainer's tree
> > > because it's a separate phy driver.
> > >
> > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > the phy setup be done directly in the DP driver, if it's essentially a
> > > single device?
> > >
> > > That being said, usually what those kind of questions mean is that
> > > you're missing a comment or something in the commit log to provide that
> > > context in the first place, so it would be great to add that context
> > > here.
> > >
> > > And it will avoid the situation we're now in where multiple reviewers
> > > ask the same questions over and over again :)
> > >
> > At first I didn't understand your reply, then I realized I gave you
> > the wrong link...
> > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> >
> > The driver and phy were a single driver until v2 of this patch series
> > and the phy setup
> > was done directly in the driver (single driver, single C file).
> > Here's the relevant link to the discussion between Chun-Kuang and Markus
> >
> > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> >
> > I'll try to find a way to make it clearer for v7.
>
> OK, it makes sense then :)
>
> There's something weird though: the devices definitely look like they're
> in a separate register range, yet you mention a regmap to handle the
> shared register range. That range doesn't seem described anywhere in the
> device tree though? What is it for?
>

My understanding is that 0x1000 to 0x1fff controls the phy functionalities and
 0x2000 to 0x4fff controls "non-phy" functionalities.
And you are right, there's no description of that in the device tree whatsoever.
The ranges are in the same actual device and thus it has been decided
to not have
dt-bindings for the phy device.

The phy driver is a child of the DP driver that we register using
platform_device_register_data() and we pass along the same regmap as the DP
driver in its platform data.

I'm not sure I've understood and fully replied to the question...

> > > > > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > > > > +                                     struct drm_bridge_state *old_state)
> > > > > > +{
> > > > > > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > > > > +     struct drm_connector *conn;
> > > > > > +     struct drm_connector_state *conn_state;
> > > > > > +     struct drm_crtc *crtc;
> > > > > > +     struct drm_crtc_state *crtc_state;
> > > > > > +     int ret = 0;
> > > > > > +     int i;
> > > > > > +
> > > > > > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > > > > +                                                     bridge->encoder);
> > > > > > +     if (!conn) {
> > > > > > +             drm_err(mtk_dp->drm_dev,
> > > > > > +                     "Can't enable bridge as connector is missing\n");
> > > > > > +             return;
> > > > > > +     }
> > > > > > +
> > > > > > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > > > >
> > > > > This should be protected by a mutex (just like any resource shared
> > > > > between KMS and ALSA)
> > > >
> > > > Ok.
> > >
> > > I forgot to ask (even though checkpatch does mention it iirc), but since
> > > you have multiple mutex it would be nice to have a comment for each
> > > mutex stating exactly what it protects, and against what.
> > >
> > > It's hard otherwise to remember or figure out what the locks are here
> > > for.
> > >
> > > > > > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > > > > > +     if (ret)
> > > > > > +             return ret;
> > > > >
> > > > > pdata?
> > > > >
> > > > can you elaborate?
> > >
> > > Sorry, yeah, pdata is usually the abbreviation used in linux for the
> > > platform_data mechanism, but you're using the DT to retrieve your
> > > resources (and platform_data usually don't involve any parsing), so the
> > > name is odd.
> > >
> > > > > > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > > index 384074f69111b..e6e88e3cd811d 100644
> > > > > > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> > > > > >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> > > > > >  };
> > > > > >
> > > > > > +enum TVDPLL_CLK {
> > > > > > +     TVDPLL_PLL = 0,
> > > > > > +     TVDPLL_D2 = 2,
> > > > > > +     TVDPLL_D4 = 4,
> > > > > > +     TVDPLL_D8 = 8,
> > > > > > +     TVDPLL_D16 = 16,
> > > > > > +};
> > > > > > +
> > > > > >  struct mtk_dpi {
> > > > > >       struct drm_encoder encoder;
> > > > > >       struct drm_bridge bridge;
> > > > > > @@ -71,8 +79,10 @@ struct mtk_dpi {
> > > > > >       void __iomem *regs;
> > > > > >       struct device *dev;
> > > > > >       struct clk *engine_clk;
> > > > > > +     struct clk *dpi_ck_cg;
> > > > > >       struct clk *pixel_clk;
> > > > > >       struct clk *tvd_clk;
> > > > > > +     struct clk *pclk_src[5];
> > > > > >       int irq;
> > > > > >       struct drm_display_mode mode;
> > > > > >       const struct mtk_dpi_conf *conf;
> > > > > > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> > > > > >       u32 hvsize_mask;
> > > > > >       u32 channel_swap_shift;
> > > > > >       u32 yuv422_en_bit;
> > > > > > +     u32 csc_enable_bit;
> > > > > >       const struct mtk_dpi_yc_limit *limit;
> > > > > >  };
> > > > > >
> > > > > > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> > > > > >
> > > > > >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> > > > > >  {
> > > > > > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > > > > > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> > > > > >  }
> > > > > >
> > > > > >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > > > > > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> > > > > >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> > > > > >  }
> > > > > >
> > > > > > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > > > > > +{
> > > > > > +     u32 matrix_sel = 0;
> > > > > > +
> > > > > > +     switch (format) {
> > > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > > > > > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > > > > > +             if (dpi->mode.hdisplay <= 720)
> > > > > > +                     matrix_sel = 0x2;
> > > > > > +             break;
> > > > > > +     default:
> > > > > > +             break;
> > > > > > +     }
> > > > > > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > > > > > +}
> > > > > > +
> > > > > >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> > > > > >                                       enum mtk_dpi_out_color_format format)
> > > > > >  {
> > > > > >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> > > > > >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> > > > > >               mtk_dpi_config_yuv422_enable(dpi, false);
> > > > > > -             if (dpi->conf->csc_support)
> > > > > > +             if (dpi->conf->csc_support) {
> > > > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > > > +             }
> > > > > >               if (dpi->conf->swap_input_support)
> > > > > >                       mtk_dpi_config_swap_input(dpi, false);
> > > > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> > > > > >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> > > > > >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> > > > > >               mtk_dpi_config_yuv422_enable(dpi, true);
> > > > > > -             if (dpi->conf->csc_support)
> > > > > > +             if (dpi->conf->csc_support) {
> > > > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > > > +             }
> > > > > >               if (dpi->conf->swap_input_support)
> > > > > >                       mtk_dpi_config_swap_input(dpi, true);
> > > > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > > > > > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> > > > > >       mtk_dpi_disable(dpi);
> > > > > >       clk_disable_unprepare(dpi->pixel_clk);
> > > > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > > > +     clk_disable_unprepare(dpi->tvd_clk);
> > > > > >  }
> > > > > >
> > > > > >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > > > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > > >       if (++dpi->refcount != 1)
> > > > > >               return 0;
> > > > > >
> > > > > > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > > > > > +     if (ret) {
> > > > > > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > > > > > +             goto err_pixel;
> > > > > > +     }
> > > > > > +
> > > > > >       ret = clk_prepare_enable(dpi->engine_clk);
> > > > > >       if (ret) {
> > > > > >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> > > > > >               goto err_refcount;
> > > > > >       }
> > > > > >
> > > > > > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > > > > > +     if (ret) {
> > > > > > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > > > > > +             goto err_ck_cg;
> > > > > > +     }
> > > > > > +
> > > > > >       ret = clk_prepare_enable(dpi->pixel_clk);
> > > > > >       if (ret) {
> > > > > >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > > > > > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > > >       if (dpi->pinctrl && dpi->pins_dpi)
> > > > > >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> > > > > >
> > > > > > -     mtk_dpi_enable(dpi);
> > > > > >       return 0;
> > > > > >
> > > > > >  err_pixel:
> > > > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > > > +err_ck_cg:
> > > > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > > >  err_refcount:
> > > > > >       dpi->refcount--;
> > > > > > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > > > >       pll_rate = clk_get_rate(dpi->tvd_clk);
> > > > > >
> > > > > >       vm.pixelclock = pll_rate / factor;
> > > > > > -     if (dpi->conf->is_dpintf)
> > > > > > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > > > > > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > > > +     if (dpi->conf->is_dpintf) {
> > > > > > +             if (factor == 1)
> > > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > > > +             else if (factor == 2)
> > > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > > > > > +             else if (factor == 4)
> > > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > > > > > +             else
> > > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > > > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > > >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> > > > > >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> > > > > >       else
> > > > > > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > > > >       }
> > > > > >       mtk_dpi_sw_reset(dpi, false);
> > > > > >
> > > > > > +     mtk_dpi_enable(dpi);
> > > > > > +
> > > > > >       return 0;
> > > > > >  }
> > > > > >
> > > > > > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> > > > > >       u32 *input_fmts;
> > > > > >
> > > > > >       *num_input_fmts = 0;
> > > > > > -
> > > > > >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> > > > > >                            GFP_KERNEL);
> > > > > >       if (!input_fmts)
> > > > > > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > > > >               if (dpi->conf->num_output_fmts)
> > > > > >                       out_bus_format = dpi->conf->output_fmts[0];
> > > > > >
> > > > > > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > > > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > > >               bridge_state->input_bus_cfg.format,
> > > > > >               bridge_state->output_bus_cfg.format);
> > > > > >
> > > > > > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > > > >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> > > > > >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> > > > > >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > > > > > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > > > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > > > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > > > > > +     else
> > > > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > > >
> > > > > >       return 0;
> > > > > >  }
> > > > > > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> > > > > >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> > > > > >  };
> > > > > >
> > > > > > +static const u32 mt8195_output_fmts[] = {
> > > > > > +     MEDIA_BUS_FMT_RGB888_1X24,
> > > > > > +     MEDIA_BUS_FMT_YUV8_1X24,
> > > > > > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > > > > > +};
> > > > > > +
> > > > > >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> > > > > >       .c_bottom = 0x0010,
> > > > > >       .c_top = 0x0FE0,
> > > > > > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> > > > > >       .hvsize_mask = HSIZE_MASK,
> > > > > >       .channel_swap_shift = CH_SWAP,
> > > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > > >       .limit = &mtk_dpi_limit,
> > > > > >  };
> > > > > >
> > > > > > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> > > > > >       .hvsize_mask = HSIZE_MASK,
> > > > > >       .channel_swap_shift = CH_SWAP,
> > > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > > >       .limit = &mtk_dpi_limit,
> > > > > >  };
> > > > > >
> > > > > > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> > > > > >       .hvsize_mask = HSIZE_MASK,
> > > > > >       .channel_swap_shift = CH_SWAP,
> > > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > > >       .limit = &mtk_dpi_limit,
> > > > > >  };
> > > > > >
> > > > > > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> > > > > >       .hvsize_mask = HSIZE_MASK,
> > > > > >       .channel_swap_shift = CH_SWAP,
> > > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > > >       .limit = &mtk_dpi_limit,
> > > > > >  };
> > > > > >
> > > > > >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> > > > > >       .cal_factor = mt8195_dpintf_calculate_factor,
> > > > > > -     .output_fmts = mt8173_output_fmts,
> > > > > > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > > > > > +     .output_fmts = mt8195_output_fmts,
> > > > > > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> > > > > >       .is_dpintf = true,
> > > > > > +     .csc_support = true,
> > > > > >       .dimension_mask = DPINTF_HPW_MASK,
> > > > > >       .hvsize_mask = DPINTF_HSIZE_MASK,
> > > > > >       .channel_swap_shift = DPINTF_CH_SWAP,
> > > > > >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > > > > > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> > > > > >       .limit = &mtk_dpintf_limit,
> > > > > >  };
> > > > > >
> > > > > > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > > > >               return ret;
> > > > > >       }
> > > > > >
> > > > > > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > > > > > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > > > > > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > > > > > +             if (ret != -EPROBE_DEFER)
> > > > > > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > > > > > +                             ret);
> > > > > > +
> > > > > > +             return ret;
> > > > > > +     }
> > > > > > +
> > > > > >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> > > > > >       if (IS_ERR(dpi->pixel_clk)) {
> > > > > >               ret = PTR_ERR(dpi->pixel_clk);
> > > > > > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > > > >               return ret;
> > > > > >       }
> > > > > >
> > > > > > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > > > > > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > > > > > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > > > > > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > > > > > +
> > > > > >       dpi->irq = platform_get_irq(pdev, 0);
> > > > > >       if (dpi->irq <= 0)
> > > > > >               return -EINVAL;
> > > > >
> > > > > All those changes look unrelated as well?
> > > > >
> > > > Do you mean all the changes in mtk_dpi.c ? They are in support of
> > > > enabling the mt8195 dpintf driver... so, not sure they are unlreated?
> > >
> > > I don't know that driver at all, so it's probably related if you say so
> > > :)
> > >
> > > The DPI interface seems to be a panel interface, so it's a bit weird to
> > > me why you would need to change things in the panel interface while
> > > claiming to support a DP output.
> > >
> > > Especially since it looks like you have some support for CSC, and
> > > support for additional output formats?
> > >
> > > I guess the DPI interface feeds the DP that essentially acts as a RGB ->
> > > DP bridge?
> > >
> > > If so, then the part adding support for CSC and additional formats
> > > should be split into a separate patch, and it should be mentioned in the
> > > commit log
> > >
> > > Maxime
> >
> > I see what you mean, I'll split the patches functionally...
> > I'm discovering the gpu/drm tree, so It may take me a while to get it right :D
>
> It's not really yet another weird drm rule :)
>
> Splitting your patches is valuable for a bunch of reasons that apply to
> basically every tree:
>
>   - It's easier to review (and maintainers like that). It's also easier
>     to justify in a commit log, so it leads to a better history over the
>     long term.
>
>   - It's also easier to merge since you can merge smaller chunks when
>     they are ready, even if the entire set is not. Smaller chunks also
>     mean it's easier to apply (for the maintainer) and rebase (for you).
>
>   - Finally, and possibly the strongest technical argument, it's much
>     more helpful when you're bisecting. Taking the above patch as an
>     example, let's assume you introduced a bug in some RGB panel with
>     the colors being off due to the CSC changes. If you run git bisect,
>     you'll end up with that big patch affecting multiple things and it
>     would still be difficult to figure out what's wrong exactly. If it
>     was split, you would end up only on the changeset that introduces
>     the regression, which is very likely to be much easier to analyze.
>
> Maxime

Thx,
Guillaume.

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-02 14:48               ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-02 14:48 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Hi,

Quoting Maxime Ripard (2021-11-25 15:30:34)
> On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > Hi,
> > Thanks for all your input, really appreciated.
> >
> > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > Hi,
> > >
> > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > The driver creates a child device for the phy. The child device will
> > > > > > never exist without the parent being active. As they are sharing a
> > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > both can work with the same register range. The phy driver sets device
> > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > to control the phy properties.
> > > > >
> > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > you need a separate PHY driver in the first place?
> > > >
> > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > >
> > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > >
> > > It's a bit of a circular argument though :)
> > >
> > > It's a separate phy driver because it needs to go through another
> > > maintainer's tree, but it needs to go through another maintainer's tree
> > > because it's a separate phy driver.
> > >
> > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > the phy setup be done directly in the DP driver, if it's essentially a
> > > single device?
> > >
> > > That being said, usually what those kind of questions mean is that
> > > you're missing a comment or something in the commit log to provide that
> > > context in the first place, so it would be great to add that context
> > > here.
> > >
> > > And it will avoid the situation we're now in where multiple reviewers
> > > ask the same questions over and over again :)
> > >
> > At first I didn't understand your reply, then I realized I gave you
> > the wrong link...
> > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> >
> > The driver and phy were a single driver until v2 of this patch series
> > and the phy setup
> > was done directly in the driver (single driver, single C file).
> > Here's the relevant link to the discussion between Chun-Kuang and Markus
> >
> > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> >
> > I'll try to find a way to make it clearer for v7.
>
> OK, it makes sense then :)
>
> There's something weird though: the devices definitely look like they're
> in a separate register range, yet you mention a regmap to handle the
> shared register range. That range doesn't seem described anywhere in the
> device tree though? What is it for?
>

My understanding is that 0x1000 to 0x1fff controls the phy functionalities and
 0x2000 to 0x4fff controls "non-phy" functionalities.
And you are right, there's no description of that in the device tree whatsoever.
The ranges are in the same actual device and thus it has been decided
to not have
dt-bindings for the phy device.

The phy driver is a child of the DP driver that we register using
platform_device_register_data() and we pass along the same regmap as the DP
driver in its platform data.

I'm not sure I've understood and fully replied to the question...

> > > > > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > > > > +                                     struct drm_bridge_state *old_state)
> > > > > > +{
> > > > > > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > > > > +     struct drm_connector *conn;
> > > > > > +     struct drm_connector_state *conn_state;
> > > > > > +     struct drm_crtc *crtc;
> > > > > > +     struct drm_crtc_state *crtc_state;
> > > > > > +     int ret = 0;
> > > > > > +     int i;
> > > > > > +
> > > > > > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > > > > +                                                     bridge->encoder);
> > > > > > +     if (!conn) {
> > > > > > +             drm_err(mtk_dp->drm_dev,
> > > > > > +                     "Can't enable bridge as connector is missing\n");
> > > > > > +             return;
> > > > > > +     }
> > > > > > +
> > > > > > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > > > >
> > > > > This should be protected by a mutex (just like any resource shared
> > > > > between KMS and ALSA)
> > > >
> > > > Ok.
> > >
> > > I forgot to ask (even though checkpatch does mention it iirc), but since
> > > you have multiple mutex it would be nice to have a comment for each
> > > mutex stating exactly what it protects, and against what.
> > >
> > > It's hard otherwise to remember or figure out what the locks are here
> > > for.
> > >
> > > > > > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > > > > > +     if (ret)
> > > > > > +             return ret;
> > > > >
> > > > > pdata?
> > > > >
> > > > can you elaborate?
> > >
> > > Sorry, yeah, pdata is usually the abbreviation used in linux for the
> > > platform_data mechanism, but you're using the DT to retrieve your
> > > resources (and platform_data usually don't involve any parsing), so the
> > > name is odd.
> > >
> > > > > > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > > index 384074f69111b..e6e88e3cd811d 100644
> > > > > > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> > > > > >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> > > > > >  };
> > > > > >
> > > > > > +enum TVDPLL_CLK {
> > > > > > +     TVDPLL_PLL = 0,
> > > > > > +     TVDPLL_D2 = 2,
> > > > > > +     TVDPLL_D4 = 4,
> > > > > > +     TVDPLL_D8 = 8,
> > > > > > +     TVDPLL_D16 = 16,
> > > > > > +};
> > > > > > +
> > > > > >  struct mtk_dpi {
> > > > > >       struct drm_encoder encoder;
> > > > > >       struct drm_bridge bridge;
> > > > > > @@ -71,8 +79,10 @@ struct mtk_dpi {
> > > > > >       void __iomem *regs;
> > > > > >       struct device *dev;
> > > > > >       struct clk *engine_clk;
> > > > > > +     struct clk *dpi_ck_cg;
> > > > > >       struct clk *pixel_clk;
> > > > > >       struct clk *tvd_clk;
> > > > > > +     struct clk *pclk_src[5];
> > > > > >       int irq;
> > > > > >       struct drm_display_mode mode;
> > > > > >       const struct mtk_dpi_conf *conf;
> > > > > > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> > > > > >       u32 hvsize_mask;
> > > > > >       u32 channel_swap_shift;
> > > > > >       u32 yuv422_en_bit;
> > > > > > +     u32 csc_enable_bit;
> > > > > >       const struct mtk_dpi_yc_limit *limit;
> > > > > >  };
> > > > > >
> > > > > > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> > > > > >
> > > > > >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> > > > > >  {
> > > > > > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > > > > > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> > > > > >  }
> > > > > >
> > > > > >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > > > > > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> > > > > >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> > > > > >  }
> > > > > >
> > > > > > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > > > > > +{
> > > > > > +     u32 matrix_sel = 0;
> > > > > > +
> > > > > > +     switch (format) {
> > > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > > > > > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > > > > > +             if (dpi->mode.hdisplay <= 720)
> > > > > > +                     matrix_sel = 0x2;
> > > > > > +             break;
> > > > > > +     default:
> > > > > > +             break;
> > > > > > +     }
> > > > > > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > > > > > +}
> > > > > > +
> > > > > >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> > > > > >                                       enum mtk_dpi_out_color_format format)
> > > > > >  {
> > > > > >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> > > > > >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> > > > > >               mtk_dpi_config_yuv422_enable(dpi, false);
> > > > > > -             if (dpi->conf->csc_support)
> > > > > > +             if (dpi->conf->csc_support) {
> > > > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > > > +             }
> > > > > >               if (dpi->conf->swap_input_support)
> > > > > >                       mtk_dpi_config_swap_input(dpi, false);
> > > > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> > > > > >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> > > > > >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> > > > > >               mtk_dpi_config_yuv422_enable(dpi, true);
> > > > > > -             if (dpi->conf->csc_support)
> > > > > > +             if (dpi->conf->csc_support) {
> > > > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > > > +             }
> > > > > >               if (dpi->conf->swap_input_support)
> > > > > >                       mtk_dpi_config_swap_input(dpi, true);
> > > > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > > > > > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> > > > > >       mtk_dpi_disable(dpi);
> > > > > >       clk_disable_unprepare(dpi->pixel_clk);
> > > > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > > > +     clk_disable_unprepare(dpi->tvd_clk);
> > > > > >  }
> > > > > >
> > > > > >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > > > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > > >       if (++dpi->refcount != 1)
> > > > > >               return 0;
> > > > > >
> > > > > > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > > > > > +     if (ret) {
> > > > > > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > > > > > +             goto err_pixel;
> > > > > > +     }
> > > > > > +
> > > > > >       ret = clk_prepare_enable(dpi->engine_clk);
> > > > > >       if (ret) {
> > > > > >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> > > > > >               goto err_refcount;
> > > > > >       }
> > > > > >
> > > > > > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > > > > > +     if (ret) {
> > > > > > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > > > > > +             goto err_ck_cg;
> > > > > > +     }
> > > > > > +
> > > > > >       ret = clk_prepare_enable(dpi->pixel_clk);
> > > > > >       if (ret) {
> > > > > >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > > > > > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > > >       if (dpi->pinctrl && dpi->pins_dpi)
> > > > > >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> > > > > >
> > > > > > -     mtk_dpi_enable(dpi);
> > > > > >       return 0;
> > > > > >
> > > > > >  err_pixel:
> > > > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > > > +err_ck_cg:
> > > > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > > >  err_refcount:
> > > > > >       dpi->refcount--;
> > > > > > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > > > >       pll_rate = clk_get_rate(dpi->tvd_clk);
> > > > > >
> > > > > >       vm.pixelclock = pll_rate / factor;
> > > > > > -     if (dpi->conf->is_dpintf)
> > > > > > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > > > > > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > > > +     if (dpi->conf->is_dpintf) {
> > > > > > +             if (factor == 1)
> > > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > > > +             else if (factor == 2)
> > > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > > > > > +             else if (factor == 4)
> > > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > > > > > +             else
> > > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > > > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > > >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> > > > > >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> > > > > >       else
> > > > > > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > > > >       }
> > > > > >       mtk_dpi_sw_reset(dpi, false);
> > > > > >
> > > > > > +     mtk_dpi_enable(dpi);
> > > > > > +
> > > > > >       return 0;
> > > > > >  }
> > > > > >
> > > > > > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> > > > > >       u32 *input_fmts;
> > > > > >
> > > > > >       *num_input_fmts = 0;
> > > > > > -
> > > > > >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> > > > > >                            GFP_KERNEL);
> > > > > >       if (!input_fmts)
> > > > > > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > > > >               if (dpi->conf->num_output_fmts)
> > > > > >                       out_bus_format = dpi->conf->output_fmts[0];
> > > > > >
> > > > > > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > > > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > > >               bridge_state->input_bus_cfg.format,
> > > > > >               bridge_state->output_bus_cfg.format);
> > > > > >
> > > > > > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > > > >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> > > > > >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> > > > > >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > > > > > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > > > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > > > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > > > > > +     else
> > > > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > > >
> > > > > >       return 0;
> > > > > >  }
> > > > > > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> > > > > >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> > > > > >  };
> > > > > >
> > > > > > +static const u32 mt8195_output_fmts[] = {
> > > > > > +     MEDIA_BUS_FMT_RGB888_1X24,
> > > > > > +     MEDIA_BUS_FMT_YUV8_1X24,
> > > > > > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > > > > > +};
> > > > > > +
> > > > > >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> > > > > >       .c_bottom = 0x0010,
> > > > > >       .c_top = 0x0FE0,
> > > > > > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> > > > > >       .hvsize_mask = HSIZE_MASK,
> > > > > >       .channel_swap_shift = CH_SWAP,
> > > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > > >       .limit = &mtk_dpi_limit,
> > > > > >  };
> > > > > >
> > > > > > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> > > > > >       .hvsize_mask = HSIZE_MASK,
> > > > > >       .channel_swap_shift = CH_SWAP,
> > > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > > >       .limit = &mtk_dpi_limit,
> > > > > >  };
> > > > > >
> > > > > > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> > > > > >       .hvsize_mask = HSIZE_MASK,
> > > > > >       .channel_swap_shift = CH_SWAP,
> > > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > > >       .limit = &mtk_dpi_limit,
> > > > > >  };
> > > > > >
> > > > > > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> > > > > >       .hvsize_mask = HSIZE_MASK,
> > > > > >       .channel_swap_shift = CH_SWAP,
> > > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > > >       .limit = &mtk_dpi_limit,
> > > > > >  };
> > > > > >
> > > > > >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> > > > > >       .cal_factor = mt8195_dpintf_calculate_factor,
> > > > > > -     .output_fmts = mt8173_output_fmts,
> > > > > > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > > > > > +     .output_fmts = mt8195_output_fmts,
> > > > > > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> > > > > >       .is_dpintf = true,
> > > > > > +     .csc_support = true,
> > > > > >       .dimension_mask = DPINTF_HPW_MASK,
> > > > > >       .hvsize_mask = DPINTF_HSIZE_MASK,
> > > > > >       .channel_swap_shift = DPINTF_CH_SWAP,
> > > > > >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > > > > > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> > > > > >       .limit = &mtk_dpintf_limit,
> > > > > >  };
> > > > > >
> > > > > > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > > > >               return ret;
> > > > > >       }
> > > > > >
> > > > > > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > > > > > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > > > > > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > > > > > +             if (ret != -EPROBE_DEFER)
> > > > > > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > > > > > +                             ret);
> > > > > > +
> > > > > > +             return ret;
> > > > > > +     }
> > > > > > +
> > > > > >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> > > > > >       if (IS_ERR(dpi->pixel_clk)) {
> > > > > >               ret = PTR_ERR(dpi->pixel_clk);
> > > > > > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > > > >               return ret;
> > > > > >       }
> > > > > >
> > > > > > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > > > > > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > > > > > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > > > > > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > > > > > +
> > > > > >       dpi->irq = platform_get_irq(pdev, 0);
> > > > > >       if (dpi->irq <= 0)
> > > > > >               return -EINVAL;
> > > > >
> > > > > All those changes look unrelated as well?
> > > > >
> > > > Do you mean all the changes in mtk_dpi.c ? They are in support of
> > > > enabling the mt8195 dpintf driver... so, not sure they are unlreated?
> > >
> > > I don't know that driver at all, so it's probably related if you say so
> > > :)
> > >
> > > The DPI interface seems to be a panel interface, so it's a bit weird to
> > > me why you would need to change things in the panel interface while
> > > claiming to support a DP output.
> > >
> > > Especially since it looks like you have some support for CSC, and
> > > support for additional output formats?
> > >
> > > I guess the DPI interface feeds the DP that essentially acts as a RGB ->
> > > DP bridge?
> > >
> > > If so, then the part adding support for CSC and additional formats
> > > should be split into a separate patch, and it should be mentioned in the
> > > commit log
> > >
> > > Maxime
> >
> > I see what you mean, I'll split the patches functionally...
> > I'm discovering the gpu/drm tree, so It may take me a while to get it right :D
>
> It's not really yet another weird drm rule :)
>
> Splitting your patches is valuable for a bunch of reasons that apply to
> basically every tree:
>
>   - It's easier to review (and maintainers like that). It's also easier
>     to justify in a commit log, so it leads to a better history over the
>     long term.
>
>   - It's also easier to merge since you can merge smaller chunks when
>     they are ready, even if the entire set is not. Smaller chunks also
>     mean it's easier to apply (for the maintainer) and rebase (for you).
>
>   - Finally, and possibly the strongest technical argument, it's much
>     more helpful when you're bisecting. Taking the above patch as an
>     example, let's assume you introduced a bug in some RGB panel with
>     the colors being off due to the CSC changes. If you run git bisect,
>     you'll end up with that big patch affecting multiple things and it
>     would still be difficult to figure out what's wrong exactly. If it
>     was split, you would end up only on the changeset that introduces
>     the regression, which is very likely to be much easier to analyze.
>
> Maxime

Thx,
Guillaume.

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-02 14:48               ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-02 14:48 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Hi,

Quoting Maxime Ripard (2021-11-25 15:30:34)
> On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > Hi,
> > Thanks for all your input, really appreciated.
> >
> > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > Hi,
> > >
> > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > The driver creates a child device for the phy. The child device will
> > > > > > never exist without the parent being active. As they are sharing a
> > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > both can work with the same register range. The phy driver sets device
> > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > to control the phy properties.
> > > > >
> > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > you need a separate PHY driver in the first place?
> > > >
> > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > >
> > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > >
> > > It's a bit of a circular argument though :)
> > >
> > > It's a separate phy driver because it needs to go through another
> > > maintainer's tree, but it needs to go through another maintainer's tree
> > > because it's a separate phy driver.
> > >
> > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > the phy setup be done directly in the DP driver, if it's essentially a
> > > single device?
> > >
> > > That being said, usually what those kind of questions mean is that
> > > you're missing a comment or something in the commit log to provide that
> > > context in the first place, so it would be great to add that context
> > > here.
> > >
> > > And it will avoid the situation we're now in where multiple reviewers
> > > ask the same questions over and over again :)
> > >
> > At first I didn't understand your reply, then I realized I gave you
> > the wrong link...
> > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> >
> > The driver and phy were a single driver until v2 of this patch series
> > and the phy setup
> > was done directly in the driver (single driver, single C file).
> > Here's the relevant link to the discussion between Chun-Kuang and Markus
> >
> > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> >
> > I'll try to find a way to make it clearer for v7.
>
> OK, it makes sense then :)
>
> There's something weird though: the devices definitely look like they're
> in a separate register range, yet you mention a regmap to handle the
> shared register range. That range doesn't seem described anywhere in the
> device tree though? What is it for?
>

My understanding is that 0x1000 to 0x1fff controls the phy functionalities and
 0x2000 to 0x4fff controls "non-phy" functionalities.
And you are right, there's no description of that in the device tree whatsoever.
The ranges are in the same actual device and thus it has been decided
to not have
dt-bindings for the phy device.

The phy driver is a child of the DP driver that we register using
platform_device_register_data() and we pass along the same regmap as the DP
driver in its platform data.

I'm not sure I've understood and fully replied to the question...

> > > > > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > > > > +                                     struct drm_bridge_state *old_state)
> > > > > > +{
> > > > > > +     struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > > > > +     struct drm_connector *conn;
> > > > > > +     struct drm_connector_state *conn_state;
> > > > > > +     struct drm_crtc *crtc;
> > > > > > +     struct drm_crtc_state *crtc_state;
> > > > > > +     int ret = 0;
> > > > > > +     int i;
> > > > > > +
> > > > > > +     conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > > > > +                                                     bridge->encoder);
> > > > > > +     if (!conn) {
> > > > > > +             drm_err(mtk_dp->drm_dev,
> > > > > > +                     "Can't enable bridge as connector is missing\n");
> > > > > > +             return;
> > > > > > +     }
> > > > > > +
> > > > > > +     memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > > > >
> > > > > This should be protected by a mutex (just like any resource shared
> > > > > between KMS and ALSA)
> > > >
> > > > Ok.
> > >
> > > I forgot to ask (even though checkpatch does mention it iirc), but since
> > > you have multiple mutex it would be nice to have a comment for each
> > > mutex stating exactly what it protects, and against what.
> > >
> > > It's hard otherwise to remember or figure out what the locks are here
> > > for.
> > >
> > > > > > +     ret = mtk_dp_dt_parse_pdata(mtk_dp, pdev);
> > > > > > +     if (ret)
> > > > > > +             return ret;
> > > > >
> > > > > pdata?
> > > > >
> > > > can you elaborate?
> > >
> > > Sorry, yeah, pdata is usually the abbreviation used in linux for the
> > > platform_data mechanism, but you're using the DT to retrieve your
> > > resources (and platform_data usually don't involve any parsing), so the
> > > name is odd.
> > >
> > > > > > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > > index 384074f69111b..e6e88e3cd811d 100644
> > > > > > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > > > > > @@ -63,6 +63,14 @@ enum mtk_dpi_out_color_format {
> > > > > >       MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL
> > > > > >  };
> > > > > >
> > > > > > +enum TVDPLL_CLK {
> > > > > > +     TVDPLL_PLL = 0,
> > > > > > +     TVDPLL_D2 = 2,
> > > > > > +     TVDPLL_D4 = 4,
> > > > > > +     TVDPLL_D8 = 8,
> > > > > > +     TVDPLL_D16 = 16,
> > > > > > +};
> > > > > > +
> > > > > >  struct mtk_dpi {
> > > > > >       struct drm_encoder encoder;
> > > > > >       struct drm_bridge bridge;
> > > > > > @@ -71,8 +79,10 @@ struct mtk_dpi {
> > > > > >       void __iomem *regs;
> > > > > >       struct device *dev;
> > > > > >       struct clk *engine_clk;
> > > > > > +     struct clk *dpi_ck_cg;
> > > > > >       struct clk *pixel_clk;
> > > > > >       struct clk *tvd_clk;
> > > > > > +     struct clk *pclk_src[5];
> > > > > >       int irq;
> > > > > >       struct drm_display_mode mode;
> > > > > >       const struct mtk_dpi_conf *conf;
> > > > > > @@ -135,6 +145,7 @@ struct mtk_dpi_conf {
> > > > > >       u32 hvsize_mask;
> > > > > >       u32 channel_swap_shift;
> > > > > >       u32 yuv422_en_bit;
> > > > > > +     u32 csc_enable_bit;
> > > > > >       const struct mtk_dpi_yc_limit *limit;
> > > > > >  };
> > > > > >
> > > > > > @@ -365,7 +376,7 @@ static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable)
> > > > > >
> > > > > >  static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable)
> > > > > >  {
> > > > > > -     mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
> > > > > > +     mtk_dpi_mask(dpi, DPI_CON, enable ? dpi->conf->csc_enable_bit : 0, dpi->conf->csc_enable_bit);
> > > > > >  }
> > > > > >
> > > > > >  static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable)
> > > > > > @@ -384,22 +395,45 @@ static void mtk_dpi_config_disable_edge(struct mtk_dpi *dpi)
> > > > > >               mtk_dpi_mask(dpi, dpi->conf->reg_h_fre_con, 0, EDGE_SEL_EN);
> > > > > >  }
> > > > > >
> > > > > > +static void mtk_dpi_matrix_sel(struct mtk_dpi *dpi, enum mtk_dpi_out_color_format format)
> > > > > > +{
> > > > > > +     u32 matrix_sel = 0;
> > > > > > +
> > > > > > +     switch (format) {
> > > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422:
> > > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL:
> > > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444:
> > > > > > +     case MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL:
> > > > > > +     case MTK_DPI_COLOR_FORMAT_XV_YCC:
> > > > > > +             if (dpi->mode.hdisplay <= 720)
> > > > > > +                     matrix_sel = 0x2;
> > > > > > +             break;
> > > > > > +     default:
> > > > > > +             break;
> > > > > > +     }
> > > > > > +     mtk_dpi_mask(dpi, DPI_MATRIX_SET, matrix_sel, INT_MATRIX_SEL_MASK);
> > > > > > +}
> > > > > > +
> > > > > >  static void mtk_dpi_config_color_format(struct mtk_dpi *dpi,
> > > > > >                                       enum mtk_dpi_out_color_format format)
> > > > > >  {
> > > > > >       if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) ||
> > > > > >           (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) {
> > > > > >               mtk_dpi_config_yuv422_enable(dpi, false);
> > > > > > -             if (dpi->conf->csc_support)
> > > > > > +             if (dpi->conf->csc_support) {
> > > > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > > > +             }
> > > > > >               if (dpi->conf->swap_input_support)
> > > > > >                       mtk_dpi_config_swap_input(dpi, false);
> > > > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR);
> > > > > >       } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) ||
> > > > > >                  (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) {
> > > > > >               mtk_dpi_config_yuv422_enable(dpi, true);
> > > > > > -             if (dpi->conf->csc_support)
> > > > > > +             if (dpi->conf->csc_support) {
> > > > > >                       mtk_dpi_config_csc_enable(dpi, true);
> > > > > > +                     mtk_dpi_matrix_sel(dpi, format);
> > > > > > +             }
> > > > > >               if (dpi->conf->swap_input_support)
> > > > > >                       mtk_dpi_config_swap_input(dpi, true);
> > > > > >               mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB);
> > > > > > @@ -441,6 +475,8 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
> > > > > >       mtk_dpi_disable(dpi);
> > > > > >       clk_disable_unprepare(dpi->pixel_clk);
> > > > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > > > +     clk_disable_unprepare(dpi->tvd_clk);
> > > > > >  }
> > > > > >
> > > > > >  static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > > > @@ -450,12 +486,24 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > > >       if (++dpi->refcount != 1)
> > > > > >               return 0;
> > > > > >
> > > > > > +     ret = clk_prepare_enable(dpi->tvd_clk);
> > > > > > +     if (ret) {
> > > > > > +             dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
> > > > > > +             goto err_pixel;
> > > > > > +     }
> > > > > > +
> > > > > >       ret = clk_prepare_enable(dpi->engine_clk);
> > > > > >       if (ret) {
> > > > > >               dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
> > > > > >               goto err_refcount;
> > > > > >       }
> > > > > >
> > > > > > +     ret = clk_prepare_enable(dpi->dpi_ck_cg);
> > > > > > +     if (ret) {
> > > > > > +             dev_err(dpi->dev, "Failed to enable dpi_ck_cg clock: %d\n", ret);
> > > > > > +             goto err_ck_cg;
> > > > > > +     }
> > > > > > +
> > > > > >       ret = clk_prepare_enable(dpi->pixel_clk);
> > > > > >       if (ret) {
> > > > > >               dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
> > > > > > @@ -465,10 +513,11 @@ static int mtk_dpi_power_on(struct mtk_dpi *dpi)
> > > > > >       if (dpi->pinctrl && dpi->pins_dpi)
> > > > > >               pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
> > > > > >
> > > > > > -     mtk_dpi_enable(dpi);
> > > > > >       return 0;
> > > > > >
> > > > > >  err_pixel:
> > > > > > +     clk_disable_unprepare(dpi->dpi_ck_cg);
> > > > > > +err_ck_cg:
> > > > > >       clk_disable_unprepare(dpi->engine_clk);
> > > > > >  err_refcount:
> > > > > >       dpi->refcount--;
> > > > > > @@ -500,9 +549,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > > > >       pll_rate = clk_get_rate(dpi->tvd_clk);
> > > > > >
> > > > > >       vm.pixelclock = pll_rate / factor;
> > > > > > -     if (dpi->conf->is_dpintf)
> > > > > > -             clk_set_rate(dpi->pixel_clk, vm.pixelclock / 4);
> > > > > > -     else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > > > +     if (dpi->conf->is_dpintf) {
> > > > > > +             if (factor == 1)
> > > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > > > +             else if (factor == 2)
> > > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[3]);
> > > > > > +             else if (factor == 4)
> > > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[4]);
> > > > > > +             else
> > > > > > +                     clk_set_parent(dpi->pixel_clk, dpi->pclk_src[2]);
> > > > > > +     } else if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
> > > > > >                (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
> > > > > >               clk_set_rate(dpi->pixel_clk, vm.pixelclock * 2);
> > > > > >       else
> > > > > > @@ -581,6 +637,8 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
> > > > > >       }
> > > > > >       mtk_dpi_sw_reset(dpi, false);
> > > > > >
> > > > > > +     mtk_dpi_enable(dpi);
> > > > > > +
> > > > > >       return 0;
> > > > > >  }
> > > > > >
> > > > > > @@ -623,7 +681,6 @@ static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> > > > > >       u32 *input_fmts;
> > > > > >
> > > > > >       *num_input_fmts = 0;
> > > > > > -
> > > > > >       input_fmts = kcalloc(1, sizeof(*input_fmts),
> > > > > >                            GFP_KERNEL);
> > > > > >       if (!input_fmts)
> > > > > > @@ -649,7 +706,7 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > > > >               if (dpi->conf->num_output_fmts)
> > > > > >                       out_bus_format = dpi->conf->output_fmts[0];
> > > > > >
> > > > > > -     dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > > > +     dev_info(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
> > > > > >               bridge_state->input_bus_cfg.format,
> > > > > >               bridge_state->output_bus_cfg.format);
> > > > > >
> > > > > > @@ -657,7 +714,10 @@ static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
> > > > > >       dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS;
> > > > > >       dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB;
> > > > > >       dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
> > > > > > -     dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > > > +     if (out_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> > > > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL;
> > > > > > +     else
> > > > > > +             dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB;
> > > > > >
> > > > > >       return 0;
> > > > > >  }
> > > > > > @@ -835,6 +895,12 @@ static const u32 mt8183_output_fmts[] = {
> > > > > >       MEDIA_BUS_FMT_RGB888_2X12_BE,
> > > > > >  };
> > > > > >
> > > > > > +static const u32 mt8195_output_fmts[] = {
> > > > > > +     MEDIA_BUS_FMT_RGB888_1X24,
> > > > > > +     MEDIA_BUS_FMT_YUV8_1X24,
> > > > > > +     MEDIA_BUS_FMT_YUYV8_1X16,
> > > > > > +};
> > > > > > +
> > > > > >  static const struct mtk_dpi_yc_limit mtk_dpi_limit = {
> > > > > >       .c_bottom = 0x0010,
> > > > > >       .c_top = 0x0FE0,
> > > > > > @@ -862,6 +928,7 @@ static const struct mtk_dpi_conf mt8173_conf = {
> > > > > >       .hvsize_mask = HSIZE_MASK,
> > > > > >       .channel_swap_shift = CH_SWAP,
> > > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > > >       .limit = &mtk_dpi_limit,
> > > > > >  };
> > > > > >
> > > > > > @@ -879,6 +946,7 @@ static const struct mtk_dpi_conf mt2701_conf = {
> > > > > >       .hvsize_mask = HSIZE_MASK,
> > > > > >       .channel_swap_shift = CH_SWAP,
> > > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > > >       .limit = &mtk_dpi_limit,
> > > > > >  };
> > > > > >
> > > > > > @@ -895,6 +963,7 @@ static const struct mtk_dpi_conf mt8183_conf = {
> > > > > >       .hvsize_mask = HSIZE_MASK,
> > > > > >       .channel_swap_shift = CH_SWAP,
> > > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > > >       .limit = &mtk_dpi_limit,
> > > > > >  };
> > > > > >
> > > > > > @@ -911,18 +980,21 @@ static const struct mtk_dpi_conf mt8192_conf = {
> > > > > >       .hvsize_mask = HSIZE_MASK,
> > > > > >       .channel_swap_shift = CH_SWAP,
> > > > > >       .yuv422_en_bit = YUV422_EN,
> > > > > > +     .csc_enable_bit = CSC_ENABLE,
> > > > > >       .limit = &mtk_dpi_limit,
> > > > > >  };
> > > > > >
> > > > > >  static const struct mtk_dpi_conf mt8195_dpintf_conf = {
> > > > > >       .cal_factor = mt8195_dpintf_calculate_factor,
> > > > > > -     .output_fmts = mt8173_output_fmts,
> > > > > > -     .num_output_fmts = ARRAY_SIZE(mt8173_output_fmts),
> > > > > > +     .output_fmts = mt8195_output_fmts,
> > > > > > +     .num_output_fmts = ARRAY_SIZE(mt8195_output_fmts),
> > > > > >       .is_dpintf = true,
> > > > > > +     .csc_support = true,
> > > > > >       .dimension_mask = DPINTF_HPW_MASK,
> > > > > >       .hvsize_mask = DPINTF_HSIZE_MASK,
> > > > > >       .channel_swap_shift = DPINTF_CH_SWAP,
> > > > > >       .yuv422_en_bit = DPINTF_YUV422_EN,
> > > > > > +     .csc_enable_bit = DPINTF_CSC_ENABLE,
> > > > > >       .limit = &mtk_dpintf_limit,
> > > > > >  };
> > > > > >
> > > > > > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > > > >               return ret;
> > > > > >       }
> > > > > >
> > > > > > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
> > > > > > +     if (IS_ERR(dpi->dpi_ck_cg)) {
> > > > > > +             ret = PTR_ERR(dpi->dpi_ck_cg);
> > > > > > +             if (ret != -EPROBE_DEFER)
> > > > > > +                     dev_err(dev, "Failed to get dpi ck cg clock: %d\n",
> > > > > > +                             ret);
> > > > > > +
> > > > > > +             return ret;
> > > > > > +     }
> > > > > > +
> > > > > >       dpi->pixel_clk = devm_clk_get(dev, "pixel");
> > > > > >       if (IS_ERR(dpi->pixel_clk)) {
> > > > > >               ret = PTR_ERR(dpi->pixel_clk);
> > > > > > @@ -997,6 +1079,11 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> > > > > >               return ret;
> > > > > >       }
> > > > > >
> > > > > > +     dpi->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
> > > > > > +     dpi->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
> > > > > > +     dpi->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
> > > > > > +     dpi->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
> > > > > > +
> > > > > >       dpi->irq = platform_get_irq(pdev, 0);
> > > > > >       if (dpi->irq <= 0)
> > > > > >               return -EINVAL;
> > > > >
> > > > > All those changes look unrelated as well?
> > > > >
> > > > Do you mean all the changes in mtk_dpi.c ? They are in support of
> > > > enabling the mt8195 dpintf driver... so, not sure they are unlreated?
> > >
> > > I don't know that driver at all, so it's probably related if you say so
> > > :)
> > >
> > > The DPI interface seems to be a panel interface, so it's a bit weird to
> > > me why you would need to change things in the panel interface while
> > > claiming to support a DP output.
> > >
> > > Especially since it looks like you have some support for CSC, and
> > > support for additional output formats?
> > >
> > > I guess the DPI interface feeds the DP that essentially acts as a RGB ->
> > > DP bridge?
> > >
> > > If so, then the part adding support for CSC and additional formats
> > > should be split into a separate patch, and it should be mentioned in the
> > > commit log
> > >
> > > Maxime
> >
> > I see what you mean, I'll split the patches functionally...
> > I'm discovering the gpu/drm tree, so It may take me a while to get it right :D
>
> It's not really yet another weird drm rule :)
>
> Splitting your patches is valuable for a bunch of reasons that apply to
> basically every tree:
>
>   - It's easier to review (and maintainers like that). It's also easier
>     to justify in a commit log, so it leads to a better history over the
>     long term.
>
>   - It's also easier to merge since you can merge smaller chunks when
>     they are ready, even if the entire set is not. Smaller chunks also
>     mean it's easier to apply (for the maintainer) and rebase (for you).
>
>   - Finally, and possibly the strongest technical argument, it's much
>     more helpful when you're bisecting. Taking the above patch as an
>     example, let's assume you introduced a bug in some RGB panel with
>     the colors being off due to the CSC changes. If you run git bisect,
>     you'll end up with that big patch affecting multiple things and it
>     would still be difficult to figure out what's wrong exactly. If it
>     was split, you would end up only on the changeset that introduces
>     the regression, which is very likely to be much easier to analyze.
>
> Maxime

Thx,
Guillaume.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-11-25 15:27     ` Chun-Kuang Hu
  (?)
  (?)
@ 2021-12-02 15:31       ` Guillaume Ranquet
  -1 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-02 15:31 UTC (permalink / raw)
  To: Chun-Kuang Hu
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	DRI Development, ARM/Mediatek SoC support, Linux ARM

Hi Chun-Kuang.

Quoting Chun-Kuang Hu (2021-11-25 16:27:45)
> Hi, Guillaume:
>
> This is a big patch, so I give some comment first.
>
> Guillaume Ranquet <granquet@baylibre.com> 於 2021年11月10日 週三 下午9:06寫道:
> >
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > according phy driver mediatek-dp-phy.
> >
> > It supports both functional units on the mt8195, the embedded
> > DisplayPort as well as the external DisplayPort units. It offers
> > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > to 4 lanes.
> >
> > The driver creates a child device for the phy. The child device will
> > never exist without the parent being active. As they are sharing a
> > register range, the parent passes a regmap pointer to the child so that
> > both can work with the same register range. The phy driver sets device
> > data that is read by the parent to get the phy device that can be used
> > to control the phy properties.
> >
> > This driver is based on an initial version by
> > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > Reported-by: kernel test robot <lkp@intel.com>
> > ---
> >  drivers/gpu/drm/drm_edid.c              |    2 +-
>
> Separate this to another patch.
>
> >  drivers/gpu/drm/mediatek/Kconfig        |    7 +
> >  drivers/gpu/drm/mediatek/Makefile       |    2 +
> >  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
> >  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
> >  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
>
> Ditto.
>
> >  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
>
> Ditto.
>
> >  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
>
> Ditto
>
> >  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
>
> Ditto
>
yes my bad, I've made a bunch of fixup which ended up in the wrong place.
It will be fixed for the next version.

> >  9 files changed, 3799 insertions(+), 13 deletions(-)
> >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
> >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> >
> > diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> > index 500279a82167a..bfd98b50ceb5b 100644
> > --- a/drivers/gpu/drm/drm_edid.c
> > +++ b/drivers/gpu/drm/drm_edid.c
> > @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
> >          * modes and forbids YCRCB422 support for all video modes per
> >          * HDMI 1.3 spec.
> >          */
> > -       info->color_formats = DRM_COLOR_FORMAT_RGB444;
> > +       info->color_formats |= DRM_COLOR_FORMAT_RGB444;
> >
> >         /* YCRCB444 is optional according to spec. */
> >         if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
> > diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> > index 2976d21e9a34a..029b94c716131 100644
> > --- a/drivers/gpu/drm/mediatek/Kconfig
> > +++ b/drivers/gpu/drm/mediatek/Kconfig
> > @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
> >         select PHY_MTK_HDMI
> >         help
> >           DRM/KMS HDMI driver for Mediatek SoCs
> > +
> > +config MTK_DPTX_SUPPORT
> > +       tristate "DRM DPTX Support for Mediatek SoCs"
> > +       depends on DRM_MEDIATEK
> > +       select PHY_MTK_DP
> > +       help
> > +         DRM/KMS Display Port driver for Mediatek SoCs.
> > diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> > index 29098d7c8307c..d86a6406055e6 100644
> > --- a/drivers/gpu/drm/mediatek/Makefile
> > +++ b/drivers/gpu/drm/mediatek/Makefile
> > @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
> >                           mtk_hdmi_ddc.o
> >
> >  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> > +
> > +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> > new file mode 100644
> > index 0000000000000..83087219d5a5e
> > --- /dev/null
> > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> > @@ -0,0 +1,3094 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2019 MediaTek Inc.
> > + * Copyright (c) 2021 BayLibre
> > + */
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_bridge.h>
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_dp_helper.h>
> > +#include <drm/drm_edid.h>
> > +#include <drm/drm_of.h>
> > +#include <drm/drm_panel.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/drm_probe_helper.h>
> > +#include <linux/arm-smccc.h>
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/errno.h>
> > +#include <linux/kernel.h>
> > +#include <linux/nvmem-consumer.h>
> > +#include <linux/of.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <sound/hdmi-codec.h>
> > +#include <video/videomode.h>
> > +
> > +#include "mtk_dp_reg.h"
> > +
> > +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> > +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> > +
> > +#define MTK_DP_MAX_LANES 4
> > +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> > +
> > +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> > +
> > +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> > +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> > +
> > +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> > +
> > +#define MTK_DP_DP_VERSION_11 0x11
> > +
> > +enum mtk_dp_state {
> > +       MTK_DP_STATE_INITIAL,
> > +       MTK_DP_STATE_IDLE,
> > +       MTK_DP_STATE_PREPARE,
> > +       MTK_DP_STATE_NORMAL,
> > +};
> > +
> > +enum mtk_dp_train_state {
> > +       MTK_DP_TRAIN_STATE_STARTUP = 0,
> > +       MTK_DP_TRAIN_STATE_CHECKCAP,
> > +       MTK_DP_TRAIN_STATE_CHECKEDID,
> > +       MTK_DP_TRAIN_STATE_TRAINING_PRE,
> > +       MTK_DP_TRAIN_STATE_TRAINING,
> > +       MTK_DP_TRAIN_STATE_CHECKTIMING,
> > +       MTK_DP_TRAIN_STATE_NORMAL,
> > +       MTK_DP_TRAIN_STATE_POWERSAVE,
> > +       MTK_DP_TRAIN_STATE_DPIDLE,
> > +};
> > +
> > +struct mtk_dp_timings {
> > +       struct videomode vm;
> > +
> > +       u16 htotal;
> > +       u16 vtotal;
> > +       u8 frame_rate;
> > +       u32 pix_rate_khz;
> > +};
> > +
> > +struct mtk_dp_train_info {
> > +       bool tps3;
> > +       bool tps4;
> > +       bool sink_ssc;
> > +       bool cable_plugged_in;
> > +       bool cable_state_change;
> > +       bool cr_done;
> > +       bool eq_done;
> > +
> > +       // link_rate is in multiple of 0.27Gbps
> > +       int link_rate;
> > +       int lane_count;
> > +
> > +       int irq_status;
> > +       int check_cap_count;
> > +};
> > +
> > +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> > +enum mtk_dp_color_format {
> > +       MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> > +       MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> > +       MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> > +       MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> > +       MTK_DP_COLOR_FORMAT_YONLY = 4,
> > +       MTK_DP_COLOR_FORMAT_RAW = 5,
> > +       MTK_DP_COLOR_FORMAT_RESERVED = 6,
> > +       MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> > +       MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> > +};
> > +
> > +// Multiple of 0.27Gbps
> > +enum mtk_dp_linkrate {
> > +       MTK_DP_LINKRATE_RBR = 0x6,
> > +       MTK_DP_LINKRATE_HBR = 0xA,
> > +       MTK_DP_LINKRATE_HBR2 = 0x14,
> > +       MTK_DP_LINKRATE_HBR25 = 0x19,
> > +       MTK_DP_LINKRATE_HBR3 = 0x1E,
> > +};
> > +
> > +// Same values as used for DP Spec MISC0 bits 5,6,7
> > +enum mtk_dp_color_depth {
> > +       MTK_DP_COLOR_DEPTH_6BIT = 0,
> > +       MTK_DP_COLOR_DEPTH_8BIT = 1,
> > +       MTK_DP_COLOR_DEPTH_10BIT = 2,
> > +       MTK_DP_COLOR_DEPTH_12BIT = 3,
> > +       MTK_DP_COLOR_DEPTH_16BIT = 4,
> > +       MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> > +};
> > +
> > +struct mtk_dp_audio_cfg {
> > +       int sample_rate;
> > +       int word_length_bits;
> > +       int channels;
> > +};
> > +
> > +struct mtk_dp_info {
> > +       enum mtk_dp_color_depth depth;
> > +       enum mtk_dp_color_format format;
> > +       struct mtk_dp_audio_cfg audio_caps;
> > +       struct mtk_dp_timings timings;
> > +};
> > +
> > +struct dp_cal_data {
> > +       unsigned int glb_bias_trim;
> > +       unsigned int clktx_impse;
> > +
> > +       unsigned int ln0_tx_impsel_pmos;
> > +       unsigned int ln0_tx_impsel_nmos;
> > +       unsigned int ln1_tx_impsel_pmos;
> > +       unsigned int ln1_tx_impsel_nmos;
> > +       unsigned int ln2_tx_impsel_pmos;
> > +       unsigned int ln2_tx_impsel_nmos;
> > +       unsigned int ln3_tx_impsel_pmos;
> > +       unsigned int ln3_tx_impsel_nmos;
> > +};
> > +
> > +struct mtk_dp {
> > +       struct device *dev;
> > +       struct platform_device *phy_dev;
> > +       struct phy *phy;
> > +       struct dp_cal_data cal_data;
> > +
> > +       struct drm_device *drm_dev;
> > +       struct drm_bridge bridge;
> > +       struct drm_bridge *next_bridge;
> > +       struct drm_dp_aux aux;
> > +
> > +       struct mutex edid_lock;
> > +       struct edid *edid;
> > +
> > +       u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> > +
> > +       struct mtk_dp_info info;
> > +       enum mtk_dp_state state;
> > +
> > +       struct mtk_dp_train_info train_info;
> > +       enum mtk_dp_train_state train_state;
> > +       unsigned int input_fmt;
> > +
> > +       struct regmap *regs;
> > +       struct clk *dp_tx_clk;
> > +
> > +       bool enabled;
> > +       bool audio_enable;
> > +
> > +       bool has_fec;
> > +       struct mutex dp_lock;
> > +
> > +       struct mutex update_plugged_status_lock;
> > +
> > +       hdmi_codec_plugged_cb plugged_cb;
> > +       struct device *codec_dev;
> > +       u8 connector_eld[MAX_ELD_BYTES];
> > +};
> > +
> > +enum mtk_dp_sdp_type {
> > +       MTK_DP_SDP_NONE = 0x00,
> > +       MTK_DP_SDP_ACM = 0x01,
> > +       MTK_DP_SDP_ISRC = 0x02,
> > +       MTK_DP_SDP_AVI = 0x03,
> > +       MTK_DP_SDP_AUI = 0x04,
> > +       MTK_DP_SDP_SPD = 0x05,
> > +       MTK_DP_SDP_MPEG = 0x06,
> > +       MTK_DP_SDP_NTSC = 0x07,
> > +       MTK_DP_SDP_VSP = 0x08,
> > +       MTK_DP_SDP_VSC = 0x09,
> > +       MTK_DP_SDP_EXT = 0x0A,
> > +       MTK_DP_SDP_PPS0 = 0x0B,
> > +       MTK_DP_SDP_PPS1 = 0x0C,
> > +       MTK_DP_SDP_PPS2 = 0x0D,
> > +       MTK_DP_SDP_PPS3 = 0x0E,
> > +       MTK_DP_SDP_DRM = 0x10,
> > +       MTK_DP_SDP_MAX_NUM
> > +};
> > +
> > +struct mtk_dp_sdp_packet {
> > +       enum mtk_dp_sdp_type type;
> > +       struct dp_sdp sdp;
> > +};
> > +
> > +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> > +union mtk_dp_audio_channel_status {
> > +       struct {
> > +               u8 rev : 1;
> > +               u8 is_lpcm : 1;
> > +               u8 copy_right : 1;
> > +               u8 additional_format_info : 3;
> > +               u8 channel_status_mode : 2;
> > +               u8 category_code;
> > +               u8 src_num : 4;
> > +               u8 channel_num : 4;
> > +               u8 sampling_freq : 4;
> > +               u8 clk_accuracy : 2;
> > +               u8 rev2 : 2;
> > +               u8 word_len : 4;
> > +               u8 original_sampling_freq : 4;
> > +       } iec;
> > +
> > +       u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> > +};
> > +
> > +static struct regmap_config mtk_dp_regmap_config = {
> > +       .reg_bits = 32,
> > +       .val_bits = 32,
> > +       .reg_stride = 4,
> > +       .max_register = SEC_OFFSET + 0x90,
> > +       .name = "mtk-dp-registers",
> > +};
> > +
> > +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
>
> Separate edp and displayport into two patches. For example, the first
> patch support edp only, and the second patch add displayport function.
>
I have made the opposite as it seemed a bit more logical from how it's coded
now.
I've made a single patch with DP and a new one adding eDP support on top of it.
The change is quite small, adding the eDP on top of DP:
 drivers/gpu/drm/mediatek/mtk_dp.c | 103
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
 1 file changed, 74 insertions(+), 29 deletions(-)
> > +{
> > +       return mtk_dp->next_bridge != NULL;
> > +}
> > +
>
> [snip]
>
> > +
> > +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> > +                                   struct drm_connector *connector)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +       bool enabled = mtk_dp->enabled;
> > +       struct edid *new_edid = NULL;
> > +
> > +       if (!enabled)
> > +               drm_bridge_chain_pre_enable(bridge);
>
> In mtk_hdmi_bridge_get_edid(), there does not check the power. Why in
> this function need this?
> Does mtk hdmi driver has a bug?
>
I don't know, but I will be asking around.
> > +
> > +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> > +       usleep_range(2000, 5000);
> > +
> > +       if (mtk_dp_plug_state(mtk_dp))
> > +               new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> > +
> > +       if (!enabled)
> > +               drm_bridge_chain_post_disable(bridge);
> > +
> > +       mutex_lock(&mtk_dp->edid_lock);
> > +       kfree(mtk_dp->edid);
> > +       mtk_dp->edid = NULL;
> > +
> > +       if (!new_edid) {
> > +               mutex_unlock(&mtk_dp->edid_lock);
> > +               return NULL;
> > +       }
> > +
> > +       mtk_dp->edid = drm_edid_duplicate(new_edid);
> > +       mutex_unlock(&mtk_dp->edid_lock);
> > +
> > +       return new_edid;
> > +}
> > +
> > +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> > +                                  struct drm_dp_aux_msg *msg)
> > +{
> > +       ssize_t err = -EAGAIN;
> > +       struct mtk_dp *mtk_dp;
> > +       bool is_read;
> > +       u8 request;
> > +       size_t accessed_bytes = 0;
> > +       int retry = 3, ret = 0;
> > +
> > +       mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> > +
> > +       if (!mtk_dp->train_info.cable_plugged_in ||
> > +           mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> > +               mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> > +               err = -EAGAIN;
> > +               goto err;
> > +       }
> > +
> > +       switch (msg->request) {
> > +       case DP_AUX_I2C_MOT:
> > +       case DP_AUX_I2C_WRITE:
> > +       case DP_AUX_NATIVE_WRITE:
> > +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> > +       case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> > +               request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> > +               is_read = false;
> > +               break;
> > +       case DP_AUX_I2C_READ:
> > +       case DP_AUX_NATIVE_READ:
> > +       case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> > +               request = msg->request;
> > +               is_read = true;
> > +               break;
> > +       default:
> > +               drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> > +                       msg->request);
> > +               err = -EINVAL;
> > +               goto err;
>
> Directly return here.
>
Hmm, not sure yet, it seems I still need to prime msg->reply anyway.
Though the error handling can be simplified a bit.

> > +       }
> > +
> > +       if (msg->size == 0) {
> > +               mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> > +                                      msg->address + accessed_bytes,
> > +                                      msg->buffer + accessed_bytes, 0);
> > +       } else {
> > +               while (accessed_bytes < msg->size) {
> > +                       size_t to_access =
> > +                               min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> > +                                     msg->size - accessed_bytes);
> > +                       retry = 3;
> > +                       while (retry--) {
> > +                               ret = mtk_dp_aux_do_transfer(
> > +                                       mtk_dp, is_read, request,
> > +                                       msg->address + accessed_bytes,
> > +                                       msg->buffer + accessed_bytes,
> > +                                       to_access);
> > +                               if (ret == 0)
> > +                                       break;
> > +                               udelay(50);
> > +                       }
> > +                       if (!retry && ret) {
> > +                               drm_info(mtk_dp->drm_dev,
> > +                                        "Failed to do AUX transfer: %d\n",
> > +                                        ret);
> > +                               break;
> > +                       }
> > +                       accessed_bytes += to_access;
> > +               }
> > +       }
> > +err:
> > +       if (!ret) {
> > +               msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > +               ret = msg->size;
> > +       } else {
> > +               msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> > +               return err;
> > +       }
> > +
> > +       msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > +       return msg->size;
> > +}
> > +
> > +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> > +{
> > +       drm_dp_aux_init(&mtk_dp->aux);
> > +       mtk_dp->aux.name = "aux_mtk_dp";
> > +       mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> > +}
> > +
> > +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> > +{
> > +       mutex_lock(&mtk_dp->dp_lock);
> > +
> > +       mtk_dp_hwirq_enable(mtk_dp, false);
> > +       mtk_dp_power_disable(mtk_dp);
> > +       phy_exit(mtk_dp->phy);
> > +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > +
> > +       mutex_unlock(&mtk_dp->dp_lock);
> > +}
> > +
> > +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> > +{
> > +       int ret = 0;
> > +
> > +       mutex_lock(&mtk_dp->dp_lock);
> > +
> > +       ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> > +       if (ret < 0) {
> > +               dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> > +               goto err;
> > +       }
> > +       ret = phy_init(mtk_dp->phy);
> > +       if (ret) {
> > +               dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> > +               goto err_phy_init;
> > +       }
> > +       ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> > +       if (ret) {
> > +               dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> > +               goto err_phy_config;
> > +       }
> > +
> > +       mtk_dp_init_port(mtk_dp);
> > +       mtk_dp_power_enable(mtk_dp);
> > +       mtk_dp_hwirq_enable(mtk_dp, true);
> > +
> > +err_phy_config:
> > +       phy_exit(mtk_dp->phy);
> > +err_phy_init:
> > +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > +err:
> > +       mutex_unlock(&mtk_dp->dp_lock);
> > +       return ret;
> > +}
> > +
> > +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> > +                               enum drm_bridge_attach_flags flags)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +       int ret;
> > +
> > +       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> > +               dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> > +               return -EINVAL;
> > +       }
> > +
> > +       ret = mtk_dp_poweron(mtk_dp);
>
> Move the power on to mtk_dp_bridge_atomic_enable().
>
> > +       if (ret)
> > +               return ret;
> > +
> > +       if (mtk_dp->next_bridge) {
> > +               ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> > +                                       &mtk_dp->bridge, flags);
> > +               if (ret) {
> > +                       drm_warn(mtk_dp->drm_dev,
> > +                                "Failed to attach external bridge: %d\n", ret);
> > +                       goto err_bridge_attach;
> > +               }
> > +       }
> > +
> > +       mtk_dp->drm_dev = bridge->dev;
> > +
> > +       return 0;
> > +
> > +err_bridge_attach:
> > +       mtk_dp_poweroff(mtk_dp);
> > +       return ret;
> > +}
> > +
> > +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +
> > +       mtk_dp->drm_dev = NULL;
> > +
> > +       mtk_dp_poweroff(mtk_dp);
> > +}
> > +
> > +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> > +                                        struct drm_bridge_state *old_state)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +
> > +       mtk_dp_video_mute(mtk_dp, true);
> > +       mtk_dp_audio_mute(mtk_dp, true);
> > +       mtk_dp->state = MTK_DP_STATE_IDLE;
> > +       mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > +
> > +       mtk_dp->enabled = false;
> > +       msleep(100);
> > +}
> > +
> > +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> > +                                         struct drm_display_mode *mode)
> > +{
> > +       struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> > +
> > +       drm_display_mode_to_videomode(mode, &timings->vm);
> > +       timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> > +       timings->htotal = mode->htotal;
> > +       timings->vtotal = mode->vtotal;
> > +}
> > +
> > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > +                                       struct drm_bridge_state *old_state)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +       struct drm_connector *conn;
> > +       struct drm_connector_state *conn_state;
> > +       struct drm_crtc *crtc;
> > +       struct drm_crtc_state *crtc_state;
> > +       int ret = 0;
> > +       int i;
> > +
> > +       conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > +                                                       bridge->encoder);
>
> mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
>                                                        bridge->encoder);
>
The connector doesn't seem to be used anywhere else in the driver.
Though I can add a drm_connector in the mtk_dp struct.

> > +       if (!conn) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as connector is missing\n");
> > +               return;
> > +       }
> > +
> > +       memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > +
> > +       conn_state =
> > +               drm_atomic_get_new_connector_state(old_state->base.state, conn);
> > +       if (!conn_state) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as connector state is missing\n");
> > +               return;
> > +       }
> > +
> > +       crtc = conn_state->crtc;
> > +       if (!crtc) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as connector state doesn't have a crtc\n");
> > +               return;
> > +       }
> > +
> > +       crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> > +       if (!crtc_state) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as crtc state is missing\n");
> > +               return;
> > +       }
> > +
> > +       mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);
>
> Refer to mtk_hdmi_bridge_atomic_enable() for getting the mode.
>
I'm sorry, I don't understand what I'm supposed to do here.
Could you elaborate?

> > +       if (!mtk_dp_parse_capabilities(mtk_dp)) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as nothing is plugged in\n");
> > +               return;
> > +       }
> > +
> > +       //training
>
> Run checkpatch first.
>
> Regards,
> Chun-Kuang.
>
> > +       for (i = 0; i < 50; i++) {
> > +               ret = mtk_dp_train_handler(mtk_dp);
> > +               if (ret) {
> > +                       drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> > +                               ret);
> > +                       return;
> > +               }
> > +
> > +               ret = mtk_dp_state_handler(mtk_dp);
> > +               if (ret) {
> > +                       drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> > +                               ret);
> > +                       return;
> > +               }
> > +       }
> > +
> > +       mtk_dp->enabled = true;
> > +       mtk_dp_update_plugged_status(mtk_dp);
> > +}
> > +
>
>
>
> > --
> > 2.32.0
> >

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-02 15:31       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-02 15:31 UTC (permalink / raw)
  To: Chun-Kuang Hu
  Cc: David Airlie, Thomas Zimmermann, linux-kernel,
	Markus Schneider-Pargmann, ARM/Mediatek SoC support,
	DRI Development, Matthias Brugger, Linux ARM, kernel test robot

Hi Chun-Kuang.

Quoting Chun-Kuang Hu (2021-11-25 16:27:45)
> Hi, Guillaume:
>
> This is a big patch, so I give some comment first.
>
> Guillaume Ranquet <granquet@baylibre.com> 於 2021年11月10日 週三 下午9:06寫道:
> >
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > according phy driver mediatek-dp-phy.
> >
> > It supports both functional units on the mt8195, the embedded
> > DisplayPort as well as the external DisplayPort units. It offers
> > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > to 4 lanes.
> >
> > The driver creates a child device for the phy. The child device will
> > never exist without the parent being active. As they are sharing a
> > register range, the parent passes a regmap pointer to the child so that
> > both can work with the same register range. The phy driver sets device
> > data that is read by the parent to get the phy device that can be used
> > to control the phy properties.
> >
> > This driver is based on an initial version by
> > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > Reported-by: kernel test robot <lkp@intel.com>
> > ---
> >  drivers/gpu/drm/drm_edid.c              |    2 +-
>
> Separate this to another patch.
>
> >  drivers/gpu/drm/mediatek/Kconfig        |    7 +
> >  drivers/gpu/drm/mediatek/Makefile       |    2 +
> >  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
> >  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
> >  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
>
> Ditto.
>
> >  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
>
> Ditto.
>
> >  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
>
> Ditto
>
> >  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
>
> Ditto
>
yes my bad, I've made a bunch of fixup which ended up in the wrong place.
It will be fixed for the next version.

> >  9 files changed, 3799 insertions(+), 13 deletions(-)
> >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
> >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> >
> > diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> > index 500279a82167a..bfd98b50ceb5b 100644
> > --- a/drivers/gpu/drm/drm_edid.c
> > +++ b/drivers/gpu/drm/drm_edid.c
> > @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
> >          * modes and forbids YCRCB422 support for all video modes per
> >          * HDMI 1.3 spec.
> >          */
> > -       info->color_formats = DRM_COLOR_FORMAT_RGB444;
> > +       info->color_formats |= DRM_COLOR_FORMAT_RGB444;
> >
> >         /* YCRCB444 is optional according to spec. */
> >         if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
> > diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> > index 2976d21e9a34a..029b94c716131 100644
> > --- a/drivers/gpu/drm/mediatek/Kconfig
> > +++ b/drivers/gpu/drm/mediatek/Kconfig
> > @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
> >         select PHY_MTK_HDMI
> >         help
> >           DRM/KMS HDMI driver for Mediatek SoCs
> > +
> > +config MTK_DPTX_SUPPORT
> > +       tristate "DRM DPTX Support for Mediatek SoCs"
> > +       depends on DRM_MEDIATEK
> > +       select PHY_MTK_DP
> > +       help
> > +         DRM/KMS Display Port driver for Mediatek SoCs.
> > diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> > index 29098d7c8307c..d86a6406055e6 100644
> > --- a/drivers/gpu/drm/mediatek/Makefile
> > +++ b/drivers/gpu/drm/mediatek/Makefile
> > @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
> >                           mtk_hdmi_ddc.o
> >
> >  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> > +
> > +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> > new file mode 100644
> > index 0000000000000..83087219d5a5e
> > --- /dev/null
> > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> > @@ -0,0 +1,3094 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2019 MediaTek Inc.
> > + * Copyright (c) 2021 BayLibre
> > + */
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_bridge.h>
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_dp_helper.h>
> > +#include <drm/drm_edid.h>
> > +#include <drm/drm_of.h>
> > +#include <drm/drm_panel.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/drm_probe_helper.h>
> > +#include <linux/arm-smccc.h>
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/errno.h>
> > +#include <linux/kernel.h>
> > +#include <linux/nvmem-consumer.h>
> > +#include <linux/of.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <sound/hdmi-codec.h>
> > +#include <video/videomode.h>
> > +
> > +#include "mtk_dp_reg.h"
> > +
> > +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> > +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> > +
> > +#define MTK_DP_MAX_LANES 4
> > +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> > +
> > +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> > +
> > +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> > +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> > +
> > +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> > +
> > +#define MTK_DP_DP_VERSION_11 0x11
> > +
> > +enum mtk_dp_state {
> > +       MTK_DP_STATE_INITIAL,
> > +       MTK_DP_STATE_IDLE,
> > +       MTK_DP_STATE_PREPARE,
> > +       MTK_DP_STATE_NORMAL,
> > +};
> > +
> > +enum mtk_dp_train_state {
> > +       MTK_DP_TRAIN_STATE_STARTUP = 0,
> > +       MTK_DP_TRAIN_STATE_CHECKCAP,
> > +       MTK_DP_TRAIN_STATE_CHECKEDID,
> > +       MTK_DP_TRAIN_STATE_TRAINING_PRE,
> > +       MTK_DP_TRAIN_STATE_TRAINING,
> > +       MTK_DP_TRAIN_STATE_CHECKTIMING,
> > +       MTK_DP_TRAIN_STATE_NORMAL,
> > +       MTK_DP_TRAIN_STATE_POWERSAVE,
> > +       MTK_DP_TRAIN_STATE_DPIDLE,
> > +};
> > +
> > +struct mtk_dp_timings {
> > +       struct videomode vm;
> > +
> > +       u16 htotal;
> > +       u16 vtotal;
> > +       u8 frame_rate;
> > +       u32 pix_rate_khz;
> > +};
> > +
> > +struct mtk_dp_train_info {
> > +       bool tps3;
> > +       bool tps4;
> > +       bool sink_ssc;
> > +       bool cable_plugged_in;
> > +       bool cable_state_change;
> > +       bool cr_done;
> > +       bool eq_done;
> > +
> > +       // link_rate is in multiple of 0.27Gbps
> > +       int link_rate;
> > +       int lane_count;
> > +
> > +       int irq_status;
> > +       int check_cap_count;
> > +};
> > +
> > +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> > +enum mtk_dp_color_format {
> > +       MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> > +       MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> > +       MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> > +       MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> > +       MTK_DP_COLOR_FORMAT_YONLY = 4,
> > +       MTK_DP_COLOR_FORMAT_RAW = 5,
> > +       MTK_DP_COLOR_FORMAT_RESERVED = 6,
> > +       MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> > +       MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> > +};
> > +
> > +// Multiple of 0.27Gbps
> > +enum mtk_dp_linkrate {
> > +       MTK_DP_LINKRATE_RBR = 0x6,
> > +       MTK_DP_LINKRATE_HBR = 0xA,
> > +       MTK_DP_LINKRATE_HBR2 = 0x14,
> > +       MTK_DP_LINKRATE_HBR25 = 0x19,
> > +       MTK_DP_LINKRATE_HBR3 = 0x1E,
> > +};
> > +
> > +// Same values as used for DP Spec MISC0 bits 5,6,7
> > +enum mtk_dp_color_depth {
> > +       MTK_DP_COLOR_DEPTH_6BIT = 0,
> > +       MTK_DP_COLOR_DEPTH_8BIT = 1,
> > +       MTK_DP_COLOR_DEPTH_10BIT = 2,
> > +       MTK_DP_COLOR_DEPTH_12BIT = 3,
> > +       MTK_DP_COLOR_DEPTH_16BIT = 4,
> > +       MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> > +};
> > +
> > +struct mtk_dp_audio_cfg {
> > +       int sample_rate;
> > +       int word_length_bits;
> > +       int channels;
> > +};
> > +
> > +struct mtk_dp_info {
> > +       enum mtk_dp_color_depth depth;
> > +       enum mtk_dp_color_format format;
> > +       struct mtk_dp_audio_cfg audio_caps;
> > +       struct mtk_dp_timings timings;
> > +};
> > +
> > +struct dp_cal_data {
> > +       unsigned int glb_bias_trim;
> > +       unsigned int clktx_impse;
> > +
> > +       unsigned int ln0_tx_impsel_pmos;
> > +       unsigned int ln0_tx_impsel_nmos;
> > +       unsigned int ln1_tx_impsel_pmos;
> > +       unsigned int ln1_tx_impsel_nmos;
> > +       unsigned int ln2_tx_impsel_pmos;
> > +       unsigned int ln2_tx_impsel_nmos;
> > +       unsigned int ln3_tx_impsel_pmos;
> > +       unsigned int ln3_tx_impsel_nmos;
> > +};
> > +
> > +struct mtk_dp {
> > +       struct device *dev;
> > +       struct platform_device *phy_dev;
> > +       struct phy *phy;
> > +       struct dp_cal_data cal_data;
> > +
> > +       struct drm_device *drm_dev;
> > +       struct drm_bridge bridge;
> > +       struct drm_bridge *next_bridge;
> > +       struct drm_dp_aux aux;
> > +
> > +       struct mutex edid_lock;
> > +       struct edid *edid;
> > +
> > +       u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> > +
> > +       struct mtk_dp_info info;
> > +       enum mtk_dp_state state;
> > +
> > +       struct mtk_dp_train_info train_info;
> > +       enum mtk_dp_train_state train_state;
> > +       unsigned int input_fmt;
> > +
> > +       struct regmap *regs;
> > +       struct clk *dp_tx_clk;
> > +
> > +       bool enabled;
> > +       bool audio_enable;
> > +
> > +       bool has_fec;
> > +       struct mutex dp_lock;
> > +
> > +       struct mutex update_plugged_status_lock;
> > +
> > +       hdmi_codec_plugged_cb plugged_cb;
> > +       struct device *codec_dev;
> > +       u8 connector_eld[MAX_ELD_BYTES];
> > +};
> > +
> > +enum mtk_dp_sdp_type {
> > +       MTK_DP_SDP_NONE = 0x00,
> > +       MTK_DP_SDP_ACM = 0x01,
> > +       MTK_DP_SDP_ISRC = 0x02,
> > +       MTK_DP_SDP_AVI = 0x03,
> > +       MTK_DP_SDP_AUI = 0x04,
> > +       MTK_DP_SDP_SPD = 0x05,
> > +       MTK_DP_SDP_MPEG = 0x06,
> > +       MTK_DP_SDP_NTSC = 0x07,
> > +       MTK_DP_SDP_VSP = 0x08,
> > +       MTK_DP_SDP_VSC = 0x09,
> > +       MTK_DP_SDP_EXT = 0x0A,
> > +       MTK_DP_SDP_PPS0 = 0x0B,
> > +       MTK_DP_SDP_PPS1 = 0x0C,
> > +       MTK_DP_SDP_PPS2 = 0x0D,
> > +       MTK_DP_SDP_PPS3 = 0x0E,
> > +       MTK_DP_SDP_DRM = 0x10,
> > +       MTK_DP_SDP_MAX_NUM
> > +};
> > +
> > +struct mtk_dp_sdp_packet {
> > +       enum mtk_dp_sdp_type type;
> > +       struct dp_sdp sdp;
> > +};
> > +
> > +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> > +union mtk_dp_audio_channel_status {
> > +       struct {
> > +               u8 rev : 1;
> > +               u8 is_lpcm : 1;
> > +               u8 copy_right : 1;
> > +               u8 additional_format_info : 3;
> > +               u8 channel_status_mode : 2;
> > +               u8 category_code;
> > +               u8 src_num : 4;
> > +               u8 channel_num : 4;
> > +               u8 sampling_freq : 4;
> > +               u8 clk_accuracy : 2;
> > +               u8 rev2 : 2;
> > +               u8 word_len : 4;
> > +               u8 original_sampling_freq : 4;
> > +       } iec;
> > +
> > +       u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> > +};
> > +
> > +static struct regmap_config mtk_dp_regmap_config = {
> > +       .reg_bits = 32,
> > +       .val_bits = 32,
> > +       .reg_stride = 4,
> > +       .max_register = SEC_OFFSET + 0x90,
> > +       .name = "mtk-dp-registers",
> > +};
> > +
> > +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
>
> Separate edp and displayport into two patches. For example, the first
> patch support edp only, and the second patch add displayport function.
>
I have made the opposite as it seemed a bit more logical from how it's coded
now.
I've made a single patch with DP and a new one adding eDP support on top of it.
The change is quite small, adding the eDP on top of DP:
 drivers/gpu/drm/mediatek/mtk_dp.c | 103
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
 1 file changed, 74 insertions(+), 29 deletions(-)
> > +{
> > +       return mtk_dp->next_bridge != NULL;
> > +}
> > +
>
> [snip]
>
> > +
> > +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> > +                                   struct drm_connector *connector)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +       bool enabled = mtk_dp->enabled;
> > +       struct edid *new_edid = NULL;
> > +
> > +       if (!enabled)
> > +               drm_bridge_chain_pre_enable(bridge);
>
> In mtk_hdmi_bridge_get_edid(), there does not check the power. Why in
> this function need this?
> Does mtk hdmi driver has a bug?
>
I don't know, but I will be asking around.
> > +
> > +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> > +       usleep_range(2000, 5000);
> > +
> > +       if (mtk_dp_plug_state(mtk_dp))
> > +               new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> > +
> > +       if (!enabled)
> > +               drm_bridge_chain_post_disable(bridge);
> > +
> > +       mutex_lock(&mtk_dp->edid_lock);
> > +       kfree(mtk_dp->edid);
> > +       mtk_dp->edid = NULL;
> > +
> > +       if (!new_edid) {
> > +               mutex_unlock(&mtk_dp->edid_lock);
> > +               return NULL;
> > +       }
> > +
> > +       mtk_dp->edid = drm_edid_duplicate(new_edid);
> > +       mutex_unlock(&mtk_dp->edid_lock);
> > +
> > +       return new_edid;
> > +}
> > +
> > +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> > +                                  struct drm_dp_aux_msg *msg)
> > +{
> > +       ssize_t err = -EAGAIN;
> > +       struct mtk_dp *mtk_dp;
> > +       bool is_read;
> > +       u8 request;
> > +       size_t accessed_bytes = 0;
> > +       int retry = 3, ret = 0;
> > +
> > +       mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> > +
> > +       if (!mtk_dp->train_info.cable_plugged_in ||
> > +           mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> > +               mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> > +               err = -EAGAIN;
> > +               goto err;
> > +       }
> > +
> > +       switch (msg->request) {
> > +       case DP_AUX_I2C_MOT:
> > +       case DP_AUX_I2C_WRITE:
> > +       case DP_AUX_NATIVE_WRITE:
> > +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> > +       case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> > +               request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> > +               is_read = false;
> > +               break;
> > +       case DP_AUX_I2C_READ:
> > +       case DP_AUX_NATIVE_READ:
> > +       case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> > +               request = msg->request;
> > +               is_read = true;
> > +               break;
> > +       default:
> > +               drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> > +                       msg->request);
> > +               err = -EINVAL;
> > +               goto err;
>
> Directly return here.
>
Hmm, not sure yet, it seems I still need to prime msg->reply anyway.
Though the error handling can be simplified a bit.

> > +       }
> > +
> > +       if (msg->size == 0) {
> > +               mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> > +                                      msg->address + accessed_bytes,
> > +                                      msg->buffer + accessed_bytes, 0);
> > +       } else {
> > +               while (accessed_bytes < msg->size) {
> > +                       size_t to_access =
> > +                               min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> > +                                     msg->size - accessed_bytes);
> > +                       retry = 3;
> > +                       while (retry--) {
> > +                               ret = mtk_dp_aux_do_transfer(
> > +                                       mtk_dp, is_read, request,
> > +                                       msg->address + accessed_bytes,
> > +                                       msg->buffer + accessed_bytes,
> > +                                       to_access);
> > +                               if (ret == 0)
> > +                                       break;
> > +                               udelay(50);
> > +                       }
> > +                       if (!retry && ret) {
> > +                               drm_info(mtk_dp->drm_dev,
> > +                                        "Failed to do AUX transfer: %d\n",
> > +                                        ret);
> > +                               break;
> > +                       }
> > +                       accessed_bytes += to_access;
> > +               }
> > +       }
> > +err:
> > +       if (!ret) {
> > +               msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > +               ret = msg->size;
> > +       } else {
> > +               msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> > +               return err;
> > +       }
> > +
> > +       msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > +       return msg->size;
> > +}
> > +
> > +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> > +{
> > +       drm_dp_aux_init(&mtk_dp->aux);
> > +       mtk_dp->aux.name = "aux_mtk_dp";
> > +       mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> > +}
> > +
> > +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> > +{
> > +       mutex_lock(&mtk_dp->dp_lock);
> > +
> > +       mtk_dp_hwirq_enable(mtk_dp, false);
> > +       mtk_dp_power_disable(mtk_dp);
> > +       phy_exit(mtk_dp->phy);
> > +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > +
> > +       mutex_unlock(&mtk_dp->dp_lock);
> > +}
> > +
> > +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> > +{
> > +       int ret = 0;
> > +
> > +       mutex_lock(&mtk_dp->dp_lock);
> > +
> > +       ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> > +       if (ret < 0) {
> > +               dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> > +               goto err;
> > +       }
> > +       ret = phy_init(mtk_dp->phy);
> > +       if (ret) {
> > +               dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> > +               goto err_phy_init;
> > +       }
> > +       ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> > +       if (ret) {
> > +               dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> > +               goto err_phy_config;
> > +       }
> > +
> > +       mtk_dp_init_port(mtk_dp);
> > +       mtk_dp_power_enable(mtk_dp);
> > +       mtk_dp_hwirq_enable(mtk_dp, true);
> > +
> > +err_phy_config:
> > +       phy_exit(mtk_dp->phy);
> > +err_phy_init:
> > +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > +err:
> > +       mutex_unlock(&mtk_dp->dp_lock);
> > +       return ret;
> > +}
> > +
> > +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> > +                               enum drm_bridge_attach_flags flags)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +       int ret;
> > +
> > +       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> > +               dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> > +               return -EINVAL;
> > +       }
> > +
> > +       ret = mtk_dp_poweron(mtk_dp);
>
> Move the power on to mtk_dp_bridge_atomic_enable().
>
> > +       if (ret)
> > +               return ret;
> > +
> > +       if (mtk_dp->next_bridge) {
> > +               ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> > +                                       &mtk_dp->bridge, flags);
> > +               if (ret) {
> > +                       drm_warn(mtk_dp->drm_dev,
> > +                                "Failed to attach external bridge: %d\n", ret);
> > +                       goto err_bridge_attach;
> > +               }
> > +       }
> > +
> > +       mtk_dp->drm_dev = bridge->dev;
> > +
> > +       return 0;
> > +
> > +err_bridge_attach:
> > +       mtk_dp_poweroff(mtk_dp);
> > +       return ret;
> > +}
> > +
> > +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +
> > +       mtk_dp->drm_dev = NULL;
> > +
> > +       mtk_dp_poweroff(mtk_dp);
> > +}
> > +
> > +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> > +                                        struct drm_bridge_state *old_state)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +
> > +       mtk_dp_video_mute(mtk_dp, true);
> > +       mtk_dp_audio_mute(mtk_dp, true);
> > +       mtk_dp->state = MTK_DP_STATE_IDLE;
> > +       mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > +
> > +       mtk_dp->enabled = false;
> > +       msleep(100);
> > +}
> > +
> > +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> > +                                         struct drm_display_mode *mode)
> > +{
> > +       struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> > +
> > +       drm_display_mode_to_videomode(mode, &timings->vm);
> > +       timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> > +       timings->htotal = mode->htotal;
> > +       timings->vtotal = mode->vtotal;
> > +}
> > +
> > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > +                                       struct drm_bridge_state *old_state)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +       struct drm_connector *conn;
> > +       struct drm_connector_state *conn_state;
> > +       struct drm_crtc *crtc;
> > +       struct drm_crtc_state *crtc_state;
> > +       int ret = 0;
> > +       int i;
> > +
> > +       conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > +                                                       bridge->encoder);
>
> mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
>                                                        bridge->encoder);
>
The connector doesn't seem to be used anywhere else in the driver.
Though I can add a drm_connector in the mtk_dp struct.

> > +       if (!conn) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as connector is missing\n");
> > +               return;
> > +       }
> > +
> > +       memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > +
> > +       conn_state =
> > +               drm_atomic_get_new_connector_state(old_state->base.state, conn);
> > +       if (!conn_state) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as connector state is missing\n");
> > +               return;
> > +       }
> > +
> > +       crtc = conn_state->crtc;
> > +       if (!crtc) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as connector state doesn't have a crtc\n");
> > +               return;
> > +       }
> > +
> > +       crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> > +       if (!crtc_state) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as crtc state is missing\n");
> > +               return;
> > +       }
> > +
> > +       mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);
>
> Refer to mtk_hdmi_bridge_atomic_enable() for getting the mode.
>
I'm sorry, I don't understand what I'm supposed to do here.
Could you elaborate?

> > +       if (!mtk_dp_parse_capabilities(mtk_dp)) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as nothing is plugged in\n");
> > +               return;
> > +       }
> > +
> > +       //training
>
> Run checkpatch first.
>
> Regards,
> Chun-Kuang.
>
> > +       for (i = 0; i < 50; i++) {
> > +               ret = mtk_dp_train_handler(mtk_dp);
> > +               if (ret) {
> > +                       drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> > +                               ret);
> > +                       return;
> > +               }
> > +
> > +               ret = mtk_dp_state_handler(mtk_dp);
> > +               if (ret) {
> > +                       drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> > +                               ret);
> > +                       return;
> > +               }
> > +       }
> > +
> > +       mtk_dp->enabled = true;
> > +       mtk_dp_update_plugged_status(mtk_dp);
> > +}
> > +
>
>
>
> > --
> > 2.32.0
> >

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-02 15:31       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-02 15:31 UTC (permalink / raw)
  To: Chun-Kuang Hu
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	DRI Development, ARM/Mediatek SoC support, Linux ARM

Hi Chun-Kuang.

Quoting Chun-Kuang Hu (2021-11-25 16:27:45)
> Hi, Guillaume:
>
> This is a big patch, so I give some comment first.
>
> Guillaume Ranquet <granquet@baylibre.com> 於 2021年11月10日 週三 下午9:06寫道:
> >
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > according phy driver mediatek-dp-phy.
> >
> > It supports both functional units on the mt8195, the embedded
> > DisplayPort as well as the external DisplayPort units. It offers
> > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > to 4 lanes.
> >
> > The driver creates a child device for the phy. The child device will
> > never exist without the parent being active. As they are sharing a
> > register range, the parent passes a regmap pointer to the child so that
> > both can work with the same register range. The phy driver sets device
> > data that is read by the parent to get the phy device that can be used
> > to control the phy properties.
> >
> > This driver is based on an initial version by
> > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > Reported-by: kernel test robot <lkp@intel.com>
> > ---
> >  drivers/gpu/drm/drm_edid.c              |    2 +-
>
> Separate this to another patch.
>
> >  drivers/gpu/drm/mediatek/Kconfig        |    7 +
> >  drivers/gpu/drm/mediatek/Makefile       |    2 +
> >  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
> >  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
> >  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
>
> Ditto.
>
> >  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
>
> Ditto.
>
> >  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
>
> Ditto
>
> >  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
>
> Ditto
>
yes my bad, I've made a bunch of fixup which ended up in the wrong place.
It will be fixed for the next version.

> >  9 files changed, 3799 insertions(+), 13 deletions(-)
> >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
> >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> >
> > diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> > index 500279a82167a..bfd98b50ceb5b 100644
> > --- a/drivers/gpu/drm/drm_edid.c
> > +++ b/drivers/gpu/drm/drm_edid.c
> > @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
> >          * modes and forbids YCRCB422 support for all video modes per
> >          * HDMI 1.3 spec.
> >          */
> > -       info->color_formats = DRM_COLOR_FORMAT_RGB444;
> > +       info->color_formats |= DRM_COLOR_FORMAT_RGB444;
> >
> >         /* YCRCB444 is optional according to spec. */
> >         if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
> > diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> > index 2976d21e9a34a..029b94c716131 100644
> > --- a/drivers/gpu/drm/mediatek/Kconfig
> > +++ b/drivers/gpu/drm/mediatek/Kconfig
> > @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
> >         select PHY_MTK_HDMI
> >         help
> >           DRM/KMS HDMI driver for Mediatek SoCs
> > +
> > +config MTK_DPTX_SUPPORT
> > +       tristate "DRM DPTX Support for Mediatek SoCs"
> > +       depends on DRM_MEDIATEK
> > +       select PHY_MTK_DP
> > +       help
> > +         DRM/KMS Display Port driver for Mediatek SoCs.
> > diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> > index 29098d7c8307c..d86a6406055e6 100644
> > --- a/drivers/gpu/drm/mediatek/Makefile
> > +++ b/drivers/gpu/drm/mediatek/Makefile
> > @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
> >                           mtk_hdmi_ddc.o
> >
> >  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> > +
> > +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> > new file mode 100644
> > index 0000000000000..83087219d5a5e
> > --- /dev/null
> > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> > @@ -0,0 +1,3094 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2019 MediaTek Inc.
> > + * Copyright (c) 2021 BayLibre
> > + */
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_bridge.h>
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_dp_helper.h>
> > +#include <drm/drm_edid.h>
> > +#include <drm/drm_of.h>
> > +#include <drm/drm_panel.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/drm_probe_helper.h>
> > +#include <linux/arm-smccc.h>
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/errno.h>
> > +#include <linux/kernel.h>
> > +#include <linux/nvmem-consumer.h>
> > +#include <linux/of.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <sound/hdmi-codec.h>
> > +#include <video/videomode.h>
> > +
> > +#include "mtk_dp_reg.h"
> > +
> > +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> > +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> > +
> > +#define MTK_DP_MAX_LANES 4
> > +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> > +
> > +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> > +
> > +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> > +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> > +
> > +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> > +
> > +#define MTK_DP_DP_VERSION_11 0x11
> > +
> > +enum mtk_dp_state {
> > +       MTK_DP_STATE_INITIAL,
> > +       MTK_DP_STATE_IDLE,
> > +       MTK_DP_STATE_PREPARE,
> > +       MTK_DP_STATE_NORMAL,
> > +};
> > +
> > +enum mtk_dp_train_state {
> > +       MTK_DP_TRAIN_STATE_STARTUP = 0,
> > +       MTK_DP_TRAIN_STATE_CHECKCAP,
> > +       MTK_DP_TRAIN_STATE_CHECKEDID,
> > +       MTK_DP_TRAIN_STATE_TRAINING_PRE,
> > +       MTK_DP_TRAIN_STATE_TRAINING,
> > +       MTK_DP_TRAIN_STATE_CHECKTIMING,
> > +       MTK_DP_TRAIN_STATE_NORMAL,
> > +       MTK_DP_TRAIN_STATE_POWERSAVE,
> > +       MTK_DP_TRAIN_STATE_DPIDLE,
> > +};
> > +
> > +struct mtk_dp_timings {
> > +       struct videomode vm;
> > +
> > +       u16 htotal;
> > +       u16 vtotal;
> > +       u8 frame_rate;
> > +       u32 pix_rate_khz;
> > +};
> > +
> > +struct mtk_dp_train_info {
> > +       bool tps3;
> > +       bool tps4;
> > +       bool sink_ssc;
> > +       bool cable_plugged_in;
> > +       bool cable_state_change;
> > +       bool cr_done;
> > +       bool eq_done;
> > +
> > +       // link_rate is in multiple of 0.27Gbps
> > +       int link_rate;
> > +       int lane_count;
> > +
> > +       int irq_status;
> > +       int check_cap_count;
> > +};
> > +
> > +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> > +enum mtk_dp_color_format {
> > +       MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> > +       MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> > +       MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> > +       MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> > +       MTK_DP_COLOR_FORMAT_YONLY = 4,
> > +       MTK_DP_COLOR_FORMAT_RAW = 5,
> > +       MTK_DP_COLOR_FORMAT_RESERVED = 6,
> > +       MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> > +       MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> > +};
> > +
> > +// Multiple of 0.27Gbps
> > +enum mtk_dp_linkrate {
> > +       MTK_DP_LINKRATE_RBR = 0x6,
> > +       MTK_DP_LINKRATE_HBR = 0xA,
> > +       MTK_DP_LINKRATE_HBR2 = 0x14,
> > +       MTK_DP_LINKRATE_HBR25 = 0x19,
> > +       MTK_DP_LINKRATE_HBR3 = 0x1E,
> > +};
> > +
> > +// Same values as used for DP Spec MISC0 bits 5,6,7
> > +enum mtk_dp_color_depth {
> > +       MTK_DP_COLOR_DEPTH_6BIT = 0,
> > +       MTK_DP_COLOR_DEPTH_8BIT = 1,
> > +       MTK_DP_COLOR_DEPTH_10BIT = 2,
> > +       MTK_DP_COLOR_DEPTH_12BIT = 3,
> > +       MTK_DP_COLOR_DEPTH_16BIT = 4,
> > +       MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> > +};
> > +
> > +struct mtk_dp_audio_cfg {
> > +       int sample_rate;
> > +       int word_length_bits;
> > +       int channels;
> > +};
> > +
> > +struct mtk_dp_info {
> > +       enum mtk_dp_color_depth depth;
> > +       enum mtk_dp_color_format format;
> > +       struct mtk_dp_audio_cfg audio_caps;
> > +       struct mtk_dp_timings timings;
> > +};
> > +
> > +struct dp_cal_data {
> > +       unsigned int glb_bias_trim;
> > +       unsigned int clktx_impse;
> > +
> > +       unsigned int ln0_tx_impsel_pmos;
> > +       unsigned int ln0_tx_impsel_nmos;
> > +       unsigned int ln1_tx_impsel_pmos;
> > +       unsigned int ln1_tx_impsel_nmos;
> > +       unsigned int ln2_tx_impsel_pmos;
> > +       unsigned int ln2_tx_impsel_nmos;
> > +       unsigned int ln3_tx_impsel_pmos;
> > +       unsigned int ln3_tx_impsel_nmos;
> > +};
> > +
> > +struct mtk_dp {
> > +       struct device *dev;
> > +       struct platform_device *phy_dev;
> > +       struct phy *phy;
> > +       struct dp_cal_data cal_data;
> > +
> > +       struct drm_device *drm_dev;
> > +       struct drm_bridge bridge;
> > +       struct drm_bridge *next_bridge;
> > +       struct drm_dp_aux aux;
> > +
> > +       struct mutex edid_lock;
> > +       struct edid *edid;
> > +
> > +       u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> > +
> > +       struct mtk_dp_info info;
> > +       enum mtk_dp_state state;
> > +
> > +       struct mtk_dp_train_info train_info;
> > +       enum mtk_dp_train_state train_state;
> > +       unsigned int input_fmt;
> > +
> > +       struct regmap *regs;
> > +       struct clk *dp_tx_clk;
> > +
> > +       bool enabled;
> > +       bool audio_enable;
> > +
> > +       bool has_fec;
> > +       struct mutex dp_lock;
> > +
> > +       struct mutex update_plugged_status_lock;
> > +
> > +       hdmi_codec_plugged_cb plugged_cb;
> > +       struct device *codec_dev;
> > +       u8 connector_eld[MAX_ELD_BYTES];
> > +};
> > +
> > +enum mtk_dp_sdp_type {
> > +       MTK_DP_SDP_NONE = 0x00,
> > +       MTK_DP_SDP_ACM = 0x01,
> > +       MTK_DP_SDP_ISRC = 0x02,
> > +       MTK_DP_SDP_AVI = 0x03,
> > +       MTK_DP_SDP_AUI = 0x04,
> > +       MTK_DP_SDP_SPD = 0x05,
> > +       MTK_DP_SDP_MPEG = 0x06,
> > +       MTK_DP_SDP_NTSC = 0x07,
> > +       MTK_DP_SDP_VSP = 0x08,
> > +       MTK_DP_SDP_VSC = 0x09,
> > +       MTK_DP_SDP_EXT = 0x0A,
> > +       MTK_DP_SDP_PPS0 = 0x0B,
> > +       MTK_DP_SDP_PPS1 = 0x0C,
> > +       MTK_DP_SDP_PPS2 = 0x0D,
> > +       MTK_DP_SDP_PPS3 = 0x0E,
> > +       MTK_DP_SDP_DRM = 0x10,
> > +       MTK_DP_SDP_MAX_NUM
> > +};
> > +
> > +struct mtk_dp_sdp_packet {
> > +       enum mtk_dp_sdp_type type;
> > +       struct dp_sdp sdp;
> > +};
> > +
> > +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> > +union mtk_dp_audio_channel_status {
> > +       struct {
> > +               u8 rev : 1;
> > +               u8 is_lpcm : 1;
> > +               u8 copy_right : 1;
> > +               u8 additional_format_info : 3;
> > +               u8 channel_status_mode : 2;
> > +               u8 category_code;
> > +               u8 src_num : 4;
> > +               u8 channel_num : 4;
> > +               u8 sampling_freq : 4;
> > +               u8 clk_accuracy : 2;
> > +               u8 rev2 : 2;
> > +               u8 word_len : 4;
> > +               u8 original_sampling_freq : 4;
> > +       } iec;
> > +
> > +       u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> > +};
> > +
> > +static struct regmap_config mtk_dp_regmap_config = {
> > +       .reg_bits = 32,
> > +       .val_bits = 32,
> > +       .reg_stride = 4,
> > +       .max_register = SEC_OFFSET + 0x90,
> > +       .name = "mtk-dp-registers",
> > +};
> > +
> > +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
>
> Separate edp and displayport into two patches. For example, the first
> patch support edp only, and the second patch add displayport function.
>
I have made the opposite as it seemed a bit more logical from how it's coded
now.
I've made a single patch with DP and a new one adding eDP support on top of it.
The change is quite small, adding the eDP on top of DP:
 drivers/gpu/drm/mediatek/mtk_dp.c | 103
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
 1 file changed, 74 insertions(+), 29 deletions(-)
> > +{
> > +       return mtk_dp->next_bridge != NULL;
> > +}
> > +
>
> [snip]
>
> > +
> > +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> > +                                   struct drm_connector *connector)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +       bool enabled = mtk_dp->enabled;
> > +       struct edid *new_edid = NULL;
> > +
> > +       if (!enabled)
> > +               drm_bridge_chain_pre_enable(bridge);
>
> In mtk_hdmi_bridge_get_edid(), there does not check the power. Why in
> this function need this?
> Does mtk hdmi driver has a bug?
>
I don't know, but I will be asking around.
> > +
> > +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> > +       usleep_range(2000, 5000);
> > +
> > +       if (mtk_dp_plug_state(mtk_dp))
> > +               new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> > +
> > +       if (!enabled)
> > +               drm_bridge_chain_post_disable(bridge);
> > +
> > +       mutex_lock(&mtk_dp->edid_lock);
> > +       kfree(mtk_dp->edid);
> > +       mtk_dp->edid = NULL;
> > +
> > +       if (!new_edid) {
> > +               mutex_unlock(&mtk_dp->edid_lock);
> > +               return NULL;
> > +       }
> > +
> > +       mtk_dp->edid = drm_edid_duplicate(new_edid);
> > +       mutex_unlock(&mtk_dp->edid_lock);
> > +
> > +       return new_edid;
> > +}
> > +
> > +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> > +                                  struct drm_dp_aux_msg *msg)
> > +{
> > +       ssize_t err = -EAGAIN;
> > +       struct mtk_dp *mtk_dp;
> > +       bool is_read;
> > +       u8 request;
> > +       size_t accessed_bytes = 0;
> > +       int retry = 3, ret = 0;
> > +
> > +       mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> > +
> > +       if (!mtk_dp->train_info.cable_plugged_in ||
> > +           mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> > +               mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> > +               err = -EAGAIN;
> > +               goto err;
> > +       }
> > +
> > +       switch (msg->request) {
> > +       case DP_AUX_I2C_MOT:
> > +       case DP_AUX_I2C_WRITE:
> > +       case DP_AUX_NATIVE_WRITE:
> > +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> > +       case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> > +               request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> > +               is_read = false;
> > +               break;
> > +       case DP_AUX_I2C_READ:
> > +       case DP_AUX_NATIVE_READ:
> > +       case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> > +               request = msg->request;
> > +               is_read = true;
> > +               break;
> > +       default:
> > +               drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> > +                       msg->request);
> > +               err = -EINVAL;
> > +               goto err;
>
> Directly return here.
>
Hmm, not sure yet, it seems I still need to prime msg->reply anyway.
Though the error handling can be simplified a bit.

> > +       }
> > +
> > +       if (msg->size == 0) {
> > +               mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> > +                                      msg->address + accessed_bytes,
> > +                                      msg->buffer + accessed_bytes, 0);
> > +       } else {
> > +               while (accessed_bytes < msg->size) {
> > +                       size_t to_access =
> > +                               min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> > +                                     msg->size - accessed_bytes);
> > +                       retry = 3;
> > +                       while (retry--) {
> > +                               ret = mtk_dp_aux_do_transfer(
> > +                                       mtk_dp, is_read, request,
> > +                                       msg->address + accessed_bytes,
> > +                                       msg->buffer + accessed_bytes,
> > +                                       to_access);
> > +                               if (ret == 0)
> > +                                       break;
> > +                               udelay(50);
> > +                       }
> > +                       if (!retry && ret) {
> > +                               drm_info(mtk_dp->drm_dev,
> > +                                        "Failed to do AUX transfer: %d\n",
> > +                                        ret);
> > +                               break;
> > +                       }
> > +                       accessed_bytes += to_access;
> > +               }
> > +       }
> > +err:
> > +       if (!ret) {
> > +               msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > +               ret = msg->size;
> > +       } else {
> > +               msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> > +               return err;
> > +       }
> > +
> > +       msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > +       return msg->size;
> > +}
> > +
> > +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> > +{
> > +       drm_dp_aux_init(&mtk_dp->aux);
> > +       mtk_dp->aux.name = "aux_mtk_dp";
> > +       mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> > +}
> > +
> > +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> > +{
> > +       mutex_lock(&mtk_dp->dp_lock);
> > +
> > +       mtk_dp_hwirq_enable(mtk_dp, false);
> > +       mtk_dp_power_disable(mtk_dp);
> > +       phy_exit(mtk_dp->phy);
> > +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > +
> > +       mutex_unlock(&mtk_dp->dp_lock);
> > +}
> > +
> > +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> > +{
> > +       int ret = 0;
> > +
> > +       mutex_lock(&mtk_dp->dp_lock);
> > +
> > +       ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> > +       if (ret < 0) {
> > +               dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> > +               goto err;
> > +       }
> > +       ret = phy_init(mtk_dp->phy);
> > +       if (ret) {
> > +               dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> > +               goto err_phy_init;
> > +       }
> > +       ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> > +       if (ret) {
> > +               dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> > +               goto err_phy_config;
> > +       }
> > +
> > +       mtk_dp_init_port(mtk_dp);
> > +       mtk_dp_power_enable(mtk_dp);
> > +       mtk_dp_hwirq_enable(mtk_dp, true);
> > +
> > +err_phy_config:
> > +       phy_exit(mtk_dp->phy);
> > +err_phy_init:
> > +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > +err:
> > +       mutex_unlock(&mtk_dp->dp_lock);
> > +       return ret;
> > +}
> > +
> > +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> > +                               enum drm_bridge_attach_flags flags)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +       int ret;
> > +
> > +       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> > +               dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> > +               return -EINVAL;
> > +       }
> > +
> > +       ret = mtk_dp_poweron(mtk_dp);
>
> Move the power on to mtk_dp_bridge_atomic_enable().
>
> > +       if (ret)
> > +               return ret;
> > +
> > +       if (mtk_dp->next_bridge) {
> > +               ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> > +                                       &mtk_dp->bridge, flags);
> > +               if (ret) {
> > +                       drm_warn(mtk_dp->drm_dev,
> > +                                "Failed to attach external bridge: %d\n", ret);
> > +                       goto err_bridge_attach;
> > +               }
> > +       }
> > +
> > +       mtk_dp->drm_dev = bridge->dev;
> > +
> > +       return 0;
> > +
> > +err_bridge_attach:
> > +       mtk_dp_poweroff(mtk_dp);
> > +       return ret;
> > +}
> > +
> > +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +
> > +       mtk_dp->drm_dev = NULL;
> > +
> > +       mtk_dp_poweroff(mtk_dp);
> > +}
> > +
> > +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> > +                                        struct drm_bridge_state *old_state)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +
> > +       mtk_dp_video_mute(mtk_dp, true);
> > +       mtk_dp_audio_mute(mtk_dp, true);
> > +       mtk_dp->state = MTK_DP_STATE_IDLE;
> > +       mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > +
> > +       mtk_dp->enabled = false;
> > +       msleep(100);
> > +}
> > +
> > +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> > +                                         struct drm_display_mode *mode)
> > +{
> > +       struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> > +
> > +       drm_display_mode_to_videomode(mode, &timings->vm);
> > +       timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> > +       timings->htotal = mode->htotal;
> > +       timings->vtotal = mode->vtotal;
> > +}
> > +
> > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > +                                       struct drm_bridge_state *old_state)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +       struct drm_connector *conn;
> > +       struct drm_connector_state *conn_state;
> > +       struct drm_crtc *crtc;
> > +       struct drm_crtc_state *crtc_state;
> > +       int ret = 0;
> > +       int i;
> > +
> > +       conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > +                                                       bridge->encoder);
>
> mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
>                                                        bridge->encoder);
>
The connector doesn't seem to be used anywhere else in the driver.
Though I can add a drm_connector in the mtk_dp struct.

> > +       if (!conn) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as connector is missing\n");
> > +               return;
> > +       }
> > +
> > +       memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > +
> > +       conn_state =
> > +               drm_atomic_get_new_connector_state(old_state->base.state, conn);
> > +       if (!conn_state) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as connector state is missing\n");
> > +               return;
> > +       }
> > +
> > +       crtc = conn_state->crtc;
> > +       if (!crtc) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as connector state doesn't have a crtc\n");
> > +               return;
> > +       }
> > +
> > +       crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> > +       if (!crtc_state) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as crtc state is missing\n");
> > +               return;
> > +       }
> > +
> > +       mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);
>
> Refer to mtk_hdmi_bridge_atomic_enable() for getting the mode.
>
I'm sorry, I don't understand what I'm supposed to do here.
Could you elaborate?

> > +       if (!mtk_dp_parse_capabilities(mtk_dp)) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as nothing is plugged in\n");
> > +               return;
> > +       }
> > +
> > +       //training
>
> Run checkpatch first.
>
> Regards,
> Chun-Kuang.
>
> > +       for (i = 0; i < 50; i++) {
> > +               ret = mtk_dp_train_handler(mtk_dp);
> > +               if (ret) {
> > +                       drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> > +                               ret);
> > +                       return;
> > +               }
> > +
> > +               ret = mtk_dp_state_handler(mtk_dp);
> > +               if (ret) {
> > +                       drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> > +                               ret);
> > +                       return;
> > +               }
> > +       }
> > +
> > +       mtk_dp->enabled = true;
> > +       mtk_dp_update_plugged_status(mtk_dp);
> > +}
> > +
>
>
>
> > --
> > 2.32.0
> >

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-02 15:31       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-02 15:31 UTC (permalink / raw)
  To: Chun-Kuang Hu
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	DRI Development, ARM/Mediatek SoC support, Linux ARM

Hi Chun-Kuang.

Quoting Chun-Kuang Hu (2021-11-25 16:27:45)
> Hi, Guillaume:
>
> This is a big patch, so I give some comment first.
>
> Guillaume Ranquet <granquet@baylibre.com> 於 2021年11月10日 週三 下午9:06寫道:
> >
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > according phy driver mediatek-dp-phy.
> >
> > It supports both functional units on the mt8195, the embedded
> > DisplayPort as well as the external DisplayPort units. It offers
> > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > to 4 lanes.
> >
> > The driver creates a child device for the phy. The child device will
> > never exist without the parent being active. As they are sharing a
> > register range, the parent passes a regmap pointer to the child so that
> > both can work with the same register range. The phy driver sets device
> > data that is read by the parent to get the phy device that can be used
> > to control the phy properties.
> >
> > This driver is based on an initial version by
> > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > Reported-by: kernel test robot <lkp@intel.com>
> > ---
> >  drivers/gpu/drm/drm_edid.c              |    2 +-
>
> Separate this to another patch.
>
> >  drivers/gpu/drm/mediatek/Kconfig        |    7 +
> >  drivers/gpu/drm/mediatek/Makefile       |    2 +
> >  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
> >  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
> >  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
>
> Ditto.
>
> >  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
>
> Ditto.
>
> >  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
>
> Ditto
>
> >  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
>
> Ditto
>
yes my bad, I've made a bunch of fixup which ended up in the wrong place.
It will be fixed for the next version.

> >  9 files changed, 3799 insertions(+), 13 deletions(-)
> >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
> >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> >
> > diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> > index 500279a82167a..bfd98b50ceb5b 100644
> > --- a/drivers/gpu/drm/drm_edid.c
> > +++ b/drivers/gpu/drm/drm_edid.c
> > @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
> >          * modes and forbids YCRCB422 support for all video modes per
> >          * HDMI 1.3 spec.
> >          */
> > -       info->color_formats = DRM_COLOR_FORMAT_RGB444;
> > +       info->color_formats |= DRM_COLOR_FORMAT_RGB444;
> >
> >         /* YCRCB444 is optional according to spec. */
> >         if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
> > diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> > index 2976d21e9a34a..029b94c716131 100644
> > --- a/drivers/gpu/drm/mediatek/Kconfig
> > +++ b/drivers/gpu/drm/mediatek/Kconfig
> > @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
> >         select PHY_MTK_HDMI
> >         help
> >           DRM/KMS HDMI driver for Mediatek SoCs
> > +
> > +config MTK_DPTX_SUPPORT
> > +       tristate "DRM DPTX Support for Mediatek SoCs"
> > +       depends on DRM_MEDIATEK
> > +       select PHY_MTK_DP
> > +       help
> > +         DRM/KMS Display Port driver for Mediatek SoCs.
> > diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> > index 29098d7c8307c..d86a6406055e6 100644
> > --- a/drivers/gpu/drm/mediatek/Makefile
> > +++ b/drivers/gpu/drm/mediatek/Makefile
> > @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
> >                           mtk_hdmi_ddc.o
> >
> >  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> > +
> > +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> > new file mode 100644
> > index 0000000000000..83087219d5a5e
> > --- /dev/null
> > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> > @@ -0,0 +1,3094 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2019 MediaTek Inc.
> > + * Copyright (c) 2021 BayLibre
> > + */
> > +
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_bridge.h>
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_dp_helper.h>
> > +#include <drm/drm_edid.h>
> > +#include <drm/drm_of.h>
> > +#include <drm/drm_panel.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/drm_probe_helper.h>
> > +#include <linux/arm-smccc.h>
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/errno.h>
> > +#include <linux/kernel.h>
> > +#include <linux/nvmem-consumer.h>
> > +#include <linux/of.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <sound/hdmi-codec.h>
> > +#include <video/videomode.h>
> > +
> > +#include "mtk_dp_reg.h"
> > +
> > +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> > +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> > +
> > +#define MTK_DP_MAX_LANES 4
> > +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> > +
> > +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> > +
> > +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> > +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> > +
> > +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> > +
> > +#define MTK_DP_DP_VERSION_11 0x11
> > +
> > +enum mtk_dp_state {
> > +       MTK_DP_STATE_INITIAL,
> > +       MTK_DP_STATE_IDLE,
> > +       MTK_DP_STATE_PREPARE,
> > +       MTK_DP_STATE_NORMAL,
> > +};
> > +
> > +enum mtk_dp_train_state {
> > +       MTK_DP_TRAIN_STATE_STARTUP = 0,
> > +       MTK_DP_TRAIN_STATE_CHECKCAP,
> > +       MTK_DP_TRAIN_STATE_CHECKEDID,
> > +       MTK_DP_TRAIN_STATE_TRAINING_PRE,
> > +       MTK_DP_TRAIN_STATE_TRAINING,
> > +       MTK_DP_TRAIN_STATE_CHECKTIMING,
> > +       MTK_DP_TRAIN_STATE_NORMAL,
> > +       MTK_DP_TRAIN_STATE_POWERSAVE,
> > +       MTK_DP_TRAIN_STATE_DPIDLE,
> > +};
> > +
> > +struct mtk_dp_timings {
> > +       struct videomode vm;
> > +
> > +       u16 htotal;
> > +       u16 vtotal;
> > +       u8 frame_rate;
> > +       u32 pix_rate_khz;
> > +};
> > +
> > +struct mtk_dp_train_info {
> > +       bool tps3;
> > +       bool tps4;
> > +       bool sink_ssc;
> > +       bool cable_plugged_in;
> > +       bool cable_state_change;
> > +       bool cr_done;
> > +       bool eq_done;
> > +
> > +       // link_rate is in multiple of 0.27Gbps
> > +       int link_rate;
> > +       int lane_count;
> > +
> > +       int irq_status;
> > +       int check_cap_count;
> > +};
> > +
> > +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> > +enum mtk_dp_color_format {
> > +       MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> > +       MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> > +       MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> > +       MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> > +       MTK_DP_COLOR_FORMAT_YONLY = 4,
> > +       MTK_DP_COLOR_FORMAT_RAW = 5,
> > +       MTK_DP_COLOR_FORMAT_RESERVED = 6,
> > +       MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> > +       MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> > +};
> > +
> > +// Multiple of 0.27Gbps
> > +enum mtk_dp_linkrate {
> > +       MTK_DP_LINKRATE_RBR = 0x6,
> > +       MTK_DP_LINKRATE_HBR = 0xA,
> > +       MTK_DP_LINKRATE_HBR2 = 0x14,
> > +       MTK_DP_LINKRATE_HBR25 = 0x19,
> > +       MTK_DP_LINKRATE_HBR3 = 0x1E,
> > +};
> > +
> > +// Same values as used for DP Spec MISC0 bits 5,6,7
> > +enum mtk_dp_color_depth {
> > +       MTK_DP_COLOR_DEPTH_6BIT = 0,
> > +       MTK_DP_COLOR_DEPTH_8BIT = 1,
> > +       MTK_DP_COLOR_DEPTH_10BIT = 2,
> > +       MTK_DP_COLOR_DEPTH_12BIT = 3,
> > +       MTK_DP_COLOR_DEPTH_16BIT = 4,
> > +       MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> > +};
> > +
> > +struct mtk_dp_audio_cfg {
> > +       int sample_rate;
> > +       int word_length_bits;
> > +       int channels;
> > +};
> > +
> > +struct mtk_dp_info {
> > +       enum mtk_dp_color_depth depth;
> > +       enum mtk_dp_color_format format;
> > +       struct mtk_dp_audio_cfg audio_caps;
> > +       struct mtk_dp_timings timings;
> > +};
> > +
> > +struct dp_cal_data {
> > +       unsigned int glb_bias_trim;
> > +       unsigned int clktx_impse;
> > +
> > +       unsigned int ln0_tx_impsel_pmos;
> > +       unsigned int ln0_tx_impsel_nmos;
> > +       unsigned int ln1_tx_impsel_pmos;
> > +       unsigned int ln1_tx_impsel_nmos;
> > +       unsigned int ln2_tx_impsel_pmos;
> > +       unsigned int ln2_tx_impsel_nmos;
> > +       unsigned int ln3_tx_impsel_pmos;
> > +       unsigned int ln3_tx_impsel_nmos;
> > +};
> > +
> > +struct mtk_dp {
> > +       struct device *dev;
> > +       struct platform_device *phy_dev;
> > +       struct phy *phy;
> > +       struct dp_cal_data cal_data;
> > +
> > +       struct drm_device *drm_dev;
> > +       struct drm_bridge bridge;
> > +       struct drm_bridge *next_bridge;
> > +       struct drm_dp_aux aux;
> > +
> > +       struct mutex edid_lock;
> > +       struct edid *edid;
> > +
> > +       u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> > +
> > +       struct mtk_dp_info info;
> > +       enum mtk_dp_state state;
> > +
> > +       struct mtk_dp_train_info train_info;
> > +       enum mtk_dp_train_state train_state;
> > +       unsigned int input_fmt;
> > +
> > +       struct regmap *regs;
> > +       struct clk *dp_tx_clk;
> > +
> > +       bool enabled;
> > +       bool audio_enable;
> > +
> > +       bool has_fec;
> > +       struct mutex dp_lock;
> > +
> > +       struct mutex update_plugged_status_lock;
> > +
> > +       hdmi_codec_plugged_cb plugged_cb;
> > +       struct device *codec_dev;
> > +       u8 connector_eld[MAX_ELD_BYTES];
> > +};
> > +
> > +enum mtk_dp_sdp_type {
> > +       MTK_DP_SDP_NONE = 0x00,
> > +       MTK_DP_SDP_ACM = 0x01,
> > +       MTK_DP_SDP_ISRC = 0x02,
> > +       MTK_DP_SDP_AVI = 0x03,
> > +       MTK_DP_SDP_AUI = 0x04,
> > +       MTK_DP_SDP_SPD = 0x05,
> > +       MTK_DP_SDP_MPEG = 0x06,
> > +       MTK_DP_SDP_NTSC = 0x07,
> > +       MTK_DP_SDP_VSP = 0x08,
> > +       MTK_DP_SDP_VSC = 0x09,
> > +       MTK_DP_SDP_EXT = 0x0A,
> > +       MTK_DP_SDP_PPS0 = 0x0B,
> > +       MTK_DP_SDP_PPS1 = 0x0C,
> > +       MTK_DP_SDP_PPS2 = 0x0D,
> > +       MTK_DP_SDP_PPS3 = 0x0E,
> > +       MTK_DP_SDP_DRM = 0x10,
> > +       MTK_DP_SDP_MAX_NUM
> > +};
> > +
> > +struct mtk_dp_sdp_packet {
> > +       enum mtk_dp_sdp_type type;
> > +       struct dp_sdp sdp;
> > +};
> > +
> > +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> > +union mtk_dp_audio_channel_status {
> > +       struct {
> > +               u8 rev : 1;
> > +               u8 is_lpcm : 1;
> > +               u8 copy_right : 1;
> > +               u8 additional_format_info : 3;
> > +               u8 channel_status_mode : 2;
> > +               u8 category_code;
> > +               u8 src_num : 4;
> > +               u8 channel_num : 4;
> > +               u8 sampling_freq : 4;
> > +               u8 clk_accuracy : 2;
> > +               u8 rev2 : 2;
> > +               u8 word_len : 4;
> > +               u8 original_sampling_freq : 4;
> > +       } iec;
> > +
> > +       u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> > +};
> > +
> > +static struct regmap_config mtk_dp_regmap_config = {
> > +       .reg_bits = 32,
> > +       .val_bits = 32,
> > +       .reg_stride = 4,
> > +       .max_register = SEC_OFFSET + 0x90,
> > +       .name = "mtk-dp-registers",
> > +};
> > +
> > +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
>
> Separate edp and displayport into two patches. For example, the first
> patch support edp only, and the second patch add displayport function.
>
I have made the opposite as it seemed a bit more logical from how it's coded
now.
I've made a single patch with DP and a new one adding eDP support on top of it.
The change is quite small, adding the eDP on top of DP:
 drivers/gpu/drm/mediatek/mtk_dp.c | 103
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
 1 file changed, 74 insertions(+), 29 deletions(-)
> > +{
> > +       return mtk_dp->next_bridge != NULL;
> > +}
> > +
>
> [snip]
>
> > +
> > +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> > +                                   struct drm_connector *connector)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +       bool enabled = mtk_dp->enabled;
> > +       struct edid *new_edid = NULL;
> > +
> > +       if (!enabled)
> > +               drm_bridge_chain_pre_enable(bridge);
>
> In mtk_hdmi_bridge_get_edid(), there does not check the power. Why in
> this function need this?
> Does mtk hdmi driver has a bug?
>
I don't know, but I will be asking around.
> > +
> > +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> > +       usleep_range(2000, 5000);
> > +
> > +       if (mtk_dp_plug_state(mtk_dp))
> > +               new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> > +
> > +       if (!enabled)
> > +               drm_bridge_chain_post_disable(bridge);
> > +
> > +       mutex_lock(&mtk_dp->edid_lock);
> > +       kfree(mtk_dp->edid);
> > +       mtk_dp->edid = NULL;
> > +
> > +       if (!new_edid) {
> > +               mutex_unlock(&mtk_dp->edid_lock);
> > +               return NULL;
> > +       }
> > +
> > +       mtk_dp->edid = drm_edid_duplicate(new_edid);
> > +       mutex_unlock(&mtk_dp->edid_lock);
> > +
> > +       return new_edid;
> > +}
> > +
> > +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> > +                                  struct drm_dp_aux_msg *msg)
> > +{
> > +       ssize_t err = -EAGAIN;
> > +       struct mtk_dp *mtk_dp;
> > +       bool is_read;
> > +       u8 request;
> > +       size_t accessed_bytes = 0;
> > +       int retry = 3, ret = 0;
> > +
> > +       mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> > +
> > +       if (!mtk_dp->train_info.cable_plugged_in ||
> > +           mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> > +               mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> > +               err = -EAGAIN;
> > +               goto err;
> > +       }
> > +
> > +       switch (msg->request) {
> > +       case DP_AUX_I2C_MOT:
> > +       case DP_AUX_I2C_WRITE:
> > +       case DP_AUX_NATIVE_WRITE:
> > +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> > +       case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> > +               request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> > +               is_read = false;
> > +               break;
> > +       case DP_AUX_I2C_READ:
> > +       case DP_AUX_NATIVE_READ:
> > +       case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> > +               request = msg->request;
> > +               is_read = true;
> > +               break;
> > +       default:
> > +               drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> > +                       msg->request);
> > +               err = -EINVAL;
> > +               goto err;
>
> Directly return here.
>
Hmm, not sure yet, it seems I still need to prime msg->reply anyway.
Though the error handling can be simplified a bit.

> > +       }
> > +
> > +       if (msg->size == 0) {
> > +               mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> > +                                      msg->address + accessed_bytes,
> > +                                      msg->buffer + accessed_bytes, 0);
> > +       } else {
> > +               while (accessed_bytes < msg->size) {
> > +                       size_t to_access =
> > +                               min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> > +                                     msg->size - accessed_bytes);
> > +                       retry = 3;
> > +                       while (retry--) {
> > +                               ret = mtk_dp_aux_do_transfer(
> > +                                       mtk_dp, is_read, request,
> > +                                       msg->address + accessed_bytes,
> > +                                       msg->buffer + accessed_bytes,
> > +                                       to_access);
> > +                               if (ret == 0)
> > +                                       break;
> > +                               udelay(50);
> > +                       }
> > +                       if (!retry && ret) {
> > +                               drm_info(mtk_dp->drm_dev,
> > +                                        "Failed to do AUX transfer: %d\n",
> > +                                        ret);
> > +                               break;
> > +                       }
> > +                       accessed_bytes += to_access;
> > +               }
> > +       }
> > +err:
> > +       if (!ret) {
> > +               msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > +               ret = msg->size;
> > +       } else {
> > +               msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> > +               return err;
> > +       }
> > +
> > +       msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > +       return msg->size;
> > +}
> > +
> > +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> > +{
> > +       drm_dp_aux_init(&mtk_dp->aux);
> > +       mtk_dp->aux.name = "aux_mtk_dp";
> > +       mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> > +}
> > +
> > +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> > +{
> > +       mutex_lock(&mtk_dp->dp_lock);
> > +
> > +       mtk_dp_hwirq_enable(mtk_dp, false);
> > +       mtk_dp_power_disable(mtk_dp);
> > +       phy_exit(mtk_dp->phy);
> > +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > +
> > +       mutex_unlock(&mtk_dp->dp_lock);
> > +}
> > +
> > +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> > +{
> > +       int ret = 0;
> > +
> > +       mutex_lock(&mtk_dp->dp_lock);
> > +
> > +       ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> > +       if (ret < 0) {
> > +               dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> > +               goto err;
> > +       }
> > +       ret = phy_init(mtk_dp->phy);
> > +       if (ret) {
> > +               dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> > +               goto err_phy_init;
> > +       }
> > +       ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> > +       if (ret) {
> > +               dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> > +               goto err_phy_config;
> > +       }
> > +
> > +       mtk_dp_init_port(mtk_dp);
> > +       mtk_dp_power_enable(mtk_dp);
> > +       mtk_dp_hwirq_enable(mtk_dp, true);
> > +
> > +err_phy_config:
> > +       phy_exit(mtk_dp->phy);
> > +err_phy_init:
> > +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > +err:
> > +       mutex_unlock(&mtk_dp->dp_lock);
> > +       return ret;
> > +}
> > +
> > +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> > +                               enum drm_bridge_attach_flags flags)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +       int ret;
> > +
> > +       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> > +               dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> > +               return -EINVAL;
> > +       }
> > +
> > +       ret = mtk_dp_poweron(mtk_dp);
>
> Move the power on to mtk_dp_bridge_atomic_enable().
>
> > +       if (ret)
> > +               return ret;
> > +
> > +       if (mtk_dp->next_bridge) {
> > +               ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> > +                                       &mtk_dp->bridge, flags);
> > +               if (ret) {
> > +                       drm_warn(mtk_dp->drm_dev,
> > +                                "Failed to attach external bridge: %d\n", ret);
> > +                       goto err_bridge_attach;
> > +               }
> > +       }
> > +
> > +       mtk_dp->drm_dev = bridge->dev;
> > +
> > +       return 0;
> > +
> > +err_bridge_attach:
> > +       mtk_dp_poweroff(mtk_dp);
> > +       return ret;
> > +}
> > +
> > +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +
> > +       mtk_dp->drm_dev = NULL;
> > +
> > +       mtk_dp_poweroff(mtk_dp);
> > +}
> > +
> > +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> > +                                        struct drm_bridge_state *old_state)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +
> > +       mtk_dp_video_mute(mtk_dp, true);
> > +       mtk_dp_audio_mute(mtk_dp, true);
> > +       mtk_dp->state = MTK_DP_STATE_IDLE;
> > +       mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > +
> > +       mtk_dp->enabled = false;
> > +       msleep(100);
> > +}
> > +
> > +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> > +                                         struct drm_display_mode *mode)
> > +{
> > +       struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> > +
> > +       drm_display_mode_to_videomode(mode, &timings->vm);
> > +       timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> > +       timings->htotal = mode->htotal;
> > +       timings->vtotal = mode->vtotal;
> > +}
> > +
> > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > +                                       struct drm_bridge_state *old_state)
> > +{
> > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > +       struct drm_connector *conn;
> > +       struct drm_connector_state *conn_state;
> > +       struct drm_crtc *crtc;
> > +       struct drm_crtc_state *crtc_state;
> > +       int ret = 0;
> > +       int i;
> > +
> > +       conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > +                                                       bridge->encoder);
>
> mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
>                                                        bridge->encoder);
>
The connector doesn't seem to be used anywhere else in the driver.
Though I can add a drm_connector in the mtk_dp struct.

> > +       if (!conn) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as connector is missing\n");
> > +               return;
> > +       }
> > +
> > +       memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > +
> > +       conn_state =
> > +               drm_atomic_get_new_connector_state(old_state->base.state, conn);
> > +       if (!conn_state) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as connector state is missing\n");
> > +               return;
> > +       }
> > +
> > +       crtc = conn_state->crtc;
> > +       if (!crtc) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as connector state doesn't have a crtc\n");
> > +               return;
> > +       }
> > +
> > +       crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> > +       if (!crtc_state) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as crtc state is missing\n");
> > +               return;
> > +       }
> > +
> > +       mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);
>
> Refer to mtk_hdmi_bridge_atomic_enable() for getting the mode.
>
I'm sorry, I don't understand what I'm supposed to do here.
Could you elaborate?

> > +       if (!mtk_dp_parse_capabilities(mtk_dp)) {
> > +               drm_err(mtk_dp->drm_dev,
> > +                       "Can't enable bridge as nothing is plugged in\n");
> > +               return;
> > +       }
> > +
> > +       //training
>
> Run checkpatch first.
>
> Regards,
> Chun-Kuang.
>
> > +       for (i = 0; i < 50; i++) {
> > +               ret = mtk_dp_train_handler(mtk_dp);
> > +               if (ret) {
> > +                       drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> > +                               ret);
> > +                       return;
> > +               }
> > +
> > +               ret = mtk_dp_state_handler(mtk_dp);
> > +               if (ret) {
> > +                       drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> > +                               ret);
> > +                       return;
> > +               }
> > +       }
> > +
> > +       mtk_dp->enabled = true;
> > +       mtk_dp_update_plugged_status(mtk_dp);
> > +}
> > +
>
>
>
> > --
> > 2.32.0
> >

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-11-10 13:06   ` Guillaume Ranquet
  (?)
  (?)
@ 2021-12-09  6:29     ` Hsin-Yi Wang
  -1 siblings, 0 replies; 122+ messages in thread
From: Hsin-Yi Wang @ 2021-12-09  6:29 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Chun-Kuang Hu, Philipp Zabel,
	Matthias Brugger, Markus Schneider-Pargmann, kernel test robot,
	linux-kernel, dri-devel, linux-mediatek, linux-arm-kernel

On Wed, Nov 10, 2021 at 9:08 PM Guillaume Ranquet <granquet@baylibre.com> wrote:
>
> From: Markus Schneider-Pargmann <msp@baylibre.com>
>
> This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> according phy driver mediatek-dp-phy.
>
> It supports both functional units on the mt8195, the embedded
> DisplayPort as well as the external DisplayPort units. It offers
> hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> to 4 lanes.
>
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.
>
> This driver is based on an initial version by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
>
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reported-by: kernel test robot <lkp@intel.com>
> ---


<snip>

> +static int mtk_dp_probe(struct platform_device *pdev)
> +{
> +       struct mtk_dp *mtk_dp;
> +       struct device *dev = &pdev->dev;
> +       int ret;
> +       int irq_num = 0;
> +       struct drm_panel *panel = NULL;
> +
> +       mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> +       if (!mtk_dp)
> +               return -ENOMEM;
> +
> +       mtk_dp->dev = dev;
> +
> +       irq_num = platform_get_irq(pdev, 0);
> +       if (irq_num < 0) {
> +               dev_err(dev, "failed to request dp irq resource\n");
> +               return -EPROBE_DEFER;
> +       }
> +
> +       ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel,
> +                                         &mtk_dp->next_bridge);
> +       if (ret == -ENODEV) {
> +               dev_info(
> +                       dev,
> +                       "No panel connected in devicetree, continuing as external DP\n");
> +               mtk_dp->next_bridge = NULL;
> +       } else if (ret) {
> +               dev_err(dev, "Failed to find panel or bridge: %d\n", ret);

Hi,

We're seeing
[    0.424599] mediatek-drm-dp 1c500000.edp_tx: Failed to find panel
or bridge: -517

It's probably better to use dev_err_probe here.

Thanks

> +               return ret;
> +       }
> +
> +       if (panel) {
> +               mtk_dp->next_bridge = devm_drm_panel_bridge_add(dev, panel);
> +               if (IS_ERR(mtk_dp->next_bridge)) {
> +                       ret = PTR_ERR(mtk_dp->next_bridge);
> +                       dev_err(dev, "Failed to create bridge: %d\n", ret);
> +                       return -EPROBE_DEFER;
> +               }
> +       }


<snip>

> _______________________________________________
> Linux-mediatek mailing list
> Linux-mediatek@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-09  6:29     ` Hsin-Yi Wang
  0 siblings, 0 replies; 122+ messages in thread
From: Hsin-Yi Wang @ 2021-12-09  6:29 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Chun-Kuang Hu, Philipp Zabel,
	Matthias Brugger, Markus Schneider-Pargmann, kernel test robot,
	linux-kernel, dri-devel, linux-mediatek, linux-arm-kernel

On Wed, Nov 10, 2021 at 9:08 PM Guillaume Ranquet <granquet@baylibre.com> wrote:
>
> From: Markus Schneider-Pargmann <msp@baylibre.com>
>
> This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> according phy driver mediatek-dp-phy.
>
> It supports both functional units on the mt8195, the embedded
> DisplayPort as well as the external DisplayPort units. It offers
> hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> to 4 lanes.
>
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.
>
> This driver is based on an initial version by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
>
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reported-by: kernel test robot <lkp@intel.com>
> ---


<snip>

> +static int mtk_dp_probe(struct platform_device *pdev)
> +{
> +       struct mtk_dp *mtk_dp;
> +       struct device *dev = &pdev->dev;
> +       int ret;
> +       int irq_num = 0;
> +       struct drm_panel *panel = NULL;
> +
> +       mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> +       if (!mtk_dp)
> +               return -ENOMEM;
> +
> +       mtk_dp->dev = dev;
> +
> +       irq_num = platform_get_irq(pdev, 0);
> +       if (irq_num < 0) {
> +               dev_err(dev, "failed to request dp irq resource\n");
> +               return -EPROBE_DEFER;
> +       }
> +
> +       ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel,
> +                                         &mtk_dp->next_bridge);
> +       if (ret == -ENODEV) {
> +               dev_info(
> +                       dev,
> +                       "No panel connected in devicetree, continuing as external DP\n");
> +               mtk_dp->next_bridge = NULL;
> +       } else if (ret) {
> +               dev_err(dev, "Failed to find panel or bridge: %d\n", ret);

Hi,

We're seeing
[    0.424599] mediatek-drm-dp 1c500000.edp_tx: Failed to find panel
or bridge: -517

It's probably better to use dev_err_probe here.

Thanks

> +               return ret;
> +       }
> +
> +       if (panel) {
> +               mtk_dp->next_bridge = devm_drm_panel_bridge_add(dev, panel);
> +               if (IS_ERR(mtk_dp->next_bridge)) {
> +                       ret = PTR_ERR(mtk_dp->next_bridge);
> +                       dev_err(dev, "Failed to create bridge: %d\n", ret);
> +                       return -EPROBE_DEFER;
> +               }
> +       }


<snip>

> _______________________________________________
> Linux-mediatek mailing list
> Linux-mediatek@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-mediatek

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-09  6:29     ` Hsin-Yi Wang
  0 siblings, 0 replies; 122+ messages in thread
From: Hsin-Yi Wang @ 2021-12-09  6:29 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Chun-Kuang Hu, Philipp Zabel,
	Matthias Brugger, Markus Schneider-Pargmann, kernel test robot,
	linux-kernel, dri-devel, linux-mediatek, linux-arm-kernel

On Wed, Nov 10, 2021 at 9:08 PM Guillaume Ranquet <granquet@baylibre.com> wrote:
>
> From: Markus Schneider-Pargmann <msp@baylibre.com>
>
> This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> according phy driver mediatek-dp-phy.
>
> It supports both functional units on the mt8195, the embedded
> DisplayPort as well as the external DisplayPort units. It offers
> hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> to 4 lanes.
>
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.
>
> This driver is based on an initial version by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
>
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reported-by: kernel test robot <lkp@intel.com>
> ---


<snip>

> +static int mtk_dp_probe(struct platform_device *pdev)
> +{
> +       struct mtk_dp *mtk_dp;
> +       struct device *dev = &pdev->dev;
> +       int ret;
> +       int irq_num = 0;
> +       struct drm_panel *panel = NULL;
> +
> +       mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> +       if (!mtk_dp)
> +               return -ENOMEM;
> +
> +       mtk_dp->dev = dev;
> +
> +       irq_num = platform_get_irq(pdev, 0);
> +       if (irq_num < 0) {
> +               dev_err(dev, "failed to request dp irq resource\n");
> +               return -EPROBE_DEFER;
> +       }
> +
> +       ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel,
> +                                         &mtk_dp->next_bridge);
> +       if (ret == -ENODEV) {
> +               dev_info(
> +                       dev,
> +                       "No panel connected in devicetree, continuing as external DP\n");
> +               mtk_dp->next_bridge = NULL;
> +       } else if (ret) {
> +               dev_err(dev, "Failed to find panel or bridge: %d\n", ret);

Hi,

We're seeing
[    0.424599] mediatek-drm-dp 1c500000.edp_tx: Failed to find panel
or bridge: -517

It's probably better to use dev_err_probe here.

Thanks

> +               return ret;
> +       }
> +
> +       if (panel) {
> +               mtk_dp->next_bridge = devm_drm_panel_bridge_add(dev, panel);
> +               if (IS_ERR(mtk_dp->next_bridge)) {
> +                       ret = PTR_ERR(mtk_dp->next_bridge);
> +                       dev_err(dev, "Failed to create bridge: %d\n", ret);
> +                       return -EPROBE_DEFER;
> +               }
> +       }


<snip>

> _______________________________________________
> Linux-mediatek mailing list
> Linux-mediatek@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-mediatek

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-09  6:29     ` Hsin-Yi Wang
  0 siblings, 0 replies; 122+ messages in thread
From: Hsin-Yi Wang @ 2021-12-09  6:29 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Chun-Kuang Hu, Thomas Zimmermann, David Airlie, linux-kernel,
	Markus Schneider-Pargmann, linux-mediatek, dri-devel,
	Matthias Brugger, linux-arm-kernel, kernel test robot

On Wed, Nov 10, 2021 at 9:08 PM Guillaume Ranquet <granquet@baylibre.com> wrote:
>
> From: Markus Schneider-Pargmann <msp@baylibre.com>
>
> This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> according phy driver mediatek-dp-phy.
>
> It supports both functional units on the mt8195, the embedded
> DisplayPort as well as the external DisplayPort units. It offers
> hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> to 4 lanes.
>
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.
>
> This driver is based on an initial version by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
>
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reported-by: kernel test robot <lkp@intel.com>
> ---


<snip>

> +static int mtk_dp_probe(struct platform_device *pdev)
> +{
> +       struct mtk_dp *mtk_dp;
> +       struct device *dev = &pdev->dev;
> +       int ret;
> +       int irq_num = 0;
> +       struct drm_panel *panel = NULL;
> +
> +       mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> +       if (!mtk_dp)
> +               return -ENOMEM;
> +
> +       mtk_dp->dev = dev;
> +
> +       irq_num = platform_get_irq(pdev, 0);
> +       if (irq_num < 0) {
> +               dev_err(dev, "failed to request dp irq resource\n");
> +               return -EPROBE_DEFER;
> +       }
> +
> +       ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel,
> +                                         &mtk_dp->next_bridge);
> +       if (ret == -ENODEV) {
> +               dev_info(
> +                       dev,
> +                       "No panel connected in devicetree, continuing as external DP\n");
> +               mtk_dp->next_bridge = NULL;
> +       } else if (ret) {
> +               dev_err(dev, "Failed to find panel or bridge: %d\n", ret);

Hi,

We're seeing
[    0.424599] mediatek-drm-dp 1c500000.edp_tx: Failed to find panel
or bridge: -517

It's probably better to use dev_err_probe here.

Thanks

> +               return ret;
> +       }
> +
> +       if (panel) {
> +               mtk_dp->next_bridge = devm_drm_panel_bridge_add(dev, panel);
> +               if (IS_ERR(mtk_dp->next_bridge)) {
> +                       ret = PTR_ERR(mtk_dp->next_bridge);
> +                       dev_err(dev, "Failed to create bridge: %d\n", ret);
> +                       return -EPROBE_DEFER;
> +               }
> +       }


<snip>

> _______________________________________________
> Linux-mediatek mailing list
> Linux-mediatek@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-11-10 13:06   ` Guillaume Ranquet
  (?)
  (?)
@ 2021-12-10 10:17     ` AngeloGioacchino Del Regno
  -1 siblings, 0 replies; 122+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-12-10 10:17 UTC (permalink / raw)
  To: Guillaume Ranquet, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Daniel Vetter, Chun-Kuang Hu,
	Philipp Zabel, Matthias Brugger
  Cc: Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Il 10/11/21 14:06, Guillaume Ranquet ha scritto:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> according phy driver mediatek-dp-phy.
> 
> It supports both functional units on the mt8195, the embedded
> DisplayPort as well as the external DisplayPort units. It offers
> hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> to 4 lanes.
> 
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.
> 
> This driver is based on an initial version by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reported-by: kernel test robot <lkp@intel.com>

Hello Markus, Guillaume,

there is a critical issue with this patch. Please check below.

> ---
>   drivers/gpu/drm/drm_edid.c              |    2 +-
>   drivers/gpu/drm/mediatek/Kconfig        |    7 +
>   drivers/gpu/drm/mediatek/Makefile       |    2 +
>   drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
>   drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
>   drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
>   drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
>   drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
>   drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
>   9 files changed, 3799 insertions(+), 13 deletions(-)
>   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> 

<snip>

> diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> index 384074f69111b..e6e88e3cd811d 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c

<snip>

> @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
>   		return ret;
>   	}
>   
> +	dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");

mtk_dpi is used on MT2701, MT7183, MT8183, MT8192, but these platforms haven't
got any "ck_cg" clock defined in their device-trees (regardless of whether these
can support adding this clock or not, any code change shall be retro-compatible
hence not breaking compatibility/functionality with older device-trees).

Reminding that:
- mediatek-drm uses the component framework
- mtk_drm_drv is the component master
- mtk_drm_drv bind() won't be called unless all of the components added with
   match aren't calling component_add()

... this change not only breaks DisplayPort support for *all* of the
aforementioned SoCs, but also makes the entire mediatek-drm to not finish
probing, producing a global breakage that also includes DSI and the entire
stack of components of that master (so, no display on all of them).

To avoid breaking any SoC that's not MT8195, please use devm_clk_get_optional()
here in the next version.

Thanks,
- Angelo


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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-10 10:17     ` AngeloGioacchino Del Regno
  0 siblings, 0 replies; 122+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-12-10 10:17 UTC (permalink / raw)
  To: Guillaume Ranquet, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Daniel Vetter, Chun-Kuang Hu,
	Philipp Zabel, Matthias Brugger
  Cc: Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Il 10/11/21 14:06, Guillaume Ranquet ha scritto:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> according phy driver mediatek-dp-phy.
> 
> It supports both functional units on the mt8195, the embedded
> DisplayPort as well as the external DisplayPort units. It offers
> hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> to 4 lanes.
> 
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.
> 
> This driver is based on an initial version by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reported-by: kernel test robot <lkp@intel.com>

Hello Markus, Guillaume,

there is a critical issue with this patch. Please check below.

> ---
>   drivers/gpu/drm/drm_edid.c              |    2 +-
>   drivers/gpu/drm/mediatek/Kconfig        |    7 +
>   drivers/gpu/drm/mediatek/Makefile       |    2 +
>   drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
>   drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
>   drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
>   drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
>   drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
>   drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
>   9 files changed, 3799 insertions(+), 13 deletions(-)
>   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> 

<snip>

> diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> index 384074f69111b..e6e88e3cd811d 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c

<snip>

> @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
>   		return ret;
>   	}
>   
> +	dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");

mtk_dpi is used on MT2701, MT7183, MT8183, MT8192, but these platforms haven't
got any "ck_cg" clock defined in their device-trees (regardless of whether these
can support adding this clock or not, any code change shall be retro-compatible
hence not breaking compatibility/functionality with older device-trees).

Reminding that:
- mediatek-drm uses the component framework
- mtk_drm_drv is the component master
- mtk_drm_drv bind() won't be called unless all of the components added with
   match aren't calling component_add()

... this change not only breaks DisplayPort support for *all* of the
aforementioned SoCs, but also makes the entire mediatek-drm to not finish
probing, producing a global breakage that also includes DSI and the entire
stack of components of that master (so, no display on all of them).

To avoid breaking any SoC that's not MT8195, please use devm_clk_get_optional()
here in the next version.

Thanks,
- Angelo


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-10 10:17     ` AngeloGioacchino Del Regno
  0 siblings, 0 replies; 122+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-12-10 10:17 UTC (permalink / raw)
  To: Guillaume Ranquet, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Daniel Vetter, Chun-Kuang Hu,
	Philipp Zabel, Matthias Brugger
  Cc: kernel test robot, linux-kernel, dri-devel,
	Markus Schneider-Pargmann, linux-mediatek, linux-arm-kernel

Il 10/11/21 14:06, Guillaume Ranquet ha scritto:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> according phy driver mediatek-dp-phy.
> 
> It supports both functional units on the mt8195, the embedded
> DisplayPort as well as the external DisplayPort units. It offers
> hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> to 4 lanes.
> 
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.
> 
> This driver is based on an initial version by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reported-by: kernel test robot <lkp@intel.com>

Hello Markus, Guillaume,

there is a critical issue with this patch. Please check below.

> ---
>   drivers/gpu/drm/drm_edid.c              |    2 +-
>   drivers/gpu/drm/mediatek/Kconfig        |    7 +
>   drivers/gpu/drm/mediatek/Makefile       |    2 +
>   drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
>   drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
>   drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
>   drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
>   drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
>   drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
>   9 files changed, 3799 insertions(+), 13 deletions(-)
>   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> 

<snip>

> diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> index 384074f69111b..e6e88e3cd811d 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c

<snip>

> @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
>   		return ret;
>   	}
>   
> +	dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");

mtk_dpi is used on MT2701, MT7183, MT8183, MT8192, but these platforms haven't
got any "ck_cg" clock defined in their device-trees (regardless of whether these
can support adding this clock or not, any code change shall be retro-compatible
hence not breaking compatibility/functionality with older device-trees).

Reminding that:
- mediatek-drm uses the component framework
- mtk_drm_drv is the component master
- mtk_drm_drv bind() won't be called unless all of the components added with
   match aren't calling component_add()

... this change not only breaks DisplayPort support for *all* of the
aforementioned SoCs, but also makes the entire mediatek-drm to not finish
probing, producing a global breakage that also includes DSI and the entire
stack of components of that master (so, no display on all of them).

To avoid breaking any SoC that's not MT8195, please use devm_clk_get_optional()
here in the next version.

Thanks,
- Angelo


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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-10 10:17     ` AngeloGioacchino Del Regno
  0 siblings, 0 replies; 122+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-12-10 10:17 UTC (permalink / raw)
  To: Guillaume Ranquet, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Daniel Vetter, Chun-Kuang Hu,
	Philipp Zabel, Matthias Brugger
  Cc: Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Il 10/11/21 14:06, Guillaume Ranquet ha scritto:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> according phy driver mediatek-dp-phy.
> 
> It supports both functional units on the mt8195, the embedded
> DisplayPort as well as the external DisplayPort units. It offers
> hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> to 4 lanes.
> 
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.
> 
> This driver is based on an initial version by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Reported-by: kernel test robot <lkp@intel.com>

Hello Markus, Guillaume,

there is a critical issue with this patch. Please check below.

> ---
>   drivers/gpu/drm/drm_edid.c              |    2 +-
>   drivers/gpu/drm/mediatek/Kconfig        |    7 +
>   drivers/gpu/drm/mediatek/Makefile       |    2 +
>   drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
>   drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
>   drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
>   drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
>   drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
>   drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
>   9 files changed, 3799 insertions(+), 13 deletions(-)
>   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> 

<snip>

> diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> index 384074f69111b..e6e88e3cd811d 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c

<snip>

> @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
>   		return ret;
>   	}
>   
> +	dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");

mtk_dpi is used on MT2701, MT7183, MT8183, MT8192, but these platforms haven't
got any "ck_cg" clock defined in their device-trees (regardless of whether these
can support adding this clock or not, any code change shall be retro-compatible
hence not breaking compatibility/functionality with older device-trees).

Reminding that:
- mediatek-drm uses the component framework
- mtk_drm_drv is the component master
- mtk_drm_drv bind() won't be called unless all of the components added with
   match aren't calling component_add()

... this change not only breaks DisplayPort support for *all* of the
aforementioned SoCs, but also makes the entire mediatek-drm to not finish
probing, producing a global breakage that also includes DSI and the entire
stack of components of that master (so, no display on all of them).

To avoid breaking any SoC that's not MT8195, please use devm_clk_get_optional()
here in the next version.

Thanks,
- Angelo


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-12-02 14:48               ` Guillaume Ranquet
  (?)
  (?)
@ 2021-12-13 16:54                 ` Maxime Ripard
  -1 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-12-13 16:54 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 3923 bytes --]

On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> Hi,
> 
> Quoting Maxime Ripard (2021-11-25 15:30:34)
> > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > Hi,
> > > Thanks for all your input, really appreciated.
> > >
> > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > Hi,
> > > >
> > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > to control the phy properties.
> > > > > >
> > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > you need a separate PHY driver in the first place?
> > > > >
> > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > >
> > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > >
> > > > It's a bit of a circular argument though :)
> > > >
> > > > It's a separate phy driver because it needs to go through another
> > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > because it's a separate phy driver.
> > > >
> > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > single device?
> > > >
> > > > That being said, usually what those kind of questions mean is that
> > > > you're missing a comment or something in the commit log to provide that
> > > > context in the first place, so it would be great to add that context
> > > > here.
> > > >
> > > > And it will avoid the situation we're now in where multiple reviewers
> > > > ask the same questions over and over again :)
> > > >
> > > At first I didn't understand your reply, then I realized I gave you
> > > the wrong link...
> > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > >
> > > The driver and phy were a single driver until v2 of this patch series
> > > and the phy setup
> > > was done directly in the driver (single driver, single C file).
> > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > >
> > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > >
> > > I'll try to find a way to make it clearer for v7.
> >
> > OK, it makes sense then :)
> >
> > There's something weird though: the devices definitely look like they're
> > in a separate register range, yet you mention a regmap to handle the
> > shared register range. That range doesn't seem described anywhere in the
> > device tree though? What is it for?
>
> My understanding is that 0x1000 to 0x1fff controls the phy
> functionalities and 0x2000 to 0x4fff controls "non-phy"
> functionalities. And you are right, there's no description of that in
> the device tree whatsoever. The ranges are in the same actual device
> and thus it has been decided to not have dt-bindings for the phy
> device.

Sure, that last part makes sense, but then I'm not sure why you don't
have the full register range in the device node you have in the DT?

> The phy driver is a child of the DP driver that we register using
> platform_device_register_data() and we pass along the same regmap as
> the DP driver in its platform data.

Especially if it's used by something, it should be described in the DT
somewhere.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-13 16:54                 ` Maxime Ripard
  0 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-12-13 16:54 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 3923 bytes --]

On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> Hi,
> 
> Quoting Maxime Ripard (2021-11-25 15:30:34)
> > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > Hi,
> > > Thanks for all your input, really appreciated.
> > >
> > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > Hi,
> > > >
> > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > to control the phy properties.
> > > > > >
> > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > you need a separate PHY driver in the first place?
> > > > >
> > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > >
> > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > >
> > > > It's a bit of a circular argument though :)
> > > >
> > > > It's a separate phy driver because it needs to go through another
> > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > because it's a separate phy driver.
> > > >
> > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > single device?
> > > >
> > > > That being said, usually what those kind of questions mean is that
> > > > you're missing a comment or something in the commit log to provide that
> > > > context in the first place, so it would be great to add that context
> > > > here.
> > > >
> > > > And it will avoid the situation we're now in where multiple reviewers
> > > > ask the same questions over and over again :)
> > > >
> > > At first I didn't understand your reply, then I realized I gave you
> > > the wrong link...
> > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > >
> > > The driver and phy were a single driver until v2 of this patch series
> > > and the phy setup
> > > was done directly in the driver (single driver, single C file).
> > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > >
> > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > >
> > > I'll try to find a way to make it clearer for v7.
> >
> > OK, it makes sense then :)
> >
> > There's something weird though: the devices definitely look like they're
> > in a separate register range, yet you mention a regmap to handle the
> > shared register range. That range doesn't seem described anywhere in the
> > device tree though? What is it for?
>
> My understanding is that 0x1000 to 0x1fff controls the phy
> functionalities and 0x2000 to 0x4fff controls "non-phy"
> functionalities. And you are right, there's no description of that in
> the device tree whatsoever. The ranges are in the same actual device
> and thus it has been decided to not have dt-bindings for the phy
> device.

Sure, that last part makes sense, but then I'm not sure why you don't
have the full register range in the device node you have in the DT?

> The phy driver is a child of the DP driver that we register using
> platform_device_register_data() and we pass along the same regmap as
> the DP driver in its platform data.

Especially if it's used by something, it should be described in the DT
somewhere.

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 170 bytes --]

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-13 16:54                 ` Maxime Ripard
  0 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-12-13 16:54 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Chun-Kuang Hu, David Airlie, Thomas Zimmermann, linux-kernel,
	dri-devel, Markus Schneider-Pargmann, linux-mediatek,
	Matthias Brugger, linux-arm-kernel, kernel test robot

[-- Attachment #1: Type: text/plain, Size: 3923 bytes --]

On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> Hi,
> 
> Quoting Maxime Ripard (2021-11-25 15:30:34)
> > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > Hi,
> > > Thanks for all your input, really appreciated.
> > >
> > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > Hi,
> > > >
> > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > to control the phy properties.
> > > > > >
> > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > you need a separate PHY driver in the first place?
> > > > >
> > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > >
> > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > >
> > > > It's a bit of a circular argument though :)
> > > >
> > > > It's a separate phy driver because it needs to go through another
> > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > because it's a separate phy driver.
> > > >
> > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > single device?
> > > >
> > > > That being said, usually what those kind of questions mean is that
> > > > you're missing a comment or something in the commit log to provide that
> > > > context in the first place, so it would be great to add that context
> > > > here.
> > > >
> > > > And it will avoid the situation we're now in where multiple reviewers
> > > > ask the same questions over and over again :)
> > > >
> > > At first I didn't understand your reply, then I realized I gave you
> > > the wrong link...
> > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > >
> > > The driver and phy were a single driver until v2 of this patch series
> > > and the phy setup
> > > was done directly in the driver (single driver, single C file).
> > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > >
> > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > >
> > > I'll try to find a way to make it clearer for v7.
> >
> > OK, it makes sense then :)
> >
> > There's something weird though: the devices definitely look like they're
> > in a separate register range, yet you mention a regmap to handle the
> > shared register range. That range doesn't seem described anywhere in the
> > device tree though? What is it for?
>
> My understanding is that 0x1000 to 0x1fff controls the phy
> functionalities and 0x2000 to 0x4fff controls "non-phy"
> functionalities. And you are right, there's no description of that in
> the device tree whatsoever. The ranges are in the same actual device
> and thus it has been decided to not have dt-bindings for the phy
> device.

Sure, that last part makes sense, but then I'm not sure why you don't
have the full register range in the device node you have in the DT?

> The phy driver is a child of the DP driver that we register using
> platform_device_register_data() and we pass along the same regmap as
> the DP driver in its platform data.

Especially if it's used by something, it should be described in the DT
somewhere.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-13 16:54                 ` Maxime Ripard
  0 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-12-13 16:54 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 3923 bytes --]

On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> Hi,
> 
> Quoting Maxime Ripard (2021-11-25 15:30:34)
> > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > Hi,
> > > Thanks for all your input, really appreciated.
> > >
> > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > Hi,
> > > >
> > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > to control the phy properties.
> > > > > >
> > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > you need a separate PHY driver in the first place?
> > > > >
> > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > >
> > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > >
> > > > It's a bit of a circular argument though :)
> > > >
> > > > It's a separate phy driver because it needs to go through another
> > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > because it's a separate phy driver.
> > > >
> > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > single device?
> > > >
> > > > That being said, usually what those kind of questions mean is that
> > > > you're missing a comment or something in the commit log to provide that
> > > > context in the first place, so it would be great to add that context
> > > > here.
> > > >
> > > > And it will avoid the situation we're now in where multiple reviewers
> > > > ask the same questions over and over again :)
> > > >
> > > At first I didn't understand your reply, then I realized I gave you
> > > the wrong link...
> > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > >
> > > The driver and phy were a single driver until v2 of this patch series
> > > and the phy setup
> > > was done directly in the driver (single driver, single C file).
> > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > >
> > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > >
> > > I'll try to find a way to make it clearer for v7.
> >
> > OK, it makes sense then :)
> >
> > There's something weird though: the devices definitely look like they're
> > in a separate register range, yet you mention a regmap to handle the
> > shared register range. That range doesn't seem described anywhere in the
> > device tree though? What is it for?
>
> My understanding is that 0x1000 to 0x1fff controls the phy
> functionalities and 0x2000 to 0x4fff controls "non-phy"
> functionalities. And you are right, there's no description of that in
> the device tree whatsoever. The ranges are in the same actual device
> and thus it has been decided to not have dt-bindings for the phy
> device.

Sure, that last part makes sense, but then I'm not sure why you don't
have the full register range in the device node you have in the DT?

> The phy driver is a child of the DP driver that we register using
> platform_device_register_data() and we pass along the same regmap as
> the DP driver in its platform data.

Especially if it's used by something, it should be described in the DT
somewhere.

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-12-10 10:17     ` AngeloGioacchino Del Regno
  (?)
  (?)
@ 2021-12-15 13:56       ` Guillaume Ranquet
  -1 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 13:56 UTC (permalink / raw)
  To: AngeloGioacchino Del Regno, Chun-Kuang Hu, Daniel Vetter,
	David Airlie, Maarten Lankhorst, Matthias Brugger, Maxime Ripard,
	Philipp Zabel, Thomas Zimmermann
  Cc: Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Hi Angelo,

Quoting AngeloGioacchino Del Regno (2021-12-10 11:17:44)
> Il 10/11/21 14:06, Guillaume Ranquet ha scritto:
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > according phy driver mediatek-dp-phy.
> >
> > It supports both functional units on the mt8195, the embedded
> > DisplayPort as well as the external DisplayPort units. It offers
> > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > to 4 lanes.
> >
> > The driver creates a child device for the phy. The child device will
> > never exist without the parent being active. As they are sharing a
> > register range, the parent passes a regmap pointer to the child so that
> > both can work with the same register range. The phy driver sets device
> > data that is read by the parent to get the phy device that can be used
> > to control the phy properties.
> >
> > This driver is based on an initial version by
> > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > Reported-by: kernel test robot <lkp@intel.com>
>
> Hello Markus, Guillaume,
>
> there is a critical issue with this patch. Please check below.
>
> > ---
> >   drivers/gpu/drm/drm_edid.c              |    2 +-
> >   drivers/gpu/drm/mediatek/Kconfig        |    7 +
> >   drivers/gpu/drm/mediatek/Makefile       |    2 +
> >   drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
> >   drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
> >   drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
> >   drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
> >   drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
> >   drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
> >   9 files changed, 3799 insertions(+), 13 deletions(-)
> >   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
> >   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> >
>
> <snip>
>
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > index 384074f69111b..e6e88e3cd811d 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
>
> <snip>
>
> > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> >               return ret;
> >       }
> >
> > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
>
> mtk_dpi is used on MT2701, MT7183, MT8183, MT8192, but these platforms haven't
> got any "ck_cg" clock defined in their device-trees (regardless of whether these
> can support adding this clock or not, any code change shall be retro-compatible
> hence not breaking compatibility/functionality with older device-trees).
>
> Reminding that:
> - mediatek-drm uses the component framework
> - mtk_drm_drv is the component master
> - mtk_drm_drv bind() won't be called unless all of the components added with
>    match aren't calling component_add()
>
> ... this change not only breaks DisplayPort support for *all* of the
> aforementioned SoCs, but also makes the entire mediatek-drm to not finish
> probing, producing a global breakage that also includes DSI and the entire
> stack of components of that master (so, no display on all of them).
>
> To avoid breaking any SoC that's not MT8195, please use devm_clk_get_optional()
> here in the next version.
>
> Thanks,
> - Angelo
>

This is a good catch, I will update for v7.

Thank you very much for your review.

Thx,
Guillaume.

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 13:56       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 13:56 UTC (permalink / raw)
  To: AngeloGioacchino Del Regno, Chun-Kuang Hu, Daniel Vetter,
	David Airlie, Maarten Lankhorst, Matthias Brugger, Maxime Ripard,
	Philipp Zabel, Thomas Zimmermann
  Cc: Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Hi Angelo,

Quoting AngeloGioacchino Del Regno (2021-12-10 11:17:44)
> Il 10/11/21 14:06, Guillaume Ranquet ha scritto:
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > according phy driver mediatek-dp-phy.
> >
> > It supports both functional units on the mt8195, the embedded
> > DisplayPort as well as the external DisplayPort units. It offers
> > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > to 4 lanes.
> >
> > The driver creates a child device for the phy. The child device will
> > never exist without the parent being active. As they are sharing a
> > register range, the parent passes a regmap pointer to the child so that
> > both can work with the same register range. The phy driver sets device
> > data that is read by the parent to get the phy device that can be used
> > to control the phy properties.
> >
> > This driver is based on an initial version by
> > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > Reported-by: kernel test robot <lkp@intel.com>
>
> Hello Markus, Guillaume,
>
> there is a critical issue with this patch. Please check below.
>
> > ---
> >   drivers/gpu/drm/drm_edid.c              |    2 +-
> >   drivers/gpu/drm/mediatek/Kconfig        |    7 +
> >   drivers/gpu/drm/mediatek/Makefile       |    2 +
> >   drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
> >   drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
> >   drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
> >   drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
> >   drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
> >   drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
> >   9 files changed, 3799 insertions(+), 13 deletions(-)
> >   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
> >   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> >
>
> <snip>
>
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > index 384074f69111b..e6e88e3cd811d 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
>
> <snip>
>
> > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> >               return ret;
> >       }
> >
> > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
>
> mtk_dpi is used on MT2701, MT7183, MT8183, MT8192, but these platforms haven't
> got any "ck_cg" clock defined in their device-trees (regardless of whether these
> can support adding this clock or not, any code change shall be retro-compatible
> hence not breaking compatibility/functionality with older device-trees).
>
> Reminding that:
> - mediatek-drm uses the component framework
> - mtk_drm_drv is the component master
> - mtk_drm_drv bind() won't be called unless all of the components added with
>    match aren't calling component_add()
>
> ... this change not only breaks DisplayPort support for *all* of the
> aforementioned SoCs, but also makes the entire mediatek-drm to not finish
> probing, producing a global breakage that also includes DSI and the entire
> stack of components of that master (so, no display on all of them).
>
> To avoid breaking any SoC that's not MT8195, please use devm_clk_get_optional()
> here in the next version.
>
> Thanks,
> - Angelo
>

This is a good catch, I will update for v7.

Thank you very much for your review.

Thx,
Guillaume.

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 13:56       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 13:56 UTC (permalink / raw)
  To: AngeloGioacchino Del Regno, Chun-Kuang Hu, Daniel Vetter,
	David Airlie, Maarten Lankhorst, Matthias Brugger, Maxime Ripard,
	Philipp Zabel, Thomas Zimmermann
  Cc: kernel test robot, linux-kernel, dri-devel,
	Markus Schneider-Pargmann, linux-mediatek, linux-arm-kernel

Hi Angelo,

Quoting AngeloGioacchino Del Regno (2021-12-10 11:17:44)
> Il 10/11/21 14:06, Guillaume Ranquet ha scritto:
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > according phy driver mediatek-dp-phy.
> >
> > It supports both functional units on the mt8195, the embedded
> > DisplayPort as well as the external DisplayPort units. It offers
> > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > to 4 lanes.
> >
> > The driver creates a child device for the phy. The child device will
> > never exist without the parent being active. As they are sharing a
> > register range, the parent passes a regmap pointer to the child so that
> > both can work with the same register range. The phy driver sets device
> > data that is read by the parent to get the phy device that can be used
> > to control the phy properties.
> >
> > This driver is based on an initial version by
> > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > Reported-by: kernel test robot <lkp@intel.com>
>
> Hello Markus, Guillaume,
>
> there is a critical issue with this patch. Please check below.
>
> > ---
> >   drivers/gpu/drm/drm_edid.c              |    2 +-
> >   drivers/gpu/drm/mediatek/Kconfig        |    7 +
> >   drivers/gpu/drm/mediatek/Makefile       |    2 +
> >   drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
> >   drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
> >   drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
> >   drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
> >   drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
> >   drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
> >   9 files changed, 3799 insertions(+), 13 deletions(-)
> >   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
> >   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> >
>
> <snip>
>
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > index 384074f69111b..e6e88e3cd811d 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
>
> <snip>
>
> > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> >               return ret;
> >       }
> >
> > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
>
> mtk_dpi is used on MT2701, MT7183, MT8183, MT8192, but these platforms haven't
> got any "ck_cg" clock defined in their device-trees (regardless of whether these
> can support adding this clock or not, any code change shall be retro-compatible
> hence not breaking compatibility/functionality with older device-trees).
>
> Reminding that:
> - mediatek-drm uses the component framework
> - mtk_drm_drv is the component master
> - mtk_drm_drv bind() won't be called unless all of the components added with
>    match aren't calling component_add()
>
> ... this change not only breaks DisplayPort support for *all* of the
> aforementioned SoCs, but also makes the entire mediatek-drm to not finish
> probing, producing a global breakage that also includes DSI and the entire
> stack of components of that master (so, no display on all of them).
>
> To avoid breaking any SoC that's not MT8195, please use devm_clk_get_optional()
> here in the next version.
>
> Thanks,
> - Angelo
>

This is a good catch, I will update for v7.

Thank you very much for your review.

Thx,
Guillaume.

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 13:56       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 13:56 UTC (permalink / raw)
  To: AngeloGioacchino Del Regno, Chun-Kuang Hu, Daniel Vetter,
	David Airlie, Maarten Lankhorst, Matthias Brugger, Maxime Ripard,
	Philipp Zabel, Thomas Zimmermann
  Cc: Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Hi Angelo,

Quoting AngeloGioacchino Del Regno (2021-12-10 11:17:44)
> Il 10/11/21 14:06, Guillaume Ranquet ha scritto:
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > according phy driver mediatek-dp-phy.
> >
> > It supports both functional units on the mt8195, the embedded
> > DisplayPort as well as the external DisplayPort units. It offers
> > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > to 4 lanes.
> >
> > The driver creates a child device for the phy. The child device will
> > never exist without the parent being active. As they are sharing a
> > register range, the parent passes a regmap pointer to the child so that
> > both can work with the same register range. The phy driver sets device
> > data that is read by the parent to get the phy device that can be used
> > to control the phy properties.
> >
> > This driver is based on an initial version by
> > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > Reported-by: kernel test robot <lkp@intel.com>
>
> Hello Markus, Guillaume,
>
> there is a critical issue with this patch. Please check below.
>
> > ---
> >   drivers/gpu/drm/drm_edid.c              |    2 +-
> >   drivers/gpu/drm/mediatek/Kconfig        |    7 +
> >   drivers/gpu/drm/mediatek/Makefile       |    2 +
> >   drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
> >   drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
> >   drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
> >   drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
> >   drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
> >   drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
> >   9 files changed, 3799 insertions(+), 13 deletions(-)
> >   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
> >   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> >
>
> <snip>
>
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > index 384074f69111b..e6e88e3cd811d 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
>
> <snip>
>
> > @@ -979,6 +1051,16 @@ static int mtk_dpi_probe(struct platform_device *pdev)
> >               return ret;
> >       }
> >
> > +     dpi->dpi_ck_cg = devm_clk_get(dev, "ck_cg");
>
> mtk_dpi is used on MT2701, MT7183, MT8183, MT8192, but these platforms haven't
> got any "ck_cg" clock defined in their device-trees (regardless of whether these
> can support adding this clock or not, any code change shall be retro-compatible
> hence not breaking compatibility/functionality with older device-trees).
>
> Reminding that:
> - mediatek-drm uses the component framework
> - mtk_drm_drv is the component master
> - mtk_drm_drv bind() won't be called unless all of the components added with
>    match aren't calling component_add()
>
> ... this change not only breaks DisplayPort support for *all* of the
> aforementioned SoCs, but also makes the entire mediatek-drm to not finish
> probing, producing a global breakage that also includes DSI and the entire
> stack of components of that master (so, no display on all of them).
>
> To avoid breaking any SoC that's not MT8195, please use devm_clk_get_optional()
> here in the next version.
>
> Thanks,
> - Angelo
>

This is a good catch, I will update for v7.

Thank you very much for your review.

Thx,
Guillaume.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-12-09  6:29     ` Hsin-Yi Wang
  (?)
  (?)
@ 2021-12-15 13:59       ` Guillaume Ranquet
  -1 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 13:59 UTC (permalink / raw)
  To: Hsin-Yi Wang
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Chun-Kuang Hu, Philipp Zabel,
	Matthias Brugger, Markus Schneider-Pargmann, kernel test robot,
	linux-kernel, dri-devel, linux-mediatek, linux-arm-kernel

Hi Hsin-Yi,

Quoting Hsin-Yi Wang (2021-12-09 07:29:30)
> On Wed, Nov 10, 2021 at 9:08 PM Guillaume Ranquet <granquet@baylibre.com> wrote:
> >
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > according phy driver mediatek-dp-phy.
> >
> > It supports both functional units on the mt8195, the embedded
> > DisplayPort as well as the external DisplayPort units. It offers
> > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > to 4 lanes.
> >
> > The driver creates a child device for the phy. The child device will
> > never exist without the parent being active. As they are sharing a
> > register range, the parent passes a regmap pointer to the child so that
> > both can work with the same register range. The phy driver sets device
> > data that is read by the parent to get the phy device that can be used
> > to control the phy properties.
> >
> > This driver is based on an initial version by
> > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > Reported-by: kernel test robot <lkp@intel.com>
> > ---
>
>
> <snip>
>
> > +static int mtk_dp_probe(struct platform_device *pdev)
> > +{
> > +       struct mtk_dp *mtk_dp;
> > +       struct device *dev = &pdev->dev;
> > +       int ret;
> > +       int irq_num = 0;
> > +       struct drm_panel *panel = NULL;
> > +
> > +       mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> > +       if (!mtk_dp)
> > +               return -ENOMEM;
> > +
> > +       mtk_dp->dev = dev;
> > +
> > +       irq_num = platform_get_irq(pdev, 0);
> > +       if (irq_num < 0) {
> > +               dev_err(dev, "failed to request dp irq resource\n");
> > +               return -EPROBE_DEFER;
> > +       }
> > +
> > +       ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel,
> > +                                         &mtk_dp->next_bridge);
> > +       if (ret == -ENODEV) {
> > +               dev_info(
> > +                       dev,
> > +                       "No panel connected in devicetree, continuing as external DP\n");
> > +               mtk_dp->next_bridge = NULL;
> > +       } else if (ret) {
> > +               dev_err(dev, "Failed to find panel or bridge: %d\n", ret);
>
> Hi,
>
> We're seeing
> [    0.424599] mediatek-drm-dp 1c500000.edp_tx: Failed to find panel
> or bridge: -517
>
> It's probably better to use dev_err_probe here.
>
> Thanks

Good suggestion, it will be in v7.

Thanks for your review,
Guillaume.
>
> > +               return ret;
> > +       }
> > +
> > +       if (panel) {
> > +               mtk_dp->next_bridge = devm_drm_panel_bridge_add(dev, panel);
> > +               if (IS_ERR(mtk_dp->next_bridge)) {
> > +                       ret = PTR_ERR(mtk_dp->next_bridge);
> > +                       dev_err(dev, "Failed to create bridge: %d\n", ret);
> > +                       return -EPROBE_DEFER;
> > +               }
> > +       }
>
>
> <snip>
>
> > _______________________________________________
> > Linux-mediatek mailing list
> > Linux-mediatek@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 13:59       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 13:59 UTC (permalink / raw)
  To: Hsin-Yi Wang
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Chun-Kuang Hu, Philipp Zabel,
	Matthias Brugger, Markus Schneider-Pargmann, kernel test robot,
	linux-kernel, dri-devel, linux-mediatek, linux-arm-kernel

Hi Hsin-Yi,

Quoting Hsin-Yi Wang (2021-12-09 07:29:30)
> On Wed, Nov 10, 2021 at 9:08 PM Guillaume Ranquet <granquet@baylibre.com> wrote:
> >
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > according phy driver mediatek-dp-phy.
> >
> > It supports both functional units on the mt8195, the embedded
> > DisplayPort as well as the external DisplayPort units. It offers
> > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > to 4 lanes.
> >
> > The driver creates a child device for the phy. The child device will
> > never exist without the parent being active. As they are sharing a
> > register range, the parent passes a regmap pointer to the child so that
> > both can work with the same register range. The phy driver sets device
> > data that is read by the parent to get the phy device that can be used
> > to control the phy properties.
> >
> > This driver is based on an initial version by
> > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > Reported-by: kernel test robot <lkp@intel.com>
> > ---
>
>
> <snip>
>
> > +static int mtk_dp_probe(struct platform_device *pdev)
> > +{
> > +       struct mtk_dp *mtk_dp;
> > +       struct device *dev = &pdev->dev;
> > +       int ret;
> > +       int irq_num = 0;
> > +       struct drm_panel *panel = NULL;
> > +
> > +       mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> > +       if (!mtk_dp)
> > +               return -ENOMEM;
> > +
> > +       mtk_dp->dev = dev;
> > +
> > +       irq_num = platform_get_irq(pdev, 0);
> > +       if (irq_num < 0) {
> > +               dev_err(dev, "failed to request dp irq resource\n");
> > +               return -EPROBE_DEFER;
> > +       }
> > +
> > +       ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel,
> > +                                         &mtk_dp->next_bridge);
> > +       if (ret == -ENODEV) {
> > +               dev_info(
> > +                       dev,
> > +                       "No panel connected in devicetree, continuing as external DP\n");
> > +               mtk_dp->next_bridge = NULL;
> > +       } else if (ret) {
> > +               dev_err(dev, "Failed to find panel or bridge: %d\n", ret);
>
> Hi,
>
> We're seeing
> [    0.424599] mediatek-drm-dp 1c500000.edp_tx: Failed to find panel
> or bridge: -517
>
> It's probably better to use dev_err_probe here.
>
> Thanks

Good suggestion, it will be in v7.

Thanks for your review,
Guillaume.
>
> > +               return ret;
> > +       }
> > +
> > +       if (panel) {
> > +               mtk_dp->next_bridge = devm_drm_panel_bridge_add(dev, panel);
> > +               if (IS_ERR(mtk_dp->next_bridge)) {
> > +                       ret = PTR_ERR(mtk_dp->next_bridge);
> > +                       dev_err(dev, "Failed to create bridge: %d\n", ret);
> > +                       return -EPROBE_DEFER;
> > +               }
> > +       }
>
>
> <snip>
>
> > _______________________________________________
> > Linux-mediatek mailing list
> > Linux-mediatek@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-mediatek

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 13:59       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 13:59 UTC (permalink / raw)
  To: Hsin-Yi Wang
  Cc: Chun-Kuang Hu, Thomas Zimmermann, David Airlie, linux-kernel,
	Markus Schneider-Pargmann, linux-mediatek, dri-devel,
	Matthias Brugger, linux-arm-kernel, kernel test robot

Hi Hsin-Yi,

Quoting Hsin-Yi Wang (2021-12-09 07:29:30)
> On Wed, Nov 10, 2021 at 9:08 PM Guillaume Ranquet <granquet@baylibre.com> wrote:
> >
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > according phy driver mediatek-dp-phy.
> >
> > It supports both functional units on the mt8195, the embedded
> > DisplayPort as well as the external DisplayPort units. It offers
> > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > to 4 lanes.
> >
> > The driver creates a child device for the phy. The child device will
> > never exist without the parent being active. As they are sharing a
> > register range, the parent passes a regmap pointer to the child so that
> > both can work with the same register range. The phy driver sets device
> > data that is read by the parent to get the phy device that can be used
> > to control the phy properties.
> >
> > This driver is based on an initial version by
> > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > Reported-by: kernel test robot <lkp@intel.com>
> > ---
>
>
> <snip>
>
> > +static int mtk_dp_probe(struct platform_device *pdev)
> > +{
> > +       struct mtk_dp *mtk_dp;
> > +       struct device *dev = &pdev->dev;
> > +       int ret;
> > +       int irq_num = 0;
> > +       struct drm_panel *panel = NULL;
> > +
> > +       mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> > +       if (!mtk_dp)
> > +               return -ENOMEM;
> > +
> > +       mtk_dp->dev = dev;
> > +
> > +       irq_num = platform_get_irq(pdev, 0);
> > +       if (irq_num < 0) {
> > +               dev_err(dev, "failed to request dp irq resource\n");
> > +               return -EPROBE_DEFER;
> > +       }
> > +
> > +       ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel,
> > +                                         &mtk_dp->next_bridge);
> > +       if (ret == -ENODEV) {
> > +               dev_info(
> > +                       dev,
> > +                       "No panel connected in devicetree, continuing as external DP\n");
> > +               mtk_dp->next_bridge = NULL;
> > +       } else if (ret) {
> > +               dev_err(dev, "Failed to find panel or bridge: %d\n", ret);
>
> Hi,
>
> We're seeing
> [    0.424599] mediatek-drm-dp 1c500000.edp_tx: Failed to find panel
> or bridge: -517
>
> It's probably better to use dev_err_probe here.
>
> Thanks

Good suggestion, it will be in v7.

Thanks for your review,
Guillaume.
>
> > +               return ret;
> > +       }
> > +
> > +       if (panel) {
> > +               mtk_dp->next_bridge = devm_drm_panel_bridge_add(dev, panel);
> > +               if (IS_ERR(mtk_dp->next_bridge)) {
> > +                       ret = PTR_ERR(mtk_dp->next_bridge);
> > +                       dev_err(dev, "Failed to create bridge: %d\n", ret);
> > +                       return -EPROBE_DEFER;
> > +               }
> > +       }
>
>
> <snip>
>
> > _______________________________________________
> > Linux-mediatek mailing list
> > Linux-mediatek@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 13:59       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 13:59 UTC (permalink / raw)
  To: Hsin-Yi Wang
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Chun-Kuang Hu, Philipp Zabel,
	Matthias Brugger, Markus Schneider-Pargmann, kernel test robot,
	linux-kernel, dri-devel, linux-mediatek, linux-arm-kernel

Hi Hsin-Yi,

Quoting Hsin-Yi Wang (2021-12-09 07:29:30)
> On Wed, Nov 10, 2021 at 9:08 PM Guillaume Ranquet <granquet@baylibre.com> wrote:
> >
> > From: Markus Schneider-Pargmann <msp@baylibre.com>
> >
> > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > according phy driver mediatek-dp-phy.
> >
> > It supports both functional units on the mt8195, the embedded
> > DisplayPort as well as the external DisplayPort units. It offers
> > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > to 4 lanes.
> >
> > The driver creates a child device for the phy. The child device will
> > never exist without the parent being active. As they are sharing a
> > register range, the parent passes a regmap pointer to the child so that
> > both can work with the same register range. The phy driver sets device
> > data that is read by the parent to get the phy device that can be used
> > to control the phy properties.
> >
> > This driver is based on an initial version by
> > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> >
> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > Reported-by: kernel test robot <lkp@intel.com>
> > ---
>
>
> <snip>
>
> > +static int mtk_dp_probe(struct platform_device *pdev)
> > +{
> > +       struct mtk_dp *mtk_dp;
> > +       struct device *dev = &pdev->dev;
> > +       int ret;
> > +       int irq_num = 0;
> > +       struct drm_panel *panel = NULL;
> > +
> > +       mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> > +       if (!mtk_dp)
> > +               return -ENOMEM;
> > +
> > +       mtk_dp->dev = dev;
> > +
> > +       irq_num = platform_get_irq(pdev, 0);
> > +       if (irq_num < 0) {
> > +               dev_err(dev, "failed to request dp irq resource\n");
> > +               return -EPROBE_DEFER;
> > +       }
> > +
> > +       ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel,
> > +                                         &mtk_dp->next_bridge);
> > +       if (ret == -ENODEV) {
> > +               dev_info(
> > +                       dev,
> > +                       "No panel connected in devicetree, continuing as external DP\n");
> > +               mtk_dp->next_bridge = NULL;
> > +       } else if (ret) {
> > +               dev_err(dev, "Failed to find panel or bridge: %d\n", ret);
>
> Hi,
>
> We're seeing
> [    0.424599] mediatek-drm-dp 1c500000.edp_tx: Failed to find panel
> or bridge: -517
>
> It's probably better to use dev_err_probe here.
>
> Thanks

Good suggestion, it will be in v7.

Thanks for your review,
Guillaume.
>
> > +               return ret;
> > +       }
> > +
> > +       if (panel) {
> > +               mtk_dp->next_bridge = devm_drm_panel_bridge_add(dev, panel);
> > +               if (IS_ERR(mtk_dp->next_bridge)) {
> > +                       ret = PTR_ERR(mtk_dp->next_bridge);
> > +                       dev_err(dev, "Failed to create bridge: %d\n", ret);
> > +                       return -EPROBE_DEFER;
> > +               }
> > +       }
>
>
> <snip>
>
> > _______________________________________________
> > Linux-mediatek mailing list
> > Linux-mediatek@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-mediatek

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-12-02 15:31       ` Guillaume Ranquet
  (?)
  (?)
@ 2021-12-15 14:13         ` Guillaume Ranquet
  -1 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 14:13 UTC (permalink / raw)
  To: Chun-Kuang Hu
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	DRI Development, ARM/Mediatek SoC support, Linux ARM

Hi Chun-Kuang.

Quoting Guillaume Ranquet (2021-12-02 16:31:03)
> Hi Chun-Kuang.
>
> Quoting Chun-Kuang Hu (2021-11-25 16:27:45)
> > Hi, Guillaume:
> >
> > This is a big patch, so I give some comment first.
> >
> > Guillaume Ranquet <granquet@baylibre.com> 於 2021年11月10日 週三 下午9:06寫道:
> > >
> > > From: Markus Schneider-Pargmann <msp@baylibre.com>
> > >
> > > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > > according phy driver mediatek-dp-phy.
> > >
> > > It supports both functional units on the mt8195, the embedded
> > > DisplayPort as well as the external DisplayPort units. It offers
> > > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > > to 4 lanes.
> > >
> > > The driver creates a child device for the phy. The child device will
> > > never exist without the parent being active. As they are sharing a
> > > register range, the parent passes a regmap pointer to the child so that
> > > both can work with the same register range. The phy driver sets device
> > > data that is read by the parent to get the phy device that can be used
> > > to control the phy properties.
> > >
> > > This driver is based on an initial version by
> > > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> > >
> > > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > > Reported-by: kernel test robot <lkp@intel.com>
> > > ---
> > >  drivers/gpu/drm/drm_edid.c              |    2 +-
> >
> > Separate this to another patch.
> >
> > >  drivers/gpu/drm/mediatek/Kconfig        |    7 +
> > >  drivers/gpu/drm/mediatek/Makefile       |    2 +
> > >  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
> > >  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
> > >  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
> >
> > Ditto.
> >
> > >  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
> >
> > Ditto.
> >
> > >  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
> >
> > Ditto
> >
> > >  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
> >
> > Ditto
> >
> yes my bad, I've made a bunch of fixup which ended up in the wrong place.
> It will be fixed for the next version.
>
> > >  9 files changed, 3799 insertions(+), 13 deletions(-)
> > >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
> > >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> > >
> > > diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> > > index 500279a82167a..bfd98b50ceb5b 100644
> > > --- a/drivers/gpu/drm/drm_edid.c
> > > +++ b/drivers/gpu/drm/drm_edid.c
> > > @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
> > >          * modes and forbids YCRCB422 support for all video modes per
> > >          * HDMI 1.3 spec.
> > >          */
> > > -       info->color_formats = DRM_COLOR_FORMAT_RGB444;
> > > +       info->color_formats |= DRM_COLOR_FORMAT_RGB444;
> > >
> > >         /* YCRCB444 is optional according to spec. */
> > >         if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
> > > diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> > > index 2976d21e9a34a..029b94c716131 100644
> > > --- a/drivers/gpu/drm/mediatek/Kconfig
> > > +++ b/drivers/gpu/drm/mediatek/Kconfig
> > > @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
> > >         select PHY_MTK_HDMI
> > >         help
> > >           DRM/KMS HDMI driver for Mediatek SoCs
> > > +
> > > +config MTK_DPTX_SUPPORT
> > > +       tristate "DRM DPTX Support for Mediatek SoCs"
> > > +       depends on DRM_MEDIATEK
> > > +       select PHY_MTK_DP
> > > +       help
> > > +         DRM/KMS Display Port driver for Mediatek SoCs.
> > > diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> > > index 29098d7c8307c..d86a6406055e6 100644
> > > --- a/drivers/gpu/drm/mediatek/Makefile
> > > +++ b/drivers/gpu/drm/mediatek/Makefile
> > > @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
> > >                           mtk_hdmi_ddc.o
> > >
> > >  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> > > +
> > > +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> > > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> > > new file mode 100644
> > > index 0000000000000..83087219d5a5e
> > > --- /dev/null
> > > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> > > @@ -0,0 +1,3094 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Copyright (c) 2019 MediaTek Inc.
> > > + * Copyright (c) 2021 BayLibre
> > > + */
> > > +
> > > +#include <drm/drm_atomic_helper.h>
> > > +#include <drm/drm_bridge.h>
> > > +#include <drm/drm_crtc.h>
> > > +#include <drm/drm_dp_helper.h>
> > > +#include <drm/drm_edid.h>
> > > +#include <drm/drm_of.h>
> > > +#include <drm/drm_panel.h>
> > > +#include <drm/drm_print.h>
> > > +#include <drm/drm_probe_helper.h>
> > > +#include <linux/arm-smccc.h>
> > > +#include <linux/clk.h>
> > > +#include <linux/delay.h>
> > > +#include <linux/errno.h>
> > > +#include <linux/kernel.h>
> > > +#include <linux/nvmem-consumer.h>
> > > +#include <linux/of.h>
> > > +#include <linux/of_irq.h>
> > > +#include <linux/of_platform.h>
> > > +#include <linux/phy/phy.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/pm_runtime.h>
> > > +#include <linux/regmap.h>
> > > +#include <sound/hdmi-codec.h>
> > > +#include <video/videomode.h>
> > > +
> > > +#include "mtk_dp_reg.h"
> > > +
> > > +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> > > +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> > > +
> > > +#define MTK_DP_MAX_LANES 4
> > > +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> > > +
> > > +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> > > +
> > > +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> > > +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> > > +
> > > +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> > > +
> > > +#define MTK_DP_DP_VERSION_11 0x11
> > > +
> > > +enum mtk_dp_state {
> > > +       MTK_DP_STATE_INITIAL,
> > > +       MTK_DP_STATE_IDLE,
> > > +       MTK_DP_STATE_PREPARE,
> > > +       MTK_DP_STATE_NORMAL,
> > > +};
> > > +
> > > +enum mtk_dp_train_state {
> > > +       MTK_DP_TRAIN_STATE_STARTUP = 0,
> > > +       MTK_DP_TRAIN_STATE_CHECKCAP,
> > > +       MTK_DP_TRAIN_STATE_CHECKEDID,
> > > +       MTK_DP_TRAIN_STATE_TRAINING_PRE,
> > > +       MTK_DP_TRAIN_STATE_TRAINING,
> > > +       MTK_DP_TRAIN_STATE_CHECKTIMING,
> > > +       MTK_DP_TRAIN_STATE_NORMAL,
> > > +       MTK_DP_TRAIN_STATE_POWERSAVE,
> > > +       MTK_DP_TRAIN_STATE_DPIDLE,
> > > +};
> > > +
> > > +struct mtk_dp_timings {
> > > +       struct videomode vm;
> > > +
> > > +       u16 htotal;
> > > +       u16 vtotal;
> > > +       u8 frame_rate;
> > > +       u32 pix_rate_khz;
> > > +};
> > > +
> > > +struct mtk_dp_train_info {
> > > +       bool tps3;
> > > +       bool tps4;
> > > +       bool sink_ssc;
> > > +       bool cable_plugged_in;
> > > +       bool cable_state_change;
> > > +       bool cr_done;
> > > +       bool eq_done;
> > > +
> > > +       // link_rate is in multiple of 0.27Gbps
> > > +       int link_rate;
> > > +       int lane_count;
> > > +
> > > +       int irq_status;
> > > +       int check_cap_count;
> > > +};
> > > +
> > > +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> > > +enum mtk_dp_color_format {
> > > +       MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> > > +       MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> > > +       MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> > > +       MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> > > +       MTK_DP_COLOR_FORMAT_YONLY = 4,
> > > +       MTK_DP_COLOR_FORMAT_RAW = 5,
> > > +       MTK_DP_COLOR_FORMAT_RESERVED = 6,
> > > +       MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> > > +       MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> > > +};
> > > +
> > > +// Multiple of 0.27Gbps
> > > +enum mtk_dp_linkrate {
> > > +       MTK_DP_LINKRATE_RBR = 0x6,
> > > +       MTK_DP_LINKRATE_HBR = 0xA,
> > > +       MTK_DP_LINKRATE_HBR2 = 0x14,
> > > +       MTK_DP_LINKRATE_HBR25 = 0x19,
> > > +       MTK_DP_LINKRATE_HBR3 = 0x1E,
> > > +};
> > > +
> > > +// Same values as used for DP Spec MISC0 bits 5,6,7
> > > +enum mtk_dp_color_depth {
> > > +       MTK_DP_COLOR_DEPTH_6BIT = 0,
> > > +       MTK_DP_COLOR_DEPTH_8BIT = 1,
> > > +       MTK_DP_COLOR_DEPTH_10BIT = 2,
> > > +       MTK_DP_COLOR_DEPTH_12BIT = 3,
> > > +       MTK_DP_COLOR_DEPTH_16BIT = 4,
> > > +       MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> > > +};
> > > +
> > > +struct mtk_dp_audio_cfg {
> > > +       int sample_rate;
> > > +       int word_length_bits;
> > > +       int channels;
> > > +};
> > > +
> > > +struct mtk_dp_info {
> > > +       enum mtk_dp_color_depth depth;
> > > +       enum mtk_dp_color_format format;
> > > +       struct mtk_dp_audio_cfg audio_caps;
> > > +       struct mtk_dp_timings timings;
> > > +};
> > > +
> > > +struct dp_cal_data {
> > > +       unsigned int glb_bias_trim;
> > > +       unsigned int clktx_impse;
> > > +
> > > +       unsigned int ln0_tx_impsel_pmos;
> > > +       unsigned int ln0_tx_impsel_nmos;
> > > +       unsigned int ln1_tx_impsel_pmos;
> > > +       unsigned int ln1_tx_impsel_nmos;
> > > +       unsigned int ln2_tx_impsel_pmos;
> > > +       unsigned int ln2_tx_impsel_nmos;
> > > +       unsigned int ln3_tx_impsel_pmos;
> > > +       unsigned int ln3_tx_impsel_nmos;
> > > +};
> > > +
> > > +struct mtk_dp {
> > > +       struct device *dev;
> > > +       struct platform_device *phy_dev;
> > > +       struct phy *phy;
> > > +       struct dp_cal_data cal_data;
> > > +
> > > +       struct drm_device *drm_dev;
> > > +       struct drm_bridge bridge;
> > > +       struct drm_bridge *next_bridge;
> > > +       struct drm_dp_aux aux;
> > > +
> > > +       struct mutex edid_lock;
> > > +       struct edid *edid;
> > > +
> > > +       u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> > > +
> > > +       struct mtk_dp_info info;
> > > +       enum mtk_dp_state state;
> > > +
> > > +       struct mtk_dp_train_info train_info;
> > > +       enum mtk_dp_train_state train_state;
> > > +       unsigned int input_fmt;
> > > +
> > > +       struct regmap *regs;
> > > +       struct clk *dp_tx_clk;
> > > +
> > > +       bool enabled;
> > > +       bool audio_enable;
> > > +
> > > +       bool has_fec;
> > > +       struct mutex dp_lock;
> > > +
> > > +       struct mutex update_plugged_status_lock;
> > > +
> > > +       hdmi_codec_plugged_cb plugged_cb;
> > > +       struct device *codec_dev;
> > > +       u8 connector_eld[MAX_ELD_BYTES];
> > > +};
> > > +
> > > +enum mtk_dp_sdp_type {
> > > +       MTK_DP_SDP_NONE = 0x00,
> > > +       MTK_DP_SDP_ACM = 0x01,
> > > +       MTK_DP_SDP_ISRC = 0x02,
> > > +       MTK_DP_SDP_AVI = 0x03,
> > > +       MTK_DP_SDP_AUI = 0x04,
> > > +       MTK_DP_SDP_SPD = 0x05,
> > > +       MTK_DP_SDP_MPEG = 0x06,
> > > +       MTK_DP_SDP_NTSC = 0x07,
> > > +       MTK_DP_SDP_VSP = 0x08,
> > > +       MTK_DP_SDP_VSC = 0x09,
> > > +       MTK_DP_SDP_EXT = 0x0A,
> > > +       MTK_DP_SDP_PPS0 = 0x0B,
> > > +       MTK_DP_SDP_PPS1 = 0x0C,
> > > +       MTK_DP_SDP_PPS2 = 0x0D,
> > > +       MTK_DP_SDP_PPS3 = 0x0E,
> > > +       MTK_DP_SDP_DRM = 0x10,
> > > +       MTK_DP_SDP_MAX_NUM
> > > +};
> > > +
> > > +struct mtk_dp_sdp_packet {
> > > +       enum mtk_dp_sdp_type type;
> > > +       struct dp_sdp sdp;
> > > +};
> > > +
> > > +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> > > +union mtk_dp_audio_channel_status {
> > > +       struct {
> > > +               u8 rev : 1;
> > > +               u8 is_lpcm : 1;
> > > +               u8 copy_right : 1;
> > > +               u8 additional_format_info : 3;
> > > +               u8 channel_status_mode : 2;
> > > +               u8 category_code;
> > > +               u8 src_num : 4;
> > > +               u8 channel_num : 4;
> > > +               u8 sampling_freq : 4;
> > > +               u8 clk_accuracy : 2;
> > > +               u8 rev2 : 2;
> > > +               u8 word_len : 4;
> > > +               u8 original_sampling_freq : 4;
> > > +       } iec;
> > > +
> > > +       u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> > > +};
> > > +
> > > +static struct regmap_config mtk_dp_regmap_config = {
> > > +       .reg_bits = 32,
> > > +       .val_bits = 32,
> > > +       .reg_stride = 4,
> > > +       .max_register = SEC_OFFSET + 0x90,
> > > +       .name = "mtk-dp-registers",
> > > +};
> > > +
> > > +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
> >
> > Separate edp and displayport into two patches. For example, the first
> > patch support edp only, and the second patch add displayport function.
> >
> I have made the opposite as it seemed a bit more logical from how it's coded
> now.
> I've made a single patch with DP and a new one adding eDP support on top of it.
> The change is quite small, adding the eDP on top of DP:
>  drivers/gpu/drm/mediatek/mtk_dp.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
>  1 file changed, 74 insertions(+), 29 deletions(-)
> > > +{
> > > +       return mtk_dp->next_bridge != NULL;
> > > +}
> > > +
> >
> > [snip]
> >
> > > +
> > > +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> > > +                                   struct drm_connector *connector)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +       bool enabled = mtk_dp->enabled;
> > > +       struct edid *new_edid = NULL;
> > > +
> > > +       if (!enabled)
> > > +               drm_bridge_chain_pre_enable(bridge);
> >
> > In mtk_hdmi_bridge_get_edid(), there does not check the power. Why in
> > this function need this?
> > Does mtk hdmi driver has a bug?
> >
> I don't know, but I will be asking around.
In short, no, the hdmi driver does not have a bug.
We have to check power here as this driver handles both a DP and an eDP panel.

As the power is controlled by the panel driver in the case of eDP, we have
to make sure the bridge is enabled before trying to read the edid.

Hope this answers the question.

> > > +
> > > +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> > > +       usleep_range(2000, 5000);
> > > +
> > > +       if (mtk_dp_plug_state(mtk_dp))
> > > +               new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> > > +
> > > +       if (!enabled)
> > > +               drm_bridge_chain_post_disable(bridge);
> > > +
> > > +       mutex_lock(&mtk_dp->edid_lock);
> > > +       kfree(mtk_dp->edid);
> > > +       mtk_dp->edid = NULL;
> > > +
> > > +       if (!new_edid) {
> > > +               mutex_unlock(&mtk_dp->edid_lock);
> > > +               return NULL;
> > > +       }
> > > +
> > > +       mtk_dp->edid = drm_edid_duplicate(new_edid);
> > > +       mutex_unlock(&mtk_dp->edid_lock);
> > > +
> > > +       return new_edid;
> > > +}
> > > +
> > > +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> > > +                                  struct drm_dp_aux_msg *msg)
> > > +{
> > > +       ssize_t err = -EAGAIN;
> > > +       struct mtk_dp *mtk_dp;
> > > +       bool is_read;
> > > +       u8 request;
> > > +       size_t accessed_bytes = 0;
> > > +       int retry = 3, ret = 0;
> > > +
> > > +       mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> > > +
> > > +       if (!mtk_dp->train_info.cable_plugged_in ||
> > > +           mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> > > +               mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> > > +               err = -EAGAIN;
> > > +               goto err;
> > > +       }
> > > +
> > > +       switch (msg->request) {
> > > +       case DP_AUX_I2C_MOT:
> > > +       case DP_AUX_I2C_WRITE:
> > > +       case DP_AUX_NATIVE_WRITE:
> > > +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> > > +       case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> > > +               request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> > > +               is_read = false;
> > > +               break;
> > > +       case DP_AUX_I2C_READ:
> > > +       case DP_AUX_NATIVE_READ:
> > > +       case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> > > +               request = msg->request;
> > > +               is_read = true;
> > > +               break;
> > > +       default:
> > > +               drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> > > +                       msg->request);
> > > +               err = -EINVAL;
> > > +               goto err;
> >
> > Directly return here.
> >
> Hmm, not sure yet, it seems I still need to prime msg->reply anyway.
> Though the error handling can be simplified a bit.
>
> > > +       }
> > > +
> > > +       if (msg->size == 0) {
> > > +               mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> > > +                                      msg->address + accessed_bytes,
> > > +                                      msg->buffer + accessed_bytes, 0);
> > > +       } else {
> > > +               while (accessed_bytes < msg->size) {
> > > +                       size_t to_access =
> > > +                               min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> > > +                                     msg->size - accessed_bytes);
> > > +                       retry = 3;
> > > +                       while (retry--) {
> > > +                               ret = mtk_dp_aux_do_transfer(
> > > +                                       mtk_dp, is_read, request,
> > > +                                       msg->address + accessed_bytes,
> > > +                                       msg->buffer + accessed_bytes,
> > > +                                       to_access);
> > > +                               if (ret == 0)
> > > +                                       break;
> > > +                               udelay(50);
> > > +                       }
> > > +                       if (!retry && ret) {
> > > +                               drm_info(mtk_dp->drm_dev,
> > > +                                        "Failed to do AUX transfer: %d\n",
> > > +                                        ret);
> > > +                               break;
> > > +                       }
> > > +                       accessed_bytes += to_access;
> > > +               }
> > > +       }
> > > +err:
> > > +       if (!ret) {
> > > +               msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > > +               ret = msg->size;
> > > +       } else {
> > > +               msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> > > +               return err;
> > > +       }
> > > +
> > > +       msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > > +       return msg->size;
> > > +}
> > > +
> > > +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> > > +{
> > > +       drm_dp_aux_init(&mtk_dp->aux);
> > > +       mtk_dp->aux.name = "aux_mtk_dp";
> > > +       mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> > > +}
> > > +
> > > +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> > > +{
> > > +       mutex_lock(&mtk_dp->dp_lock);
> > > +
> > > +       mtk_dp_hwirq_enable(mtk_dp, false);
> > > +       mtk_dp_power_disable(mtk_dp);
> > > +       phy_exit(mtk_dp->phy);
> > > +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > > +
> > > +       mutex_unlock(&mtk_dp->dp_lock);
> > > +}
> > > +
> > > +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> > > +{
> > > +       int ret = 0;
> > > +
> > > +       mutex_lock(&mtk_dp->dp_lock);
> > > +
> > > +       ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> > > +       if (ret < 0) {
> > > +               dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> > > +               goto err;
> > > +       }
> > > +       ret = phy_init(mtk_dp->phy);
> > > +       if (ret) {
> > > +               dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> > > +               goto err_phy_init;
> > > +       }
> > > +       ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> > > +       if (ret) {
> > > +               dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> > > +               goto err_phy_config;
> > > +       }
> > > +
> > > +       mtk_dp_init_port(mtk_dp);
> > > +       mtk_dp_power_enable(mtk_dp);
> > > +       mtk_dp_hwirq_enable(mtk_dp, true);
> > > +
> > > +err_phy_config:
> > > +       phy_exit(mtk_dp->phy);
> > > +err_phy_init:
> > > +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > > +err:
> > > +       mutex_unlock(&mtk_dp->dp_lock);
> > > +       return ret;
> > > +}
> > > +
> > > +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> > > +                               enum drm_bridge_attach_flags flags)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +       int ret;
> > > +
> > > +       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> > > +               dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> > > +               return -EINVAL;
> > > +       }
> > > +
> > > +       ret = mtk_dp_poweron(mtk_dp);
> >
> > Move the power on to mtk_dp_bridge_atomic_enable().
> >
Moving the power-on sequence to the mtk_dp_bridge_atoimc_enable()
breaks the eDP functionality.
For the time being (ie: v7), the power on will stay in the
bridge_attach() callback, sorry.

> > > +       if (ret)
> > > +               return ret;
> > > +
> > > +       if (mtk_dp->next_bridge) {
> > > +               ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> > > +                                       &mtk_dp->bridge, flags);
> > > +               if (ret) {
> > > +                       drm_warn(mtk_dp->drm_dev,
> > > +                                "Failed to attach external bridge: %d\n", ret);
> > > +                       goto err_bridge_attach;
> > > +               }
> > > +       }
> > > +
> > > +       mtk_dp->drm_dev = bridge->dev;
> > > +
> > > +       return 0;
> > > +
> > > +err_bridge_attach:
> > > +       mtk_dp_poweroff(mtk_dp);
> > > +       return ret;
> > > +}
> > > +
> > > +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +
> > > +       mtk_dp->drm_dev = NULL;
> > > +
> > > +       mtk_dp_poweroff(mtk_dp);
> > > +}
> > > +
> > > +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> > > +                                        struct drm_bridge_state *old_state)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +
> > > +       mtk_dp_video_mute(mtk_dp, true);
> > > +       mtk_dp_audio_mute(mtk_dp, true);
> > > +       mtk_dp->state = MTK_DP_STATE_IDLE;
> > > +       mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > > +
> > > +       mtk_dp->enabled = false;
> > > +       msleep(100);
> > > +}
> > > +
> > > +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> > > +                                         struct drm_display_mode *mode)
> > > +{
> > > +       struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> > > +
> > > +       drm_display_mode_to_videomode(mode, &timings->vm);
> > > +       timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> > > +       timings->htotal = mode->htotal;
> > > +       timings->vtotal = mode->vtotal;
> > > +}
> > > +
> > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > +                                       struct drm_bridge_state *old_state)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +       struct drm_connector *conn;
> > > +       struct drm_connector_state *conn_state;
> > > +       struct drm_crtc *crtc;
> > > +       struct drm_crtc_state *crtc_state;
> > > +       int ret = 0;
> > > +       int i;
> > > +
> > > +       conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > +                                                       bridge->encoder);
> >
> > mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> >                                                        bridge->encoder);
> >
> The connector doesn't seem to be used anywhere else in the driver.
> Though I can add a drm_connector in the mtk_dp struct.
>
> > > +       if (!conn) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as connector is missing\n");
> > > +               return;
> > > +       }
> > > +
> > > +       memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > > +
> > > +       conn_state =
> > > +               drm_atomic_get_new_connector_state(old_state->base.state, conn);
> > > +       if (!conn_state) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as connector state is missing\n");
> > > +               return;
> > > +       }
> > > +
> > > +       crtc = conn_state->crtc;
> > > +       if (!crtc) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as connector state doesn't have a crtc\n");
> > > +               return;
> > > +       }
> > > +
> > > +       crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> > > +       if (!crtc_state) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as crtc state is missing\n");
> > > +               return;
> > > +       }
> > > +
> > > +       mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);
> >
> > Refer to mtk_hdmi_bridge_atomic_enable() for getting the mode.
> >
> I'm sorry, I don't understand what I'm supposed to do here.
> Could you elaborate?
>
> > > +       if (!mtk_dp_parse_capabilities(mtk_dp)) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as nothing is plugged in\n");
> > > +               return;
> > > +       }
> > > +
> > > +       //training
> >
> > Run checkpatch first.
> >
> > Regards,
> > Chun-Kuang.
> >
> > > +       for (i = 0; i < 50; i++) {
> > > +               ret = mtk_dp_train_handler(mtk_dp);
> > > +               if (ret) {
> > > +                       drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> > > +                               ret);
> > > +                       return;
> > > +               }
> > > +
> > > +               ret = mtk_dp_state_handler(mtk_dp);
> > > +               if (ret) {
> > > +                       drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> > > +                               ret);
> > > +                       return;
> > > +               }
> > > +       }
> > > +
> > > +       mtk_dp->enabled = true;
> > > +       mtk_dp_update_plugged_status(mtk_dp);
> > > +}
> > > +
> >
> >
> >
> > > --
> > > 2.32.0
> > >

Thx,
Guillaume.

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 14:13         ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 14:13 UTC (permalink / raw)
  To: Chun-Kuang Hu
  Cc: David Airlie, Thomas Zimmermann, linux-kernel,
	Markus Schneider-Pargmann, ARM/Mediatek SoC support,
	DRI Development, Matthias Brugger, Linux ARM, kernel test robot

Hi Chun-Kuang.

Quoting Guillaume Ranquet (2021-12-02 16:31:03)
> Hi Chun-Kuang.
>
> Quoting Chun-Kuang Hu (2021-11-25 16:27:45)
> > Hi, Guillaume:
> >
> > This is a big patch, so I give some comment first.
> >
> > Guillaume Ranquet <granquet@baylibre.com> 於 2021年11月10日 週三 下午9:06寫道:
> > >
> > > From: Markus Schneider-Pargmann <msp@baylibre.com>
> > >
> > > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > > according phy driver mediatek-dp-phy.
> > >
> > > It supports both functional units on the mt8195, the embedded
> > > DisplayPort as well as the external DisplayPort units. It offers
> > > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > > to 4 lanes.
> > >
> > > The driver creates a child device for the phy. The child device will
> > > never exist without the parent being active. As they are sharing a
> > > register range, the parent passes a regmap pointer to the child so that
> > > both can work with the same register range. The phy driver sets device
> > > data that is read by the parent to get the phy device that can be used
> > > to control the phy properties.
> > >
> > > This driver is based on an initial version by
> > > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> > >
> > > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > > Reported-by: kernel test robot <lkp@intel.com>
> > > ---
> > >  drivers/gpu/drm/drm_edid.c              |    2 +-
> >
> > Separate this to another patch.
> >
> > >  drivers/gpu/drm/mediatek/Kconfig        |    7 +
> > >  drivers/gpu/drm/mediatek/Makefile       |    2 +
> > >  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
> > >  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
> > >  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
> >
> > Ditto.
> >
> > >  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
> >
> > Ditto.
> >
> > >  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
> >
> > Ditto
> >
> > >  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
> >
> > Ditto
> >
> yes my bad, I've made a bunch of fixup which ended up in the wrong place.
> It will be fixed for the next version.
>
> > >  9 files changed, 3799 insertions(+), 13 deletions(-)
> > >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
> > >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> > >
> > > diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> > > index 500279a82167a..bfd98b50ceb5b 100644
> > > --- a/drivers/gpu/drm/drm_edid.c
> > > +++ b/drivers/gpu/drm/drm_edid.c
> > > @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
> > >          * modes and forbids YCRCB422 support for all video modes per
> > >          * HDMI 1.3 spec.
> > >          */
> > > -       info->color_formats = DRM_COLOR_FORMAT_RGB444;
> > > +       info->color_formats |= DRM_COLOR_FORMAT_RGB444;
> > >
> > >         /* YCRCB444 is optional according to spec. */
> > >         if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
> > > diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> > > index 2976d21e9a34a..029b94c716131 100644
> > > --- a/drivers/gpu/drm/mediatek/Kconfig
> > > +++ b/drivers/gpu/drm/mediatek/Kconfig
> > > @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
> > >         select PHY_MTK_HDMI
> > >         help
> > >           DRM/KMS HDMI driver for Mediatek SoCs
> > > +
> > > +config MTK_DPTX_SUPPORT
> > > +       tristate "DRM DPTX Support for Mediatek SoCs"
> > > +       depends on DRM_MEDIATEK
> > > +       select PHY_MTK_DP
> > > +       help
> > > +         DRM/KMS Display Port driver for Mediatek SoCs.
> > > diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> > > index 29098d7c8307c..d86a6406055e6 100644
> > > --- a/drivers/gpu/drm/mediatek/Makefile
> > > +++ b/drivers/gpu/drm/mediatek/Makefile
> > > @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
> > >                           mtk_hdmi_ddc.o
> > >
> > >  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> > > +
> > > +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> > > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> > > new file mode 100644
> > > index 0000000000000..83087219d5a5e
> > > --- /dev/null
> > > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> > > @@ -0,0 +1,3094 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Copyright (c) 2019 MediaTek Inc.
> > > + * Copyright (c) 2021 BayLibre
> > > + */
> > > +
> > > +#include <drm/drm_atomic_helper.h>
> > > +#include <drm/drm_bridge.h>
> > > +#include <drm/drm_crtc.h>
> > > +#include <drm/drm_dp_helper.h>
> > > +#include <drm/drm_edid.h>
> > > +#include <drm/drm_of.h>
> > > +#include <drm/drm_panel.h>
> > > +#include <drm/drm_print.h>
> > > +#include <drm/drm_probe_helper.h>
> > > +#include <linux/arm-smccc.h>
> > > +#include <linux/clk.h>
> > > +#include <linux/delay.h>
> > > +#include <linux/errno.h>
> > > +#include <linux/kernel.h>
> > > +#include <linux/nvmem-consumer.h>
> > > +#include <linux/of.h>
> > > +#include <linux/of_irq.h>
> > > +#include <linux/of_platform.h>
> > > +#include <linux/phy/phy.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/pm_runtime.h>
> > > +#include <linux/regmap.h>
> > > +#include <sound/hdmi-codec.h>
> > > +#include <video/videomode.h>
> > > +
> > > +#include "mtk_dp_reg.h"
> > > +
> > > +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> > > +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> > > +
> > > +#define MTK_DP_MAX_LANES 4
> > > +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> > > +
> > > +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> > > +
> > > +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> > > +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> > > +
> > > +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> > > +
> > > +#define MTK_DP_DP_VERSION_11 0x11
> > > +
> > > +enum mtk_dp_state {
> > > +       MTK_DP_STATE_INITIAL,
> > > +       MTK_DP_STATE_IDLE,
> > > +       MTK_DP_STATE_PREPARE,
> > > +       MTK_DP_STATE_NORMAL,
> > > +};
> > > +
> > > +enum mtk_dp_train_state {
> > > +       MTK_DP_TRAIN_STATE_STARTUP = 0,
> > > +       MTK_DP_TRAIN_STATE_CHECKCAP,
> > > +       MTK_DP_TRAIN_STATE_CHECKEDID,
> > > +       MTK_DP_TRAIN_STATE_TRAINING_PRE,
> > > +       MTK_DP_TRAIN_STATE_TRAINING,
> > > +       MTK_DP_TRAIN_STATE_CHECKTIMING,
> > > +       MTK_DP_TRAIN_STATE_NORMAL,
> > > +       MTK_DP_TRAIN_STATE_POWERSAVE,
> > > +       MTK_DP_TRAIN_STATE_DPIDLE,
> > > +};
> > > +
> > > +struct mtk_dp_timings {
> > > +       struct videomode vm;
> > > +
> > > +       u16 htotal;
> > > +       u16 vtotal;
> > > +       u8 frame_rate;
> > > +       u32 pix_rate_khz;
> > > +};
> > > +
> > > +struct mtk_dp_train_info {
> > > +       bool tps3;
> > > +       bool tps4;
> > > +       bool sink_ssc;
> > > +       bool cable_plugged_in;
> > > +       bool cable_state_change;
> > > +       bool cr_done;
> > > +       bool eq_done;
> > > +
> > > +       // link_rate is in multiple of 0.27Gbps
> > > +       int link_rate;
> > > +       int lane_count;
> > > +
> > > +       int irq_status;
> > > +       int check_cap_count;
> > > +};
> > > +
> > > +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> > > +enum mtk_dp_color_format {
> > > +       MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> > > +       MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> > > +       MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> > > +       MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> > > +       MTK_DP_COLOR_FORMAT_YONLY = 4,
> > > +       MTK_DP_COLOR_FORMAT_RAW = 5,
> > > +       MTK_DP_COLOR_FORMAT_RESERVED = 6,
> > > +       MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> > > +       MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> > > +};
> > > +
> > > +// Multiple of 0.27Gbps
> > > +enum mtk_dp_linkrate {
> > > +       MTK_DP_LINKRATE_RBR = 0x6,
> > > +       MTK_DP_LINKRATE_HBR = 0xA,
> > > +       MTK_DP_LINKRATE_HBR2 = 0x14,
> > > +       MTK_DP_LINKRATE_HBR25 = 0x19,
> > > +       MTK_DP_LINKRATE_HBR3 = 0x1E,
> > > +};
> > > +
> > > +// Same values as used for DP Spec MISC0 bits 5,6,7
> > > +enum mtk_dp_color_depth {
> > > +       MTK_DP_COLOR_DEPTH_6BIT = 0,
> > > +       MTK_DP_COLOR_DEPTH_8BIT = 1,
> > > +       MTK_DP_COLOR_DEPTH_10BIT = 2,
> > > +       MTK_DP_COLOR_DEPTH_12BIT = 3,
> > > +       MTK_DP_COLOR_DEPTH_16BIT = 4,
> > > +       MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> > > +};
> > > +
> > > +struct mtk_dp_audio_cfg {
> > > +       int sample_rate;
> > > +       int word_length_bits;
> > > +       int channels;
> > > +};
> > > +
> > > +struct mtk_dp_info {
> > > +       enum mtk_dp_color_depth depth;
> > > +       enum mtk_dp_color_format format;
> > > +       struct mtk_dp_audio_cfg audio_caps;
> > > +       struct mtk_dp_timings timings;
> > > +};
> > > +
> > > +struct dp_cal_data {
> > > +       unsigned int glb_bias_trim;
> > > +       unsigned int clktx_impse;
> > > +
> > > +       unsigned int ln0_tx_impsel_pmos;
> > > +       unsigned int ln0_tx_impsel_nmos;
> > > +       unsigned int ln1_tx_impsel_pmos;
> > > +       unsigned int ln1_tx_impsel_nmos;
> > > +       unsigned int ln2_tx_impsel_pmos;
> > > +       unsigned int ln2_tx_impsel_nmos;
> > > +       unsigned int ln3_tx_impsel_pmos;
> > > +       unsigned int ln3_tx_impsel_nmos;
> > > +};
> > > +
> > > +struct mtk_dp {
> > > +       struct device *dev;
> > > +       struct platform_device *phy_dev;
> > > +       struct phy *phy;
> > > +       struct dp_cal_data cal_data;
> > > +
> > > +       struct drm_device *drm_dev;
> > > +       struct drm_bridge bridge;
> > > +       struct drm_bridge *next_bridge;
> > > +       struct drm_dp_aux aux;
> > > +
> > > +       struct mutex edid_lock;
> > > +       struct edid *edid;
> > > +
> > > +       u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> > > +
> > > +       struct mtk_dp_info info;
> > > +       enum mtk_dp_state state;
> > > +
> > > +       struct mtk_dp_train_info train_info;
> > > +       enum mtk_dp_train_state train_state;
> > > +       unsigned int input_fmt;
> > > +
> > > +       struct regmap *regs;
> > > +       struct clk *dp_tx_clk;
> > > +
> > > +       bool enabled;
> > > +       bool audio_enable;
> > > +
> > > +       bool has_fec;
> > > +       struct mutex dp_lock;
> > > +
> > > +       struct mutex update_plugged_status_lock;
> > > +
> > > +       hdmi_codec_plugged_cb plugged_cb;
> > > +       struct device *codec_dev;
> > > +       u8 connector_eld[MAX_ELD_BYTES];
> > > +};
> > > +
> > > +enum mtk_dp_sdp_type {
> > > +       MTK_DP_SDP_NONE = 0x00,
> > > +       MTK_DP_SDP_ACM = 0x01,
> > > +       MTK_DP_SDP_ISRC = 0x02,
> > > +       MTK_DP_SDP_AVI = 0x03,
> > > +       MTK_DP_SDP_AUI = 0x04,
> > > +       MTK_DP_SDP_SPD = 0x05,
> > > +       MTK_DP_SDP_MPEG = 0x06,
> > > +       MTK_DP_SDP_NTSC = 0x07,
> > > +       MTK_DP_SDP_VSP = 0x08,
> > > +       MTK_DP_SDP_VSC = 0x09,
> > > +       MTK_DP_SDP_EXT = 0x0A,
> > > +       MTK_DP_SDP_PPS0 = 0x0B,
> > > +       MTK_DP_SDP_PPS1 = 0x0C,
> > > +       MTK_DP_SDP_PPS2 = 0x0D,
> > > +       MTK_DP_SDP_PPS3 = 0x0E,
> > > +       MTK_DP_SDP_DRM = 0x10,
> > > +       MTK_DP_SDP_MAX_NUM
> > > +};
> > > +
> > > +struct mtk_dp_sdp_packet {
> > > +       enum mtk_dp_sdp_type type;
> > > +       struct dp_sdp sdp;
> > > +};
> > > +
> > > +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> > > +union mtk_dp_audio_channel_status {
> > > +       struct {
> > > +               u8 rev : 1;
> > > +               u8 is_lpcm : 1;
> > > +               u8 copy_right : 1;
> > > +               u8 additional_format_info : 3;
> > > +               u8 channel_status_mode : 2;
> > > +               u8 category_code;
> > > +               u8 src_num : 4;
> > > +               u8 channel_num : 4;
> > > +               u8 sampling_freq : 4;
> > > +               u8 clk_accuracy : 2;
> > > +               u8 rev2 : 2;
> > > +               u8 word_len : 4;
> > > +               u8 original_sampling_freq : 4;
> > > +       } iec;
> > > +
> > > +       u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> > > +};
> > > +
> > > +static struct regmap_config mtk_dp_regmap_config = {
> > > +       .reg_bits = 32,
> > > +       .val_bits = 32,
> > > +       .reg_stride = 4,
> > > +       .max_register = SEC_OFFSET + 0x90,
> > > +       .name = "mtk-dp-registers",
> > > +};
> > > +
> > > +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
> >
> > Separate edp and displayport into two patches. For example, the first
> > patch support edp only, and the second patch add displayport function.
> >
> I have made the opposite as it seemed a bit more logical from how it's coded
> now.
> I've made a single patch with DP and a new one adding eDP support on top of it.
> The change is quite small, adding the eDP on top of DP:
>  drivers/gpu/drm/mediatek/mtk_dp.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
>  1 file changed, 74 insertions(+), 29 deletions(-)
> > > +{
> > > +       return mtk_dp->next_bridge != NULL;
> > > +}
> > > +
> >
> > [snip]
> >
> > > +
> > > +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> > > +                                   struct drm_connector *connector)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +       bool enabled = mtk_dp->enabled;
> > > +       struct edid *new_edid = NULL;
> > > +
> > > +       if (!enabled)
> > > +               drm_bridge_chain_pre_enable(bridge);
> >
> > In mtk_hdmi_bridge_get_edid(), there does not check the power. Why in
> > this function need this?
> > Does mtk hdmi driver has a bug?
> >
> I don't know, but I will be asking around.
In short, no, the hdmi driver does not have a bug.
We have to check power here as this driver handles both a DP and an eDP panel.

As the power is controlled by the panel driver in the case of eDP, we have
to make sure the bridge is enabled before trying to read the edid.

Hope this answers the question.

> > > +
> > > +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> > > +       usleep_range(2000, 5000);
> > > +
> > > +       if (mtk_dp_plug_state(mtk_dp))
> > > +               new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> > > +
> > > +       if (!enabled)
> > > +               drm_bridge_chain_post_disable(bridge);
> > > +
> > > +       mutex_lock(&mtk_dp->edid_lock);
> > > +       kfree(mtk_dp->edid);
> > > +       mtk_dp->edid = NULL;
> > > +
> > > +       if (!new_edid) {
> > > +               mutex_unlock(&mtk_dp->edid_lock);
> > > +               return NULL;
> > > +       }
> > > +
> > > +       mtk_dp->edid = drm_edid_duplicate(new_edid);
> > > +       mutex_unlock(&mtk_dp->edid_lock);
> > > +
> > > +       return new_edid;
> > > +}
> > > +
> > > +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> > > +                                  struct drm_dp_aux_msg *msg)
> > > +{
> > > +       ssize_t err = -EAGAIN;
> > > +       struct mtk_dp *mtk_dp;
> > > +       bool is_read;
> > > +       u8 request;
> > > +       size_t accessed_bytes = 0;
> > > +       int retry = 3, ret = 0;
> > > +
> > > +       mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> > > +
> > > +       if (!mtk_dp->train_info.cable_plugged_in ||
> > > +           mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> > > +               mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> > > +               err = -EAGAIN;
> > > +               goto err;
> > > +       }
> > > +
> > > +       switch (msg->request) {
> > > +       case DP_AUX_I2C_MOT:
> > > +       case DP_AUX_I2C_WRITE:
> > > +       case DP_AUX_NATIVE_WRITE:
> > > +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> > > +       case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> > > +               request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> > > +               is_read = false;
> > > +               break;
> > > +       case DP_AUX_I2C_READ:
> > > +       case DP_AUX_NATIVE_READ:
> > > +       case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> > > +               request = msg->request;
> > > +               is_read = true;
> > > +               break;
> > > +       default:
> > > +               drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> > > +                       msg->request);
> > > +               err = -EINVAL;
> > > +               goto err;
> >
> > Directly return here.
> >
> Hmm, not sure yet, it seems I still need to prime msg->reply anyway.
> Though the error handling can be simplified a bit.
>
> > > +       }
> > > +
> > > +       if (msg->size == 0) {
> > > +               mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> > > +                                      msg->address + accessed_bytes,
> > > +                                      msg->buffer + accessed_bytes, 0);
> > > +       } else {
> > > +               while (accessed_bytes < msg->size) {
> > > +                       size_t to_access =
> > > +                               min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> > > +                                     msg->size - accessed_bytes);
> > > +                       retry = 3;
> > > +                       while (retry--) {
> > > +                               ret = mtk_dp_aux_do_transfer(
> > > +                                       mtk_dp, is_read, request,
> > > +                                       msg->address + accessed_bytes,
> > > +                                       msg->buffer + accessed_bytes,
> > > +                                       to_access);
> > > +                               if (ret == 0)
> > > +                                       break;
> > > +                               udelay(50);
> > > +                       }
> > > +                       if (!retry && ret) {
> > > +                               drm_info(mtk_dp->drm_dev,
> > > +                                        "Failed to do AUX transfer: %d\n",
> > > +                                        ret);
> > > +                               break;
> > > +                       }
> > > +                       accessed_bytes += to_access;
> > > +               }
> > > +       }
> > > +err:
> > > +       if (!ret) {
> > > +               msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > > +               ret = msg->size;
> > > +       } else {
> > > +               msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> > > +               return err;
> > > +       }
> > > +
> > > +       msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > > +       return msg->size;
> > > +}
> > > +
> > > +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> > > +{
> > > +       drm_dp_aux_init(&mtk_dp->aux);
> > > +       mtk_dp->aux.name = "aux_mtk_dp";
> > > +       mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> > > +}
> > > +
> > > +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> > > +{
> > > +       mutex_lock(&mtk_dp->dp_lock);
> > > +
> > > +       mtk_dp_hwirq_enable(mtk_dp, false);
> > > +       mtk_dp_power_disable(mtk_dp);
> > > +       phy_exit(mtk_dp->phy);
> > > +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > > +
> > > +       mutex_unlock(&mtk_dp->dp_lock);
> > > +}
> > > +
> > > +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> > > +{
> > > +       int ret = 0;
> > > +
> > > +       mutex_lock(&mtk_dp->dp_lock);
> > > +
> > > +       ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> > > +       if (ret < 0) {
> > > +               dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> > > +               goto err;
> > > +       }
> > > +       ret = phy_init(mtk_dp->phy);
> > > +       if (ret) {
> > > +               dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> > > +               goto err_phy_init;
> > > +       }
> > > +       ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> > > +       if (ret) {
> > > +               dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> > > +               goto err_phy_config;
> > > +       }
> > > +
> > > +       mtk_dp_init_port(mtk_dp);
> > > +       mtk_dp_power_enable(mtk_dp);
> > > +       mtk_dp_hwirq_enable(mtk_dp, true);
> > > +
> > > +err_phy_config:
> > > +       phy_exit(mtk_dp->phy);
> > > +err_phy_init:
> > > +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > > +err:
> > > +       mutex_unlock(&mtk_dp->dp_lock);
> > > +       return ret;
> > > +}
> > > +
> > > +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> > > +                               enum drm_bridge_attach_flags flags)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +       int ret;
> > > +
> > > +       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> > > +               dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> > > +               return -EINVAL;
> > > +       }
> > > +
> > > +       ret = mtk_dp_poweron(mtk_dp);
> >
> > Move the power on to mtk_dp_bridge_atomic_enable().
> >
Moving the power-on sequence to the mtk_dp_bridge_atoimc_enable()
breaks the eDP functionality.
For the time being (ie: v7), the power on will stay in the
bridge_attach() callback, sorry.

> > > +       if (ret)
> > > +               return ret;
> > > +
> > > +       if (mtk_dp->next_bridge) {
> > > +               ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> > > +                                       &mtk_dp->bridge, flags);
> > > +               if (ret) {
> > > +                       drm_warn(mtk_dp->drm_dev,
> > > +                                "Failed to attach external bridge: %d\n", ret);
> > > +                       goto err_bridge_attach;
> > > +               }
> > > +       }
> > > +
> > > +       mtk_dp->drm_dev = bridge->dev;
> > > +
> > > +       return 0;
> > > +
> > > +err_bridge_attach:
> > > +       mtk_dp_poweroff(mtk_dp);
> > > +       return ret;
> > > +}
> > > +
> > > +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +
> > > +       mtk_dp->drm_dev = NULL;
> > > +
> > > +       mtk_dp_poweroff(mtk_dp);
> > > +}
> > > +
> > > +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> > > +                                        struct drm_bridge_state *old_state)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +
> > > +       mtk_dp_video_mute(mtk_dp, true);
> > > +       mtk_dp_audio_mute(mtk_dp, true);
> > > +       mtk_dp->state = MTK_DP_STATE_IDLE;
> > > +       mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > > +
> > > +       mtk_dp->enabled = false;
> > > +       msleep(100);
> > > +}
> > > +
> > > +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> > > +                                         struct drm_display_mode *mode)
> > > +{
> > > +       struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> > > +
> > > +       drm_display_mode_to_videomode(mode, &timings->vm);
> > > +       timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> > > +       timings->htotal = mode->htotal;
> > > +       timings->vtotal = mode->vtotal;
> > > +}
> > > +
> > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > +                                       struct drm_bridge_state *old_state)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +       struct drm_connector *conn;
> > > +       struct drm_connector_state *conn_state;
> > > +       struct drm_crtc *crtc;
> > > +       struct drm_crtc_state *crtc_state;
> > > +       int ret = 0;
> > > +       int i;
> > > +
> > > +       conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > +                                                       bridge->encoder);
> >
> > mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> >                                                        bridge->encoder);
> >
> The connector doesn't seem to be used anywhere else in the driver.
> Though I can add a drm_connector in the mtk_dp struct.
>
> > > +       if (!conn) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as connector is missing\n");
> > > +               return;
> > > +       }
> > > +
> > > +       memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > > +
> > > +       conn_state =
> > > +               drm_atomic_get_new_connector_state(old_state->base.state, conn);
> > > +       if (!conn_state) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as connector state is missing\n");
> > > +               return;
> > > +       }
> > > +
> > > +       crtc = conn_state->crtc;
> > > +       if (!crtc) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as connector state doesn't have a crtc\n");
> > > +               return;
> > > +       }
> > > +
> > > +       crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> > > +       if (!crtc_state) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as crtc state is missing\n");
> > > +               return;
> > > +       }
> > > +
> > > +       mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);
> >
> > Refer to mtk_hdmi_bridge_atomic_enable() for getting the mode.
> >
> I'm sorry, I don't understand what I'm supposed to do here.
> Could you elaborate?
>
> > > +       if (!mtk_dp_parse_capabilities(mtk_dp)) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as nothing is plugged in\n");
> > > +               return;
> > > +       }
> > > +
> > > +       //training
> >
> > Run checkpatch first.
> >
> > Regards,
> > Chun-Kuang.
> >
> > > +       for (i = 0; i < 50; i++) {
> > > +               ret = mtk_dp_train_handler(mtk_dp);
> > > +               if (ret) {
> > > +                       drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> > > +                               ret);
> > > +                       return;
> > > +               }
> > > +
> > > +               ret = mtk_dp_state_handler(mtk_dp);
> > > +               if (ret) {
> > > +                       drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> > > +                               ret);
> > > +                       return;
> > > +               }
> > > +       }
> > > +
> > > +       mtk_dp->enabled = true;
> > > +       mtk_dp_update_plugged_status(mtk_dp);
> > > +}
> > > +
> >
> >
> >
> > > --
> > > 2.32.0
> > >

Thx,
Guillaume.

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 14:13         ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 14:13 UTC (permalink / raw)
  To: Chun-Kuang Hu
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	DRI Development, ARM/Mediatek SoC support, Linux ARM

Hi Chun-Kuang.

Quoting Guillaume Ranquet (2021-12-02 16:31:03)
> Hi Chun-Kuang.
>
> Quoting Chun-Kuang Hu (2021-11-25 16:27:45)
> > Hi, Guillaume:
> >
> > This is a big patch, so I give some comment first.
> >
> > Guillaume Ranquet <granquet@baylibre.com> 於 2021年11月10日 週三 下午9:06寫道:
> > >
> > > From: Markus Schneider-Pargmann <msp@baylibre.com>
> > >
> > > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > > according phy driver mediatek-dp-phy.
> > >
> > > It supports both functional units on the mt8195, the embedded
> > > DisplayPort as well as the external DisplayPort units. It offers
> > > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > > to 4 lanes.
> > >
> > > The driver creates a child device for the phy. The child device will
> > > never exist without the parent being active. As they are sharing a
> > > register range, the parent passes a regmap pointer to the child so that
> > > both can work with the same register range. The phy driver sets device
> > > data that is read by the parent to get the phy device that can be used
> > > to control the phy properties.
> > >
> > > This driver is based on an initial version by
> > > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> > >
> > > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > > Reported-by: kernel test robot <lkp@intel.com>
> > > ---
> > >  drivers/gpu/drm/drm_edid.c              |    2 +-
> >
> > Separate this to another patch.
> >
> > >  drivers/gpu/drm/mediatek/Kconfig        |    7 +
> > >  drivers/gpu/drm/mediatek/Makefile       |    2 +
> > >  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
> > >  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
> > >  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
> >
> > Ditto.
> >
> > >  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
> >
> > Ditto.
> >
> > >  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
> >
> > Ditto
> >
> > >  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
> >
> > Ditto
> >
> yes my bad, I've made a bunch of fixup which ended up in the wrong place.
> It will be fixed for the next version.
>
> > >  9 files changed, 3799 insertions(+), 13 deletions(-)
> > >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
> > >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> > >
> > > diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> > > index 500279a82167a..bfd98b50ceb5b 100644
> > > --- a/drivers/gpu/drm/drm_edid.c
> > > +++ b/drivers/gpu/drm/drm_edid.c
> > > @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
> > >          * modes and forbids YCRCB422 support for all video modes per
> > >          * HDMI 1.3 spec.
> > >          */
> > > -       info->color_formats = DRM_COLOR_FORMAT_RGB444;
> > > +       info->color_formats |= DRM_COLOR_FORMAT_RGB444;
> > >
> > >         /* YCRCB444 is optional according to spec. */
> > >         if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
> > > diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> > > index 2976d21e9a34a..029b94c716131 100644
> > > --- a/drivers/gpu/drm/mediatek/Kconfig
> > > +++ b/drivers/gpu/drm/mediatek/Kconfig
> > > @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
> > >         select PHY_MTK_HDMI
> > >         help
> > >           DRM/KMS HDMI driver for Mediatek SoCs
> > > +
> > > +config MTK_DPTX_SUPPORT
> > > +       tristate "DRM DPTX Support for Mediatek SoCs"
> > > +       depends on DRM_MEDIATEK
> > > +       select PHY_MTK_DP
> > > +       help
> > > +         DRM/KMS Display Port driver for Mediatek SoCs.
> > > diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> > > index 29098d7c8307c..d86a6406055e6 100644
> > > --- a/drivers/gpu/drm/mediatek/Makefile
> > > +++ b/drivers/gpu/drm/mediatek/Makefile
> > > @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
> > >                           mtk_hdmi_ddc.o
> > >
> > >  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> > > +
> > > +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> > > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> > > new file mode 100644
> > > index 0000000000000..83087219d5a5e
> > > --- /dev/null
> > > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> > > @@ -0,0 +1,3094 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Copyright (c) 2019 MediaTek Inc.
> > > + * Copyright (c) 2021 BayLibre
> > > + */
> > > +
> > > +#include <drm/drm_atomic_helper.h>
> > > +#include <drm/drm_bridge.h>
> > > +#include <drm/drm_crtc.h>
> > > +#include <drm/drm_dp_helper.h>
> > > +#include <drm/drm_edid.h>
> > > +#include <drm/drm_of.h>
> > > +#include <drm/drm_panel.h>
> > > +#include <drm/drm_print.h>
> > > +#include <drm/drm_probe_helper.h>
> > > +#include <linux/arm-smccc.h>
> > > +#include <linux/clk.h>
> > > +#include <linux/delay.h>
> > > +#include <linux/errno.h>
> > > +#include <linux/kernel.h>
> > > +#include <linux/nvmem-consumer.h>
> > > +#include <linux/of.h>
> > > +#include <linux/of_irq.h>
> > > +#include <linux/of_platform.h>
> > > +#include <linux/phy/phy.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/pm_runtime.h>
> > > +#include <linux/regmap.h>
> > > +#include <sound/hdmi-codec.h>
> > > +#include <video/videomode.h>
> > > +
> > > +#include "mtk_dp_reg.h"
> > > +
> > > +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> > > +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> > > +
> > > +#define MTK_DP_MAX_LANES 4
> > > +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> > > +
> > > +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> > > +
> > > +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> > > +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> > > +
> > > +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> > > +
> > > +#define MTK_DP_DP_VERSION_11 0x11
> > > +
> > > +enum mtk_dp_state {
> > > +       MTK_DP_STATE_INITIAL,
> > > +       MTK_DP_STATE_IDLE,
> > > +       MTK_DP_STATE_PREPARE,
> > > +       MTK_DP_STATE_NORMAL,
> > > +};
> > > +
> > > +enum mtk_dp_train_state {
> > > +       MTK_DP_TRAIN_STATE_STARTUP = 0,
> > > +       MTK_DP_TRAIN_STATE_CHECKCAP,
> > > +       MTK_DP_TRAIN_STATE_CHECKEDID,
> > > +       MTK_DP_TRAIN_STATE_TRAINING_PRE,
> > > +       MTK_DP_TRAIN_STATE_TRAINING,
> > > +       MTK_DP_TRAIN_STATE_CHECKTIMING,
> > > +       MTK_DP_TRAIN_STATE_NORMAL,
> > > +       MTK_DP_TRAIN_STATE_POWERSAVE,
> > > +       MTK_DP_TRAIN_STATE_DPIDLE,
> > > +};
> > > +
> > > +struct mtk_dp_timings {
> > > +       struct videomode vm;
> > > +
> > > +       u16 htotal;
> > > +       u16 vtotal;
> > > +       u8 frame_rate;
> > > +       u32 pix_rate_khz;
> > > +};
> > > +
> > > +struct mtk_dp_train_info {
> > > +       bool tps3;
> > > +       bool tps4;
> > > +       bool sink_ssc;
> > > +       bool cable_plugged_in;
> > > +       bool cable_state_change;
> > > +       bool cr_done;
> > > +       bool eq_done;
> > > +
> > > +       // link_rate is in multiple of 0.27Gbps
> > > +       int link_rate;
> > > +       int lane_count;
> > > +
> > > +       int irq_status;
> > > +       int check_cap_count;
> > > +};
> > > +
> > > +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> > > +enum mtk_dp_color_format {
> > > +       MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> > > +       MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> > > +       MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> > > +       MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> > > +       MTK_DP_COLOR_FORMAT_YONLY = 4,
> > > +       MTK_DP_COLOR_FORMAT_RAW = 5,
> > > +       MTK_DP_COLOR_FORMAT_RESERVED = 6,
> > > +       MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> > > +       MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> > > +};
> > > +
> > > +// Multiple of 0.27Gbps
> > > +enum mtk_dp_linkrate {
> > > +       MTK_DP_LINKRATE_RBR = 0x6,
> > > +       MTK_DP_LINKRATE_HBR = 0xA,
> > > +       MTK_DP_LINKRATE_HBR2 = 0x14,
> > > +       MTK_DP_LINKRATE_HBR25 = 0x19,
> > > +       MTK_DP_LINKRATE_HBR3 = 0x1E,
> > > +};
> > > +
> > > +// Same values as used for DP Spec MISC0 bits 5,6,7
> > > +enum mtk_dp_color_depth {
> > > +       MTK_DP_COLOR_DEPTH_6BIT = 0,
> > > +       MTK_DP_COLOR_DEPTH_8BIT = 1,
> > > +       MTK_DP_COLOR_DEPTH_10BIT = 2,
> > > +       MTK_DP_COLOR_DEPTH_12BIT = 3,
> > > +       MTK_DP_COLOR_DEPTH_16BIT = 4,
> > > +       MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> > > +};
> > > +
> > > +struct mtk_dp_audio_cfg {
> > > +       int sample_rate;
> > > +       int word_length_bits;
> > > +       int channels;
> > > +};
> > > +
> > > +struct mtk_dp_info {
> > > +       enum mtk_dp_color_depth depth;
> > > +       enum mtk_dp_color_format format;
> > > +       struct mtk_dp_audio_cfg audio_caps;
> > > +       struct mtk_dp_timings timings;
> > > +};
> > > +
> > > +struct dp_cal_data {
> > > +       unsigned int glb_bias_trim;
> > > +       unsigned int clktx_impse;
> > > +
> > > +       unsigned int ln0_tx_impsel_pmos;
> > > +       unsigned int ln0_tx_impsel_nmos;
> > > +       unsigned int ln1_tx_impsel_pmos;
> > > +       unsigned int ln1_tx_impsel_nmos;
> > > +       unsigned int ln2_tx_impsel_pmos;
> > > +       unsigned int ln2_tx_impsel_nmos;
> > > +       unsigned int ln3_tx_impsel_pmos;
> > > +       unsigned int ln3_tx_impsel_nmos;
> > > +};
> > > +
> > > +struct mtk_dp {
> > > +       struct device *dev;
> > > +       struct platform_device *phy_dev;
> > > +       struct phy *phy;
> > > +       struct dp_cal_data cal_data;
> > > +
> > > +       struct drm_device *drm_dev;
> > > +       struct drm_bridge bridge;
> > > +       struct drm_bridge *next_bridge;
> > > +       struct drm_dp_aux aux;
> > > +
> > > +       struct mutex edid_lock;
> > > +       struct edid *edid;
> > > +
> > > +       u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> > > +
> > > +       struct mtk_dp_info info;
> > > +       enum mtk_dp_state state;
> > > +
> > > +       struct mtk_dp_train_info train_info;
> > > +       enum mtk_dp_train_state train_state;
> > > +       unsigned int input_fmt;
> > > +
> > > +       struct regmap *regs;
> > > +       struct clk *dp_tx_clk;
> > > +
> > > +       bool enabled;
> > > +       bool audio_enable;
> > > +
> > > +       bool has_fec;
> > > +       struct mutex dp_lock;
> > > +
> > > +       struct mutex update_plugged_status_lock;
> > > +
> > > +       hdmi_codec_plugged_cb plugged_cb;
> > > +       struct device *codec_dev;
> > > +       u8 connector_eld[MAX_ELD_BYTES];
> > > +};
> > > +
> > > +enum mtk_dp_sdp_type {
> > > +       MTK_DP_SDP_NONE = 0x00,
> > > +       MTK_DP_SDP_ACM = 0x01,
> > > +       MTK_DP_SDP_ISRC = 0x02,
> > > +       MTK_DP_SDP_AVI = 0x03,
> > > +       MTK_DP_SDP_AUI = 0x04,
> > > +       MTK_DP_SDP_SPD = 0x05,
> > > +       MTK_DP_SDP_MPEG = 0x06,
> > > +       MTK_DP_SDP_NTSC = 0x07,
> > > +       MTK_DP_SDP_VSP = 0x08,
> > > +       MTK_DP_SDP_VSC = 0x09,
> > > +       MTK_DP_SDP_EXT = 0x0A,
> > > +       MTK_DP_SDP_PPS0 = 0x0B,
> > > +       MTK_DP_SDP_PPS1 = 0x0C,
> > > +       MTK_DP_SDP_PPS2 = 0x0D,
> > > +       MTK_DP_SDP_PPS3 = 0x0E,
> > > +       MTK_DP_SDP_DRM = 0x10,
> > > +       MTK_DP_SDP_MAX_NUM
> > > +};
> > > +
> > > +struct mtk_dp_sdp_packet {
> > > +       enum mtk_dp_sdp_type type;
> > > +       struct dp_sdp sdp;
> > > +};
> > > +
> > > +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> > > +union mtk_dp_audio_channel_status {
> > > +       struct {
> > > +               u8 rev : 1;
> > > +               u8 is_lpcm : 1;
> > > +               u8 copy_right : 1;
> > > +               u8 additional_format_info : 3;
> > > +               u8 channel_status_mode : 2;
> > > +               u8 category_code;
> > > +               u8 src_num : 4;
> > > +               u8 channel_num : 4;
> > > +               u8 sampling_freq : 4;
> > > +               u8 clk_accuracy : 2;
> > > +               u8 rev2 : 2;
> > > +               u8 word_len : 4;
> > > +               u8 original_sampling_freq : 4;
> > > +       } iec;
> > > +
> > > +       u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> > > +};
> > > +
> > > +static struct regmap_config mtk_dp_regmap_config = {
> > > +       .reg_bits = 32,
> > > +       .val_bits = 32,
> > > +       .reg_stride = 4,
> > > +       .max_register = SEC_OFFSET + 0x90,
> > > +       .name = "mtk-dp-registers",
> > > +};
> > > +
> > > +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
> >
> > Separate edp and displayport into two patches. For example, the first
> > patch support edp only, and the second patch add displayport function.
> >
> I have made the opposite as it seemed a bit more logical from how it's coded
> now.
> I've made a single patch with DP and a new one adding eDP support on top of it.
> The change is quite small, adding the eDP on top of DP:
>  drivers/gpu/drm/mediatek/mtk_dp.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
>  1 file changed, 74 insertions(+), 29 deletions(-)
> > > +{
> > > +       return mtk_dp->next_bridge != NULL;
> > > +}
> > > +
> >
> > [snip]
> >
> > > +
> > > +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> > > +                                   struct drm_connector *connector)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +       bool enabled = mtk_dp->enabled;
> > > +       struct edid *new_edid = NULL;
> > > +
> > > +       if (!enabled)
> > > +               drm_bridge_chain_pre_enable(bridge);
> >
> > In mtk_hdmi_bridge_get_edid(), there does not check the power. Why in
> > this function need this?
> > Does mtk hdmi driver has a bug?
> >
> I don't know, but I will be asking around.
In short, no, the hdmi driver does not have a bug.
We have to check power here as this driver handles both a DP and an eDP panel.

As the power is controlled by the panel driver in the case of eDP, we have
to make sure the bridge is enabled before trying to read the edid.

Hope this answers the question.

> > > +
> > > +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> > > +       usleep_range(2000, 5000);
> > > +
> > > +       if (mtk_dp_plug_state(mtk_dp))
> > > +               new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> > > +
> > > +       if (!enabled)
> > > +               drm_bridge_chain_post_disable(bridge);
> > > +
> > > +       mutex_lock(&mtk_dp->edid_lock);
> > > +       kfree(mtk_dp->edid);
> > > +       mtk_dp->edid = NULL;
> > > +
> > > +       if (!new_edid) {
> > > +               mutex_unlock(&mtk_dp->edid_lock);
> > > +               return NULL;
> > > +       }
> > > +
> > > +       mtk_dp->edid = drm_edid_duplicate(new_edid);
> > > +       mutex_unlock(&mtk_dp->edid_lock);
> > > +
> > > +       return new_edid;
> > > +}
> > > +
> > > +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> > > +                                  struct drm_dp_aux_msg *msg)
> > > +{
> > > +       ssize_t err = -EAGAIN;
> > > +       struct mtk_dp *mtk_dp;
> > > +       bool is_read;
> > > +       u8 request;
> > > +       size_t accessed_bytes = 0;
> > > +       int retry = 3, ret = 0;
> > > +
> > > +       mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> > > +
> > > +       if (!mtk_dp->train_info.cable_plugged_in ||
> > > +           mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> > > +               mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> > > +               err = -EAGAIN;
> > > +               goto err;
> > > +       }
> > > +
> > > +       switch (msg->request) {
> > > +       case DP_AUX_I2C_MOT:
> > > +       case DP_AUX_I2C_WRITE:
> > > +       case DP_AUX_NATIVE_WRITE:
> > > +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> > > +       case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> > > +               request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> > > +               is_read = false;
> > > +               break;
> > > +       case DP_AUX_I2C_READ:
> > > +       case DP_AUX_NATIVE_READ:
> > > +       case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> > > +               request = msg->request;
> > > +               is_read = true;
> > > +               break;
> > > +       default:
> > > +               drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> > > +                       msg->request);
> > > +               err = -EINVAL;
> > > +               goto err;
> >
> > Directly return here.
> >
> Hmm, not sure yet, it seems I still need to prime msg->reply anyway.
> Though the error handling can be simplified a bit.
>
> > > +       }
> > > +
> > > +       if (msg->size == 0) {
> > > +               mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> > > +                                      msg->address + accessed_bytes,
> > > +                                      msg->buffer + accessed_bytes, 0);
> > > +       } else {
> > > +               while (accessed_bytes < msg->size) {
> > > +                       size_t to_access =
> > > +                               min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> > > +                                     msg->size - accessed_bytes);
> > > +                       retry = 3;
> > > +                       while (retry--) {
> > > +                               ret = mtk_dp_aux_do_transfer(
> > > +                                       mtk_dp, is_read, request,
> > > +                                       msg->address + accessed_bytes,
> > > +                                       msg->buffer + accessed_bytes,
> > > +                                       to_access);
> > > +                               if (ret == 0)
> > > +                                       break;
> > > +                               udelay(50);
> > > +                       }
> > > +                       if (!retry && ret) {
> > > +                               drm_info(mtk_dp->drm_dev,
> > > +                                        "Failed to do AUX transfer: %d\n",
> > > +                                        ret);
> > > +                               break;
> > > +                       }
> > > +                       accessed_bytes += to_access;
> > > +               }
> > > +       }
> > > +err:
> > > +       if (!ret) {
> > > +               msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > > +               ret = msg->size;
> > > +       } else {
> > > +               msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> > > +               return err;
> > > +       }
> > > +
> > > +       msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > > +       return msg->size;
> > > +}
> > > +
> > > +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> > > +{
> > > +       drm_dp_aux_init(&mtk_dp->aux);
> > > +       mtk_dp->aux.name = "aux_mtk_dp";
> > > +       mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> > > +}
> > > +
> > > +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> > > +{
> > > +       mutex_lock(&mtk_dp->dp_lock);
> > > +
> > > +       mtk_dp_hwirq_enable(mtk_dp, false);
> > > +       mtk_dp_power_disable(mtk_dp);
> > > +       phy_exit(mtk_dp->phy);
> > > +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > > +
> > > +       mutex_unlock(&mtk_dp->dp_lock);
> > > +}
> > > +
> > > +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> > > +{
> > > +       int ret = 0;
> > > +
> > > +       mutex_lock(&mtk_dp->dp_lock);
> > > +
> > > +       ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> > > +       if (ret < 0) {
> > > +               dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> > > +               goto err;
> > > +       }
> > > +       ret = phy_init(mtk_dp->phy);
> > > +       if (ret) {
> > > +               dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> > > +               goto err_phy_init;
> > > +       }
> > > +       ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> > > +       if (ret) {
> > > +               dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> > > +               goto err_phy_config;
> > > +       }
> > > +
> > > +       mtk_dp_init_port(mtk_dp);
> > > +       mtk_dp_power_enable(mtk_dp);
> > > +       mtk_dp_hwirq_enable(mtk_dp, true);
> > > +
> > > +err_phy_config:
> > > +       phy_exit(mtk_dp->phy);
> > > +err_phy_init:
> > > +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > > +err:
> > > +       mutex_unlock(&mtk_dp->dp_lock);
> > > +       return ret;
> > > +}
> > > +
> > > +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> > > +                               enum drm_bridge_attach_flags flags)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +       int ret;
> > > +
> > > +       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> > > +               dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> > > +               return -EINVAL;
> > > +       }
> > > +
> > > +       ret = mtk_dp_poweron(mtk_dp);
> >
> > Move the power on to mtk_dp_bridge_atomic_enable().
> >
Moving the power-on sequence to the mtk_dp_bridge_atoimc_enable()
breaks the eDP functionality.
For the time being (ie: v7), the power on will stay in the
bridge_attach() callback, sorry.

> > > +       if (ret)
> > > +               return ret;
> > > +
> > > +       if (mtk_dp->next_bridge) {
> > > +               ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> > > +                                       &mtk_dp->bridge, flags);
> > > +               if (ret) {
> > > +                       drm_warn(mtk_dp->drm_dev,
> > > +                                "Failed to attach external bridge: %d\n", ret);
> > > +                       goto err_bridge_attach;
> > > +               }
> > > +       }
> > > +
> > > +       mtk_dp->drm_dev = bridge->dev;
> > > +
> > > +       return 0;
> > > +
> > > +err_bridge_attach:
> > > +       mtk_dp_poweroff(mtk_dp);
> > > +       return ret;
> > > +}
> > > +
> > > +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +
> > > +       mtk_dp->drm_dev = NULL;
> > > +
> > > +       mtk_dp_poweroff(mtk_dp);
> > > +}
> > > +
> > > +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> > > +                                        struct drm_bridge_state *old_state)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +
> > > +       mtk_dp_video_mute(mtk_dp, true);
> > > +       mtk_dp_audio_mute(mtk_dp, true);
> > > +       mtk_dp->state = MTK_DP_STATE_IDLE;
> > > +       mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > > +
> > > +       mtk_dp->enabled = false;
> > > +       msleep(100);
> > > +}
> > > +
> > > +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> > > +                                         struct drm_display_mode *mode)
> > > +{
> > > +       struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> > > +
> > > +       drm_display_mode_to_videomode(mode, &timings->vm);
> > > +       timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> > > +       timings->htotal = mode->htotal;
> > > +       timings->vtotal = mode->vtotal;
> > > +}
> > > +
> > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > +                                       struct drm_bridge_state *old_state)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +       struct drm_connector *conn;
> > > +       struct drm_connector_state *conn_state;
> > > +       struct drm_crtc *crtc;
> > > +       struct drm_crtc_state *crtc_state;
> > > +       int ret = 0;
> > > +       int i;
> > > +
> > > +       conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > +                                                       bridge->encoder);
> >
> > mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> >                                                        bridge->encoder);
> >
> The connector doesn't seem to be used anywhere else in the driver.
> Though I can add a drm_connector in the mtk_dp struct.
>
> > > +       if (!conn) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as connector is missing\n");
> > > +               return;
> > > +       }
> > > +
> > > +       memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > > +
> > > +       conn_state =
> > > +               drm_atomic_get_new_connector_state(old_state->base.state, conn);
> > > +       if (!conn_state) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as connector state is missing\n");
> > > +               return;
> > > +       }
> > > +
> > > +       crtc = conn_state->crtc;
> > > +       if (!crtc) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as connector state doesn't have a crtc\n");
> > > +               return;
> > > +       }
> > > +
> > > +       crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> > > +       if (!crtc_state) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as crtc state is missing\n");
> > > +               return;
> > > +       }
> > > +
> > > +       mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);
> >
> > Refer to mtk_hdmi_bridge_atomic_enable() for getting the mode.
> >
> I'm sorry, I don't understand what I'm supposed to do here.
> Could you elaborate?
>
> > > +       if (!mtk_dp_parse_capabilities(mtk_dp)) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as nothing is plugged in\n");
> > > +               return;
> > > +       }
> > > +
> > > +       //training
> >
> > Run checkpatch first.
> >
> > Regards,
> > Chun-Kuang.
> >
> > > +       for (i = 0; i < 50; i++) {
> > > +               ret = mtk_dp_train_handler(mtk_dp);
> > > +               if (ret) {
> > > +                       drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> > > +                               ret);
> > > +                       return;
> > > +               }
> > > +
> > > +               ret = mtk_dp_state_handler(mtk_dp);
> > > +               if (ret) {
> > > +                       drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> > > +                               ret);
> > > +                       return;
> > > +               }
> > > +       }
> > > +
> > > +       mtk_dp->enabled = true;
> > > +       mtk_dp_update_plugged_status(mtk_dp);
> > > +}
> > > +
> >
> >
> >
> > > --
> > > 2.32.0
> > >

Thx,
Guillaume.

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 14:13         ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 14:13 UTC (permalink / raw)
  To: Chun-Kuang Hu
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	DRI Development, ARM/Mediatek SoC support, Linux ARM

Hi Chun-Kuang.

Quoting Guillaume Ranquet (2021-12-02 16:31:03)
> Hi Chun-Kuang.
>
> Quoting Chun-Kuang Hu (2021-11-25 16:27:45)
> > Hi, Guillaume:
> >
> > This is a big patch, so I give some comment first.
> >
> > Guillaume Ranquet <granquet@baylibre.com> 於 2021年11月10日 週三 下午9:06寫道:
> > >
> > > From: Markus Schneider-Pargmann <msp@baylibre.com>
> > >
> > > This patch adds a DisplayPort driver for the Mediatek mt8195 SoC and a
> > > according phy driver mediatek-dp-phy.
> > >
> > > It supports both functional units on the mt8195, the embedded
> > > DisplayPort as well as the external DisplayPort units. It offers
> > > hot-plug-detection, audio up to 8 channels, and DisplayPort 1.4 with up
> > > to 4 lanes.
> > >
> > > The driver creates a child device for the phy. The child device will
> > > never exist without the parent being active. As they are sharing a
> > > register range, the parent passes a regmap pointer to the child so that
> > > both can work with the same register range. The phy driver sets device
> > > data that is read by the parent to get the phy device that can be used
> > > to control the phy properties.
> > >
> > > This driver is based on an initial version by
> > > Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> > >
> > > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> > > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> > > Reported-by: kernel test robot <lkp@intel.com>
> > > ---
> > >  drivers/gpu/drm/drm_edid.c              |    2 +-
> >
> > Separate this to another patch.
> >
> > >  drivers/gpu/drm/mediatek/Kconfig        |    7 +
> > >  drivers/gpu/drm/mediatek/Makefile       |    2 +
> > >  drivers/gpu/drm/mediatek/mtk_dp.c       | 3094 +++++++++++++++++++++++
> > >  drivers/gpu/drm/mediatek/mtk_dp_reg.h   |  568 +++++
> > >  drivers/gpu/drm/mediatek/mtk_dpi.c      |  111 +-
> >
> > Ditto.
> >
> > >  drivers/gpu/drm/mediatek/mtk_dpi_regs.h |   26 +
> >
> > Ditto.
> >
> > >  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
> >
> > Ditto
> >
> > >  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
> >
> > Ditto
> >
> yes my bad, I've made a bunch of fixup which ended up in the wrong place.
> It will be fixed for the next version.
>
> > >  9 files changed, 3799 insertions(+), 13 deletions(-)
> > >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
> > >  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> > >
> > > diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> > > index 500279a82167a..bfd98b50ceb5b 100644
> > > --- a/drivers/gpu/drm/drm_edid.c
> > > +++ b/drivers/gpu/drm/drm_edid.c
> > > @@ -5183,7 +5183,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
> > >          * modes and forbids YCRCB422 support for all video modes per
> > >          * HDMI 1.3 spec.
> > >          */
> > > -       info->color_formats = DRM_COLOR_FORMAT_RGB444;
> > > +       info->color_formats |= DRM_COLOR_FORMAT_RGB444;
> > >
> > >         /* YCRCB444 is optional according to spec. */
> > >         if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
> > > diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> > > index 2976d21e9a34a..029b94c716131 100644
> > > --- a/drivers/gpu/drm/mediatek/Kconfig
> > > +++ b/drivers/gpu/drm/mediatek/Kconfig
> > > @@ -28,3 +28,10 @@ config DRM_MEDIATEK_HDMI
> > >         select PHY_MTK_HDMI
> > >         help
> > >           DRM/KMS HDMI driver for Mediatek SoCs
> > > +
> > > +config MTK_DPTX_SUPPORT
> > > +       tristate "DRM DPTX Support for Mediatek SoCs"
> > > +       depends on DRM_MEDIATEK
> > > +       select PHY_MTK_DP
> > > +       help
> > > +         DRM/KMS Display Port driver for Mediatek SoCs.
> > > diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> > > index 29098d7c8307c..d86a6406055e6 100644
> > > --- a/drivers/gpu/drm/mediatek/Makefile
> > > +++ b/drivers/gpu/drm/mediatek/Makefile
> > > @@ -21,3 +21,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
> > >                           mtk_hdmi_ddc.o
> > >
> > >  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> > > +
> > > +obj-$(CONFIG_MTK_DPTX_SUPPORT) += mtk_dp.o
> > > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> > > new file mode 100644
> > > index 0000000000000..83087219d5a5e
> > > --- /dev/null
> > > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> > > @@ -0,0 +1,3094 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Copyright (c) 2019 MediaTek Inc.
> > > + * Copyright (c) 2021 BayLibre
> > > + */
> > > +
> > > +#include <drm/drm_atomic_helper.h>
> > > +#include <drm/drm_bridge.h>
> > > +#include <drm/drm_crtc.h>
> > > +#include <drm/drm_dp_helper.h>
> > > +#include <drm/drm_edid.h>
> > > +#include <drm/drm_of.h>
> > > +#include <drm/drm_panel.h>
> > > +#include <drm/drm_print.h>
> > > +#include <drm/drm_probe_helper.h>
> > > +#include <linux/arm-smccc.h>
> > > +#include <linux/clk.h>
> > > +#include <linux/delay.h>
> > > +#include <linux/errno.h>
> > > +#include <linux/kernel.h>
> > > +#include <linux/nvmem-consumer.h>
> > > +#include <linux/of.h>
> > > +#include <linux/of_irq.h>
> > > +#include <linux/of_platform.h>
> > > +#include <linux/phy/phy.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/pm_runtime.h>
> > > +#include <linux/regmap.h>
> > > +#include <sound/hdmi-codec.h>
> > > +#include <video/videomode.h>
> > > +
> > > +#include "mtk_dp_reg.h"
> > > +
> > > +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> > > +#define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> > > +
> > > +#define MTK_DP_MAX_LANES 4
> > > +#define MTK_DP_MAX_LINK_RATE MTK_DP_LINKRATE_HBR3
> > > +
> > > +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> > > +
> > > +#define MTK_DP_TRAIN_RETRY_LIMIT 8
> > > +#define MTK_DP_TRAIN_MAX_ITERATIONS 5
> > > +
> > > +#define MTK_DP_AUX_WRITE_READ_WAIT_TIME_US 20
> > > +
> > > +#define MTK_DP_DP_VERSION_11 0x11
> > > +
> > > +enum mtk_dp_state {
> > > +       MTK_DP_STATE_INITIAL,
> > > +       MTK_DP_STATE_IDLE,
> > > +       MTK_DP_STATE_PREPARE,
> > > +       MTK_DP_STATE_NORMAL,
> > > +};
> > > +
> > > +enum mtk_dp_train_state {
> > > +       MTK_DP_TRAIN_STATE_STARTUP = 0,
> > > +       MTK_DP_TRAIN_STATE_CHECKCAP,
> > > +       MTK_DP_TRAIN_STATE_CHECKEDID,
> > > +       MTK_DP_TRAIN_STATE_TRAINING_PRE,
> > > +       MTK_DP_TRAIN_STATE_TRAINING,
> > > +       MTK_DP_TRAIN_STATE_CHECKTIMING,
> > > +       MTK_DP_TRAIN_STATE_NORMAL,
> > > +       MTK_DP_TRAIN_STATE_POWERSAVE,
> > > +       MTK_DP_TRAIN_STATE_DPIDLE,
> > > +};
> > > +
> > > +struct mtk_dp_timings {
> > > +       struct videomode vm;
> > > +
> > > +       u16 htotal;
> > > +       u16 vtotal;
> > > +       u8 frame_rate;
> > > +       u32 pix_rate_khz;
> > > +};
> > > +
> > > +struct mtk_dp_train_info {
> > > +       bool tps3;
> > > +       bool tps4;
> > > +       bool sink_ssc;
> > > +       bool cable_plugged_in;
> > > +       bool cable_state_change;
> > > +       bool cr_done;
> > > +       bool eq_done;
> > > +
> > > +       // link_rate is in multiple of 0.27Gbps
> > > +       int link_rate;
> > > +       int lane_count;
> > > +
> > > +       int irq_status;
> > > +       int check_cap_count;
> > > +};
> > > +
> > > +// Same values as used by the DP Spec for MISC0 bits 1 and 2
> > > +enum mtk_dp_color_format {
> > > +       MTK_DP_COLOR_FORMAT_RGB_444 = 0,
> > > +       MTK_DP_COLOR_FORMAT_YUV_422 = 1,
> > > +       MTK_DP_COLOR_FORMAT_YUV_444 = 2,
> > > +       MTK_DP_COLOR_FORMAT_YUV_420 = 3,
> > > +       MTK_DP_COLOR_FORMAT_YONLY = 4,
> > > +       MTK_DP_COLOR_FORMAT_RAW = 5,
> > > +       MTK_DP_COLOR_FORMAT_RESERVED = 6,
> > > +       MTK_DP_COLOR_FORMAT_DEFAULT = MTK_DP_COLOR_FORMAT_RGB_444,
> > > +       MTK_DP_COLOR_FORMAT_UNKNOWN = 15,
> > > +};
> > > +
> > > +// Multiple of 0.27Gbps
> > > +enum mtk_dp_linkrate {
> > > +       MTK_DP_LINKRATE_RBR = 0x6,
> > > +       MTK_DP_LINKRATE_HBR = 0xA,
> > > +       MTK_DP_LINKRATE_HBR2 = 0x14,
> > > +       MTK_DP_LINKRATE_HBR25 = 0x19,
> > > +       MTK_DP_LINKRATE_HBR3 = 0x1E,
> > > +};
> > > +
> > > +// Same values as used for DP Spec MISC0 bits 5,6,7
> > > +enum mtk_dp_color_depth {
> > > +       MTK_DP_COLOR_DEPTH_6BIT = 0,
> > > +       MTK_DP_COLOR_DEPTH_8BIT = 1,
> > > +       MTK_DP_COLOR_DEPTH_10BIT = 2,
> > > +       MTK_DP_COLOR_DEPTH_12BIT = 3,
> > > +       MTK_DP_COLOR_DEPTH_16BIT = 4,
> > > +       MTK_DP_COLOR_DEPTH_UNKNOWN = 5,
> > > +};
> > > +
> > > +struct mtk_dp_audio_cfg {
> > > +       int sample_rate;
> > > +       int word_length_bits;
> > > +       int channels;
> > > +};
> > > +
> > > +struct mtk_dp_info {
> > > +       enum mtk_dp_color_depth depth;
> > > +       enum mtk_dp_color_format format;
> > > +       struct mtk_dp_audio_cfg audio_caps;
> > > +       struct mtk_dp_timings timings;
> > > +};
> > > +
> > > +struct dp_cal_data {
> > > +       unsigned int glb_bias_trim;
> > > +       unsigned int clktx_impse;
> > > +
> > > +       unsigned int ln0_tx_impsel_pmos;
> > > +       unsigned int ln0_tx_impsel_nmos;
> > > +       unsigned int ln1_tx_impsel_pmos;
> > > +       unsigned int ln1_tx_impsel_nmos;
> > > +       unsigned int ln2_tx_impsel_pmos;
> > > +       unsigned int ln2_tx_impsel_nmos;
> > > +       unsigned int ln3_tx_impsel_pmos;
> > > +       unsigned int ln3_tx_impsel_nmos;
> > > +};
> > > +
> > > +struct mtk_dp {
> > > +       struct device *dev;
> > > +       struct platform_device *phy_dev;
> > > +       struct phy *phy;
> > > +       struct dp_cal_data cal_data;
> > > +
> > > +       struct drm_device *drm_dev;
> > > +       struct drm_bridge bridge;
> > > +       struct drm_bridge *next_bridge;
> > > +       struct drm_dp_aux aux;
> > > +
> > > +       struct mutex edid_lock;
> > > +       struct edid *edid;
> > > +
> > > +       u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> > > +
> > > +       struct mtk_dp_info info;
> > > +       enum mtk_dp_state state;
> > > +
> > > +       struct mtk_dp_train_info train_info;
> > > +       enum mtk_dp_train_state train_state;
> > > +       unsigned int input_fmt;
> > > +
> > > +       struct regmap *regs;
> > > +       struct clk *dp_tx_clk;
> > > +
> > > +       bool enabled;
> > > +       bool audio_enable;
> > > +
> > > +       bool has_fec;
> > > +       struct mutex dp_lock;
> > > +
> > > +       struct mutex update_plugged_status_lock;
> > > +
> > > +       hdmi_codec_plugged_cb plugged_cb;
> > > +       struct device *codec_dev;
> > > +       u8 connector_eld[MAX_ELD_BYTES];
> > > +};
> > > +
> > > +enum mtk_dp_sdp_type {
> > > +       MTK_DP_SDP_NONE = 0x00,
> > > +       MTK_DP_SDP_ACM = 0x01,
> > > +       MTK_DP_SDP_ISRC = 0x02,
> > > +       MTK_DP_SDP_AVI = 0x03,
> > > +       MTK_DP_SDP_AUI = 0x04,
> > > +       MTK_DP_SDP_SPD = 0x05,
> > > +       MTK_DP_SDP_MPEG = 0x06,
> > > +       MTK_DP_SDP_NTSC = 0x07,
> > > +       MTK_DP_SDP_VSP = 0x08,
> > > +       MTK_DP_SDP_VSC = 0x09,
> > > +       MTK_DP_SDP_EXT = 0x0A,
> > > +       MTK_DP_SDP_PPS0 = 0x0B,
> > > +       MTK_DP_SDP_PPS1 = 0x0C,
> > > +       MTK_DP_SDP_PPS2 = 0x0D,
> > > +       MTK_DP_SDP_PPS3 = 0x0E,
> > > +       MTK_DP_SDP_DRM = 0x10,
> > > +       MTK_DP_SDP_MAX_NUM
> > > +};
> > > +
> > > +struct mtk_dp_sdp_packet {
> > > +       enum mtk_dp_sdp_type type;
> > > +       struct dp_sdp sdp;
> > > +};
> > > +
> > > +#define MTK_DP_IEC_CHANNEL_STATUS_LEN 5
> > > +union mtk_dp_audio_channel_status {
> > > +       struct {
> > > +               u8 rev : 1;
> > > +               u8 is_lpcm : 1;
> > > +               u8 copy_right : 1;
> > > +               u8 additional_format_info : 3;
> > > +               u8 channel_status_mode : 2;
> > > +               u8 category_code;
> > > +               u8 src_num : 4;
> > > +               u8 channel_num : 4;
> > > +               u8 sampling_freq : 4;
> > > +               u8 clk_accuracy : 2;
> > > +               u8 rev2 : 2;
> > > +               u8 word_len : 4;
> > > +               u8 original_sampling_freq : 4;
> > > +       } iec;
> > > +
> > > +       u8 buf[MTK_DP_IEC_CHANNEL_STATUS_LEN];
> > > +};
> > > +
> > > +static struct regmap_config mtk_dp_regmap_config = {
> > > +       .reg_bits = 32,
> > > +       .val_bits = 32,
> > > +       .reg_stride = 4,
> > > +       .max_register = SEC_OFFSET + 0x90,
> > > +       .name = "mtk-dp-registers",
> > > +};
> > > +
> > > +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
> >
> > Separate edp and displayport into two patches. For example, the first
> > patch support edp only, and the second patch add displayport function.
> >
> I have made the opposite as it seemed a bit more logical from how it's coded
> now.
> I've made a single patch with DP and a new one adding eDP support on top of it.
> The change is quite small, adding the eDP on top of DP:
>  drivers/gpu/drm/mediatek/mtk_dp.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
>  1 file changed, 74 insertions(+), 29 deletions(-)
> > > +{
> > > +       return mtk_dp->next_bridge != NULL;
> > > +}
> > > +
> >
> > [snip]
> >
> > > +
> > > +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> > > +                                   struct drm_connector *connector)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +       bool enabled = mtk_dp->enabled;
> > > +       struct edid *new_edid = NULL;
> > > +
> > > +       if (!enabled)
> > > +               drm_bridge_chain_pre_enable(bridge);
> >
> > In mtk_hdmi_bridge_get_edid(), there does not check the power. Why in
> > this function need this?
> > Does mtk hdmi driver has a bug?
> >
> I don't know, but I will be asking around.
In short, no, the hdmi driver does not have a bug.
We have to check power here as this driver handles both a DP and an eDP panel.

As the power is controlled by the panel driver in the case of eDP, we have
to make sure the bridge is enabled before trying to read the edid.

Hope this answers the question.

> > > +
> > > +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> > > +       usleep_range(2000, 5000);
> > > +
> > > +       if (mtk_dp_plug_state(mtk_dp))
> > > +               new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> > > +
> > > +       if (!enabled)
> > > +               drm_bridge_chain_post_disable(bridge);
> > > +
> > > +       mutex_lock(&mtk_dp->edid_lock);
> > > +       kfree(mtk_dp->edid);
> > > +       mtk_dp->edid = NULL;
> > > +
> > > +       if (!new_edid) {
> > > +               mutex_unlock(&mtk_dp->edid_lock);
> > > +               return NULL;
> > > +       }
> > > +
> > > +       mtk_dp->edid = drm_edid_duplicate(new_edid);
> > > +       mutex_unlock(&mtk_dp->edid_lock);
> > > +
> > > +       return new_edid;
> > > +}
> > > +
> > > +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> > > +                                  struct drm_dp_aux_msg *msg)
> > > +{
> > > +       ssize_t err = -EAGAIN;
> > > +       struct mtk_dp *mtk_dp;
> > > +       bool is_read;
> > > +       u8 request;
> > > +       size_t accessed_bytes = 0;
> > > +       int retry = 3, ret = 0;
> > > +
> > > +       mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> > > +
> > > +       if (!mtk_dp->train_info.cable_plugged_in ||
> > > +           mtk_dp->train_info.irq_status & MTK_DP_HPD_DISCONNECT) {
> > > +               mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKCAP;
> > > +               err = -EAGAIN;
> > > +               goto err;
> > > +       }
> > > +
> > > +       switch (msg->request) {
> > > +       case DP_AUX_I2C_MOT:
> > > +       case DP_AUX_I2C_WRITE:
> > > +       case DP_AUX_NATIVE_WRITE:
> > > +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> > > +       case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> > > +               request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> > > +               is_read = false;
> > > +               break;
> > > +       case DP_AUX_I2C_READ:
> > > +       case DP_AUX_NATIVE_READ:
> > > +       case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> > > +               request = msg->request;
> > > +               is_read = true;
> > > +               break;
> > > +       default:
> > > +               drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> > > +                       msg->request);
> > > +               err = -EINVAL;
> > > +               goto err;
> >
> > Directly return here.
> >
> Hmm, not sure yet, it seems I still need to prime msg->reply anyway.
> Though the error handling can be simplified a bit.
>
> > > +       }
> > > +
> > > +       if (msg->size == 0) {
> > > +               mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> > > +                                      msg->address + accessed_bytes,
> > > +                                      msg->buffer + accessed_bytes, 0);
> > > +       } else {
> > > +               while (accessed_bytes < msg->size) {
> > > +                       size_t to_access =
> > > +                               min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> > > +                                     msg->size - accessed_bytes);
> > > +                       retry = 3;
> > > +                       while (retry--) {
> > > +                               ret = mtk_dp_aux_do_transfer(
> > > +                                       mtk_dp, is_read, request,
> > > +                                       msg->address + accessed_bytes,
> > > +                                       msg->buffer + accessed_bytes,
> > > +                                       to_access);
> > > +                               if (ret == 0)
> > > +                                       break;
> > > +                               udelay(50);
> > > +                       }
> > > +                       if (!retry && ret) {
> > > +                               drm_info(mtk_dp->drm_dev,
> > > +                                        "Failed to do AUX transfer: %d\n",
> > > +                                        ret);
> > > +                               break;
> > > +                       }
> > > +                       accessed_bytes += to_access;
> > > +               }
> > > +       }
> > > +err:
> > > +       if (!ret) {
> > > +               msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > > +               ret = msg->size;
> > > +       } else {
> > > +               msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> > > +               return err;
> > > +       }
> > > +
> > > +       msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> > > +       return msg->size;
> > > +}
> > > +
> > > +static void mtk_dp_aux_init(struct mtk_dp *mtk_dp)
> > > +{
> > > +       drm_dp_aux_init(&mtk_dp->aux);
> > > +       mtk_dp->aux.name = "aux_mtk_dp";
> > > +       mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> > > +}
> > > +
> > > +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> > > +{
> > > +       mutex_lock(&mtk_dp->dp_lock);
> > > +
> > > +       mtk_dp_hwirq_enable(mtk_dp, false);
> > > +       mtk_dp_power_disable(mtk_dp);
> > > +       phy_exit(mtk_dp->phy);
> > > +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > > +
> > > +       mutex_unlock(&mtk_dp->dp_lock);
> > > +}
> > > +
> > > +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> > > +{
> > > +       int ret = 0;
> > > +
> > > +       mutex_lock(&mtk_dp->dp_lock);
> > > +
> > > +       ret = clk_prepare_enable(mtk_dp->dp_tx_clk);
> > > +       if (ret < 0) {
> > > +               dev_err(mtk_dp->dev, "Fail to enable clock: %d\n", ret);
> > > +               goto err;
> > > +       }
> > > +       ret = phy_init(mtk_dp->phy);
> > > +       if (ret) {
> > > +               dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> > > +               goto err_phy_init;
> > > +       }
> > > +       ret = mtk_dp_phy_configure(mtk_dp, MTK_DP_LINKRATE_RBR, 1);
> > > +       if (ret) {
> > > +               dev_err(mtk_dp->dev, "Failed to configure phy: %d\n", ret);
> > > +               goto err_phy_config;
> > > +       }
> > > +
> > > +       mtk_dp_init_port(mtk_dp);
> > > +       mtk_dp_power_enable(mtk_dp);
> > > +       mtk_dp_hwirq_enable(mtk_dp, true);
> > > +
> > > +err_phy_config:
> > > +       phy_exit(mtk_dp->phy);
> > > +err_phy_init:
> > > +       clk_disable_unprepare(mtk_dp->dp_tx_clk);
> > > +err:
> > > +       mutex_unlock(&mtk_dp->dp_lock);
> > > +       return ret;
> > > +}
> > > +
> > > +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> > > +                               enum drm_bridge_attach_flags flags)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +       int ret;
> > > +
> > > +       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> > > +               dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> > > +               return -EINVAL;
> > > +       }
> > > +
> > > +       ret = mtk_dp_poweron(mtk_dp);
> >
> > Move the power on to mtk_dp_bridge_atomic_enable().
> >
Moving the power-on sequence to the mtk_dp_bridge_atoimc_enable()
breaks the eDP functionality.
For the time being (ie: v7), the power on will stay in the
bridge_attach() callback, sorry.

> > > +       if (ret)
> > > +               return ret;
> > > +
> > > +       if (mtk_dp->next_bridge) {
> > > +               ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> > > +                                       &mtk_dp->bridge, flags);
> > > +               if (ret) {
> > > +                       drm_warn(mtk_dp->drm_dev,
> > > +                                "Failed to attach external bridge: %d\n", ret);
> > > +                       goto err_bridge_attach;
> > > +               }
> > > +       }
> > > +
> > > +       mtk_dp->drm_dev = bridge->dev;
> > > +
> > > +       return 0;
> > > +
> > > +err_bridge_attach:
> > > +       mtk_dp_poweroff(mtk_dp);
> > > +       return ret;
> > > +}
> > > +
> > > +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +
> > > +       mtk_dp->drm_dev = NULL;
> > > +
> > > +       mtk_dp_poweroff(mtk_dp);
> > > +}
> > > +
> > > +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> > > +                                        struct drm_bridge_state *old_state)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +
> > > +       mtk_dp_video_mute(mtk_dp, true);
> > > +       mtk_dp_audio_mute(mtk_dp, true);
> > > +       mtk_dp->state = MTK_DP_STATE_IDLE;
> > > +       mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP;
> > > +
> > > +       mtk_dp->enabled = false;
> > > +       msleep(100);
> > > +}
> > > +
> > > +static void mtk_dp_parse_drm_mode_timings(struct mtk_dp *mtk_dp,
> > > +                                         struct drm_display_mode *mode)
> > > +{
> > > +       struct mtk_dp_timings *timings = &mtk_dp->info.timings;
> > > +
> > > +       drm_display_mode_to_videomode(mode, &timings->vm);
> > > +       timings->frame_rate = mode->clock * 1000 / mode->htotal / mode->vtotal;
> > > +       timings->htotal = mode->htotal;
> > > +       timings->vtotal = mode->vtotal;
> > > +}
> > > +
> > > +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> > > +                                       struct drm_bridge_state *old_state)
> > > +{
> > > +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> > > +       struct drm_connector *conn;
> > > +       struct drm_connector_state *conn_state;
> > > +       struct drm_crtc *crtc;
> > > +       struct drm_crtc_state *crtc_state;
> > > +       int ret = 0;
> > > +       int i;
> > > +
> > > +       conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> > > +                                                       bridge->encoder);
> >
> > mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> >                                                        bridge->encoder);
> >
> The connector doesn't seem to be used anywhere else in the driver.
> Though I can add a drm_connector in the mtk_dp struct.
>
> > > +       if (!conn) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as connector is missing\n");
> > > +               return;
> > > +       }
> > > +
> > > +       memcpy(mtk_dp->connector_eld, conn->eld, MAX_ELD_BYTES);
> > > +
> > > +       conn_state =
> > > +               drm_atomic_get_new_connector_state(old_state->base.state, conn);
> > > +       if (!conn_state) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as connector state is missing\n");
> > > +               return;
> > > +       }
> > > +
> > > +       crtc = conn_state->crtc;
> > > +       if (!crtc) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as connector state doesn't have a crtc\n");
> > > +               return;
> > > +       }
> > > +
> > > +       crtc_state = drm_atomic_get_new_crtc_state(old_state->base.state, crtc);
> > > +       if (!crtc_state) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as crtc state is missing\n");
> > > +               return;
> > > +       }
> > > +
> > > +       mtk_dp_parse_drm_mode_timings(mtk_dp, &crtc_state->adjusted_mode);
> >
> > Refer to mtk_hdmi_bridge_atomic_enable() for getting the mode.
> >
> I'm sorry, I don't understand what I'm supposed to do here.
> Could you elaborate?
>
> > > +       if (!mtk_dp_parse_capabilities(mtk_dp)) {
> > > +               drm_err(mtk_dp->drm_dev,
> > > +                       "Can't enable bridge as nothing is plugged in\n");
> > > +               return;
> > > +       }
> > > +
> > > +       //training
> >
> > Run checkpatch first.
> >
> > Regards,
> > Chun-Kuang.
> >
> > > +       for (i = 0; i < 50; i++) {
> > > +               ret = mtk_dp_train_handler(mtk_dp);
> > > +               if (ret) {
> > > +                       drm_err(mtk_dp->drm_dev, "Train handler failed %d\n",
> > > +                               ret);
> > > +                       return;
> > > +               }
> > > +
> > > +               ret = mtk_dp_state_handler(mtk_dp);
> > > +               if (ret) {
> > > +                       drm_err(mtk_dp->drm_dev, "State handler failed %d\n",
> > > +                               ret);
> > > +                       return;
> > > +               }
> > > +       }
> > > +
> > > +       mtk_dp->enabled = true;
> > > +       mtk_dp_update_plugged_status(mtk_dp);
> > > +}
> > > +
> >
> >
> >
> > > --
> > > 2.32.0
> > >

Thx,
Guillaume.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-12-13 16:54                 ` Maxime Ripard
  (?)
  (?)
@ 2021-12-15 15:03                   ` Guillaume Ranquet
  -1 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 15:03 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Hi Maxime,

Quoting Maxime Ripard (2021-12-13 17:54:22)
> On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> > Hi,
> >
> > Quoting Maxime Ripard (2021-11-25 15:30:34)
> > > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > > Hi,
> > > > Thanks for all your input, really appreciated.
> > > >
> > > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > > Hi,
> > > > >
> > > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > > to control the phy properties.
> > > > > > >
> > > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > > you need a separate PHY driver in the first place?
> > > > > >
> > > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > > >
> > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > > >
> > > > > It's a bit of a circular argument though :)
> > > > >
> > > > > It's a separate phy driver because it needs to go through another
> > > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > > because it's a separate phy driver.
> > > > >
> > > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > > single device?
> > > > >
> > > > > That being said, usually what those kind of questions mean is that
> > > > > you're missing a comment or something in the commit log to provide that
> > > > > context in the first place, so it would be great to add that context
> > > > > here.
> > > > >
> > > > > And it will avoid the situation we're now in where multiple reviewers
> > > > > ask the same questions over and over again :)
> > > > >
> > > > At first I didn't understand your reply, then I realized I gave you
> > > > the wrong link...
> > > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > > >
> > > > The driver and phy were a single driver until v2 of this patch series
> > > > and the phy setup
> > > > was done directly in the driver (single driver, single C file).
> > > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > > >
> > > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > > >
> > > > I'll try to find a way to make it clearer for v7.
> > >
> > > OK, it makes sense then :)
> > >
> > > There's something weird though: the devices definitely look like they're
> > > in a separate register range, yet you mention a regmap to handle the
> > > shared register range. That range doesn't seem described anywhere in the
> > > device tree though? What is it for?
> >
> > My understanding is that 0x1000 to 0x1fff controls the phy
> > functionalities and 0x2000 to 0x4fff controls "non-phy"
> > functionalities. And you are right, there's no description of that in
> > the device tree whatsoever. The ranges are in the same actual device
> > and thus it has been decided to not have dt-bindings for the phy
> > device.
>
> Sure, that last part makes sense, but then I'm not sure why you don't
> have the full register range in the device node you have in the DT?
>
> > The phy driver is a child of the DP driver that we register using
> > platform_device_register_data() and we pass along the same regmap as
> > the DP driver in its platform data.
>
> Especially if it's used by something, it should be described in the DT
> somewhere.
>
> Maxime


So, to make things crystal clear to a newbie (like me).
Would you describe it like this:
compatible = "mediatek,mt8195-dp-tx";
reg = <0 0x1c501000 0 0x0fff>,
	<0 0x1c502000 0 0x2fff>;

instead of the current description:
compatible = "mediatek,mt8195-dp-tx";
reg = <0 0x1c500000 0 0x8000>;

I haven't checked what the rest of the 0x8000 range is used for though...

Thx,
Guillaume.

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 15:03                   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 15:03 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Hi Maxime,

Quoting Maxime Ripard (2021-12-13 17:54:22)
> On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> > Hi,
> >
> > Quoting Maxime Ripard (2021-11-25 15:30:34)
> > > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > > Hi,
> > > > Thanks for all your input, really appreciated.
> > > >
> > > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > > Hi,
> > > > >
> > > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > > to control the phy properties.
> > > > > > >
> > > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > > you need a separate PHY driver in the first place?
> > > > > >
> > > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > > >
> > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > > >
> > > > > It's a bit of a circular argument though :)
> > > > >
> > > > > It's a separate phy driver because it needs to go through another
> > > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > > because it's a separate phy driver.
> > > > >
> > > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > > single device?
> > > > >
> > > > > That being said, usually what those kind of questions mean is that
> > > > > you're missing a comment or something in the commit log to provide that
> > > > > context in the first place, so it would be great to add that context
> > > > > here.
> > > > >
> > > > > And it will avoid the situation we're now in where multiple reviewers
> > > > > ask the same questions over and over again :)
> > > > >
> > > > At first I didn't understand your reply, then I realized I gave you
> > > > the wrong link...
> > > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > > >
> > > > The driver and phy were a single driver until v2 of this patch series
> > > > and the phy setup
> > > > was done directly in the driver (single driver, single C file).
> > > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > > >
> > > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > > >
> > > > I'll try to find a way to make it clearer for v7.
> > >
> > > OK, it makes sense then :)
> > >
> > > There's something weird though: the devices definitely look like they're
> > > in a separate register range, yet you mention a regmap to handle the
> > > shared register range. That range doesn't seem described anywhere in the
> > > device tree though? What is it for?
> >
> > My understanding is that 0x1000 to 0x1fff controls the phy
> > functionalities and 0x2000 to 0x4fff controls "non-phy"
> > functionalities. And you are right, there's no description of that in
> > the device tree whatsoever. The ranges are in the same actual device
> > and thus it has been decided to not have dt-bindings for the phy
> > device.
>
> Sure, that last part makes sense, but then I'm not sure why you don't
> have the full register range in the device node you have in the DT?
>
> > The phy driver is a child of the DP driver that we register using
> > platform_device_register_data() and we pass along the same regmap as
> > the DP driver in its platform data.
>
> Especially if it's used by something, it should be described in the DT
> somewhere.
>
> Maxime


So, to make things crystal clear to a newbie (like me).
Would you describe it like this:
compatible = "mediatek,mt8195-dp-tx";
reg = <0 0x1c501000 0 0x0fff>,
	<0 0x1c502000 0 0x2fff>;

instead of the current description:
compatible = "mediatek,mt8195-dp-tx";
reg = <0 0x1c500000 0 0x8000>;

I haven't checked what the rest of the 0x8000 range is used for though...

Thx,
Guillaume.

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 15:03                   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 15:03 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Chun-Kuang Hu, David Airlie, Thomas Zimmermann, linux-kernel,
	dri-devel, Markus Schneider-Pargmann, linux-mediatek,
	Matthias Brugger, linux-arm-kernel, kernel test robot

Hi Maxime,

Quoting Maxime Ripard (2021-12-13 17:54:22)
> On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> > Hi,
> >
> > Quoting Maxime Ripard (2021-11-25 15:30:34)
> > > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > > Hi,
> > > > Thanks for all your input, really appreciated.
> > > >
> > > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > > Hi,
> > > > >
> > > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > > to control the phy properties.
> > > > > > >
> > > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > > you need a separate PHY driver in the first place?
> > > > > >
> > > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > > >
> > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > > >
> > > > > It's a bit of a circular argument though :)
> > > > >
> > > > > It's a separate phy driver because it needs to go through another
> > > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > > because it's a separate phy driver.
> > > > >
> > > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > > single device?
> > > > >
> > > > > That being said, usually what those kind of questions mean is that
> > > > > you're missing a comment or something in the commit log to provide that
> > > > > context in the first place, so it would be great to add that context
> > > > > here.
> > > > >
> > > > > And it will avoid the situation we're now in where multiple reviewers
> > > > > ask the same questions over and over again :)
> > > > >
> > > > At first I didn't understand your reply, then I realized I gave you
> > > > the wrong link...
> > > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > > >
> > > > The driver and phy were a single driver until v2 of this patch series
> > > > and the phy setup
> > > > was done directly in the driver (single driver, single C file).
> > > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > > >
> > > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > > >
> > > > I'll try to find a way to make it clearer for v7.
> > >
> > > OK, it makes sense then :)
> > >
> > > There's something weird though: the devices definitely look like they're
> > > in a separate register range, yet you mention a regmap to handle the
> > > shared register range. That range doesn't seem described anywhere in the
> > > device tree though? What is it for?
> >
> > My understanding is that 0x1000 to 0x1fff controls the phy
> > functionalities and 0x2000 to 0x4fff controls "non-phy"
> > functionalities. And you are right, there's no description of that in
> > the device tree whatsoever. The ranges are in the same actual device
> > and thus it has been decided to not have dt-bindings for the phy
> > device.
>
> Sure, that last part makes sense, but then I'm not sure why you don't
> have the full register range in the device node you have in the DT?
>
> > The phy driver is a child of the DP driver that we register using
> > platform_device_register_data() and we pass along the same regmap as
> > the DP driver in its platform data.
>
> Especially if it's used by something, it should be described in the DT
> somewhere.
>
> Maxime


So, to make things crystal clear to a newbie (like me).
Would you describe it like this:
compatible = "mediatek,mt8195-dp-tx";
reg = <0 0x1c501000 0 0x0fff>,
	<0 0x1c502000 0 0x2fff>;

instead of the current description:
compatible = "mediatek,mt8195-dp-tx";
reg = <0 0x1c500000 0 0x8000>;

I haven't checked what the rest of the 0x8000 range is used for though...

Thx,
Guillaume.

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 15:03                   ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 15:03 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Hi Maxime,

Quoting Maxime Ripard (2021-12-13 17:54:22)
> On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> > Hi,
> >
> > Quoting Maxime Ripard (2021-11-25 15:30:34)
> > > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > > Hi,
> > > > Thanks for all your input, really appreciated.
> > > >
> > > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > > Hi,
> > > > >
> > > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > > to control the phy properties.
> > > > > > >
> > > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > > you need a separate PHY driver in the first place?
> > > > > >
> > > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > > >
> > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > > >
> > > > > It's a bit of a circular argument though :)
> > > > >
> > > > > It's a separate phy driver because it needs to go through another
> > > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > > because it's a separate phy driver.
> > > > >
> > > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > > single device?
> > > > >
> > > > > That being said, usually what those kind of questions mean is that
> > > > > you're missing a comment or something in the commit log to provide that
> > > > > context in the first place, so it would be great to add that context
> > > > > here.
> > > > >
> > > > > And it will avoid the situation we're now in where multiple reviewers
> > > > > ask the same questions over and over again :)
> > > > >
> > > > At first I didn't understand your reply, then I realized I gave you
> > > > the wrong link...
> > > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > > >
> > > > The driver and phy were a single driver until v2 of this patch series
> > > > and the phy setup
> > > > was done directly in the driver (single driver, single C file).
> > > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > > >
> > > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > > >
> > > > I'll try to find a way to make it clearer for v7.
> > >
> > > OK, it makes sense then :)
> > >
> > > There's something weird though: the devices definitely look like they're
> > > in a separate register range, yet you mention a regmap to handle the
> > > shared register range. That range doesn't seem described anywhere in the
> > > device tree though? What is it for?
> >
> > My understanding is that 0x1000 to 0x1fff controls the phy
> > functionalities and 0x2000 to 0x4fff controls "non-phy"
> > functionalities. And you are right, there's no description of that in
> > the device tree whatsoever. The ranges are in the same actual device
> > and thus it has been decided to not have dt-bindings for the phy
> > device.
>
> Sure, that last part makes sense, but then I'm not sure why you don't
> have the full register range in the device node you have in the DT?
>
> > The phy driver is a child of the DP driver that we register using
> > platform_device_register_data() and we pass along the same regmap as
> > the DP driver in its platform data.
>
> Especially if it's used by something, it should be described in the DT
> somewhere.
>
> Maxime


So, to make things crystal clear to a newbie (like me).
Would you describe it like this:
compatible = "mediatek,mt8195-dp-tx";
reg = <0 0x1c501000 0 0x0fff>,
	<0 0x1c502000 0 0x2fff>;

instead of the current description:
compatible = "mediatek,mt8195-dp-tx";
reg = <0 0x1c500000 0 0x8000>;

I haven't checked what the rest of the 0x8000 range is used for though...

Thx,
Guillaume.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-12-15 15:03                   ` Guillaume Ranquet
  (?)
  (?)
@ 2021-12-15 15:15                     ` Maxime Ripard
  -1 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-12-15 15:15 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 5062 bytes --]

Hi,

On Wed, Dec 15, 2021 at 09:03:01AM -0600, Guillaume Ranquet wrote:
> Quoting Maxime Ripard (2021-12-13 17:54:22)
> > On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> > > Hi,
> > >
> > > Quoting Maxime Ripard (2021-11-25 15:30:34)
> > > > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > > > Hi,
> > > > > Thanks for all your input, really appreciated.
> > > > >
> > > > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > > > Hi,
> > > > > >
> > > > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > > > to control the phy properties.
> > > > > > > >
> > > > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > > > you need a separate PHY driver in the first place?
> > > > > > >
> > > > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > > > >
> > > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > > > >
> > > > > > It's a bit of a circular argument though :)
> > > > > >
> > > > > > It's a separate phy driver because it needs to go through another
> > > > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > > > because it's a separate phy driver.
> > > > > >
> > > > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > > > single device?
> > > > > >
> > > > > > That being said, usually what those kind of questions mean is that
> > > > > > you're missing a comment or something in the commit log to provide that
> > > > > > context in the first place, so it would be great to add that context
> > > > > > here.
> > > > > >
> > > > > > And it will avoid the situation we're now in where multiple reviewers
> > > > > > ask the same questions over and over again :)
> > > > > >
> > > > > At first I didn't understand your reply, then I realized I gave you
> > > > > the wrong link...
> > > > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > > > >
> > > > > The driver and phy were a single driver until v2 of this patch series
> > > > > and the phy setup
> > > > > was done directly in the driver (single driver, single C file).
> > > > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > > > >
> > > > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > > > >
> > > > > I'll try to find a way to make it clearer for v7.
> > > >
> > > > OK, it makes sense then :)
> > > >
> > > > There's something weird though: the devices definitely look like they're
> > > > in a separate register range, yet you mention a regmap to handle the
> > > > shared register range. That range doesn't seem described anywhere in the
> > > > device tree though? What is it for?
> > >
> > > My understanding is that 0x1000 to 0x1fff controls the phy
> > > functionalities and 0x2000 to 0x4fff controls "non-phy"
> > > functionalities. And you are right, there's no description of that in
> > > the device tree whatsoever. The ranges are in the same actual device
> > > and thus it has been decided to not have dt-bindings for the phy
> > > device.
> >
> > Sure, that last part makes sense, but then I'm not sure why you don't
> > have the full register range in the device node you have in the DT?
> >
> > > The phy driver is a child of the DP driver that we register using
> > > platform_device_register_data() and we pass along the same regmap as
> > > the DP driver in its platform data.
> >
> > Especially if it's used by something, it should be described in the DT
> > somewhere.
> >
> > Maxime
> 
> 
> So, to make things crystal clear to a newbie (like me).
> Would you describe it like this:
> compatible = "mediatek,mt8195-dp-tx";
> reg = <0 0x1c501000 0 0x0fff>,
> 	<0 0x1c502000 0 0x2fff>;
> 
> instead of the current description:
> compatible = "mediatek,mt8195-dp-tx";
> reg = <0 0x1c500000 0 0x8000>;
> 
> I haven't checked what the rest of the 0x8000 range is used for though...

I'm confused, is that what you had before?

I recall you had a DTSI somewhere where you have two devices, and the
dp-tx device not having the phy range?

If the latter is what you have, and there's no overlapping ranges over
multiple nodes, then it's fine already.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 15:15                     ` Maxime Ripard
  0 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-12-15 15:15 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 5062 bytes --]

Hi,

On Wed, Dec 15, 2021 at 09:03:01AM -0600, Guillaume Ranquet wrote:
> Quoting Maxime Ripard (2021-12-13 17:54:22)
> > On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> > > Hi,
> > >
> > > Quoting Maxime Ripard (2021-11-25 15:30:34)
> > > > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > > > Hi,
> > > > > Thanks for all your input, really appreciated.
> > > > >
> > > > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > > > Hi,
> > > > > >
> > > > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > > > to control the phy properties.
> > > > > > > >
> > > > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > > > you need a separate PHY driver in the first place?
> > > > > > >
> > > > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > > > >
> > > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > > > >
> > > > > > It's a bit of a circular argument though :)
> > > > > >
> > > > > > It's a separate phy driver because it needs to go through another
> > > > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > > > because it's a separate phy driver.
> > > > > >
> > > > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > > > single device?
> > > > > >
> > > > > > That being said, usually what those kind of questions mean is that
> > > > > > you're missing a comment or something in the commit log to provide that
> > > > > > context in the first place, so it would be great to add that context
> > > > > > here.
> > > > > >
> > > > > > And it will avoid the situation we're now in where multiple reviewers
> > > > > > ask the same questions over and over again :)
> > > > > >
> > > > > At first I didn't understand your reply, then I realized I gave you
> > > > > the wrong link...
> > > > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > > > >
> > > > > The driver and phy were a single driver until v2 of this patch series
> > > > > and the phy setup
> > > > > was done directly in the driver (single driver, single C file).
> > > > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > > > >
> > > > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > > > >
> > > > > I'll try to find a way to make it clearer for v7.
> > > >
> > > > OK, it makes sense then :)
> > > >
> > > > There's something weird though: the devices definitely look like they're
> > > > in a separate register range, yet you mention a regmap to handle the
> > > > shared register range. That range doesn't seem described anywhere in the
> > > > device tree though? What is it for?
> > >
> > > My understanding is that 0x1000 to 0x1fff controls the phy
> > > functionalities and 0x2000 to 0x4fff controls "non-phy"
> > > functionalities. And you are right, there's no description of that in
> > > the device tree whatsoever. The ranges are in the same actual device
> > > and thus it has been decided to not have dt-bindings for the phy
> > > device.
> >
> > Sure, that last part makes sense, but then I'm not sure why you don't
> > have the full register range in the device node you have in the DT?
> >
> > > The phy driver is a child of the DP driver that we register using
> > > platform_device_register_data() and we pass along the same regmap as
> > > the DP driver in its platform data.
> >
> > Especially if it's used by something, it should be described in the DT
> > somewhere.
> >
> > Maxime
> 
> 
> So, to make things crystal clear to a newbie (like me).
> Would you describe it like this:
> compatible = "mediatek,mt8195-dp-tx";
> reg = <0 0x1c501000 0 0x0fff>,
> 	<0 0x1c502000 0 0x2fff>;
> 
> instead of the current description:
> compatible = "mediatek,mt8195-dp-tx";
> reg = <0 0x1c500000 0 0x8000>;
> 
> I haven't checked what the rest of the 0x8000 range is used for though...

I'm confused, is that what you had before?

I recall you had a DTSI somewhere where you have two devices, and the
dp-tx device not having the phy range?

If the latter is what you have, and there's no overlapping ranges over
multiple nodes, then it's fine already.

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 170 bytes --]

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 15:15                     ` Maxime Ripard
  0 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-12-15 15:15 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 5062 bytes --]

Hi,

On Wed, Dec 15, 2021 at 09:03:01AM -0600, Guillaume Ranquet wrote:
> Quoting Maxime Ripard (2021-12-13 17:54:22)
> > On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> > > Hi,
> > >
> > > Quoting Maxime Ripard (2021-11-25 15:30:34)
> > > > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > > > Hi,
> > > > > Thanks for all your input, really appreciated.
> > > > >
> > > > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > > > Hi,
> > > > > >
> > > > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > > > to control the phy properties.
> > > > > > > >
> > > > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > > > you need a separate PHY driver in the first place?
> > > > > > >
> > > > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > > > >
> > > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > > > >
> > > > > > It's a bit of a circular argument though :)
> > > > > >
> > > > > > It's a separate phy driver because it needs to go through another
> > > > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > > > because it's a separate phy driver.
> > > > > >
> > > > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > > > single device?
> > > > > >
> > > > > > That being said, usually what those kind of questions mean is that
> > > > > > you're missing a comment or something in the commit log to provide that
> > > > > > context in the first place, so it would be great to add that context
> > > > > > here.
> > > > > >
> > > > > > And it will avoid the situation we're now in where multiple reviewers
> > > > > > ask the same questions over and over again :)
> > > > > >
> > > > > At first I didn't understand your reply, then I realized I gave you
> > > > > the wrong link...
> > > > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > > > >
> > > > > The driver and phy were a single driver until v2 of this patch series
> > > > > and the phy setup
> > > > > was done directly in the driver (single driver, single C file).
> > > > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > > > >
> > > > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > > > >
> > > > > I'll try to find a way to make it clearer for v7.
> > > >
> > > > OK, it makes sense then :)
> > > >
> > > > There's something weird though: the devices definitely look like they're
> > > > in a separate register range, yet you mention a regmap to handle the
> > > > shared register range. That range doesn't seem described anywhere in the
> > > > device tree though? What is it for?
> > >
> > > My understanding is that 0x1000 to 0x1fff controls the phy
> > > functionalities and 0x2000 to 0x4fff controls "non-phy"
> > > functionalities. And you are right, there's no description of that in
> > > the device tree whatsoever. The ranges are in the same actual device
> > > and thus it has been decided to not have dt-bindings for the phy
> > > device.
> >
> > Sure, that last part makes sense, but then I'm not sure why you don't
> > have the full register range in the device node you have in the DT?
> >
> > > The phy driver is a child of the DP driver that we register using
> > > platform_device_register_data() and we pass along the same regmap as
> > > the DP driver in its platform data.
> >
> > Especially if it's used by something, it should be described in the DT
> > somewhere.
> >
> > Maxime
> 
> 
> So, to make things crystal clear to a newbie (like me).
> Would you describe it like this:
> compatible = "mediatek,mt8195-dp-tx";
> reg = <0 0x1c501000 0 0x0fff>,
> 	<0 0x1c502000 0 0x2fff>;
> 
> instead of the current description:
> compatible = "mediatek,mt8195-dp-tx";
> reg = <0 0x1c500000 0 0x8000>;
> 
> I haven't checked what the rest of the 0x8000 range is used for though...

I'm confused, is that what you had before?

I recall you had a DTSI somewhere where you have two devices, and the
dp-tx device not having the phy range?

If the latter is what you have, and there's no overlapping ranges over
multiple nodes, then it's fine already.

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 15:15                     ` Maxime Ripard
  0 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-12-15 15:15 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Chun-Kuang Hu, David Airlie, Thomas Zimmermann, linux-kernel,
	dri-devel, Markus Schneider-Pargmann, linux-mediatek,
	Matthias Brugger, linux-arm-kernel, kernel test robot

[-- Attachment #1: Type: text/plain, Size: 5062 bytes --]

Hi,

On Wed, Dec 15, 2021 at 09:03:01AM -0600, Guillaume Ranquet wrote:
> Quoting Maxime Ripard (2021-12-13 17:54:22)
> > On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> > > Hi,
> > >
> > > Quoting Maxime Ripard (2021-11-25 15:30:34)
> > > > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > > > Hi,
> > > > > Thanks for all your input, really appreciated.
> > > > >
> > > > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > > > Hi,
> > > > > >
> > > > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > > > to control the phy properties.
> > > > > > > >
> > > > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > > > you need a separate PHY driver in the first place?
> > > > > > >
> > > > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > > > >
> > > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > > > >
> > > > > > It's a bit of a circular argument though :)
> > > > > >
> > > > > > It's a separate phy driver because it needs to go through another
> > > > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > > > because it's a separate phy driver.
> > > > > >
> > > > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > > > single device?
> > > > > >
> > > > > > That being said, usually what those kind of questions mean is that
> > > > > > you're missing a comment or something in the commit log to provide that
> > > > > > context in the first place, so it would be great to add that context
> > > > > > here.
> > > > > >
> > > > > > And it will avoid the situation we're now in where multiple reviewers
> > > > > > ask the same questions over and over again :)
> > > > > >
> > > > > At first I didn't understand your reply, then I realized I gave you
> > > > > the wrong link...
> > > > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > > > >
> > > > > The driver and phy were a single driver until v2 of this patch series
> > > > > and the phy setup
> > > > > was done directly in the driver (single driver, single C file).
> > > > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > > > >
> > > > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > > > >
> > > > > I'll try to find a way to make it clearer for v7.
> > > >
> > > > OK, it makes sense then :)
> > > >
> > > > There's something weird though: the devices definitely look like they're
> > > > in a separate register range, yet you mention a regmap to handle the
> > > > shared register range. That range doesn't seem described anywhere in the
> > > > device tree though? What is it for?
> > >
> > > My understanding is that 0x1000 to 0x1fff controls the phy
> > > functionalities and 0x2000 to 0x4fff controls "non-phy"
> > > functionalities. And you are right, there's no description of that in
> > > the device tree whatsoever. The ranges are in the same actual device
> > > and thus it has been decided to not have dt-bindings for the phy
> > > device.
> >
> > Sure, that last part makes sense, but then I'm not sure why you don't
> > have the full register range in the device node you have in the DT?
> >
> > > The phy driver is a child of the DP driver that we register using
> > > platform_device_register_data() and we pass along the same regmap as
> > > the DP driver in its platform data.
> >
> > Especially if it's used by something, it should be described in the DT
> > somewhere.
> >
> > Maxime
> 
> 
> So, to make things crystal clear to a newbie (like me).
> Would you describe it like this:
> compatible = "mediatek,mt8195-dp-tx";
> reg = <0 0x1c501000 0 0x0fff>,
> 	<0 0x1c502000 0 0x2fff>;
> 
> instead of the current description:
> compatible = "mediatek,mt8195-dp-tx";
> reg = <0 0x1c500000 0 0x8000>;
> 
> I haven't checked what the rest of the 0x8000 range is used for though...

I'm confused, is that what you had before?

I recall you had a DTSI somewhere where you have two devices, and the
dp-tx device not having the phy range?

If the latter is what you have, and there's no overlapping ranges over
multiple nodes, then it's fine already.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-12-15 15:15                     ` Maxime Ripard
  (?)
  (?)
@ 2021-12-15 16:02                       ` Guillaume Ranquet
  -1 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 16:02 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Quoting Maxime Ripard (2021-12-15 16:15:08)
> Hi,
>
> On Wed, Dec 15, 2021 at 09:03:01AM -0600, Guillaume Ranquet wrote:
> > Quoting Maxime Ripard (2021-12-13 17:54:22)
> > > On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> > > > Hi,
> > > >
> > > > Quoting Maxime Ripard (2021-11-25 15:30:34)
> > > > > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > > > > Hi,
> > > > > > Thanks for all your input, really appreciated.
> > > > > >
> > > > > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > > > > Hi,
> > > > > > >
> > > > > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > > > > to control the phy properties.
> > > > > > > > >
> > > > > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > > > > you need a separate PHY driver in the first place?
> > > > > > > >
> > > > > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > > > > >
> > > > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > > > > >
> > > > > > > It's a bit of a circular argument though :)
> > > > > > >
> > > > > > > It's a separate phy driver because it needs to go through another
> > > > > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > > > > because it's a separate phy driver.
> > > > > > >
> > > > > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > > > > single device?
> > > > > > >
> > > > > > > That being said, usually what those kind of questions mean is that
> > > > > > > you're missing a comment or something in the commit log to provide that
> > > > > > > context in the first place, so it would be great to add that context
> > > > > > > here.
> > > > > > >
> > > > > > > And it will avoid the situation we're now in where multiple reviewers
> > > > > > > ask the same questions over and over again :)
> > > > > > >
> > > > > > At first I didn't understand your reply, then I realized I gave you
> > > > > > the wrong link...
> > > > > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > > > > >
> > > > > > The driver and phy were a single driver until v2 of this patch series
> > > > > > and the phy setup
> > > > > > was done directly in the driver (single driver, single C file).
> > > > > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > > > > >
> > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > > > > >
> > > > > > I'll try to find a way to make it clearer for v7.
> > > > >
> > > > > OK, it makes sense then :)
> > > > >
> > > > > There's something weird though: the devices definitely look like they're
> > > > > in a separate register range, yet you mention a regmap to handle the
> > > > > shared register range. That range doesn't seem described anywhere in the
> > > > > device tree though? What is it for?
> > > >
> > > > My understanding is that 0x1000 to 0x1fff controls the phy
> > > > functionalities and 0x2000 to 0x4fff controls "non-phy"
> > > > functionalities. And you are right, there's no description of that in
> > > > the device tree whatsoever. The ranges are in the same actual device
> > > > and thus it has been decided to not have dt-bindings for the phy
> > > > device.
> > >
> > > Sure, that last part makes sense, but then I'm not sure why you don't
> > > have the full register range in the device node you have in the DT?
> > >
> > > > The phy driver is a child of the DP driver that we register using
> > > > platform_device_register_data() and we pass along the same regmap as
> > > > the DP driver in its platform data.
> > >
> > > Especially if it's used by something, it should be described in the DT
> > > somewhere.
> > >
> > > Maxime
> >
> >
> > So, to make things crystal clear to a newbie (like me).
> > Would you describe it like this:
> > compatible = "mediatek,mt8195-dp-tx";
> > reg = <0 0x1c501000 0 0x0fff>,
> >       <0 0x1c502000 0 0x2fff>;
> >
> > instead of the current description:
> > compatible = "mediatek,mt8195-dp-tx";
> > reg = <0 0x1c500000 0 0x8000>;
> >
> > I haven't checked what the rest of the 0x8000 range is used for though...
>
> I'm confused, is that what you had before?
>
> I recall you had a DTSI somewhere where you have two devices, and the
> dp-tx device not having the phy range?
>
> If the latter is what you have, and there's no overlapping ranges over
> multiple nodes, then it's fine already.
>
> Maxime

This is what I have today in the mt8195.dtsi I'm using for testing purpose.
Provided by mediatek on their vendor branch, not sure if it has been submitted
to the list yet:

        edp_tx: edp_tx@1c500000 {
            status = "disabled";
            compatible = "mediatek,mt8195-dp-tx";
            reg = <0 0x1c500000 0 0x8000>;
            nvmem-cells = <&dp_calibration>;
            nvmem-cell-names = "dp_calibration_data";
            power-domains = <&spm MT8195_POWER_DOMAIN_EPD_TX>;
            interrupts = <GIC_SPI 676 IRQ_TYPE_LEVEL_HIGH 0>;
        };

        dp_tx: dp_tx@1c600000 {
            compatible = "mediatek,mt8195-dp-tx";
            reg = <0 0x1c600000 0 0x8000>;
            nvmem-cells = <&dp_calibration>;
            nvmem-cell-names = "dp_calibration_data";
            power-domains = <&spm MT8195_POWER_DOMAIN_DP_TX>;
            interrupts = <GIC_SPI 458 IRQ_TYPE_LEVEL_HIGH 0>;
            status = "disabled";
        };

With no device tree node for the dp-phy.
The phy range is included in the reg range of the dp-tx device.

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 16:02                       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 16:02 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Quoting Maxime Ripard (2021-12-15 16:15:08)
> Hi,
>
> On Wed, Dec 15, 2021 at 09:03:01AM -0600, Guillaume Ranquet wrote:
> > Quoting Maxime Ripard (2021-12-13 17:54:22)
> > > On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> > > > Hi,
> > > >
> > > > Quoting Maxime Ripard (2021-11-25 15:30:34)
> > > > > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > > > > Hi,
> > > > > > Thanks for all your input, really appreciated.
> > > > > >
> > > > > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > > > > Hi,
> > > > > > >
> > > > > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > > > > to control the phy properties.
> > > > > > > > >
> > > > > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > > > > you need a separate PHY driver in the first place?
> > > > > > > >
> > > > > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > > > > >
> > > > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > > > > >
> > > > > > > It's a bit of a circular argument though :)
> > > > > > >
> > > > > > > It's a separate phy driver because it needs to go through another
> > > > > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > > > > because it's a separate phy driver.
> > > > > > >
> > > > > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > > > > single device?
> > > > > > >
> > > > > > > That being said, usually what those kind of questions mean is that
> > > > > > > you're missing a comment or something in the commit log to provide that
> > > > > > > context in the first place, so it would be great to add that context
> > > > > > > here.
> > > > > > >
> > > > > > > And it will avoid the situation we're now in where multiple reviewers
> > > > > > > ask the same questions over and over again :)
> > > > > > >
> > > > > > At first I didn't understand your reply, then I realized I gave you
> > > > > > the wrong link...
> > > > > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > > > > >
> > > > > > The driver and phy were a single driver until v2 of this patch series
> > > > > > and the phy setup
> > > > > > was done directly in the driver (single driver, single C file).
> > > > > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > > > > >
> > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > > > > >
> > > > > > I'll try to find a way to make it clearer for v7.
> > > > >
> > > > > OK, it makes sense then :)
> > > > >
> > > > > There's something weird though: the devices definitely look like they're
> > > > > in a separate register range, yet you mention a regmap to handle the
> > > > > shared register range. That range doesn't seem described anywhere in the
> > > > > device tree though? What is it for?
> > > >
> > > > My understanding is that 0x1000 to 0x1fff controls the phy
> > > > functionalities and 0x2000 to 0x4fff controls "non-phy"
> > > > functionalities. And you are right, there's no description of that in
> > > > the device tree whatsoever. The ranges are in the same actual device
> > > > and thus it has been decided to not have dt-bindings for the phy
> > > > device.
> > >
> > > Sure, that last part makes sense, but then I'm not sure why you don't
> > > have the full register range in the device node you have in the DT?
> > >
> > > > The phy driver is a child of the DP driver that we register using
> > > > platform_device_register_data() and we pass along the same regmap as
> > > > the DP driver in its platform data.
> > >
> > > Especially if it's used by something, it should be described in the DT
> > > somewhere.
> > >
> > > Maxime
> >
> >
> > So, to make things crystal clear to a newbie (like me).
> > Would you describe it like this:
> > compatible = "mediatek,mt8195-dp-tx";
> > reg = <0 0x1c501000 0 0x0fff>,
> >       <0 0x1c502000 0 0x2fff>;
> >
> > instead of the current description:
> > compatible = "mediatek,mt8195-dp-tx";
> > reg = <0 0x1c500000 0 0x8000>;
> >
> > I haven't checked what the rest of the 0x8000 range is used for though...
>
> I'm confused, is that what you had before?
>
> I recall you had a DTSI somewhere where you have two devices, and the
> dp-tx device not having the phy range?
>
> If the latter is what you have, and there's no overlapping ranges over
> multiple nodes, then it's fine already.
>
> Maxime

This is what I have today in the mt8195.dtsi I'm using for testing purpose.
Provided by mediatek on their vendor branch, not sure if it has been submitted
to the list yet:

        edp_tx: edp_tx@1c500000 {
            status = "disabled";
            compatible = "mediatek,mt8195-dp-tx";
            reg = <0 0x1c500000 0 0x8000>;
            nvmem-cells = <&dp_calibration>;
            nvmem-cell-names = "dp_calibration_data";
            power-domains = <&spm MT8195_POWER_DOMAIN_EPD_TX>;
            interrupts = <GIC_SPI 676 IRQ_TYPE_LEVEL_HIGH 0>;
        };

        dp_tx: dp_tx@1c600000 {
            compatible = "mediatek,mt8195-dp-tx";
            reg = <0 0x1c600000 0 0x8000>;
            nvmem-cells = <&dp_calibration>;
            nvmem-cell-names = "dp_calibration_data";
            power-domains = <&spm MT8195_POWER_DOMAIN_DP_TX>;
            interrupts = <GIC_SPI 458 IRQ_TYPE_LEVEL_HIGH 0>;
            status = "disabled";
        };

With no device tree node for the dp-phy.
The phy range is included in the reg range of the dp-tx device.

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 16:02                       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 16:02 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Chun-Kuang Hu, David Airlie, Thomas Zimmermann, linux-kernel,
	dri-devel, Markus Schneider-Pargmann, linux-mediatek,
	Matthias Brugger, linux-arm-kernel, kernel test robot

Quoting Maxime Ripard (2021-12-15 16:15:08)
> Hi,
>
> On Wed, Dec 15, 2021 at 09:03:01AM -0600, Guillaume Ranquet wrote:
> > Quoting Maxime Ripard (2021-12-13 17:54:22)
> > > On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> > > > Hi,
> > > >
> > > > Quoting Maxime Ripard (2021-11-25 15:30:34)
> > > > > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > > > > Hi,
> > > > > > Thanks for all your input, really appreciated.
> > > > > >
> > > > > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > > > > Hi,
> > > > > > >
> > > > > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > > > > to control the phy properties.
> > > > > > > > >
> > > > > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > > > > you need a separate PHY driver in the first place?
> > > > > > > >
> > > > > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > > > > >
> > > > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > > > > >
> > > > > > > It's a bit of a circular argument though :)
> > > > > > >
> > > > > > > It's a separate phy driver because it needs to go through another
> > > > > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > > > > because it's a separate phy driver.
> > > > > > >
> > > > > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > > > > single device?
> > > > > > >
> > > > > > > That being said, usually what those kind of questions mean is that
> > > > > > > you're missing a comment or something in the commit log to provide that
> > > > > > > context in the first place, so it would be great to add that context
> > > > > > > here.
> > > > > > >
> > > > > > > And it will avoid the situation we're now in where multiple reviewers
> > > > > > > ask the same questions over and over again :)
> > > > > > >
> > > > > > At first I didn't understand your reply, then I realized I gave you
> > > > > > the wrong link...
> > > > > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > > > > >
> > > > > > The driver and phy were a single driver until v2 of this patch series
> > > > > > and the phy setup
> > > > > > was done directly in the driver (single driver, single C file).
> > > > > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > > > > >
> > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > > > > >
> > > > > > I'll try to find a way to make it clearer for v7.
> > > > >
> > > > > OK, it makes sense then :)
> > > > >
> > > > > There's something weird though: the devices definitely look like they're
> > > > > in a separate register range, yet you mention a regmap to handle the
> > > > > shared register range. That range doesn't seem described anywhere in the
> > > > > device tree though? What is it for?
> > > >
> > > > My understanding is that 0x1000 to 0x1fff controls the phy
> > > > functionalities and 0x2000 to 0x4fff controls "non-phy"
> > > > functionalities. And you are right, there's no description of that in
> > > > the device tree whatsoever. The ranges are in the same actual device
> > > > and thus it has been decided to not have dt-bindings for the phy
> > > > device.
> > >
> > > Sure, that last part makes sense, but then I'm not sure why you don't
> > > have the full register range in the device node you have in the DT?
> > >
> > > > The phy driver is a child of the DP driver that we register using
> > > > platform_device_register_data() and we pass along the same regmap as
> > > > the DP driver in its platform data.
> > >
> > > Especially if it's used by something, it should be described in the DT
> > > somewhere.
> > >
> > > Maxime
> >
> >
> > So, to make things crystal clear to a newbie (like me).
> > Would you describe it like this:
> > compatible = "mediatek,mt8195-dp-tx";
> > reg = <0 0x1c501000 0 0x0fff>,
> >       <0 0x1c502000 0 0x2fff>;
> >
> > instead of the current description:
> > compatible = "mediatek,mt8195-dp-tx";
> > reg = <0 0x1c500000 0 0x8000>;
> >
> > I haven't checked what the rest of the 0x8000 range is used for though...
>
> I'm confused, is that what you had before?
>
> I recall you had a DTSI somewhere where you have two devices, and the
> dp-tx device not having the phy range?
>
> If the latter is what you have, and there's no overlapping ranges over
> multiple nodes, then it's fine already.
>
> Maxime

This is what I have today in the mt8195.dtsi I'm using for testing purpose.
Provided by mediatek on their vendor branch, not sure if it has been submitted
to the list yet:

        edp_tx: edp_tx@1c500000 {
            status = "disabled";
            compatible = "mediatek,mt8195-dp-tx";
            reg = <0 0x1c500000 0 0x8000>;
            nvmem-cells = <&dp_calibration>;
            nvmem-cell-names = "dp_calibration_data";
            power-domains = <&spm MT8195_POWER_DOMAIN_EPD_TX>;
            interrupts = <GIC_SPI 676 IRQ_TYPE_LEVEL_HIGH 0>;
        };

        dp_tx: dp_tx@1c600000 {
            compatible = "mediatek,mt8195-dp-tx";
            reg = <0 0x1c600000 0 0x8000>;
            nvmem-cells = <&dp_calibration>;
            nvmem-cell-names = "dp_calibration_data";
            power-domains = <&spm MT8195_POWER_DOMAIN_DP_TX>;
            interrupts = <GIC_SPI 458 IRQ_TYPE_LEVEL_HIGH 0>;
            status = "disabled";
        };

With no device tree node for the dp-phy.
The phy range is included in the reg range of the dp-tx device.

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 16:02                       ` Guillaume Ranquet
  0 siblings, 0 replies; 122+ messages in thread
From: Guillaume Ranquet @ 2021-12-15 16:02 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

Quoting Maxime Ripard (2021-12-15 16:15:08)
> Hi,
>
> On Wed, Dec 15, 2021 at 09:03:01AM -0600, Guillaume Ranquet wrote:
> > Quoting Maxime Ripard (2021-12-13 17:54:22)
> > > On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> > > > Hi,
> > > >
> > > > Quoting Maxime Ripard (2021-11-25 15:30:34)
> > > > > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > > > > Hi,
> > > > > > Thanks for all your input, really appreciated.
> > > > > >
> > > > > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > > > > Hi,
> > > > > > >
> > > > > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > > > > to control the phy properties.
> > > > > > > > >
> > > > > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > > > > you need a separate PHY driver in the first place?
> > > > > > > >
> > > > > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > > > > >
> > > > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > > > > >
> > > > > > > It's a bit of a circular argument though :)
> > > > > > >
> > > > > > > It's a separate phy driver because it needs to go through another
> > > > > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > > > > because it's a separate phy driver.
> > > > > > >
> > > > > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > > > > single device?
> > > > > > >
> > > > > > > That being said, usually what those kind of questions mean is that
> > > > > > > you're missing a comment or something in the commit log to provide that
> > > > > > > context in the first place, so it would be great to add that context
> > > > > > > here.
> > > > > > >
> > > > > > > And it will avoid the situation we're now in where multiple reviewers
> > > > > > > ask the same questions over and over again :)
> > > > > > >
> > > > > > At first I didn't understand your reply, then I realized I gave you
> > > > > > the wrong link...
> > > > > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > > > > >
> > > > > > The driver and phy were a single driver until v2 of this patch series
> > > > > > and the phy setup
> > > > > > was done directly in the driver (single driver, single C file).
> > > > > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > > > > >
> > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > > > > >
> > > > > > I'll try to find a way to make it clearer for v7.
> > > > >
> > > > > OK, it makes sense then :)
> > > > >
> > > > > There's something weird though: the devices definitely look like they're
> > > > > in a separate register range, yet you mention a regmap to handle the
> > > > > shared register range. That range doesn't seem described anywhere in the
> > > > > device tree though? What is it for?
> > > >
> > > > My understanding is that 0x1000 to 0x1fff controls the phy
> > > > functionalities and 0x2000 to 0x4fff controls "non-phy"
> > > > functionalities. And you are right, there's no description of that in
> > > > the device tree whatsoever. The ranges are in the same actual device
> > > > and thus it has been decided to not have dt-bindings for the phy
> > > > device.
> > >
> > > Sure, that last part makes sense, but then I'm not sure why you don't
> > > have the full register range in the device node you have in the DT?
> > >
> > > > The phy driver is a child of the DP driver that we register using
> > > > platform_device_register_data() and we pass along the same regmap as
> > > > the DP driver in its platform data.
> > >
> > > Especially if it's used by something, it should be described in the DT
> > > somewhere.
> > >
> > > Maxime
> >
> >
> > So, to make things crystal clear to a newbie (like me).
> > Would you describe it like this:
> > compatible = "mediatek,mt8195-dp-tx";
> > reg = <0 0x1c501000 0 0x0fff>,
> >       <0 0x1c502000 0 0x2fff>;
> >
> > instead of the current description:
> > compatible = "mediatek,mt8195-dp-tx";
> > reg = <0 0x1c500000 0 0x8000>;
> >
> > I haven't checked what the rest of the 0x8000 range is used for though...
>
> I'm confused, is that what you had before?
>
> I recall you had a DTSI somewhere where you have two devices, and the
> dp-tx device not having the phy range?
>
> If the latter is what you have, and there's no overlapping ranges over
> multiple nodes, then it's fine already.
>
> Maxime

This is what I have today in the mt8195.dtsi I'm using for testing purpose.
Provided by mediatek on their vendor branch, not sure if it has been submitted
to the list yet:

        edp_tx: edp_tx@1c500000 {
            status = "disabled";
            compatible = "mediatek,mt8195-dp-tx";
            reg = <0 0x1c500000 0 0x8000>;
            nvmem-cells = <&dp_calibration>;
            nvmem-cell-names = "dp_calibration_data";
            power-domains = <&spm MT8195_POWER_DOMAIN_EPD_TX>;
            interrupts = <GIC_SPI 676 IRQ_TYPE_LEVEL_HIGH 0>;
        };

        dp_tx: dp_tx@1c600000 {
            compatible = "mediatek,mt8195-dp-tx";
            reg = <0 0x1c600000 0 0x8000>;
            nvmem-cells = <&dp_calibration>;
            nvmem-cell-names = "dp_calibration_data";
            power-domains = <&spm MT8195_POWER_DOMAIN_DP_TX>;
            interrupts = <GIC_SPI 458 IRQ_TYPE_LEVEL_HIGH 0>;
            status = "disabled";
        };

With no device tree node for the dp-phy.
The phy range is included in the reg range of the dp-tx device.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
  2021-12-15 16:02                       ` Guillaume Ranquet
  (?)
  (?)
@ 2021-12-15 16:06                         ` Maxime Ripard
  -1 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-12-15 16:06 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 6815 bytes --]

On Wed, Dec 15, 2021 at 10:02:22AM -0600, Guillaume Ranquet wrote:
> Quoting Maxime Ripard (2021-12-15 16:15:08)
> > Hi,
> >
> > On Wed, Dec 15, 2021 at 09:03:01AM -0600, Guillaume Ranquet wrote:
> > > Quoting Maxime Ripard (2021-12-13 17:54:22)
> > > > On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> > > > > Hi,
> > > > >
> > > > > Quoting Maxime Ripard (2021-11-25 15:30:34)
> > > > > > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > > > > > Hi,
> > > > > > > Thanks for all your input, really appreciated.
> > > > > > >
> > > > > > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > > > > > Hi,
> > > > > > > >
> > > > > > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > > > > > to control the phy properties.
> > > > > > > > > >
> > > > > > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > > > > > you need a separate PHY driver in the first place?
> > > > > > > > >
> > > > > > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > > > > > >
> > > > > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > > > > > >
> > > > > > > > It's a bit of a circular argument though :)
> > > > > > > >
> > > > > > > > It's a separate phy driver because it needs to go through another
> > > > > > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > > > > > because it's a separate phy driver.
> > > > > > > >
> > > > > > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > > > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > > > > > single device?
> > > > > > > >
> > > > > > > > That being said, usually what those kind of questions mean is that
> > > > > > > > you're missing a comment or something in the commit log to provide that
> > > > > > > > context in the first place, so it would be great to add that context
> > > > > > > > here.
> > > > > > > >
> > > > > > > > And it will avoid the situation we're now in where multiple reviewers
> > > > > > > > ask the same questions over and over again :)
> > > > > > > >
> > > > > > > At first I didn't understand your reply, then I realized I gave you
> > > > > > > the wrong link...
> > > > > > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > > > > > >
> > > > > > > The driver and phy were a single driver until v2 of this patch series
> > > > > > > and the phy setup
> > > > > > > was done directly in the driver (single driver, single C file).
> > > > > > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > > > > > >
> > > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > > > > > >
> > > > > > > I'll try to find a way to make it clearer for v7.
> > > > > >
> > > > > > OK, it makes sense then :)
> > > > > >
> > > > > > There's something weird though: the devices definitely look like they're
> > > > > > in a separate register range, yet you mention a regmap to handle the
> > > > > > shared register range. That range doesn't seem described anywhere in the
> > > > > > device tree though? What is it for?
> > > > >
> > > > > My understanding is that 0x1000 to 0x1fff controls the phy
> > > > > functionalities and 0x2000 to 0x4fff controls "non-phy"
> > > > > functionalities. And you are right, there's no description of that in
> > > > > the device tree whatsoever. The ranges are in the same actual device
> > > > > and thus it has been decided to not have dt-bindings for the phy
> > > > > device.
> > > >
> > > > Sure, that last part makes sense, but then I'm not sure why you don't
> > > > have the full register range in the device node you have in the DT?
> > > >
> > > > > The phy driver is a child of the DP driver that we register using
> > > > > platform_device_register_data() and we pass along the same regmap as
> > > > > the DP driver in its platform data.
> > > >
> > > > Especially if it's used by something, it should be described in the DT
> > > > somewhere.
> > > >
> > > > Maxime
> > >
> > >
> > > So, to make things crystal clear to a newbie (like me).
> > > Would you describe it like this:
> > > compatible = "mediatek,mt8195-dp-tx";
> > > reg = <0 0x1c501000 0 0x0fff>,
> > >       <0 0x1c502000 0 0x2fff>;
> > >
> > > instead of the current description:
> > > compatible = "mediatek,mt8195-dp-tx";
> > > reg = <0 0x1c500000 0 0x8000>;
> > >
> > > I haven't checked what the rest of the 0x8000 range is used for though...
> >
> > I'm confused, is that what you had before?
> >
> > I recall you had a DTSI somewhere where you have two devices, and the
> > dp-tx device not having the phy range?
> >
> > If the latter is what you have, and there's no overlapping ranges over
> > multiple nodes, then it's fine already.
> >
> > Maxime
> 
> This is what I have today in the mt8195.dtsi I'm using for testing purpose.
> Provided by mediatek on their vendor branch, not sure if it has been submitted
> to the list yet:
> 
>         edp_tx: edp_tx@1c500000 {
>             status = "disabled";
>             compatible = "mediatek,mt8195-dp-tx";
>             reg = <0 0x1c500000 0 0x8000>;
>             nvmem-cells = <&dp_calibration>;
>             nvmem-cell-names = "dp_calibration_data";
>             power-domains = <&spm MT8195_POWER_DOMAIN_EPD_TX>;
>             interrupts = <GIC_SPI 676 IRQ_TYPE_LEVEL_HIGH 0>;
>         };
> 
>         dp_tx: dp_tx@1c600000 {
>             compatible = "mediatek,mt8195-dp-tx";
>             reg = <0 0x1c600000 0 0x8000>;
>             nvmem-cells = <&dp_calibration>;
>             nvmem-cell-names = "dp_calibration_data";
>             power-domains = <&spm MT8195_POWER_DOMAIN_DP_TX>;
>             interrupts = <GIC_SPI 458 IRQ_TYPE_LEVEL_HIGH 0>;
>             status = "disabled";
>         };
> 
> With no device tree node for the dp-phy.
> The phy range is included in the reg range of the dp-tx device.

It's all good then, sorry for the noise

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 16:06                         ` Maxime Ripard
  0 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-12-15 16:06 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Chun-Kuang Hu, David Airlie, Thomas Zimmermann, linux-kernel,
	dri-devel, Markus Schneider-Pargmann, linux-mediatek,
	Matthias Brugger, linux-arm-kernel, kernel test robot

[-- Attachment #1: Type: text/plain, Size: 6815 bytes --]

On Wed, Dec 15, 2021 at 10:02:22AM -0600, Guillaume Ranquet wrote:
> Quoting Maxime Ripard (2021-12-15 16:15:08)
> > Hi,
> >
> > On Wed, Dec 15, 2021 at 09:03:01AM -0600, Guillaume Ranquet wrote:
> > > Quoting Maxime Ripard (2021-12-13 17:54:22)
> > > > On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> > > > > Hi,
> > > > >
> > > > > Quoting Maxime Ripard (2021-11-25 15:30:34)
> > > > > > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > > > > > Hi,
> > > > > > > Thanks for all your input, really appreciated.
> > > > > > >
> > > > > > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > > > > > Hi,
> > > > > > > >
> > > > > > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > > > > > to control the phy properties.
> > > > > > > > > >
> > > > > > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > > > > > you need a separate PHY driver in the first place?
> > > > > > > > >
> > > > > > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > > > > > >
> > > > > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > > > > > >
> > > > > > > > It's a bit of a circular argument though :)
> > > > > > > >
> > > > > > > > It's a separate phy driver because it needs to go through another
> > > > > > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > > > > > because it's a separate phy driver.
> > > > > > > >
> > > > > > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > > > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > > > > > single device?
> > > > > > > >
> > > > > > > > That being said, usually what those kind of questions mean is that
> > > > > > > > you're missing a comment or something in the commit log to provide that
> > > > > > > > context in the first place, so it would be great to add that context
> > > > > > > > here.
> > > > > > > >
> > > > > > > > And it will avoid the situation we're now in where multiple reviewers
> > > > > > > > ask the same questions over and over again :)
> > > > > > > >
> > > > > > > At first I didn't understand your reply, then I realized I gave you
> > > > > > > the wrong link...
> > > > > > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > > > > > >
> > > > > > > The driver and phy were a single driver until v2 of this patch series
> > > > > > > and the phy setup
> > > > > > > was done directly in the driver (single driver, single C file).
> > > > > > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > > > > > >
> > > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > > > > > >
> > > > > > > I'll try to find a way to make it clearer for v7.
> > > > > >
> > > > > > OK, it makes sense then :)
> > > > > >
> > > > > > There's something weird though: the devices definitely look like they're
> > > > > > in a separate register range, yet you mention a regmap to handle the
> > > > > > shared register range. That range doesn't seem described anywhere in the
> > > > > > device tree though? What is it for?
> > > > >
> > > > > My understanding is that 0x1000 to 0x1fff controls the phy
> > > > > functionalities and 0x2000 to 0x4fff controls "non-phy"
> > > > > functionalities. And you are right, there's no description of that in
> > > > > the device tree whatsoever. The ranges are in the same actual device
> > > > > and thus it has been decided to not have dt-bindings for the phy
> > > > > device.
> > > >
> > > > Sure, that last part makes sense, but then I'm not sure why you don't
> > > > have the full register range in the device node you have in the DT?
> > > >
> > > > > The phy driver is a child of the DP driver that we register using
> > > > > platform_device_register_data() and we pass along the same regmap as
> > > > > the DP driver in its platform data.
> > > >
> > > > Especially if it's used by something, it should be described in the DT
> > > > somewhere.
> > > >
> > > > Maxime
> > >
> > >
> > > So, to make things crystal clear to a newbie (like me).
> > > Would you describe it like this:
> > > compatible = "mediatek,mt8195-dp-tx";
> > > reg = <0 0x1c501000 0 0x0fff>,
> > >       <0 0x1c502000 0 0x2fff>;
> > >
> > > instead of the current description:
> > > compatible = "mediatek,mt8195-dp-tx";
> > > reg = <0 0x1c500000 0 0x8000>;
> > >
> > > I haven't checked what the rest of the 0x8000 range is used for though...
> >
> > I'm confused, is that what you had before?
> >
> > I recall you had a DTSI somewhere where you have two devices, and the
> > dp-tx device not having the phy range?
> >
> > If the latter is what you have, and there's no overlapping ranges over
> > multiple nodes, then it's fine already.
> >
> > Maxime
> 
> This is what I have today in the mt8195.dtsi I'm using for testing purpose.
> Provided by mediatek on their vendor branch, not sure if it has been submitted
> to the list yet:
> 
>         edp_tx: edp_tx@1c500000 {
>             status = "disabled";
>             compatible = "mediatek,mt8195-dp-tx";
>             reg = <0 0x1c500000 0 0x8000>;
>             nvmem-cells = <&dp_calibration>;
>             nvmem-cell-names = "dp_calibration_data";
>             power-domains = <&spm MT8195_POWER_DOMAIN_EPD_TX>;
>             interrupts = <GIC_SPI 676 IRQ_TYPE_LEVEL_HIGH 0>;
>         };
> 
>         dp_tx: dp_tx@1c600000 {
>             compatible = "mediatek,mt8195-dp-tx";
>             reg = <0 0x1c600000 0 0x8000>;
>             nvmem-cells = <&dp_calibration>;
>             nvmem-cell-names = "dp_calibration_data";
>             power-domains = <&spm MT8195_POWER_DOMAIN_DP_TX>;
>             interrupts = <GIC_SPI 458 IRQ_TYPE_LEVEL_HIGH 0>;
>             status = "disabled";
>         };
> 
> With no device tree node for the dp-phy.
> The phy range is included in the reg range of the dp-tx device.

It's all good then, sorry for the noise

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 16:06                         ` Maxime Ripard
  0 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-12-15 16:06 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 6815 bytes --]

On Wed, Dec 15, 2021 at 10:02:22AM -0600, Guillaume Ranquet wrote:
> Quoting Maxime Ripard (2021-12-15 16:15:08)
> > Hi,
> >
> > On Wed, Dec 15, 2021 at 09:03:01AM -0600, Guillaume Ranquet wrote:
> > > Quoting Maxime Ripard (2021-12-13 17:54:22)
> > > > On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> > > > > Hi,
> > > > >
> > > > > Quoting Maxime Ripard (2021-11-25 15:30:34)
> > > > > > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > > > > > Hi,
> > > > > > > Thanks for all your input, really appreciated.
> > > > > > >
> > > > > > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > > > > > Hi,
> > > > > > > >
> > > > > > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > > > > > to control the phy properties.
> > > > > > > > > >
> > > > > > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > > > > > you need a separate PHY driver in the first place?
> > > > > > > > >
> > > > > > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > > > > > >
> > > > > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > > > > > >
> > > > > > > > It's a bit of a circular argument though :)
> > > > > > > >
> > > > > > > > It's a separate phy driver because it needs to go through another
> > > > > > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > > > > > because it's a separate phy driver.
> > > > > > > >
> > > > > > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > > > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > > > > > single device?
> > > > > > > >
> > > > > > > > That being said, usually what those kind of questions mean is that
> > > > > > > > you're missing a comment or something in the commit log to provide that
> > > > > > > > context in the first place, so it would be great to add that context
> > > > > > > > here.
> > > > > > > >
> > > > > > > > And it will avoid the situation we're now in where multiple reviewers
> > > > > > > > ask the same questions over and over again :)
> > > > > > > >
> > > > > > > At first I didn't understand your reply, then I realized I gave you
> > > > > > > the wrong link...
> > > > > > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > > > > > >
> > > > > > > The driver and phy were a single driver until v2 of this patch series
> > > > > > > and the phy setup
> > > > > > > was done directly in the driver (single driver, single C file).
> > > > > > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > > > > > >
> > > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > > > > > >
> > > > > > > I'll try to find a way to make it clearer for v7.
> > > > > >
> > > > > > OK, it makes sense then :)
> > > > > >
> > > > > > There's something weird though: the devices definitely look like they're
> > > > > > in a separate register range, yet you mention a regmap to handle the
> > > > > > shared register range. That range doesn't seem described anywhere in the
> > > > > > device tree though? What is it for?
> > > > >
> > > > > My understanding is that 0x1000 to 0x1fff controls the phy
> > > > > functionalities and 0x2000 to 0x4fff controls "non-phy"
> > > > > functionalities. And you are right, there's no description of that in
> > > > > the device tree whatsoever. The ranges are in the same actual device
> > > > > and thus it has been decided to not have dt-bindings for the phy
> > > > > device.
> > > >
> > > > Sure, that last part makes sense, but then I'm not sure why you don't
> > > > have the full register range in the device node you have in the DT?
> > > >
> > > > > The phy driver is a child of the DP driver that we register using
> > > > > platform_device_register_data() and we pass along the same regmap as
> > > > > the DP driver in its platform data.
> > > >
> > > > Especially if it's used by something, it should be described in the DT
> > > > somewhere.
> > > >
> > > > Maxime
> > >
> > >
> > > So, to make things crystal clear to a newbie (like me).
> > > Would you describe it like this:
> > > compatible = "mediatek,mt8195-dp-tx";
> > > reg = <0 0x1c501000 0 0x0fff>,
> > >       <0 0x1c502000 0 0x2fff>;
> > >
> > > instead of the current description:
> > > compatible = "mediatek,mt8195-dp-tx";
> > > reg = <0 0x1c500000 0 0x8000>;
> > >
> > > I haven't checked what the rest of the 0x8000 range is used for though...
> >
> > I'm confused, is that what you had before?
> >
> > I recall you had a DTSI somewhere where you have two devices, and the
> > dp-tx device not having the phy range?
> >
> > If the latter is what you have, and there's no overlapping ranges over
> > multiple nodes, then it's fine already.
> >
> > Maxime
> 
> This is what I have today in the mt8195.dtsi I'm using for testing purpose.
> Provided by mediatek on their vendor branch, not sure if it has been submitted
> to the list yet:
> 
>         edp_tx: edp_tx@1c500000 {
>             status = "disabled";
>             compatible = "mediatek,mt8195-dp-tx";
>             reg = <0 0x1c500000 0 0x8000>;
>             nvmem-cells = <&dp_calibration>;
>             nvmem-cell-names = "dp_calibration_data";
>             power-domains = <&spm MT8195_POWER_DOMAIN_EPD_TX>;
>             interrupts = <GIC_SPI 676 IRQ_TYPE_LEVEL_HIGH 0>;
>         };
> 
>         dp_tx: dp_tx@1c600000 {
>             compatible = "mediatek,mt8195-dp-tx";
>             reg = <0 0x1c600000 0 0x8000>;
>             nvmem-cells = <&dp_calibration>;
>             nvmem-cell-names = "dp_calibration_data";
>             power-domains = <&spm MT8195_POWER_DOMAIN_DP_TX>;
>             interrupts = <GIC_SPI 458 IRQ_TYPE_LEVEL_HIGH 0>;
>             status = "disabled";
>         };
> 
> With no device tree node for the dp-phy.
> The phy range is included in the reg range of the dp-tx device.

It's all good then, sorry for the noise

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 170 bytes --]

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver
@ 2021-12-15 16:06                         ` Maxime Ripard
  0 siblings, 0 replies; 122+ messages in thread
From: Maxime Ripard @ 2021-12-15 16:06 UTC (permalink / raw)
  To: Guillaume Ranquet
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie,
	Daniel Vetter, Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	Markus Schneider-Pargmann, kernel test robot, linux-kernel,
	dri-devel, linux-mediatek, linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 6815 bytes --]

On Wed, Dec 15, 2021 at 10:02:22AM -0600, Guillaume Ranquet wrote:
> Quoting Maxime Ripard (2021-12-15 16:15:08)
> > Hi,
> >
> > On Wed, Dec 15, 2021 at 09:03:01AM -0600, Guillaume Ranquet wrote:
> > > Quoting Maxime Ripard (2021-12-13 17:54:22)
> > > > On Thu, Dec 02, 2021 at 06:48:12AM -0800, Guillaume Ranquet wrote:
> > > > > Hi,
> > > > >
> > > > > Quoting Maxime Ripard (2021-11-25 15:30:34)
> > > > > > On Wed, Nov 24, 2021 at 01:45:21PM +0000, Guillaume Ranquet wrote:
> > > > > > > Hi,
> > > > > > > Thanks for all your input, really appreciated.
> > > > > > >
> > > > > > > Quoting Maxime Ripard (2021-11-16 15:51:12)
> > > > > > > > Hi,
> > > > > > > >
> > > > > > > > On Mon, Nov 15, 2021 at 09:33:52AM -0500, Guillaume Ranquet wrote:
> > > > > > > > > Quoting Maxime Ripard (2021-11-15 11:11:29)
> > > > > > > > > > > The driver creates a child device for the phy. The child device will
> > > > > > > > > > > never exist without the parent being active. As they are sharing a
> > > > > > > > > > > register range, the parent passes a regmap pointer to the child so that
> > > > > > > > > > > both can work with the same register range. The phy driver sets device
> > > > > > > > > > > data that is read by the parent to get the phy device that can be used
> > > > > > > > > > > to control the phy properties.
> > > > > > > > > >
> > > > > > > > > > If the PHY is in the same register space than the DP controller, why do
> > > > > > > > > > you need a separate PHY driver in the first place?
> > > > > > > > >
> > > > > > > > > This has been asked by Chun-Kuang Hu in a previous revision of the series:
> > > > > > > > >
> > > > > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY_-+T-wRCH2yw2XSm=ZbaBbqBQ4EqpU2P0TF90gAWQeRsg@mail.gmail.com/
> > > > > > > >
> > > > > > > > It's a bit of a circular argument though :)
> > > > > > > >
> > > > > > > > It's a separate phy driver because it needs to go through another
> > > > > > > > maintainer's tree, but it needs to go through another maintainer's tree
> > > > > > > > because it's a separate phy driver.
> > > > > > > >
> > > > > > > > It doesn't explain why it needs to be a separate phy driver? Why can't
> > > > > > > > the phy setup be done directly in the DP driver, if it's essentially a
> > > > > > > > single device?
> > > > > > > >
> > > > > > > > That being said, usually what those kind of questions mean is that
> > > > > > > > you're missing a comment or something in the commit log to provide that
> > > > > > > > context in the first place, so it would be great to add that context
> > > > > > > > here.
> > > > > > > >
> > > > > > > > And it will avoid the situation we're now in where multiple reviewers
> > > > > > > > ask the same questions over and over again :)
> > > > > > > >
> > > > > > > At first I didn't understand your reply, then I realized I gave you
> > > > > > > the wrong link...
> > > > > > > my bad! I'm struggling a bit with mail reviews, but I'll get there eventually.
> > > > > > >
> > > > > > > The driver and phy were a single driver until v2 of this patch series
> > > > > > > and the phy setup
> > > > > > > was done directly in the driver (single driver, single C file).
> > > > > > > Here's the relevant link to the discussion between Chun-Kuang and Markus
> > > > > > >
> > > > > > > https://lore.kernel.org/linux-mediatek/CAAOTY__cJMqcAieEraJ2sz4gi0Zs-aiNXz38_x7dPQea6HvYEg@mail.gmail.com/#t
> > > > > > >
> > > > > > > I'll try to find a way to make it clearer for v7.
> > > > > >
> > > > > > OK, it makes sense then :)
> > > > > >
> > > > > > There's something weird though: the devices definitely look like they're
> > > > > > in a separate register range, yet you mention a regmap to handle the
> > > > > > shared register range. That range doesn't seem described anywhere in the
> > > > > > device tree though? What is it for?
> > > > >
> > > > > My understanding is that 0x1000 to 0x1fff controls the phy
> > > > > functionalities and 0x2000 to 0x4fff controls "non-phy"
> > > > > functionalities. And you are right, there's no description of that in
> > > > > the device tree whatsoever. The ranges are in the same actual device
> > > > > and thus it has been decided to not have dt-bindings for the phy
> > > > > device.
> > > >
> > > > Sure, that last part makes sense, but then I'm not sure why you don't
> > > > have the full register range in the device node you have in the DT?
> > > >
> > > > > The phy driver is a child of the DP driver that we register using
> > > > > platform_device_register_data() and we pass along the same regmap as
> > > > > the DP driver in its platform data.
> > > >
> > > > Especially if it's used by something, it should be described in the DT
> > > > somewhere.
> > > >
> > > > Maxime
> > >
> > >
> > > So, to make things crystal clear to a newbie (like me).
> > > Would you describe it like this:
> > > compatible = "mediatek,mt8195-dp-tx";
> > > reg = <0 0x1c501000 0 0x0fff>,
> > >       <0 0x1c502000 0 0x2fff>;
> > >
> > > instead of the current description:
> > > compatible = "mediatek,mt8195-dp-tx";
> > > reg = <0 0x1c500000 0 0x8000>;
> > >
> > > I haven't checked what the rest of the 0x8000 range is used for though...
> >
> > I'm confused, is that what you had before?
> >
> > I recall you had a DTSI somewhere where you have two devices, and the
> > dp-tx device not having the phy range?
> >
> > If the latter is what you have, and there's no overlapping ranges over
> > multiple nodes, then it's fine already.
> >
> > Maxime
> 
> This is what I have today in the mt8195.dtsi I'm using for testing purpose.
> Provided by mediatek on their vendor branch, not sure if it has been submitted
> to the list yet:
> 
>         edp_tx: edp_tx@1c500000 {
>             status = "disabled";
>             compatible = "mediatek,mt8195-dp-tx";
>             reg = <0 0x1c500000 0 0x8000>;
>             nvmem-cells = <&dp_calibration>;
>             nvmem-cell-names = "dp_calibration_data";
>             power-domains = <&spm MT8195_POWER_DOMAIN_EPD_TX>;
>             interrupts = <GIC_SPI 676 IRQ_TYPE_LEVEL_HIGH 0>;
>         };
> 
>         dp_tx: dp_tx@1c600000 {
>             compatible = "mediatek,mt8195-dp-tx";
>             reg = <0 0x1c600000 0 0x8000>;
>             nvmem-cells = <&dp_calibration>;
>             nvmem-cell-names = "dp_calibration_data";
>             power-domains = <&spm MT8195_POWER_DOMAIN_DP_TX>;
>             interrupts = <GIC_SPI 458 IRQ_TYPE_LEVEL_HIGH 0>;
>             status = "disabled";
>         };
> 
> With no device tree node for the dp-phy.
> The phy range is included in the reg range of the dp-tx device.

It's all good then, sorry for the noise

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v6 5/7] drm/mediatek: dpi: Add dpintf support
  2021-11-10 13:06   ` Guillaume Ranquet
  (?)
  (?)
@ 2021-12-15 20:14     ` Matthias Brugger
  -1 siblings, 0 replies; 122+ messages in thread
From: Matthias Brugger @ 2021-12-15 20:14 UTC (permalink / raw)
  To: Guillaume Ranquet, Chun-Kuang Hu, Philipp Zabel, David Airlie,
	Daniel Vetter
  Cc: Markus Schneider-Pargmann, dri-devel, linux-mediatek,
	linux-arm-kernel, linux-kernel



On 10/11/2021 14:06, Guillaume Ranquet wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> dpintf is the displayport interface hardware unit. This unit is similar
> to dpi and can reuse most of the code.
> 
> This patch adds support for mt8195-dpintf to this dpi driver. Main
> differences are:
>   - Some features/functional components are not available for dpintf
>     which are now excluded from code execution once is_dpintf is set
>   - dpintf can and needs to choose between different clockdividers based
>     on the clockspeed. This is done by choosing a different clock parent.
>   - There are two additional clocks that need to be managed. These are
>     only set for dpintf and will be set to NULL if not supplied. The
>     clk_* calls handle these as normal clocks then.
>   - Some register contents differ slightly between the two components. To
>     work around this I added register bits/masks with a DPINTF_ prefix
>     and use them where different.
> 
> Based on a separate driver for dpintf created by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> ---
[...]
> diff --git a/include/linux/soc/mediatek/mtk-mmsys.h b/include/linux/soc/mediatek/mtk-mmsys.h
> index 4bba275e235ac..56ed2fa5f59e8 100644
> --- a/include/linux/soc/mediatek/mtk-mmsys.h
> +++ b/include/linux/soc/mediatek/mtk-mmsys.h
> @@ -19,6 +19,8 @@ enum mtk_ddp_comp_id {
>   	DDP_COMPONENT_DITHER,
>   	DDP_COMPONENT_DPI0,
>   	DDP_COMPONENT_DPI1,
> +	DDP_COMPONENT_DP_INTF0,
> +	DDP_COMPONENT_DP_INTF1,
>   	DDP_COMPONENT_DSI0,
>   	DDP_COMPONENT_DSI1,
>   	DDP_COMPONENT_DSI2,
> 

For the mtk-mmsys.h:
Acked-by: Matthias Brugger <matthias.bgg@gmail.com>

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

* Re: [PATCH v6 5/7] drm/mediatek: dpi: Add dpintf support
@ 2021-12-15 20:14     ` Matthias Brugger
  0 siblings, 0 replies; 122+ messages in thread
From: Matthias Brugger @ 2021-12-15 20:14 UTC (permalink / raw)
  To: Guillaume Ranquet, Chun-Kuang Hu, Philipp Zabel, David Airlie,
	Daniel Vetter
  Cc: Markus Schneider-Pargmann, dri-devel, linux-mediatek,
	linux-arm-kernel, linux-kernel



On 10/11/2021 14:06, Guillaume Ranquet wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> dpintf is the displayport interface hardware unit. This unit is similar
> to dpi and can reuse most of the code.
> 
> This patch adds support for mt8195-dpintf to this dpi driver. Main
> differences are:
>   - Some features/functional components are not available for dpintf
>     which are now excluded from code execution once is_dpintf is set
>   - dpintf can and needs to choose between different clockdividers based
>     on the clockspeed. This is done by choosing a different clock parent.
>   - There are two additional clocks that need to be managed. These are
>     only set for dpintf and will be set to NULL if not supplied. The
>     clk_* calls handle these as normal clocks then.
>   - Some register contents differ slightly between the two components. To
>     work around this I added register bits/masks with a DPINTF_ prefix
>     and use them where different.
> 
> Based on a separate driver for dpintf created by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> ---
[...]
> diff --git a/include/linux/soc/mediatek/mtk-mmsys.h b/include/linux/soc/mediatek/mtk-mmsys.h
> index 4bba275e235ac..56ed2fa5f59e8 100644
> --- a/include/linux/soc/mediatek/mtk-mmsys.h
> +++ b/include/linux/soc/mediatek/mtk-mmsys.h
> @@ -19,6 +19,8 @@ enum mtk_ddp_comp_id {
>   	DDP_COMPONENT_DITHER,
>   	DDP_COMPONENT_DPI0,
>   	DDP_COMPONENT_DPI1,
> +	DDP_COMPONENT_DP_INTF0,
> +	DDP_COMPONENT_DP_INTF1,
>   	DDP_COMPONENT_DSI0,
>   	DDP_COMPONENT_DSI1,
>   	DDP_COMPONENT_DSI2,
> 

For the mtk-mmsys.h:
Acked-by: Matthias Brugger <matthias.bgg@gmail.com>

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

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

* Re: [PATCH v6 5/7] drm/mediatek: dpi: Add dpintf support
@ 2021-12-15 20:14     ` Matthias Brugger
  0 siblings, 0 replies; 122+ messages in thread
From: Matthias Brugger @ 2021-12-15 20:14 UTC (permalink / raw)
  To: Guillaume Ranquet, Chun-Kuang Hu, Philipp Zabel, David Airlie,
	Daniel Vetter
  Cc: linux-kernel, linux-mediatek, linux-arm-kernel, dri-devel,
	Markus Schneider-Pargmann



On 10/11/2021 14:06, Guillaume Ranquet wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> dpintf is the displayport interface hardware unit. This unit is similar
> to dpi and can reuse most of the code.
> 
> This patch adds support for mt8195-dpintf to this dpi driver. Main
> differences are:
>   - Some features/functional components are not available for dpintf
>     which are now excluded from code execution once is_dpintf is set
>   - dpintf can and needs to choose between different clockdividers based
>     on the clockspeed. This is done by choosing a different clock parent.
>   - There are two additional clocks that need to be managed. These are
>     only set for dpintf and will be set to NULL if not supplied. The
>     clk_* calls handle these as normal clocks then.
>   - Some register contents differ slightly between the two components. To
>     work around this I added register bits/masks with a DPINTF_ prefix
>     and use them where different.
> 
> Based on a separate driver for dpintf created by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> ---
[...]
> diff --git a/include/linux/soc/mediatek/mtk-mmsys.h b/include/linux/soc/mediatek/mtk-mmsys.h
> index 4bba275e235ac..56ed2fa5f59e8 100644
> --- a/include/linux/soc/mediatek/mtk-mmsys.h
> +++ b/include/linux/soc/mediatek/mtk-mmsys.h
> @@ -19,6 +19,8 @@ enum mtk_ddp_comp_id {
>   	DDP_COMPONENT_DITHER,
>   	DDP_COMPONENT_DPI0,
>   	DDP_COMPONENT_DPI1,
> +	DDP_COMPONENT_DP_INTF0,
> +	DDP_COMPONENT_DP_INTF1,
>   	DDP_COMPONENT_DSI0,
>   	DDP_COMPONENT_DSI1,
>   	DDP_COMPONENT_DSI2,
> 

For the mtk-mmsys.h:
Acked-by: Matthias Brugger <matthias.bgg@gmail.com>

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

* Re: [PATCH v6 5/7] drm/mediatek: dpi: Add dpintf support
@ 2021-12-15 20:14     ` Matthias Brugger
  0 siblings, 0 replies; 122+ messages in thread
From: Matthias Brugger @ 2021-12-15 20:14 UTC (permalink / raw)
  To: Guillaume Ranquet, Chun-Kuang Hu, Philipp Zabel, David Airlie,
	Daniel Vetter
  Cc: Markus Schneider-Pargmann, dri-devel, linux-mediatek,
	linux-arm-kernel, linux-kernel



On 10/11/2021 14:06, Guillaume Ranquet wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> dpintf is the displayport interface hardware unit. This unit is similar
> to dpi and can reuse most of the code.
> 
> This patch adds support for mt8195-dpintf to this dpi driver. Main
> differences are:
>   - Some features/functional components are not available for dpintf
>     which are now excluded from code execution once is_dpintf is set
>   - dpintf can and needs to choose between different clockdividers based
>     on the clockspeed. This is done by choosing a different clock parent.
>   - There are two additional clocks that need to be managed. These are
>     only set for dpintf and will be set to NULL if not supplied. The
>     clk_* calls handle these as normal clocks then.
>   - Some register contents differ slightly between the two components. To
>     work around this I added register bits/masks with a DPINTF_ prefix
>     and use them where different.
> 
> Based on a separate driver for dpintf created by
> Jason-JH.Lin <jason-jh.lin@mediatek.com>.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> ---
[...]
> diff --git a/include/linux/soc/mediatek/mtk-mmsys.h b/include/linux/soc/mediatek/mtk-mmsys.h
> index 4bba275e235ac..56ed2fa5f59e8 100644
> --- a/include/linux/soc/mediatek/mtk-mmsys.h
> +++ b/include/linux/soc/mediatek/mtk-mmsys.h
> @@ -19,6 +19,8 @@ enum mtk_ddp_comp_id {
>   	DDP_COMPONENT_DITHER,
>   	DDP_COMPONENT_DPI0,
>   	DDP_COMPONENT_DPI1,
> +	DDP_COMPONENT_DP_INTF0,
> +	DDP_COMPONENT_DP_INTF1,
>   	DDP_COMPONENT_DSI0,
>   	DDP_COMPONENT_DSI1,
>   	DDP_COMPONENT_DSI2,
> 

For the mtk-mmsys.h:
Acked-by: Matthias Brugger <matthias.bgg@gmail.com>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

end of thread, other threads:[~2021-12-15 20:15 UTC | newest]

Thread overview: 122+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-10 13:06 [PATCH v6 0/7] drm/mediatek: Add mt8195 DisplayPort driver Guillaume Ranquet
2021-11-10 13:06 ` Guillaume Ranquet
2021-11-10 13:06 ` [PATCH v6 1/7] dt-bindings: mediatek,dpi: Add DP_INTF compatible Guillaume Ranquet
2021-11-10 13:06   ` Guillaume Ranquet
2021-11-10 13:06   ` Guillaume Ranquet
2021-11-10 13:06   ` Guillaume Ranquet
2021-11-10 13:06 ` [PATCH v6 2/7] dt-bindings: mediatek,dp: Add Display Port binding Guillaume Ranquet
2021-11-10 13:06   ` Guillaume Ranquet
2021-11-10 13:06   ` Guillaume Ranquet
2021-11-10 13:06   ` Guillaume Ranquet
2021-11-10 19:44   ` Rob Herring
2021-11-10 19:44     ` [PATCH v6 2/7] dt-bindings: mediatek, dp: " Rob Herring
2021-11-10 19:44     ` Rob Herring
2021-11-10 19:44     ` Rob Herring
2021-11-10 13:06 ` [PATCH v6 3/7] drm/edid: Add cea_sad helpers for freq/length Guillaume Ranquet
2021-11-10 13:06   ` Guillaume Ranquet
2021-11-10 13:06 ` [PATCH v6 4/7] video/hdmi: Add audio_infoframe packing for DP Guillaume Ranquet
2021-11-10 13:06   ` Guillaume Ranquet
2021-11-10 13:06 ` [PATCH v6 5/7] drm/mediatek: dpi: Add dpintf support Guillaume Ranquet
2021-11-10 13:06   ` Guillaume Ranquet
2021-11-10 13:06   ` Guillaume Ranquet
2021-11-10 13:06   ` Guillaume Ranquet
2021-11-26 10:22   ` AngeloGioacchino Del Regno
2021-11-26 10:22     ` AngeloGioacchino Del Regno
2021-11-26 10:22     ` AngeloGioacchino Del Regno
2021-11-26 10:22     ` AngeloGioacchino Del Regno
2021-11-26 10:23   ` AngeloGioacchino Del Regno
2021-11-26 10:23     ` AngeloGioacchino Del Regno
2021-11-26 10:23     ` AngeloGioacchino Del Regno
2021-11-26 10:23     ` AngeloGioacchino Del Regno
2021-12-15 20:14   ` Matthias Brugger
2021-12-15 20:14     ` Matthias Brugger
2021-12-15 20:14     ` Matthias Brugger
2021-12-15 20:14     ` Matthias Brugger
2021-11-10 13:06 ` [PATCH v6 6/7] phy: phy-mtk-dp: Add driver for DP phy Guillaume Ranquet
2021-11-10 13:06   ` Guillaume Ranquet
2021-11-10 13:06   ` Guillaume Ranquet
2021-11-10 13:06   ` Guillaume Ranquet
2021-11-10 13:06   ` Guillaume Ranquet
2021-11-13  7:48   ` Chunfeng Yun
2021-11-13  7:48     ` Chunfeng Yun
2021-11-13  7:48     ` Chunfeng Yun
2021-11-13  7:48     ` Chunfeng Yun
2021-11-13  7:48     ` Chunfeng Yun
2021-11-24 14:37     ` Guillaume Ranquet
2021-11-24 14:37       ` Guillaume Ranquet
2021-11-24 14:37       ` Guillaume Ranquet
2021-11-24 14:37       ` Guillaume Ranquet
2021-11-24 14:37       ` Guillaume Ranquet
2021-11-10 13:06 ` [PATCH v6 7/7] drm/mediatek: Add mt8195 DisplayPort driver Guillaume Ranquet
2021-11-10 13:06   ` Guillaume Ranquet
2021-11-10 13:06   ` Guillaume Ranquet
2021-11-15 10:11   ` Maxime Ripard
2021-11-15 10:11     ` Maxime Ripard
2021-11-15 10:11     ` Maxime Ripard
2021-11-15 14:33     ` Guillaume Ranquet
2021-11-15 14:33       ` Guillaume Ranquet
2021-11-15 14:33       ` Guillaume Ranquet
2021-11-16 14:51       ` Maxime Ripard
2021-11-16 14:51         ` Maxime Ripard
2021-11-16 14:51         ` Maxime Ripard
2021-11-16 14:51         ` Maxime Ripard
2021-11-24 13:45         ` Guillaume Ranquet
2021-11-24 13:45           ` Guillaume Ranquet
2021-11-24 13:45           ` Guillaume Ranquet
2021-11-24 13:45           ` Guillaume Ranquet
2021-11-25 14:30           ` Maxime Ripard
2021-11-25 14:30             ` Maxime Ripard
2021-11-25 14:30             ` Maxime Ripard
2021-11-25 14:30             ` Maxime Ripard
2021-12-02 14:48             ` Guillaume Ranquet
2021-12-02 14:48               ` Guillaume Ranquet
2021-12-02 14:48               ` Guillaume Ranquet
2021-12-02 14:48               ` Guillaume Ranquet
2021-12-13 16:54               ` Maxime Ripard
2021-12-13 16:54                 ` Maxime Ripard
2021-12-13 16:54                 ` Maxime Ripard
2021-12-13 16:54                 ` Maxime Ripard
2021-12-15 15:03                 ` Guillaume Ranquet
2021-12-15 15:03                   ` Guillaume Ranquet
2021-12-15 15:03                   ` Guillaume Ranquet
2021-12-15 15:03                   ` Guillaume Ranquet
2021-12-15 15:15                   ` Maxime Ripard
2021-12-15 15:15                     ` Maxime Ripard
2021-12-15 15:15                     ` Maxime Ripard
2021-12-15 15:15                     ` Maxime Ripard
2021-12-15 16:02                     ` Guillaume Ranquet
2021-12-15 16:02                       ` Guillaume Ranquet
2021-12-15 16:02                       ` Guillaume Ranquet
2021-12-15 16:02                       ` Guillaume Ranquet
2021-12-15 16:06                       ` Maxime Ripard
2021-12-15 16:06                         ` Maxime Ripard
2021-12-15 16:06                         ` Maxime Ripard
2021-12-15 16:06                         ` Maxime Ripard
2021-11-25 15:27   ` Chun-Kuang Hu
2021-11-25 15:27     ` Chun-Kuang Hu
2021-11-25 15:27     ` Chun-Kuang Hu
2021-11-25 15:27     ` Chun-Kuang Hu
2021-12-02 15:31     ` Guillaume Ranquet
2021-12-02 15:31       ` Guillaume Ranquet
2021-12-02 15:31       ` Guillaume Ranquet
2021-12-02 15:31       ` Guillaume Ranquet
2021-12-15 14:13       ` Guillaume Ranquet
2021-12-15 14:13         ` Guillaume Ranquet
2021-12-15 14:13         ` Guillaume Ranquet
2021-12-15 14:13         ` Guillaume Ranquet
2021-12-09  6:29   ` Hsin-Yi Wang
2021-12-09  6:29     ` Hsin-Yi Wang
2021-12-09  6:29     ` Hsin-Yi Wang
2021-12-09  6:29     ` Hsin-Yi Wang
2021-12-15 13:59     ` Guillaume Ranquet
2021-12-15 13:59       ` Guillaume Ranquet
2021-12-15 13:59       ` Guillaume Ranquet
2021-12-15 13:59       ` Guillaume Ranquet
2021-12-10 10:17   ` AngeloGioacchino Del Regno
2021-12-10 10:17     ` AngeloGioacchino Del Regno
2021-12-10 10:17     ` AngeloGioacchino Del Regno
2021-12-10 10:17     ` AngeloGioacchino Del Regno
2021-12-15 13:56     ` Guillaume Ranquet
2021-12-15 13:56       ` Guillaume Ranquet
2021-12-15 13:56       ` Guillaume Ranquet
2021-12-15 13:56       ` Guillaume Ranquet

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