All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 00/23] drm/rockchip: RK356x VOP2 support
@ 2022-02-17  8:29 ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

This is v6 of adding RK356x VOP2 support. Biggest change this time is
that I no longer modify struct drm_encoder, instead the rockchip drivers
now embed struct drm_encoder in a rockchip specific struct.

Sascha

Changes since v5:
- Add new patch to fix dw-hdmi of_graph binding
- Drop "drm/encoder: Add of_graph port to struct drm_encoder" and solve
  issue internally in the driver
- make checkpatch cleaner

Changes since v4:
- Reorder patches in a way that binding/dts/driver patches are closer together
- Drop clk patches already applied by Heiko

Changes since v3:
- added changelog to each patch
- Add 4k support to hdmi driver
- rebase on v5.17-rc1

Changes since v2:
- Add pin names to HDMI supply pin description
- Add hclk support to HDMI driver
- Dual license rockchip-vop2 binding, update binding
- Add HDMI connector to board dts files
- drop unnecessary gamma_lut registers from vop2
- Update dclk_vop[012] clock handling, no longer hacks needed
- Complete regmap conversion

Changes since v1:
- drop all unnecessary waiting for frames within atomic modeset and plane update
- Cluster subwin support removed
- gamma support removed
- unnecessary irq_lock removed
- interrupt handling simplified
- simplified zpos handling
- drop is_alpha_support(), use fb->format->has_alpha instead
- use devm_regulator_get() rather than devm_regulator_get_optional() for hdmi regulators
- Use fixed number of planes per video port
- Drop homegrown regmap code from vop2 driver (not complete yet)
- Add separate include file for vop2 driver to not pollute the vop include

Andy Yan (1):
  drm: rockchip: Add VOP2 driver

Benjamin Gaignard (1):
  dt-bindings: display: rockchip: dw-hdmi: Add compatible for rk3568
    HDMI

Douglas Anderson (2):
  drm/rockchip: dw_hdmi: Use auto-generated tables
  drm/rockchip: dw_hdmi: Set cur_ctr to 0 always

Michael Riesch (1):
  arm64: dts: rockchip: enable vop2 and hdmi tx on quartz64a

Nickey Yang (1):
  drm/rockchip: dw_hdmi: add default 594Mhz clk for 4K@60hz

Sascha Hauer (17):
  drm/rockchip: Embed drm_encoder into rockchip_decoder
  drm/rockchip: dw_hdmi: rename vpll clock to reference clock
  dt-bindings: display: rockchip: dw-hdmi: use "ref" as clock name
  arm64: dts: rockchip: rk3399: rename HDMI ref clock to 'ref'
  drm/rockchip: dw_hdmi: add rk3568 support
  drm/rockchip: dw_hdmi: add regulator support
  dt-bindings: display: rockchip: dw-hdmi: Add regulator support
  drm/rockchip: dw_hdmi: Add support for hclk
  dt-bindings: display: rockchip: dw-hdmi: Add additional clock
  drm/rockchip: dw_hdmi: drop mode_valid hook
  dt-bindings: display: rockchip: dw-hdmi: Make unwedge pinctrl optional
  arm64: dts: rockchip: rk356x: Add VOP2 nodes
  arm64: dts: rockchip: rk356x: Add HDMI nodes
  arm64: dts: rockchip: rk3568-evb: Enable VOP2 and hdmi
  drm/rockchip: Make VOP driver optional
  dt-bindings: display: rockchip: Add binding for VOP2
  dt-bindings: display: rockchip: dw-hdmi: fix ports description

 .../display/rockchip/rockchip,dw-hdmi.yaml    |   53 +-
 .../display/rockchip/rockchip-vop2.yaml       |  140 +
 arch/arm64/boot/dts/rockchip/rk3399.dtsi      |    2 +-
 .../boot/dts/rockchip/rk3566-quartz64-a.dts   |   47 +
 arch/arm64/boot/dts/rockchip/rk3566.dtsi      |    4 +
 .../boot/dts/rockchip/rk3568-evb1-v10.dts     |   47 +
 arch/arm64/boot/dts/rockchip/rk3568.dtsi      |    4 +
 arch/arm64/boot/dts/rockchip/rk356x.dtsi      |   83 +
 drivers/gpu/drm/rockchip/Kconfig              |   14 +
 drivers/gpu/drm/rockchip/Makefile             |    4 +-
 .../gpu/drm/rockchip/analogix_dp-rockchip.c   |   32 +-
 drivers/gpu/drm/rockchip/cdn-dp-core.c        |   18 +-
 drivers/gpu/drm/rockchip/cdn-dp-core.h        |    2 +-
 .../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c   |   17 +-
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c   |  294 +-
 drivers/gpu/drm/rockchip/inno_hdmi.c          |   32 +-
 drivers/gpu/drm/rockchip/rk3066_hdmi.c        |   34 +-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c   |    3 +-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h   |   17 +-
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c    |    2 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h   |   15 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c  | 2708 +++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h  |  477 +++
 drivers/gpu/drm/rockchip/rockchip_lvds.c      |   26 +-
 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c  |  281 ++
 include/dt-bindings/soc/rockchip,vop2.h       |   14 +
 26 files changed, 4175 insertions(+), 195 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
 create mode 100644 include/dt-bindings/soc/rockchip,vop2.h

-- 
2.30.2


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

* [PATCH v6 00/23] drm/rockchip: RK356x VOP2 support
@ 2022-02-17  8:29 ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

This is v6 of adding RK356x VOP2 support. Biggest change this time is
that I no longer modify struct drm_encoder, instead the rockchip drivers
now embed struct drm_encoder in a rockchip specific struct.

Sascha

Changes since v5:
- Add new patch to fix dw-hdmi of_graph binding
- Drop "drm/encoder: Add of_graph port to struct drm_encoder" and solve
  issue internally in the driver
- make checkpatch cleaner

Changes since v4:
- Reorder patches in a way that binding/dts/driver patches are closer together
- Drop clk patches already applied by Heiko

Changes since v3:
- added changelog to each patch
- Add 4k support to hdmi driver
- rebase on v5.17-rc1

Changes since v2:
- Add pin names to HDMI supply pin description
- Add hclk support to HDMI driver
- Dual license rockchip-vop2 binding, update binding
- Add HDMI connector to board dts files
- drop unnecessary gamma_lut registers from vop2
- Update dclk_vop[012] clock handling, no longer hacks needed
- Complete regmap conversion

Changes since v1:
- drop all unnecessary waiting for frames within atomic modeset and plane update
- Cluster subwin support removed
- gamma support removed
- unnecessary irq_lock removed
- interrupt handling simplified
- simplified zpos handling
- drop is_alpha_support(), use fb->format->has_alpha instead
- use devm_regulator_get() rather than devm_regulator_get_optional() for hdmi regulators
- Use fixed number of planes per video port
- Drop homegrown regmap code from vop2 driver (not complete yet)
- Add separate include file for vop2 driver to not pollute the vop include

Andy Yan (1):
  drm: rockchip: Add VOP2 driver

Benjamin Gaignard (1):
  dt-bindings: display: rockchip: dw-hdmi: Add compatible for rk3568
    HDMI

Douglas Anderson (2):
  drm/rockchip: dw_hdmi: Use auto-generated tables
  drm/rockchip: dw_hdmi: Set cur_ctr to 0 always

Michael Riesch (1):
  arm64: dts: rockchip: enable vop2 and hdmi tx on quartz64a

Nickey Yang (1):
  drm/rockchip: dw_hdmi: add default 594Mhz clk for 4K@60hz

Sascha Hauer (17):
  drm/rockchip: Embed drm_encoder into rockchip_decoder
  drm/rockchip: dw_hdmi: rename vpll clock to reference clock
  dt-bindings: display: rockchip: dw-hdmi: use "ref" as clock name
  arm64: dts: rockchip: rk3399: rename HDMI ref clock to 'ref'
  drm/rockchip: dw_hdmi: add rk3568 support
  drm/rockchip: dw_hdmi: add regulator support
  dt-bindings: display: rockchip: dw-hdmi: Add regulator support
  drm/rockchip: dw_hdmi: Add support for hclk
  dt-bindings: display: rockchip: dw-hdmi: Add additional clock
  drm/rockchip: dw_hdmi: drop mode_valid hook
  dt-bindings: display: rockchip: dw-hdmi: Make unwedge pinctrl optional
  arm64: dts: rockchip: rk356x: Add VOP2 nodes
  arm64: dts: rockchip: rk356x: Add HDMI nodes
  arm64: dts: rockchip: rk3568-evb: Enable VOP2 and hdmi
  drm/rockchip: Make VOP driver optional
  dt-bindings: display: rockchip: Add binding for VOP2
  dt-bindings: display: rockchip: dw-hdmi: fix ports description

 .../display/rockchip/rockchip,dw-hdmi.yaml    |   53 +-
 .../display/rockchip/rockchip-vop2.yaml       |  140 +
 arch/arm64/boot/dts/rockchip/rk3399.dtsi      |    2 +-
 .../boot/dts/rockchip/rk3566-quartz64-a.dts   |   47 +
 arch/arm64/boot/dts/rockchip/rk3566.dtsi      |    4 +
 .../boot/dts/rockchip/rk3568-evb1-v10.dts     |   47 +
 arch/arm64/boot/dts/rockchip/rk3568.dtsi      |    4 +
 arch/arm64/boot/dts/rockchip/rk356x.dtsi      |   83 +
 drivers/gpu/drm/rockchip/Kconfig              |   14 +
 drivers/gpu/drm/rockchip/Makefile             |    4 +-
 .../gpu/drm/rockchip/analogix_dp-rockchip.c   |   32 +-
 drivers/gpu/drm/rockchip/cdn-dp-core.c        |   18 +-
 drivers/gpu/drm/rockchip/cdn-dp-core.h        |    2 +-
 .../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c   |   17 +-
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c   |  294 +-
 drivers/gpu/drm/rockchip/inno_hdmi.c          |   32 +-
 drivers/gpu/drm/rockchip/rk3066_hdmi.c        |   34 +-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c   |    3 +-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h   |   17 +-
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c    |    2 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h   |   15 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c  | 2708 +++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h  |  477 +++
 drivers/gpu/drm/rockchip/rockchip_lvds.c      |   26 +-
 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c  |  281 ++
 include/dt-bindings/soc/rockchip,vop2.h       |   14 +
 26 files changed, 4175 insertions(+), 195 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
 create mode 100644 include/dt-bindings/soc/rockchip,vop2.h

-- 
2.30.2


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

* [PATCH v6 00/23] drm/rockchip: RK356x VOP2 support
@ 2022-02-17  8:29 ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

This is v6 of adding RK356x VOP2 support. Biggest change this time is
that I no longer modify struct drm_encoder, instead the rockchip drivers
now embed struct drm_encoder in a rockchip specific struct.

Sascha

Changes since v5:
- Add new patch to fix dw-hdmi of_graph binding
- Drop "drm/encoder: Add of_graph port to struct drm_encoder" and solve
  issue internally in the driver
- make checkpatch cleaner

Changes since v4:
- Reorder patches in a way that binding/dts/driver patches are closer together
- Drop clk patches already applied by Heiko

Changes since v3:
- added changelog to each patch
- Add 4k support to hdmi driver
- rebase on v5.17-rc1

Changes since v2:
- Add pin names to HDMI supply pin description
- Add hclk support to HDMI driver
- Dual license rockchip-vop2 binding, update binding
- Add HDMI connector to board dts files
- drop unnecessary gamma_lut registers from vop2
- Update dclk_vop[012] clock handling, no longer hacks needed
- Complete regmap conversion

Changes since v1:
- drop all unnecessary waiting for frames within atomic modeset and plane update
- Cluster subwin support removed
- gamma support removed
- unnecessary irq_lock removed
- interrupt handling simplified
- simplified zpos handling
- drop is_alpha_support(), use fb->format->has_alpha instead
- use devm_regulator_get() rather than devm_regulator_get_optional() for hdmi regulators
- Use fixed number of planes per video port
- Drop homegrown regmap code from vop2 driver (not complete yet)
- Add separate include file for vop2 driver to not pollute the vop include

Andy Yan (1):
  drm: rockchip: Add VOP2 driver

Benjamin Gaignard (1):
  dt-bindings: display: rockchip: dw-hdmi: Add compatible for rk3568
    HDMI

Douglas Anderson (2):
  drm/rockchip: dw_hdmi: Use auto-generated tables
  drm/rockchip: dw_hdmi: Set cur_ctr to 0 always

Michael Riesch (1):
  arm64: dts: rockchip: enable vop2 and hdmi tx on quartz64a

Nickey Yang (1):
  drm/rockchip: dw_hdmi: add default 594Mhz clk for 4K@60hz

Sascha Hauer (17):
  drm/rockchip: Embed drm_encoder into rockchip_decoder
  drm/rockchip: dw_hdmi: rename vpll clock to reference clock
  dt-bindings: display: rockchip: dw-hdmi: use "ref" as clock name
  arm64: dts: rockchip: rk3399: rename HDMI ref clock to 'ref'
  drm/rockchip: dw_hdmi: add rk3568 support
  drm/rockchip: dw_hdmi: add regulator support
  dt-bindings: display: rockchip: dw-hdmi: Add regulator support
  drm/rockchip: dw_hdmi: Add support for hclk
  dt-bindings: display: rockchip: dw-hdmi: Add additional clock
  drm/rockchip: dw_hdmi: drop mode_valid hook
  dt-bindings: display: rockchip: dw-hdmi: Make unwedge pinctrl optional
  arm64: dts: rockchip: rk356x: Add VOP2 nodes
  arm64: dts: rockchip: rk356x: Add HDMI nodes
  arm64: dts: rockchip: rk3568-evb: Enable VOP2 and hdmi
  drm/rockchip: Make VOP driver optional
  dt-bindings: display: rockchip: Add binding for VOP2
  dt-bindings: display: rockchip: dw-hdmi: fix ports description

 .../display/rockchip/rockchip,dw-hdmi.yaml    |   53 +-
 .../display/rockchip/rockchip-vop2.yaml       |  140 +
 arch/arm64/boot/dts/rockchip/rk3399.dtsi      |    2 +-
 .../boot/dts/rockchip/rk3566-quartz64-a.dts   |   47 +
 arch/arm64/boot/dts/rockchip/rk3566.dtsi      |    4 +
 .../boot/dts/rockchip/rk3568-evb1-v10.dts     |   47 +
 arch/arm64/boot/dts/rockchip/rk3568.dtsi      |    4 +
 arch/arm64/boot/dts/rockchip/rk356x.dtsi      |   83 +
 drivers/gpu/drm/rockchip/Kconfig              |   14 +
 drivers/gpu/drm/rockchip/Makefile             |    4 +-
 .../gpu/drm/rockchip/analogix_dp-rockchip.c   |   32 +-
 drivers/gpu/drm/rockchip/cdn-dp-core.c        |   18 +-
 drivers/gpu/drm/rockchip/cdn-dp-core.h        |    2 +-
 .../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c   |   17 +-
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c   |  294 +-
 drivers/gpu/drm/rockchip/inno_hdmi.c          |   32 +-
 drivers/gpu/drm/rockchip/rk3066_hdmi.c        |   34 +-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c   |    3 +-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h   |   17 +-
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c    |    2 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h   |   15 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c  | 2708 +++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h  |  477 +++
 drivers/gpu/drm/rockchip/rockchip_lvds.c      |   26 +-
 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c  |  281 ++
 include/dt-bindings/soc/rockchip,vop2.h       |   14 +
 26 files changed, 4175 insertions(+), 195 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
 create mode 100644 include/dt-bindings/soc/rockchip,vop2.h

-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 00/23] drm/rockchip: RK356x VOP2 support
@ 2022-02-17  8:29 ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

This is v6 of adding RK356x VOP2 support. Biggest change this time is
that I no longer modify struct drm_encoder, instead the rockchip drivers
now embed struct drm_encoder in a rockchip specific struct.

Sascha

Changes since v5:
- Add new patch to fix dw-hdmi of_graph binding
- Drop "drm/encoder: Add of_graph port to struct drm_encoder" and solve
  issue internally in the driver
- make checkpatch cleaner

Changes since v4:
- Reorder patches in a way that binding/dts/driver patches are closer together
- Drop clk patches already applied by Heiko

Changes since v3:
- added changelog to each patch
- Add 4k support to hdmi driver
- rebase on v5.17-rc1

Changes since v2:
- Add pin names to HDMI supply pin description
- Add hclk support to HDMI driver
- Dual license rockchip-vop2 binding, update binding
- Add HDMI connector to board dts files
- drop unnecessary gamma_lut registers from vop2
- Update dclk_vop[012] clock handling, no longer hacks needed
- Complete regmap conversion

Changes since v1:
- drop all unnecessary waiting for frames within atomic modeset and plane update
- Cluster subwin support removed
- gamma support removed
- unnecessary irq_lock removed
- interrupt handling simplified
- simplified zpos handling
- drop is_alpha_support(), use fb->format->has_alpha instead
- use devm_regulator_get() rather than devm_regulator_get_optional() for hdmi regulators
- Use fixed number of planes per video port
- Drop homegrown regmap code from vop2 driver (not complete yet)
- Add separate include file for vop2 driver to not pollute the vop include

Andy Yan (1):
  drm: rockchip: Add VOP2 driver

Benjamin Gaignard (1):
  dt-bindings: display: rockchip: dw-hdmi: Add compatible for rk3568
    HDMI

Douglas Anderson (2):
  drm/rockchip: dw_hdmi: Use auto-generated tables
  drm/rockchip: dw_hdmi: Set cur_ctr to 0 always

Michael Riesch (1):
  arm64: dts: rockchip: enable vop2 and hdmi tx on quartz64a

Nickey Yang (1):
  drm/rockchip: dw_hdmi: add default 594Mhz clk for 4K@60hz

Sascha Hauer (17):
  drm/rockchip: Embed drm_encoder into rockchip_decoder
  drm/rockchip: dw_hdmi: rename vpll clock to reference clock
  dt-bindings: display: rockchip: dw-hdmi: use "ref" as clock name
  arm64: dts: rockchip: rk3399: rename HDMI ref clock to 'ref'
  drm/rockchip: dw_hdmi: add rk3568 support
  drm/rockchip: dw_hdmi: add regulator support
  dt-bindings: display: rockchip: dw-hdmi: Add regulator support
  drm/rockchip: dw_hdmi: Add support for hclk
  dt-bindings: display: rockchip: dw-hdmi: Add additional clock
  drm/rockchip: dw_hdmi: drop mode_valid hook
  dt-bindings: display: rockchip: dw-hdmi: Make unwedge pinctrl optional
  arm64: dts: rockchip: rk356x: Add VOP2 nodes
  arm64: dts: rockchip: rk356x: Add HDMI nodes
  arm64: dts: rockchip: rk3568-evb: Enable VOP2 and hdmi
  drm/rockchip: Make VOP driver optional
  dt-bindings: display: rockchip: Add binding for VOP2
  dt-bindings: display: rockchip: dw-hdmi: fix ports description

 .../display/rockchip/rockchip,dw-hdmi.yaml    |   53 +-
 .../display/rockchip/rockchip-vop2.yaml       |  140 +
 arch/arm64/boot/dts/rockchip/rk3399.dtsi      |    2 +-
 .../boot/dts/rockchip/rk3566-quartz64-a.dts   |   47 +
 arch/arm64/boot/dts/rockchip/rk3566.dtsi      |    4 +
 .../boot/dts/rockchip/rk3568-evb1-v10.dts     |   47 +
 arch/arm64/boot/dts/rockchip/rk3568.dtsi      |    4 +
 arch/arm64/boot/dts/rockchip/rk356x.dtsi      |   83 +
 drivers/gpu/drm/rockchip/Kconfig              |   14 +
 drivers/gpu/drm/rockchip/Makefile             |    4 +-
 .../gpu/drm/rockchip/analogix_dp-rockchip.c   |   32 +-
 drivers/gpu/drm/rockchip/cdn-dp-core.c        |   18 +-
 drivers/gpu/drm/rockchip/cdn-dp-core.h        |    2 +-
 .../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c   |   17 +-
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c   |  294 +-
 drivers/gpu/drm/rockchip/inno_hdmi.c          |   32 +-
 drivers/gpu/drm/rockchip/rk3066_hdmi.c        |   34 +-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c   |    3 +-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h   |   17 +-
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c    |    2 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h   |   15 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c  | 2708 +++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h  |  477 +++
 drivers/gpu/drm/rockchip/rockchip_lvds.c      |   26 +-
 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c  |  281 ++
 include/dt-bindings/soc/rockchip,vop2.h       |   14 +
 26 files changed, 4175 insertions(+), 195 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
 create mode 100644 include/dt-bindings/soc/rockchip,vop2.h

-- 
2.30.2


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

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

* [PATCH v6 01/23] drm/rockchip: Embed drm_encoder into rockchip_decoder
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

The VOP2 driver needs rockchip specific information for a drm_encoder.

This patch creates a struct rockchip_encoder with a struct drm_encoder
embedded in it. This is used throughout the rockchip driver instead of
struct drm_encoder directly.

The information the VOP2 drivers needs is the of_graph endpoint node
of the encoder. To ease bisectability this is added here.

While at it convert the different encoder-to-driverdata macros to
static inline functions in order to gain type safety and readability.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - new patch

 .../gpu/drm/rockchip/analogix_dp-rockchip.c   | 32 +++++++++++------
 drivers/gpu/drm/rockchip/cdn-dp-core.c        | 18 ++++++----
 drivers/gpu/drm/rockchip/cdn-dp-core.h        |  2 +-
 .../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c   | 17 ++++++----
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c   | 11 ++++--
 drivers/gpu/drm/rockchip/inno_hdmi.c          | 32 +++++++++++------
 drivers/gpu/drm/rockchip/rk3066_hdmi.c        | 34 ++++++++++++-------
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h   | 11 ++++++
 drivers/gpu/drm/rockchip/rockchip_lvds.c      | 26 ++++++++------
 9 files changed, 123 insertions(+), 60 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index 8abb5ac26807e..bb33c6c217f77 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -40,8 +40,6 @@
 
 #define PSR_WAIT_LINE_FLAG_TIMEOUT_MS	100
 
-#define to_dp(nm)	container_of(nm, struct rockchip_dp_device, nm)
-
 /**
  * struct rockchip_dp_chip_data - splite the grf setting of kind of chips
  * @lcdsel_grf_reg: grf register offset of lcdc select
@@ -59,7 +57,7 @@ struct rockchip_dp_chip_data {
 struct rockchip_dp_device {
 	struct drm_device        *drm_dev;
 	struct device            *dev;
-	struct drm_encoder       encoder;
+	struct rockchip_encoder  encoder;
 	struct drm_display_mode  mode;
 
 	struct clk               *pclk;
@@ -73,6 +71,18 @@ struct rockchip_dp_device {
 	struct analogix_dp_plat_data plat_data;
 };
 
+static struct rockchip_dp_device *encoder_to_dp(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct rockchip_dp_device, encoder);
+}
+
+static struct rockchip_dp_device *pdata_encoder_to_dp(struct analogix_dp_plat_data *plat_data)
+{
+	return container_of(plat_data, struct rockchip_dp_device, plat_data);
+}
+
 static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
 {
 	reset_control_assert(dp->rst);
@@ -84,7 +94,7 @@ static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
 
 static int rockchip_dp_poweron_start(struct analogix_dp_plat_data *plat_data)
 {
-	struct rockchip_dp_device *dp = to_dp(plat_data);
+	struct rockchip_dp_device *dp = pdata_encoder_to_dp(plat_data);
 	int ret;
 
 	ret = clk_prepare_enable(dp->pclk);
@@ -105,7 +115,7 @@ static int rockchip_dp_poweron_start(struct analogix_dp_plat_data *plat_data)
 
 static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data)
 {
-	struct rockchip_dp_device *dp = to_dp(plat_data);
+	struct rockchip_dp_device *dp = pdata_encoder_to_dp(plat_data);
 
 	clk_disable_unprepare(dp->pclk);
 
@@ -166,7 +176,7 @@ struct drm_crtc *rockchip_dp_drm_get_new_crtc(struct drm_encoder *encoder,
 static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
 					   struct drm_atomic_state *state)
 {
-	struct rockchip_dp_device *dp = to_dp(encoder);
+	struct rockchip_dp_device *dp = encoder_to_dp(encoder);
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state;
 	int ret;
@@ -208,7 +218,7 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
 static void rockchip_dp_drm_encoder_disable(struct drm_encoder *encoder,
 					    struct drm_atomic_state *state)
 {
-	struct rockchip_dp_device *dp = to_dp(encoder);
+	struct rockchip_dp_device *dp = encoder_to_dp(encoder);
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *new_crtc_state = NULL;
 	int ret;
@@ -297,7 +307,7 @@ static int rockchip_dp_of_probe(struct rockchip_dp_device *dp)
 
 static int rockchip_dp_drm_create_encoder(struct rockchip_dp_device *dp)
 {
-	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_encoder *encoder = &dp->encoder.encoder;
 	struct drm_device *drm_dev = dp->drm_dev;
 	struct device *dev = dp->dev;
 	int ret;
@@ -333,7 +343,7 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
-	dp->plat_data.encoder = &dp->encoder;
+	dp->plat_data.encoder = &dp->encoder.encoder;
 
 	ret = analogix_dp_bind(dp->adp, drm_dev);
 	if (ret)
@@ -341,7 +351,7 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
 
 	return 0;
 err_cleanup_encoder:
-	dp->encoder.funcs->destroy(&dp->encoder);
+	dp->encoder.encoder.funcs->destroy(&dp->encoder.encoder);
 	return ret;
 }
 
@@ -351,7 +361,7 @@ static void rockchip_dp_unbind(struct device *dev, struct device *master,
 	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
 
 	analogix_dp_unbind(dp->adp);
-	dp->encoder.funcs->destroy(&dp->encoder);
+	dp->encoder.encoder.funcs->destroy(&dp->encoder.encoder);
 }
 
 static const struct component_ops rockchip_dp_component_ops = {
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index 16497c31d9f91..6ce1c1cdd9d68 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -26,11 +26,17 @@
 #include "cdn-dp-reg.h"
 #include "rockchip_drm_vop.h"
 
-#define connector_to_dp(c) \
-		container_of(c, struct cdn_dp_device, connector)
+static inline struct cdn_dp_device *connector_to_dp(struct drm_connector *connector)
+{
+	return container_of(connector, struct cdn_dp_device, connector);
+}
 
-#define encoder_to_dp(c) \
-		container_of(c, struct cdn_dp_device, encoder)
+static inline struct cdn_dp_device *encoder_to_dp(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct cdn_dp_device, encoder);
+}
 
 #define GRF_SOC_CON9		0x6224
 #define DP_SEL_VOP_LIT		BIT(12)
@@ -1022,7 +1028,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 
 	INIT_WORK(&dp->event_work, cdn_dp_pd_event_work);
 
-	encoder = &dp->encoder;
+	encoder = &dp->encoder.encoder;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
 							     dev->of_node);
@@ -1087,7 +1093,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
 {
 	struct cdn_dp_device *dp = dev_get_drvdata(dev);
-	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_encoder *encoder = &dp->encoder.encoder;
 	struct drm_connector *connector = &dp->connector;
 
 	cancel_work_sync(&dp->event_work);
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
index 81ac9b658a70a..29539170d3b1b 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -65,7 +65,7 @@ struct cdn_dp_device {
 	struct device *dev;
 	struct drm_device *drm_dev;
 	struct drm_connector connector;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 	struct drm_display_mode mode;
 	struct platform_device *audio_pdev;
 	struct work_struct event_work;
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
index 4ed7a68681978..110e83aad9bb4 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
@@ -181,8 +181,6 @@
 
 #define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
 
-#define to_dsi(nm)	container_of(nm, struct dw_mipi_dsi_rockchip, nm)
-
 enum {
 	DW_DSI_USAGE_IDLE,
 	DW_DSI_USAGE_DSI,
@@ -236,7 +234,7 @@ struct rockchip_dw_dsi_chip_data {
 
 struct dw_mipi_dsi_rockchip {
 	struct device *dev;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 	void __iomem *base;
 
 	struct regmap *grf_regmap;
@@ -271,6 +269,13 @@ struct dw_mipi_dsi_rockchip {
 	bool dsi_bound;
 };
 
+static struct dw_mipi_dsi_rockchip *to_dsi(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct dw_mipi_dsi_rockchip, encoder);
+}
+
 struct dphy_pll_parameter_map {
 	unsigned int max_mbps;
 	u8 hsfreqrange;
@@ -770,7 +775,7 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
 	int ret, mux;
 
 	mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node,
-						&dsi->encoder);
+						&dsi->encoder.encoder);
 	if (mux < 0)
 		return;
 
@@ -801,7 +806,7 @@ dw_mipi_dsi_encoder_helper_funcs = {
 static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi,
 					   struct drm_device *drm_dev)
 {
-	struct drm_encoder *encoder = &dsi->encoder;
+	struct drm_encoder *encoder = &dsi->encoder.encoder;
 	int ret;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
@@ -959,7 +964,7 @@ static int dw_mipi_dsi_rockchip_bind(struct device *dev,
 		goto out_pll_clk;
 	}
 
-	ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder);
+	ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder.encoder);
 	if (ret) {
 		DRM_DEV_ERROR(dev, "Failed to bind: %d\n", ret);
 		goto out_pll_clk;
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 8677c82716784..06c9ddef6f362 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -67,7 +67,7 @@ struct rockchip_hdmi_chip_data {
 struct rockchip_hdmi {
 	struct device *dev;
 	struct regmap *regmap;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 	const struct rockchip_hdmi_chip_data *chip_data;
 	struct clk *vpll_clk;
 	struct clk *grf_clk;
@@ -75,7 +75,12 @@ struct rockchip_hdmi {
 	struct phy *phy;
 };
 
-#define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
+static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct rockchip_hdmi, encoder);
+}
 
 static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 	{
@@ -511,7 +516,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	hdmi->dev = &pdev->dev;
 	hdmi->chip_data = plat_data->phy_data;
 	plat_data->phy_data = hdmi;
-	encoder = &hdmi->encoder;
+	encoder = &hdmi->encoder.encoder;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
 	/*
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 046e8ec2a71c5..0a4f72021d6af 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -26,8 +26,6 @@
 
 #include "inno_hdmi.h"
 
-#define to_inno_hdmi(x)	container_of(x, struct inno_hdmi, x)
-
 struct hdmi_data_info {
 	int vic;
 	bool sink_is_hdmi;
@@ -56,7 +54,7 @@ struct inno_hdmi {
 	void __iomem *regs;
 
 	struct drm_connector	connector;
-	struct drm_encoder	encoder;
+	struct rockchip_encoder	encoder;
 
 	struct inno_hdmi_i2c *i2c;
 	struct i2c_adapter *ddc;
@@ -67,6 +65,18 @@ struct inno_hdmi {
 	struct drm_display_mode previous_mode;
 };
 
+static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct inno_hdmi, encoder);
+}
+
+static struct inno_hdmi *connector_to_inno_hdmi(struct drm_connector *connector)
+{
+	return container_of(connector, struct inno_hdmi, connector);
+}
+
 enum {
 	CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
 	CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
@@ -483,7 +493,7 @@ static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 				       struct drm_display_mode *mode,
 				       struct drm_display_mode *adj_mode)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+	struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
 
 	inno_hdmi_setup(hdmi, adj_mode);
 
@@ -493,14 +503,14 @@ static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 
 static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+	struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
 
 	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
 }
 
 static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+	struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
 
 	inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
 }
@@ -536,7 +546,7 @@ static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
 static enum drm_connector_status
 inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+	struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
 
 	return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
 		connector_status_connected : connector_status_disconnected;
@@ -544,7 +554,7 @@ inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
 
 static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+	struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
 	struct edid *edid;
 	int ret = 0;
 
@@ -599,7 +609,7 @@ static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
 
 static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
 {
-	struct drm_encoder *encoder = &hdmi->encoder;
+	struct drm_encoder *encoder = &hdmi->encoder.encoder;
 	struct device *dev = hdmi->dev;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
@@ -879,7 +889,7 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
 	return 0;
 err_cleanup_hdmi:
 	hdmi->connector.funcs->destroy(&hdmi->connector);
-	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+	hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
 err_put_adapter:
 	i2c_put_adapter(hdmi->ddc);
 err_disable_clk:
@@ -893,7 +903,7 @@ static void inno_hdmi_unbind(struct device *dev, struct device *master,
 	struct inno_hdmi *hdmi = dev_get_drvdata(dev);
 
 	hdmi->connector.funcs->destroy(&hdmi->connector);
-	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+	hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
 
 	i2c_put_adapter(hdmi->ddc);
 	clk_disable_unprepare(hdmi->pclk);
diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
index 1c546c3a89984..319240c33dcc0 100644
--- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c
+++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
@@ -47,7 +47,7 @@ struct rk3066_hdmi {
 	void __iomem *regs;
 
 	struct drm_connector connector;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 
 	struct rk3066_hdmi_i2c *i2c;
 	struct i2c_adapter *ddc;
@@ -58,7 +58,17 @@ struct rk3066_hdmi {
 	struct drm_display_mode previous_mode;
 };
 
-#define to_rk3066_hdmi(x) container_of(x, struct rk3066_hdmi, x)
+static struct rk3066_hdmi *encoder_to_rk3066_hdmi(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct rk3066_hdmi, encoder);
+}
+
+static struct rk3066_hdmi *connector_to_rk3066_hdmi(struct drm_connector *connector)
+{
+	return container_of(connector, struct rk3066_hdmi, connector);
+}
 
 static inline u8 hdmi_readb(struct rk3066_hdmi *hdmi, u16 offset)
 {
@@ -380,7 +390,7 @@ rk3066_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 			     struct drm_display_mode *mode,
 			     struct drm_display_mode *adj_mode)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder);
+	struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder);
 
 	/* Store the display mode for plugin/DPMS poweron events. */
 	memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode));
@@ -388,7 +398,7 @@ rk3066_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 
 static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder);
+	struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder);
 	int mux, val;
 
 	mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
@@ -407,7 +417,7 @@ static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder)
 
 static void rk3066_hdmi_encoder_disable(struct drm_encoder *encoder)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder);
+	struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder);
 
 	DRM_DEV_DEBUG(hdmi->dev, "hdmi encoder disable\n");
 
@@ -455,7 +465,7 @@ struct drm_encoder_helper_funcs rk3066_hdmi_encoder_helper_funcs = {
 static enum drm_connector_status
 rk3066_hdmi_connector_detect(struct drm_connector *connector, bool force)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector);
+	struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
 
 	return (hdmi_readb(hdmi, HDMI_HPG_MENS_STA) & HDMI_HPG_IN_STATUS_HIGH) ?
 		connector_status_connected : connector_status_disconnected;
@@ -463,7 +473,7 @@ rk3066_hdmi_connector_detect(struct drm_connector *connector, bool force)
 
 static int rk3066_hdmi_connector_get_modes(struct drm_connector *connector)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector);
+	struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
 	struct edid *edid;
 	int ret = 0;
 
@@ -496,9 +506,9 @@ rk3066_hdmi_connector_mode_valid(struct drm_connector *connector,
 static struct drm_encoder *
 rk3066_hdmi_connector_best_encoder(struct drm_connector *connector)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector);
+	struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
 
-	return &hdmi->encoder;
+	return &hdmi->encoder.encoder;
 }
 
 static int
@@ -538,7 +548,7 @@ struct drm_connector_helper_funcs rk3066_hdmi_connector_helper_funcs = {
 static int
 rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi)
 {
-	struct drm_encoder *encoder = &hdmi->encoder;
+	struct drm_encoder *encoder = &hdmi->encoder.encoder;
 	struct device *dev = hdmi->dev;
 
 	encoder->possible_crtcs =
@@ -816,7 +826,7 @@ static int rk3066_hdmi_bind(struct device *dev, struct device *master,
 
 err_cleanup_hdmi:
 	hdmi->connector.funcs->destroy(&hdmi->connector);
-	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+	hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
 err_disable_i2c:
 	i2c_put_adapter(hdmi->ddc);
 err_disable_hclk:
@@ -831,7 +841,7 @@ static void rk3066_hdmi_unbind(struct device *dev, struct device *master,
 	struct rk3066_hdmi *hdmi = dev_get_drvdata(dev);
 
 	hdmi->connector.funcs->destroy(&hdmi->connector);
-	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+	hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
 
 	i2c_put_adapter(hdmi->ddc);
 	clk_disable_unprepare(hdmi->hclk);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 143a48330f849..d3e42410ae5da 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -63,4 +63,15 @@ extern struct platform_driver rockchip_dp_driver;
 extern struct platform_driver rockchip_lvds_driver;
 extern struct platform_driver vop_platform_driver;
 extern struct platform_driver rk3066_hdmi_driver;
+
+struct rockchip_encoder {
+	struct device_node *port;
+	struct drm_encoder encoder;
+};
+
+static inline struct rockchip_encoder *to_rockchip_encoder(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct rockchip_encoder, encoder);
+}
+
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
index be74c87a8be4d..4ced073c6b06c 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -36,12 +36,6 @@
 
 struct rockchip_lvds;
 
-#define connector_to_lvds(c) \
-		container_of(c, struct rockchip_lvds, connector)
-
-#define encoder_to_lvds(c) \
-		container_of(c, struct rockchip_lvds, encoder)
-
 /**
  * struct rockchip_lvds_soc_data - rockchip lvds Soc private data
  * @probe: LVDS platform probe function
@@ -65,10 +59,22 @@ struct rockchip_lvds {
 	struct drm_panel *panel;
 	struct drm_bridge *bridge;
 	struct drm_connector connector;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 	struct dev_pin_info *pins;
 };
 
+static inline struct rockchip_lvds *connector_to_lvds(struct drm_connector *connector)
+{
+	return container_of(connector, struct rockchip_lvds, connector);
+}
+
+static inline struct rockchip_lvds *encoder_to_lvds(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct rockchip_lvds, encoder);
+}
+
 static inline void rk3288_writel(struct rockchip_lvds *lvds, u32 offset,
 				 u32 val)
 {
@@ -599,7 +605,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
 		goto err_put_remote;
 	}
 
-	encoder = &lvds->encoder;
+	encoder = &lvds->encoder.encoder;
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
 							     dev->of_node);
 
@@ -674,10 +680,10 @@ static void rockchip_lvds_unbind(struct device *dev, struct device *master,
 	const struct drm_encoder_helper_funcs *encoder_funcs;
 
 	encoder_funcs = lvds->soc_data->helper_funcs;
-	encoder_funcs->disable(&lvds->encoder);
+	encoder_funcs->disable(&lvds->encoder.encoder);
 	pm_runtime_disable(dev);
 	drm_connector_cleanup(&lvds->connector);
-	drm_encoder_cleanup(&lvds->encoder);
+	drm_encoder_cleanup(&lvds->encoder.encoder);
 }
 
 static const struct component_ops rockchip_lvds_component_ops = {
-- 
2.30.2


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

* [PATCH v6 01/23] drm/rockchip: Embed drm_encoder into rockchip_decoder
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

The VOP2 driver needs rockchip specific information for a drm_encoder.

This patch creates a struct rockchip_encoder with a struct drm_encoder
embedded in it. This is used throughout the rockchip driver instead of
struct drm_encoder directly.

The information the VOP2 drivers needs is the of_graph endpoint node
of the encoder. To ease bisectability this is added here.

While at it convert the different encoder-to-driverdata macros to
static inline functions in order to gain type safety and readability.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - new patch

 .../gpu/drm/rockchip/analogix_dp-rockchip.c   | 32 +++++++++++------
 drivers/gpu/drm/rockchip/cdn-dp-core.c        | 18 ++++++----
 drivers/gpu/drm/rockchip/cdn-dp-core.h        |  2 +-
 .../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c   | 17 ++++++----
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c   | 11 ++++--
 drivers/gpu/drm/rockchip/inno_hdmi.c          | 32 +++++++++++------
 drivers/gpu/drm/rockchip/rk3066_hdmi.c        | 34 ++++++++++++-------
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h   | 11 ++++++
 drivers/gpu/drm/rockchip/rockchip_lvds.c      | 26 ++++++++------
 9 files changed, 123 insertions(+), 60 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index 8abb5ac26807e..bb33c6c217f77 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -40,8 +40,6 @@
 
 #define PSR_WAIT_LINE_FLAG_TIMEOUT_MS	100
 
-#define to_dp(nm)	container_of(nm, struct rockchip_dp_device, nm)
-
 /**
  * struct rockchip_dp_chip_data - splite the grf setting of kind of chips
  * @lcdsel_grf_reg: grf register offset of lcdc select
@@ -59,7 +57,7 @@ struct rockchip_dp_chip_data {
 struct rockchip_dp_device {
 	struct drm_device        *drm_dev;
 	struct device            *dev;
-	struct drm_encoder       encoder;
+	struct rockchip_encoder  encoder;
 	struct drm_display_mode  mode;
 
 	struct clk               *pclk;
@@ -73,6 +71,18 @@ struct rockchip_dp_device {
 	struct analogix_dp_plat_data plat_data;
 };
 
+static struct rockchip_dp_device *encoder_to_dp(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct rockchip_dp_device, encoder);
+}
+
+static struct rockchip_dp_device *pdata_encoder_to_dp(struct analogix_dp_plat_data *plat_data)
+{
+	return container_of(plat_data, struct rockchip_dp_device, plat_data);
+}
+
 static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
 {
 	reset_control_assert(dp->rst);
@@ -84,7 +94,7 @@ static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
 
 static int rockchip_dp_poweron_start(struct analogix_dp_plat_data *plat_data)
 {
-	struct rockchip_dp_device *dp = to_dp(plat_data);
+	struct rockchip_dp_device *dp = pdata_encoder_to_dp(plat_data);
 	int ret;
 
 	ret = clk_prepare_enable(dp->pclk);
@@ -105,7 +115,7 @@ static int rockchip_dp_poweron_start(struct analogix_dp_plat_data *plat_data)
 
 static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data)
 {
-	struct rockchip_dp_device *dp = to_dp(plat_data);
+	struct rockchip_dp_device *dp = pdata_encoder_to_dp(plat_data);
 
 	clk_disable_unprepare(dp->pclk);
 
@@ -166,7 +176,7 @@ struct drm_crtc *rockchip_dp_drm_get_new_crtc(struct drm_encoder *encoder,
 static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
 					   struct drm_atomic_state *state)
 {
-	struct rockchip_dp_device *dp = to_dp(encoder);
+	struct rockchip_dp_device *dp = encoder_to_dp(encoder);
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state;
 	int ret;
@@ -208,7 +218,7 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
 static void rockchip_dp_drm_encoder_disable(struct drm_encoder *encoder,
 					    struct drm_atomic_state *state)
 {
-	struct rockchip_dp_device *dp = to_dp(encoder);
+	struct rockchip_dp_device *dp = encoder_to_dp(encoder);
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *new_crtc_state = NULL;
 	int ret;
@@ -297,7 +307,7 @@ static int rockchip_dp_of_probe(struct rockchip_dp_device *dp)
 
 static int rockchip_dp_drm_create_encoder(struct rockchip_dp_device *dp)
 {
-	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_encoder *encoder = &dp->encoder.encoder;
 	struct drm_device *drm_dev = dp->drm_dev;
 	struct device *dev = dp->dev;
 	int ret;
@@ -333,7 +343,7 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
-	dp->plat_data.encoder = &dp->encoder;
+	dp->plat_data.encoder = &dp->encoder.encoder;
 
 	ret = analogix_dp_bind(dp->adp, drm_dev);
 	if (ret)
@@ -341,7 +351,7 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
 
 	return 0;
 err_cleanup_encoder:
-	dp->encoder.funcs->destroy(&dp->encoder);
+	dp->encoder.encoder.funcs->destroy(&dp->encoder.encoder);
 	return ret;
 }
 
@@ -351,7 +361,7 @@ static void rockchip_dp_unbind(struct device *dev, struct device *master,
 	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
 
 	analogix_dp_unbind(dp->adp);
-	dp->encoder.funcs->destroy(&dp->encoder);
+	dp->encoder.encoder.funcs->destroy(&dp->encoder.encoder);
 }
 
 static const struct component_ops rockchip_dp_component_ops = {
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index 16497c31d9f91..6ce1c1cdd9d68 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -26,11 +26,17 @@
 #include "cdn-dp-reg.h"
 #include "rockchip_drm_vop.h"
 
-#define connector_to_dp(c) \
-		container_of(c, struct cdn_dp_device, connector)
+static inline struct cdn_dp_device *connector_to_dp(struct drm_connector *connector)
+{
+	return container_of(connector, struct cdn_dp_device, connector);
+}
 
-#define encoder_to_dp(c) \
-		container_of(c, struct cdn_dp_device, encoder)
+static inline struct cdn_dp_device *encoder_to_dp(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct cdn_dp_device, encoder);
+}
 
 #define GRF_SOC_CON9		0x6224
 #define DP_SEL_VOP_LIT		BIT(12)
@@ -1022,7 +1028,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 
 	INIT_WORK(&dp->event_work, cdn_dp_pd_event_work);
 
-	encoder = &dp->encoder;
+	encoder = &dp->encoder.encoder;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
 							     dev->of_node);
@@ -1087,7 +1093,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
 {
 	struct cdn_dp_device *dp = dev_get_drvdata(dev);
-	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_encoder *encoder = &dp->encoder.encoder;
 	struct drm_connector *connector = &dp->connector;
 
 	cancel_work_sync(&dp->event_work);
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
index 81ac9b658a70a..29539170d3b1b 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -65,7 +65,7 @@ struct cdn_dp_device {
 	struct device *dev;
 	struct drm_device *drm_dev;
 	struct drm_connector connector;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 	struct drm_display_mode mode;
 	struct platform_device *audio_pdev;
 	struct work_struct event_work;
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
index 4ed7a68681978..110e83aad9bb4 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
@@ -181,8 +181,6 @@
 
 #define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
 
-#define to_dsi(nm)	container_of(nm, struct dw_mipi_dsi_rockchip, nm)
-
 enum {
 	DW_DSI_USAGE_IDLE,
 	DW_DSI_USAGE_DSI,
@@ -236,7 +234,7 @@ struct rockchip_dw_dsi_chip_data {
 
 struct dw_mipi_dsi_rockchip {
 	struct device *dev;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 	void __iomem *base;
 
 	struct regmap *grf_regmap;
@@ -271,6 +269,13 @@ struct dw_mipi_dsi_rockchip {
 	bool dsi_bound;
 };
 
+static struct dw_mipi_dsi_rockchip *to_dsi(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct dw_mipi_dsi_rockchip, encoder);
+}
+
 struct dphy_pll_parameter_map {
 	unsigned int max_mbps;
 	u8 hsfreqrange;
@@ -770,7 +775,7 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
 	int ret, mux;
 
 	mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node,
-						&dsi->encoder);
+						&dsi->encoder.encoder);
 	if (mux < 0)
 		return;
 
@@ -801,7 +806,7 @@ dw_mipi_dsi_encoder_helper_funcs = {
 static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi,
 					   struct drm_device *drm_dev)
 {
-	struct drm_encoder *encoder = &dsi->encoder;
+	struct drm_encoder *encoder = &dsi->encoder.encoder;
 	int ret;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
@@ -959,7 +964,7 @@ static int dw_mipi_dsi_rockchip_bind(struct device *dev,
 		goto out_pll_clk;
 	}
 
-	ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder);
+	ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder.encoder);
 	if (ret) {
 		DRM_DEV_ERROR(dev, "Failed to bind: %d\n", ret);
 		goto out_pll_clk;
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 8677c82716784..06c9ddef6f362 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -67,7 +67,7 @@ struct rockchip_hdmi_chip_data {
 struct rockchip_hdmi {
 	struct device *dev;
 	struct regmap *regmap;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 	const struct rockchip_hdmi_chip_data *chip_data;
 	struct clk *vpll_clk;
 	struct clk *grf_clk;
@@ -75,7 +75,12 @@ struct rockchip_hdmi {
 	struct phy *phy;
 };
 
-#define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
+static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct rockchip_hdmi, encoder);
+}
 
 static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 	{
@@ -511,7 +516,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	hdmi->dev = &pdev->dev;
 	hdmi->chip_data = plat_data->phy_data;
 	plat_data->phy_data = hdmi;
-	encoder = &hdmi->encoder;
+	encoder = &hdmi->encoder.encoder;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
 	/*
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 046e8ec2a71c5..0a4f72021d6af 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -26,8 +26,6 @@
 
 #include "inno_hdmi.h"
 
-#define to_inno_hdmi(x)	container_of(x, struct inno_hdmi, x)
-
 struct hdmi_data_info {
 	int vic;
 	bool sink_is_hdmi;
@@ -56,7 +54,7 @@ struct inno_hdmi {
 	void __iomem *regs;
 
 	struct drm_connector	connector;
-	struct drm_encoder	encoder;
+	struct rockchip_encoder	encoder;
 
 	struct inno_hdmi_i2c *i2c;
 	struct i2c_adapter *ddc;
@@ -67,6 +65,18 @@ struct inno_hdmi {
 	struct drm_display_mode previous_mode;
 };
 
+static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct inno_hdmi, encoder);
+}
+
+static struct inno_hdmi *connector_to_inno_hdmi(struct drm_connector *connector)
+{
+	return container_of(connector, struct inno_hdmi, connector);
+}
+
 enum {
 	CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
 	CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
@@ -483,7 +493,7 @@ static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 				       struct drm_display_mode *mode,
 				       struct drm_display_mode *adj_mode)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+	struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
 
 	inno_hdmi_setup(hdmi, adj_mode);
 
@@ -493,14 +503,14 @@ static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 
 static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+	struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
 
 	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
 }
 
 static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+	struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
 
 	inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
 }
@@ -536,7 +546,7 @@ static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
 static enum drm_connector_status
 inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+	struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
 
 	return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
 		connector_status_connected : connector_status_disconnected;
@@ -544,7 +554,7 @@ inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
 
 static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+	struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
 	struct edid *edid;
 	int ret = 0;
 
@@ -599,7 +609,7 @@ static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
 
 static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
 {
-	struct drm_encoder *encoder = &hdmi->encoder;
+	struct drm_encoder *encoder = &hdmi->encoder.encoder;
 	struct device *dev = hdmi->dev;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
@@ -879,7 +889,7 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
 	return 0;
 err_cleanup_hdmi:
 	hdmi->connector.funcs->destroy(&hdmi->connector);
-	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+	hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
 err_put_adapter:
 	i2c_put_adapter(hdmi->ddc);
 err_disable_clk:
@@ -893,7 +903,7 @@ static void inno_hdmi_unbind(struct device *dev, struct device *master,
 	struct inno_hdmi *hdmi = dev_get_drvdata(dev);
 
 	hdmi->connector.funcs->destroy(&hdmi->connector);
-	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+	hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
 
 	i2c_put_adapter(hdmi->ddc);
 	clk_disable_unprepare(hdmi->pclk);
diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
index 1c546c3a89984..319240c33dcc0 100644
--- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c
+++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
@@ -47,7 +47,7 @@ struct rk3066_hdmi {
 	void __iomem *regs;
 
 	struct drm_connector connector;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 
 	struct rk3066_hdmi_i2c *i2c;
 	struct i2c_adapter *ddc;
@@ -58,7 +58,17 @@ struct rk3066_hdmi {
 	struct drm_display_mode previous_mode;
 };
 
-#define to_rk3066_hdmi(x) container_of(x, struct rk3066_hdmi, x)
+static struct rk3066_hdmi *encoder_to_rk3066_hdmi(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct rk3066_hdmi, encoder);
+}
+
+static struct rk3066_hdmi *connector_to_rk3066_hdmi(struct drm_connector *connector)
+{
+	return container_of(connector, struct rk3066_hdmi, connector);
+}
 
 static inline u8 hdmi_readb(struct rk3066_hdmi *hdmi, u16 offset)
 {
@@ -380,7 +390,7 @@ rk3066_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 			     struct drm_display_mode *mode,
 			     struct drm_display_mode *adj_mode)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder);
+	struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder);
 
 	/* Store the display mode for plugin/DPMS poweron events. */
 	memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode));
@@ -388,7 +398,7 @@ rk3066_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 
 static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder);
+	struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder);
 	int mux, val;
 
 	mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
@@ -407,7 +417,7 @@ static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder)
 
 static void rk3066_hdmi_encoder_disable(struct drm_encoder *encoder)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder);
+	struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder);
 
 	DRM_DEV_DEBUG(hdmi->dev, "hdmi encoder disable\n");
 
@@ -455,7 +465,7 @@ struct drm_encoder_helper_funcs rk3066_hdmi_encoder_helper_funcs = {
 static enum drm_connector_status
 rk3066_hdmi_connector_detect(struct drm_connector *connector, bool force)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector);
+	struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
 
 	return (hdmi_readb(hdmi, HDMI_HPG_MENS_STA) & HDMI_HPG_IN_STATUS_HIGH) ?
 		connector_status_connected : connector_status_disconnected;
@@ -463,7 +473,7 @@ rk3066_hdmi_connector_detect(struct drm_connector *connector, bool force)
 
 static int rk3066_hdmi_connector_get_modes(struct drm_connector *connector)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector);
+	struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
 	struct edid *edid;
 	int ret = 0;
 
@@ -496,9 +506,9 @@ rk3066_hdmi_connector_mode_valid(struct drm_connector *connector,
 static struct drm_encoder *
 rk3066_hdmi_connector_best_encoder(struct drm_connector *connector)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector);
+	struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
 
-	return &hdmi->encoder;
+	return &hdmi->encoder.encoder;
 }
 
 static int
@@ -538,7 +548,7 @@ struct drm_connector_helper_funcs rk3066_hdmi_connector_helper_funcs = {
 static int
 rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi)
 {
-	struct drm_encoder *encoder = &hdmi->encoder;
+	struct drm_encoder *encoder = &hdmi->encoder.encoder;
 	struct device *dev = hdmi->dev;
 
 	encoder->possible_crtcs =
@@ -816,7 +826,7 @@ static int rk3066_hdmi_bind(struct device *dev, struct device *master,
 
 err_cleanup_hdmi:
 	hdmi->connector.funcs->destroy(&hdmi->connector);
-	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+	hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
 err_disable_i2c:
 	i2c_put_adapter(hdmi->ddc);
 err_disable_hclk:
@@ -831,7 +841,7 @@ static void rk3066_hdmi_unbind(struct device *dev, struct device *master,
 	struct rk3066_hdmi *hdmi = dev_get_drvdata(dev);
 
 	hdmi->connector.funcs->destroy(&hdmi->connector);
-	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+	hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
 
 	i2c_put_adapter(hdmi->ddc);
 	clk_disable_unprepare(hdmi->hclk);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 143a48330f849..d3e42410ae5da 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -63,4 +63,15 @@ extern struct platform_driver rockchip_dp_driver;
 extern struct platform_driver rockchip_lvds_driver;
 extern struct platform_driver vop_platform_driver;
 extern struct platform_driver rk3066_hdmi_driver;
+
+struct rockchip_encoder {
+	struct device_node *port;
+	struct drm_encoder encoder;
+};
+
+static inline struct rockchip_encoder *to_rockchip_encoder(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct rockchip_encoder, encoder);
+}
+
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
index be74c87a8be4d..4ced073c6b06c 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -36,12 +36,6 @@
 
 struct rockchip_lvds;
 
-#define connector_to_lvds(c) \
-		container_of(c, struct rockchip_lvds, connector)
-
-#define encoder_to_lvds(c) \
-		container_of(c, struct rockchip_lvds, encoder)
-
 /**
  * struct rockchip_lvds_soc_data - rockchip lvds Soc private data
  * @probe: LVDS platform probe function
@@ -65,10 +59,22 @@ struct rockchip_lvds {
 	struct drm_panel *panel;
 	struct drm_bridge *bridge;
 	struct drm_connector connector;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 	struct dev_pin_info *pins;
 };
 
+static inline struct rockchip_lvds *connector_to_lvds(struct drm_connector *connector)
+{
+	return container_of(connector, struct rockchip_lvds, connector);
+}
+
+static inline struct rockchip_lvds *encoder_to_lvds(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct rockchip_lvds, encoder);
+}
+
 static inline void rk3288_writel(struct rockchip_lvds *lvds, u32 offset,
 				 u32 val)
 {
@@ -599,7 +605,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
 		goto err_put_remote;
 	}
 
-	encoder = &lvds->encoder;
+	encoder = &lvds->encoder.encoder;
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
 							     dev->of_node);
 
@@ -674,10 +680,10 @@ static void rockchip_lvds_unbind(struct device *dev, struct device *master,
 	const struct drm_encoder_helper_funcs *encoder_funcs;
 
 	encoder_funcs = lvds->soc_data->helper_funcs;
-	encoder_funcs->disable(&lvds->encoder);
+	encoder_funcs->disable(&lvds->encoder.encoder);
 	pm_runtime_disable(dev);
 	drm_connector_cleanup(&lvds->connector);
-	drm_encoder_cleanup(&lvds->encoder);
+	drm_encoder_cleanup(&lvds->encoder.encoder);
 }
 
 static const struct component_ops rockchip_lvds_component_ops = {
-- 
2.30.2


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

* [PATCH v6 01/23] drm/rockchip: Embed drm_encoder into rockchip_decoder
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

The VOP2 driver needs rockchip specific information for a drm_encoder.

This patch creates a struct rockchip_encoder with a struct drm_encoder
embedded in it. This is used throughout the rockchip driver instead of
struct drm_encoder directly.

The information the VOP2 drivers needs is the of_graph endpoint node
of the encoder. To ease bisectability this is added here.

While at it convert the different encoder-to-driverdata macros to
static inline functions in order to gain type safety and readability.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - new patch

 .../gpu/drm/rockchip/analogix_dp-rockchip.c   | 32 +++++++++++------
 drivers/gpu/drm/rockchip/cdn-dp-core.c        | 18 ++++++----
 drivers/gpu/drm/rockchip/cdn-dp-core.h        |  2 +-
 .../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c   | 17 ++++++----
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c   | 11 ++++--
 drivers/gpu/drm/rockchip/inno_hdmi.c          | 32 +++++++++++------
 drivers/gpu/drm/rockchip/rk3066_hdmi.c        | 34 ++++++++++++-------
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h   | 11 ++++++
 drivers/gpu/drm/rockchip/rockchip_lvds.c      | 26 ++++++++------
 9 files changed, 123 insertions(+), 60 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index 8abb5ac26807e..bb33c6c217f77 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -40,8 +40,6 @@
 
 #define PSR_WAIT_LINE_FLAG_TIMEOUT_MS	100
 
-#define to_dp(nm)	container_of(nm, struct rockchip_dp_device, nm)
-
 /**
  * struct rockchip_dp_chip_data - splite the grf setting of kind of chips
  * @lcdsel_grf_reg: grf register offset of lcdc select
@@ -59,7 +57,7 @@ struct rockchip_dp_chip_data {
 struct rockchip_dp_device {
 	struct drm_device        *drm_dev;
 	struct device            *dev;
-	struct drm_encoder       encoder;
+	struct rockchip_encoder  encoder;
 	struct drm_display_mode  mode;
 
 	struct clk               *pclk;
@@ -73,6 +71,18 @@ struct rockchip_dp_device {
 	struct analogix_dp_plat_data plat_data;
 };
 
+static struct rockchip_dp_device *encoder_to_dp(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct rockchip_dp_device, encoder);
+}
+
+static struct rockchip_dp_device *pdata_encoder_to_dp(struct analogix_dp_plat_data *plat_data)
+{
+	return container_of(plat_data, struct rockchip_dp_device, plat_data);
+}
+
 static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
 {
 	reset_control_assert(dp->rst);
@@ -84,7 +94,7 @@ static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
 
 static int rockchip_dp_poweron_start(struct analogix_dp_plat_data *plat_data)
 {
-	struct rockchip_dp_device *dp = to_dp(plat_data);
+	struct rockchip_dp_device *dp = pdata_encoder_to_dp(plat_data);
 	int ret;
 
 	ret = clk_prepare_enable(dp->pclk);
@@ -105,7 +115,7 @@ static int rockchip_dp_poweron_start(struct analogix_dp_plat_data *plat_data)
 
 static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data)
 {
-	struct rockchip_dp_device *dp = to_dp(plat_data);
+	struct rockchip_dp_device *dp = pdata_encoder_to_dp(plat_data);
 
 	clk_disable_unprepare(dp->pclk);
 
@@ -166,7 +176,7 @@ struct drm_crtc *rockchip_dp_drm_get_new_crtc(struct drm_encoder *encoder,
 static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
 					   struct drm_atomic_state *state)
 {
-	struct rockchip_dp_device *dp = to_dp(encoder);
+	struct rockchip_dp_device *dp = encoder_to_dp(encoder);
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state;
 	int ret;
@@ -208,7 +218,7 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
 static void rockchip_dp_drm_encoder_disable(struct drm_encoder *encoder,
 					    struct drm_atomic_state *state)
 {
-	struct rockchip_dp_device *dp = to_dp(encoder);
+	struct rockchip_dp_device *dp = encoder_to_dp(encoder);
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *new_crtc_state = NULL;
 	int ret;
@@ -297,7 +307,7 @@ static int rockchip_dp_of_probe(struct rockchip_dp_device *dp)
 
 static int rockchip_dp_drm_create_encoder(struct rockchip_dp_device *dp)
 {
-	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_encoder *encoder = &dp->encoder.encoder;
 	struct drm_device *drm_dev = dp->drm_dev;
 	struct device *dev = dp->dev;
 	int ret;
@@ -333,7 +343,7 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
-	dp->plat_data.encoder = &dp->encoder;
+	dp->plat_data.encoder = &dp->encoder.encoder;
 
 	ret = analogix_dp_bind(dp->adp, drm_dev);
 	if (ret)
@@ -341,7 +351,7 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
 
 	return 0;
 err_cleanup_encoder:
-	dp->encoder.funcs->destroy(&dp->encoder);
+	dp->encoder.encoder.funcs->destroy(&dp->encoder.encoder);
 	return ret;
 }
 
@@ -351,7 +361,7 @@ static void rockchip_dp_unbind(struct device *dev, struct device *master,
 	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
 
 	analogix_dp_unbind(dp->adp);
-	dp->encoder.funcs->destroy(&dp->encoder);
+	dp->encoder.encoder.funcs->destroy(&dp->encoder.encoder);
 }
 
 static const struct component_ops rockchip_dp_component_ops = {
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index 16497c31d9f91..6ce1c1cdd9d68 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -26,11 +26,17 @@
 #include "cdn-dp-reg.h"
 #include "rockchip_drm_vop.h"
 
-#define connector_to_dp(c) \
-		container_of(c, struct cdn_dp_device, connector)
+static inline struct cdn_dp_device *connector_to_dp(struct drm_connector *connector)
+{
+	return container_of(connector, struct cdn_dp_device, connector);
+}
 
-#define encoder_to_dp(c) \
-		container_of(c, struct cdn_dp_device, encoder)
+static inline struct cdn_dp_device *encoder_to_dp(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct cdn_dp_device, encoder);
+}
 
 #define GRF_SOC_CON9		0x6224
 #define DP_SEL_VOP_LIT		BIT(12)
@@ -1022,7 +1028,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 
 	INIT_WORK(&dp->event_work, cdn_dp_pd_event_work);
 
-	encoder = &dp->encoder;
+	encoder = &dp->encoder.encoder;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
 							     dev->of_node);
@@ -1087,7 +1093,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
 {
 	struct cdn_dp_device *dp = dev_get_drvdata(dev);
-	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_encoder *encoder = &dp->encoder.encoder;
 	struct drm_connector *connector = &dp->connector;
 
 	cancel_work_sync(&dp->event_work);
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
index 81ac9b658a70a..29539170d3b1b 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -65,7 +65,7 @@ struct cdn_dp_device {
 	struct device *dev;
 	struct drm_device *drm_dev;
 	struct drm_connector connector;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 	struct drm_display_mode mode;
 	struct platform_device *audio_pdev;
 	struct work_struct event_work;
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
index 4ed7a68681978..110e83aad9bb4 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
@@ -181,8 +181,6 @@
 
 #define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
 
-#define to_dsi(nm)	container_of(nm, struct dw_mipi_dsi_rockchip, nm)
-
 enum {
 	DW_DSI_USAGE_IDLE,
 	DW_DSI_USAGE_DSI,
@@ -236,7 +234,7 @@ struct rockchip_dw_dsi_chip_data {
 
 struct dw_mipi_dsi_rockchip {
 	struct device *dev;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 	void __iomem *base;
 
 	struct regmap *grf_regmap;
@@ -271,6 +269,13 @@ struct dw_mipi_dsi_rockchip {
 	bool dsi_bound;
 };
 
+static struct dw_mipi_dsi_rockchip *to_dsi(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct dw_mipi_dsi_rockchip, encoder);
+}
+
 struct dphy_pll_parameter_map {
 	unsigned int max_mbps;
 	u8 hsfreqrange;
@@ -770,7 +775,7 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
 	int ret, mux;
 
 	mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node,
-						&dsi->encoder);
+						&dsi->encoder.encoder);
 	if (mux < 0)
 		return;
 
@@ -801,7 +806,7 @@ dw_mipi_dsi_encoder_helper_funcs = {
 static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi,
 					   struct drm_device *drm_dev)
 {
-	struct drm_encoder *encoder = &dsi->encoder;
+	struct drm_encoder *encoder = &dsi->encoder.encoder;
 	int ret;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
@@ -959,7 +964,7 @@ static int dw_mipi_dsi_rockchip_bind(struct device *dev,
 		goto out_pll_clk;
 	}
 
-	ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder);
+	ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder.encoder);
 	if (ret) {
 		DRM_DEV_ERROR(dev, "Failed to bind: %d\n", ret);
 		goto out_pll_clk;
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 8677c82716784..06c9ddef6f362 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -67,7 +67,7 @@ struct rockchip_hdmi_chip_data {
 struct rockchip_hdmi {
 	struct device *dev;
 	struct regmap *regmap;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 	const struct rockchip_hdmi_chip_data *chip_data;
 	struct clk *vpll_clk;
 	struct clk *grf_clk;
@@ -75,7 +75,12 @@ struct rockchip_hdmi {
 	struct phy *phy;
 };
 
-#define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
+static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct rockchip_hdmi, encoder);
+}
 
 static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 	{
@@ -511,7 +516,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	hdmi->dev = &pdev->dev;
 	hdmi->chip_data = plat_data->phy_data;
 	plat_data->phy_data = hdmi;
-	encoder = &hdmi->encoder;
+	encoder = &hdmi->encoder.encoder;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
 	/*
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 046e8ec2a71c5..0a4f72021d6af 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -26,8 +26,6 @@
 
 #include "inno_hdmi.h"
 
-#define to_inno_hdmi(x)	container_of(x, struct inno_hdmi, x)
-
 struct hdmi_data_info {
 	int vic;
 	bool sink_is_hdmi;
@@ -56,7 +54,7 @@ struct inno_hdmi {
 	void __iomem *regs;
 
 	struct drm_connector	connector;
-	struct drm_encoder	encoder;
+	struct rockchip_encoder	encoder;
 
 	struct inno_hdmi_i2c *i2c;
 	struct i2c_adapter *ddc;
@@ -67,6 +65,18 @@ struct inno_hdmi {
 	struct drm_display_mode previous_mode;
 };
 
+static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct inno_hdmi, encoder);
+}
+
+static struct inno_hdmi *connector_to_inno_hdmi(struct drm_connector *connector)
+{
+	return container_of(connector, struct inno_hdmi, connector);
+}
+
 enum {
 	CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
 	CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
@@ -483,7 +493,7 @@ static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 				       struct drm_display_mode *mode,
 				       struct drm_display_mode *adj_mode)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+	struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
 
 	inno_hdmi_setup(hdmi, adj_mode);
 
@@ -493,14 +503,14 @@ static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 
 static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+	struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
 
 	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
 }
 
 static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+	struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
 
 	inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
 }
@@ -536,7 +546,7 @@ static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
 static enum drm_connector_status
 inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+	struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
 
 	return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
 		connector_status_connected : connector_status_disconnected;
@@ -544,7 +554,7 @@ inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
 
 static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+	struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
 	struct edid *edid;
 	int ret = 0;
 
@@ -599,7 +609,7 @@ static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
 
 static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
 {
-	struct drm_encoder *encoder = &hdmi->encoder;
+	struct drm_encoder *encoder = &hdmi->encoder.encoder;
 	struct device *dev = hdmi->dev;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
@@ -879,7 +889,7 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
 	return 0;
 err_cleanup_hdmi:
 	hdmi->connector.funcs->destroy(&hdmi->connector);
-	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+	hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
 err_put_adapter:
 	i2c_put_adapter(hdmi->ddc);
 err_disable_clk:
@@ -893,7 +903,7 @@ static void inno_hdmi_unbind(struct device *dev, struct device *master,
 	struct inno_hdmi *hdmi = dev_get_drvdata(dev);
 
 	hdmi->connector.funcs->destroy(&hdmi->connector);
-	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+	hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
 
 	i2c_put_adapter(hdmi->ddc);
 	clk_disable_unprepare(hdmi->pclk);
diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
index 1c546c3a89984..319240c33dcc0 100644
--- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c
+++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
@@ -47,7 +47,7 @@ struct rk3066_hdmi {
 	void __iomem *regs;
 
 	struct drm_connector connector;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 
 	struct rk3066_hdmi_i2c *i2c;
 	struct i2c_adapter *ddc;
@@ -58,7 +58,17 @@ struct rk3066_hdmi {
 	struct drm_display_mode previous_mode;
 };
 
-#define to_rk3066_hdmi(x) container_of(x, struct rk3066_hdmi, x)
+static struct rk3066_hdmi *encoder_to_rk3066_hdmi(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct rk3066_hdmi, encoder);
+}
+
+static struct rk3066_hdmi *connector_to_rk3066_hdmi(struct drm_connector *connector)
+{
+	return container_of(connector, struct rk3066_hdmi, connector);
+}
 
 static inline u8 hdmi_readb(struct rk3066_hdmi *hdmi, u16 offset)
 {
@@ -380,7 +390,7 @@ rk3066_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 			     struct drm_display_mode *mode,
 			     struct drm_display_mode *adj_mode)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder);
+	struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder);
 
 	/* Store the display mode for plugin/DPMS poweron events. */
 	memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode));
@@ -388,7 +398,7 @@ rk3066_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 
 static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder);
+	struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder);
 	int mux, val;
 
 	mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
@@ -407,7 +417,7 @@ static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder)
 
 static void rk3066_hdmi_encoder_disable(struct drm_encoder *encoder)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder);
+	struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder);
 
 	DRM_DEV_DEBUG(hdmi->dev, "hdmi encoder disable\n");
 
@@ -455,7 +465,7 @@ struct drm_encoder_helper_funcs rk3066_hdmi_encoder_helper_funcs = {
 static enum drm_connector_status
 rk3066_hdmi_connector_detect(struct drm_connector *connector, bool force)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector);
+	struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
 
 	return (hdmi_readb(hdmi, HDMI_HPG_MENS_STA) & HDMI_HPG_IN_STATUS_HIGH) ?
 		connector_status_connected : connector_status_disconnected;
@@ -463,7 +473,7 @@ rk3066_hdmi_connector_detect(struct drm_connector *connector, bool force)
 
 static int rk3066_hdmi_connector_get_modes(struct drm_connector *connector)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector);
+	struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
 	struct edid *edid;
 	int ret = 0;
 
@@ -496,9 +506,9 @@ rk3066_hdmi_connector_mode_valid(struct drm_connector *connector,
 static struct drm_encoder *
 rk3066_hdmi_connector_best_encoder(struct drm_connector *connector)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector);
+	struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
 
-	return &hdmi->encoder;
+	return &hdmi->encoder.encoder;
 }
 
 static int
@@ -538,7 +548,7 @@ struct drm_connector_helper_funcs rk3066_hdmi_connector_helper_funcs = {
 static int
 rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi)
 {
-	struct drm_encoder *encoder = &hdmi->encoder;
+	struct drm_encoder *encoder = &hdmi->encoder.encoder;
 	struct device *dev = hdmi->dev;
 
 	encoder->possible_crtcs =
@@ -816,7 +826,7 @@ static int rk3066_hdmi_bind(struct device *dev, struct device *master,
 
 err_cleanup_hdmi:
 	hdmi->connector.funcs->destroy(&hdmi->connector);
-	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+	hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
 err_disable_i2c:
 	i2c_put_adapter(hdmi->ddc);
 err_disable_hclk:
@@ -831,7 +841,7 @@ static void rk3066_hdmi_unbind(struct device *dev, struct device *master,
 	struct rk3066_hdmi *hdmi = dev_get_drvdata(dev);
 
 	hdmi->connector.funcs->destroy(&hdmi->connector);
-	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+	hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
 
 	i2c_put_adapter(hdmi->ddc);
 	clk_disable_unprepare(hdmi->hclk);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 143a48330f849..d3e42410ae5da 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -63,4 +63,15 @@ extern struct platform_driver rockchip_dp_driver;
 extern struct platform_driver rockchip_lvds_driver;
 extern struct platform_driver vop_platform_driver;
 extern struct platform_driver rk3066_hdmi_driver;
+
+struct rockchip_encoder {
+	struct device_node *port;
+	struct drm_encoder encoder;
+};
+
+static inline struct rockchip_encoder *to_rockchip_encoder(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct rockchip_encoder, encoder);
+}
+
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
index be74c87a8be4d..4ced073c6b06c 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -36,12 +36,6 @@
 
 struct rockchip_lvds;
 
-#define connector_to_lvds(c) \
-		container_of(c, struct rockchip_lvds, connector)
-
-#define encoder_to_lvds(c) \
-		container_of(c, struct rockchip_lvds, encoder)
-
 /**
  * struct rockchip_lvds_soc_data - rockchip lvds Soc private data
  * @probe: LVDS platform probe function
@@ -65,10 +59,22 @@ struct rockchip_lvds {
 	struct drm_panel *panel;
 	struct drm_bridge *bridge;
 	struct drm_connector connector;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 	struct dev_pin_info *pins;
 };
 
+static inline struct rockchip_lvds *connector_to_lvds(struct drm_connector *connector)
+{
+	return container_of(connector, struct rockchip_lvds, connector);
+}
+
+static inline struct rockchip_lvds *encoder_to_lvds(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct rockchip_lvds, encoder);
+}
+
 static inline void rk3288_writel(struct rockchip_lvds *lvds, u32 offset,
 				 u32 val)
 {
@@ -599,7 +605,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
 		goto err_put_remote;
 	}
 
-	encoder = &lvds->encoder;
+	encoder = &lvds->encoder.encoder;
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
 							     dev->of_node);
 
@@ -674,10 +680,10 @@ static void rockchip_lvds_unbind(struct device *dev, struct device *master,
 	const struct drm_encoder_helper_funcs *encoder_funcs;
 
 	encoder_funcs = lvds->soc_data->helper_funcs;
-	encoder_funcs->disable(&lvds->encoder);
+	encoder_funcs->disable(&lvds->encoder.encoder);
 	pm_runtime_disable(dev);
 	drm_connector_cleanup(&lvds->connector);
-	drm_encoder_cleanup(&lvds->encoder);
+	drm_encoder_cleanup(&lvds->encoder.encoder);
 }
 
 static const struct component_ops rockchip_lvds_component_ops = {
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 01/23] drm/rockchip: Embed drm_encoder into rockchip_decoder
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

The VOP2 driver needs rockchip specific information for a drm_encoder.

This patch creates a struct rockchip_encoder with a struct drm_encoder
embedded in it. This is used throughout the rockchip driver instead of
struct drm_encoder directly.

The information the VOP2 drivers needs is the of_graph endpoint node
of the encoder. To ease bisectability this is added here.

While at it convert the different encoder-to-driverdata macros to
static inline functions in order to gain type safety and readability.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - new patch

 .../gpu/drm/rockchip/analogix_dp-rockchip.c   | 32 +++++++++++------
 drivers/gpu/drm/rockchip/cdn-dp-core.c        | 18 ++++++----
 drivers/gpu/drm/rockchip/cdn-dp-core.h        |  2 +-
 .../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c   | 17 ++++++----
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c   | 11 ++++--
 drivers/gpu/drm/rockchip/inno_hdmi.c          | 32 +++++++++++------
 drivers/gpu/drm/rockchip/rk3066_hdmi.c        | 34 ++++++++++++-------
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h   | 11 ++++++
 drivers/gpu/drm/rockchip/rockchip_lvds.c      | 26 ++++++++------
 9 files changed, 123 insertions(+), 60 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index 8abb5ac26807e..bb33c6c217f77 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -40,8 +40,6 @@
 
 #define PSR_WAIT_LINE_FLAG_TIMEOUT_MS	100
 
-#define to_dp(nm)	container_of(nm, struct rockchip_dp_device, nm)
-
 /**
  * struct rockchip_dp_chip_data - splite the grf setting of kind of chips
  * @lcdsel_grf_reg: grf register offset of lcdc select
@@ -59,7 +57,7 @@ struct rockchip_dp_chip_data {
 struct rockchip_dp_device {
 	struct drm_device        *drm_dev;
 	struct device            *dev;
-	struct drm_encoder       encoder;
+	struct rockchip_encoder  encoder;
 	struct drm_display_mode  mode;
 
 	struct clk               *pclk;
@@ -73,6 +71,18 @@ struct rockchip_dp_device {
 	struct analogix_dp_plat_data plat_data;
 };
 
+static struct rockchip_dp_device *encoder_to_dp(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct rockchip_dp_device, encoder);
+}
+
+static struct rockchip_dp_device *pdata_encoder_to_dp(struct analogix_dp_plat_data *plat_data)
+{
+	return container_of(plat_data, struct rockchip_dp_device, plat_data);
+}
+
 static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
 {
 	reset_control_assert(dp->rst);
@@ -84,7 +94,7 @@ static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
 
 static int rockchip_dp_poweron_start(struct analogix_dp_plat_data *plat_data)
 {
-	struct rockchip_dp_device *dp = to_dp(plat_data);
+	struct rockchip_dp_device *dp = pdata_encoder_to_dp(plat_data);
 	int ret;
 
 	ret = clk_prepare_enable(dp->pclk);
@@ -105,7 +115,7 @@ static int rockchip_dp_poweron_start(struct analogix_dp_plat_data *plat_data)
 
 static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data)
 {
-	struct rockchip_dp_device *dp = to_dp(plat_data);
+	struct rockchip_dp_device *dp = pdata_encoder_to_dp(plat_data);
 
 	clk_disable_unprepare(dp->pclk);
 
@@ -166,7 +176,7 @@ struct drm_crtc *rockchip_dp_drm_get_new_crtc(struct drm_encoder *encoder,
 static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
 					   struct drm_atomic_state *state)
 {
-	struct rockchip_dp_device *dp = to_dp(encoder);
+	struct rockchip_dp_device *dp = encoder_to_dp(encoder);
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state;
 	int ret;
@@ -208,7 +218,7 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
 static void rockchip_dp_drm_encoder_disable(struct drm_encoder *encoder,
 					    struct drm_atomic_state *state)
 {
-	struct rockchip_dp_device *dp = to_dp(encoder);
+	struct rockchip_dp_device *dp = encoder_to_dp(encoder);
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *new_crtc_state = NULL;
 	int ret;
@@ -297,7 +307,7 @@ static int rockchip_dp_of_probe(struct rockchip_dp_device *dp)
 
 static int rockchip_dp_drm_create_encoder(struct rockchip_dp_device *dp)
 {
-	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_encoder *encoder = &dp->encoder.encoder;
 	struct drm_device *drm_dev = dp->drm_dev;
 	struct device *dev = dp->dev;
 	int ret;
@@ -333,7 +343,7 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
-	dp->plat_data.encoder = &dp->encoder;
+	dp->plat_data.encoder = &dp->encoder.encoder;
 
 	ret = analogix_dp_bind(dp->adp, drm_dev);
 	if (ret)
@@ -341,7 +351,7 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
 
 	return 0;
 err_cleanup_encoder:
-	dp->encoder.funcs->destroy(&dp->encoder);
+	dp->encoder.encoder.funcs->destroy(&dp->encoder.encoder);
 	return ret;
 }
 
@@ -351,7 +361,7 @@ static void rockchip_dp_unbind(struct device *dev, struct device *master,
 	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
 
 	analogix_dp_unbind(dp->adp);
-	dp->encoder.funcs->destroy(&dp->encoder);
+	dp->encoder.encoder.funcs->destroy(&dp->encoder.encoder);
 }
 
 static const struct component_ops rockchip_dp_component_ops = {
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index 16497c31d9f91..6ce1c1cdd9d68 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -26,11 +26,17 @@
 #include "cdn-dp-reg.h"
 #include "rockchip_drm_vop.h"
 
-#define connector_to_dp(c) \
-		container_of(c, struct cdn_dp_device, connector)
+static inline struct cdn_dp_device *connector_to_dp(struct drm_connector *connector)
+{
+	return container_of(connector, struct cdn_dp_device, connector);
+}
 
-#define encoder_to_dp(c) \
-		container_of(c, struct cdn_dp_device, encoder)
+static inline struct cdn_dp_device *encoder_to_dp(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct cdn_dp_device, encoder);
+}
 
 #define GRF_SOC_CON9		0x6224
 #define DP_SEL_VOP_LIT		BIT(12)
@@ -1022,7 +1028,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 
 	INIT_WORK(&dp->event_work, cdn_dp_pd_event_work);
 
-	encoder = &dp->encoder;
+	encoder = &dp->encoder.encoder;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
 							     dev->of_node);
@@ -1087,7 +1093,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
 {
 	struct cdn_dp_device *dp = dev_get_drvdata(dev);
-	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_encoder *encoder = &dp->encoder.encoder;
 	struct drm_connector *connector = &dp->connector;
 
 	cancel_work_sync(&dp->event_work);
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
index 81ac9b658a70a..29539170d3b1b 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -65,7 +65,7 @@ struct cdn_dp_device {
 	struct device *dev;
 	struct drm_device *drm_dev;
 	struct drm_connector connector;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 	struct drm_display_mode mode;
 	struct platform_device *audio_pdev;
 	struct work_struct event_work;
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
index 4ed7a68681978..110e83aad9bb4 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
@@ -181,8 +181,6 @@
 
 #define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
 
-#define to_dsi(nm)	container_of(nm, struct dw_mipi_dsi_rockchip, nm)
-
 enum {
 	DW_DSI_USAGE_IDLE,
 	DW_DSI_USAGE_DSI,
@@ -236,7 +234,7 @@ struct rockchip_dw_dsi_chip_data {
 
 struct dw_mipi_dsi_rockchip {
 	struct device *dev;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 	void __iomem *base;
 
 	struct regmap *grf_regmap;
@@ -271,6 +269,13 @@ struct dw_mipi_dsi_rockchip {
 	bool dsi_bound;
 };
 
+static struct dw_mipi_dsi_rockchip *to_dsi(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct dw_mipi_dsi_rockchip, encoder);
+}
+
 struct dphy_pll_parameter_map {
 	unsigned int max_mbps;
 	u8 hsfreqrange;
@@ -770,7 +775,7 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
 	int ret, mux;
 
 	mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node,
-						&dsi->encoder);
+						&dsi->encoder.encoder);
 	if (mux < 0)
 		return;
 
@@ -801,7 +806,7 @@ dw_mipi_dsi_encoder_helper_funcs = {
 static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi,
 					   struct drm_device *drm_dev)
 {
-	struct drm_encoder *encoder = &dsi->encoder;
+	struct drm_encoder *encoder = &dsi->encoder.encoder;
 	int ret;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
@@ -959,7 +964,7 @@ static int dw_mipi_dsi_rockchip_bind(struct device *dev,
 		goto out_pll_clk;
 	}
 
-	ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder);
+	ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder.encoder);
 	if (ret) {
 		DRM_DEV_ERROR(dev, "Failed to bind: %d\n", ret);
 		goto out_pll_clk;
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 8677c82716784..06c9ddef6f362 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -67,7 +67,7 @@ struct rockchip_hdmi_chip_data {
 struct rockchip_hdmi {
 	struct device *dev;
 	struct regmap *regmap;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 	const struct rockchip_hdmi_chip_data *chip_data;
 	struct clk *vpll_clk;
 	struct clk *grf_clk;
@@ -75,7 +75,12 @@ struct rockchip_hdmi {
 	struct phy *phy;
 };
 
-#define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
+static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct rockchip_hdmi, encoder);
+}
 
 static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 	{
@@ -511,7 +516,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	hdmi->dev = &pdev->dev;
 	hdmi->chip_data = plat_data->phy_data;
 	plat_data->phy_data = hdmi;
-	encoder = &hdmi->encoder;
+	encoder = &hdmi->encoder.encoder;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
 	/*
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 046e8ec2a71c5..0a4f72021d6af 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -26,8 +26,6 @@
 
 #include "inno_hdmi.h"
 
-#define to_inno_hdmi(x)	container_of(x, struct inno_hdmi, x)
-
 struct hdmi_data_info {
 	int vic;
 	bool sink_is_hdmi;
@@ -56,7 +54,7 @@ struct inno_hdmi {
 	void __iomem *regs;
 
 	struct drm_connector	connector;
-	struct drm_encoder	encoder;
+	struct rockchip_encoder	encoder;
 
 	struct inno_hdmi_i2c *i2c;
 	struct i2c_adapter *ddc;
@@ -67,6 +65,18 @@ struct inno_hdmi {
 	struct drm_display_mode previous_mode;
 };
 
+static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct inno_hdmi, encoder);
+}
+
+static struct inno_hdmi *connector_to_inno_hdmi(struct drm_connector *connector)
+{
+	return container_of(connector, struct inno_hdmi, connector);
+}
+
 enum {
 	CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
 	CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
@@ -483,7 +493,7 @@ static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 				       struct drm_display_mode *mode,
 				       struct drm_display_mode *adj_mode)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+	struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
 
 	inno_hdmi_setup(hdmi, adj_mode);
 
@@ -493,14 +503,14 @@ static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 
 static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+	struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
 
 	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
 }
 
 static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+	struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
 
 	inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
 }
@@ -536,7 +546,7 @@ static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
 static enum drm_connector_status
 inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+	struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
 
 	return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
 		connector_status_connected : connector_status_disconnected;
@@ -544,7 +554,7 @@ inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
 
 static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
 {
-	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+	struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
 	struct edid *edid;
 	int ret = 0;
 
@@ -599,7 +609,7 @@ static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
 
 static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
 {
-	struct drm_encoder *encoder = &hdmi->encoder;
+	struct drm_encoder *encoder = &hdmi->encoder.encoder;
 	struct device *dev = hdmi->dev;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
@@ -879,7 +889,7 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
 	return 0;
 err_cleanup_hdmi:
 	hdmi->connector.funcs->destroy(&hdmi->connector);
-	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+	hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
 err_put_adapter:
 	i2c_put_adapter(hdmi->ddc);
 err_disable_clk:
@@ -893,7 +903,7 @@ static void inno_hdmi_unbind(struct device *dev, struct device *master,
 	struct inno_hdmi *hdmi = dev_get_drvdata(dev);
 
 	hdmi->connector.funcs->destroy(&hdmi->connector);
-	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+	hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
 
 	i2c_put_adapter(hdmi->ddc);
 	clk_disable_unprepare(hdmi->pclk);
diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
index 1c546c3a89984..319240c33dcc0 100644
--- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c
+++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
@@ -47,7 +47,7 @@ struct rk3066_hdmi {
 	void __iomem *regs;
 
 	struct drm_connector connector;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 
 	struct rk3066_hdmi_i2c *i2c;
 	struct i2c_adapter *ddc;
@@ -58,7 +58,17 @@ struct rk3066_hdmi {
 	struct drm_display_mode previous_mode;
 };
 
-#define to_rk3066_hdmi(x) container_of(x, struct rk3066_hdmi, x)
+static struct rk3066_hdmi *encoder_to_rk3066_hdmi(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct rk3066_hdmi, encoder);
+}
+
+static struct rk3066_hdmi *connector_to_rk3066_hdmi(struct drm_connector *connector)
+{
+	return container_of(connector, struct rk3066_hdmi, connector);
+}
 
 static inline u8 hdmi_readb(struct rk3066_hdmi *hdmi, u16 offset)
 {
@@ -380,7 +390,7 @@ rk3066_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 			     struct drm_display_mode *mode,
 			     struct drm_display_mode *adj_mode)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder);
+	struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder);
 
 	/* Store the display mode for plugin/DPMS poweron events. */
 	memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode));
@@ -388,7 +398,7 @@ rk3066_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 
 static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder);
+	struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder);
 	int mux, val;
 
 	mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
@@ -407,7 +417,7 @@ static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder)
 
 static void rk3066_hdmi_encoder_disable(struct drm_encoder *encoder)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder);
+	struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder);
 
 	DRM_DEV_DEBUG(hdmi->dev, "hdmi encoder disable\n");
 
@@ -455,7 +465,7 @@ struct drm_encoder_helper_funcs rk3066_hdmi_encoder_helper_funcs = {
 static enum drm_connector_status
 rk3066_hdmi_connector_detect(struct drm_connector *connector, bool force)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector);
+	struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
 
 	return (hdmi_readb(hdmi, HDMI_HPG_MENS_STA) & HDMI_HPG_IN_STATUS_HIGH) ?
 		connector_status_connected : connector_status_disconnected;
@@ -463,7 +473,7 @@ rk3066_hdmi_connector_detect(struct drm_connector *connector, bool force)
 
 static int rk3066_hdmi_connector_get_modes(struct drm_connector *connector)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector);
+	struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
 	struct edid *edid;
 	int ret = 0;
 
@@ -496,9 +506,9 @@ rk3066_hdmi_connector_mode_valid(struct drm_connector *connector,
 static struct drm_encoder *
 rk3066_hdmi_connector_best_encoder(struct drm_connector *connector)
 {
-	struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector);
+	struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
 
-	return &hdmi->encoder;
+	return &hdmi->encoder.encoder;
 }
 
 static int
@@ -538,7 +548,7 @@ struct drm_connector_helper_funcs rk3066_hdmi_connector_helper_funcs = {
 static int
 rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi)
 {
-	struct drm_encoder *encoder = &hdmi->encoder;
+	struct drm_encoder *encoder = &hdmi->encoder.encoder;
 	struct device *dev = hdmi->dev;
 
 	encoder->possible_crtcs =
@@ -816,7 +826,7 @@ static int rk3066_hdmi_bind(struct device *dev, struct device *master,
 
 err_cleanup_hdmi:
 	hdmi->connector.funcs->destroy(&hdmi->connector);
-	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+	hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
 err_disable_i2c:
 	i2c_put_adapter(hdmi->ddc);
 err_disable_hclk:
@@ -831,7 +841,7 @@ static void rk3066_hdmi_unbind(struct device *dev, struct device *master,
 	struct rk3066_hdmi *hdmi = dev_get_drvdata(dev);
 
 	hdmi->connector.funcs->destroy(&hdmi->connector);
-	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+	hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
 
 	i2c_put_adapter(hdmi->ddc);
 	clk_disable_unprepare(hdmi->hclk);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 143a48330f849..d3e42410ae5da 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -63,4 +63,15 @@ extern struct platform_driver rockchip_dp_driver;
 extern struct platform_driver rockchip_lvds_driver;
 extern struct platform_driver vop_platform_driver;
 extern struct platform_driver rk3066_hdmi_driver;
+
+struct rockchip_encoder {
+	struct device_node *port;
+	struct drm_encoder encoder;
+};
+
+static inline struct rockchip_encoder *to_rockchip_encoder(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct rockchip_encoder, encoder);
+}
+
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
index be74c87a8be4d..4ced073c6b06c 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -36,12 +36,6 @@
 
 struct rockchip_lvds;
 
-#define connector_to_lvds(c) \
-		container_of(c, struct rockchip_lvds, connector)
-
-#define encoder_to_lvds(c) \
-		container_of(c, struct rockchip_lvds, encoder)
-
 /**
  * struct rockchip_lvds_soc_data - rockchip lvds Soc private data
  * @probe: LVDS platform probe function
@@ -65,10 +59,22 @@ struct rockchip_lvds {
 	struct drm_panel *panel;
 	struct drm_bridge *bridge;
 	struct drm_connector connector;
-	struct drm_encoder encoder;
+	struct rockchip_encoder encoder;
 	struct dev_pin_info *pins;
 };
 
+static inline struct rockchip_lvds *connector_to_lvds(struct drm_connector *connector)
+{
+	return container_of(connector, struct rockchip_lvds, connector);
+}
+
+static inline struct rockchip_lvds *encoder_to_lvds(struct drm_encoder *encoder)
+{
+	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+	return container_of(rkencoder, struct rockchip_lvds, encoder);
+}
+
 static inline void rk3288_writel(struct rockchip_lvds *lvds, u32 offset,
 				 u32 val)
 {
@@ -599,7 +605,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
 		goto err_put_remote;
 	}
 
-	encoder = &lvds->encoder;
+	encoder = &lvds->encoder.encoder;
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
 							     dev->of_node);
 
@@ -674,10 +680,10 @@ static void rockchip_lvds_unbind(struct device *dev, struct device *master,
 	const struct drm_encoder_helper_funcs *encoder_funcs;
 
 	encoder_funcs = lvds->soc_data->helper_funcs;
-	encoder_funcs->disable(&lvds->encoder);
+	encoder_funcs->disable(&lvds->encoder.encoder);
 	pm_runtime_disable(dev);
 	drm_connector_cleanup(&lvds->connector);
-	drm_encoder_cleanup(&lvds->encoder);
+	drm_encoder_cleanup(&lvds->encoder.encoder);
 }
 
 static const struct component_ops rockchip_lvds_component_ops = {
-- 
2.30.2


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

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

* [PATCH v6 02/23] drm/rockchip: dw_hdmi: rename vpll clock to reference clock
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

"vpll" is a misnomer. A clock input to a device should be named after
the usage in the device, not after the clock that drives it. On the
rk3568 the same clock is driven by the HPLL.
To fix that, this patch renames the vpll clock to ref clock. The clock
name "vpll" is left for compatibility to old device trees.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 29 ++++++++++++---------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 06c9ddef6f362..1740d4c953e32 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -69,7 +69,7 @@ struct rockchip_hdmi {
 	struct regmap *regmap;
 	struct rockchip_encoder encoder;
 	const struct rockchip_hdmi_chip_data *chip_data;
-	struct clk *vpll_clk;
+	struct clk *ref_clk;
 	struct clk *grf_clk;
 	struct dw_hdmi *hdmi;
 	struct phy *phy;
@@ -201,14 +201,17 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 		return PTR_ERR(hdmi->regmap);
 	}
 
-	hdmi->vpll_clk = devm_clk_get(hdmi->dev, "vpll");
-	if (PTR_ERR(hdmi->vpll_clk) == -ENOENT) {
-		hdmi->vpll_clk = NULL;
-	} else if (PTR_ERR(hdmi->vpll_clk) == -EPROBE_DEFER) {
+	hdmi->ref_clk = devm_clk_get(hdmi->dev, "ref");
+	if (PTR_ERR(hdmi->ref_clk) == -ENOENT)
+		hdmi->ref_clk = devm_clk_get(hdmi->dev, "vpll");
+
+	if (PTR_ERR(hdmi->ref_clk) == -ENOENT) {
+		hdmi->ref_clk = NULL;
+	} else if (PTR_ERR(hdmi->ref_clk) == -EPROBE_DEFER) {
 		return -EPROBE_DEFER;
-	} else if (IS_ERR(hdmi->vpll_clk)) {
-		DRM_DEV_ERROR(hdmi->dev, "failed to get vpll clock\n");
-		return PTR_ERR(hdmi->vpll_clk);
+	} else if (IS_ERR(hdmi->ref_clk)) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to get reference clock\n");
+		return PTR_ERR(hdmi->ref_clk);
 	}
 
 	hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf");
@@ -262,7 +265,7 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
 {
 	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
 
-	clk_set_rate(hdmi->vpll_clk, adj_mode->clock * 1000);
+	clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000);
 }
 
 static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
@@ -542,9 +545,9 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
-	ret = clk_prepare_enable(hdmi->vpll_clk);
+	ret = clk_prepare_enable(hdmi->ref_clk);
 	if (ret) {
-		DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI vpll: %d\n",
+		DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n",
 			      ret);
 		return ret;
 	}
@@ -563,7 +566,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	if (IS_ERR(hdmi->hdmi)) {
 		ret = PTR_ERR(hdmi->hdmi);
 		drm_encoder_cleanup(encoder);
-		clk_disable_unprepare(hdmi->vpll_clk);
+		clk_disable_unprepare(hdmi->ref_clk);
 	}
 
 	return ret;
@@ -575,7 +578,7 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
 	struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
 
 	dw_hdmi_unbind(hdmi->hdmi);
-	clk_disable_unprepare(hdmi->vpll_clk);
+	clk_disable_unprepare(hdmi->ref_clk);
 }
 
 static const struct component_ops dw_hdmi_rockchip_ops = {
-- 
2.30.2


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

* [PATCH v6 02/23] drm/rockchip: dw_hdmi: rename vpll clock to reference clock
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

"vpll" is a misnomer. A clock input to a device should be named after
the usage in the device, not after the clock that drives it. On the
rk3568 the same clock is driven by the HPLL.
To fix that, this patch renames the vpll clock to ref clock. The clock
name "vpll" is left for compatibility to old device trees.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 29 ++++++++++++---------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 06c9ddef6f362..1740d4c953e32 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -69,7 +69,7 @@ struct rockchip_hdmi {
 	struct regmap *regmap;
 	struct rockchip_encoder encoder;
 	const struct rockchip_hdmi_chip_data *chip_data;
-	struct clk *vpll_clk;
+	struct clk *ref_clk;
 	struct clk *grf_clk;
 	struct dw_hdmi *hdmi;
 	struct phy *phy;
@@ -201,14 +201,17 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 		return PTR_ERR(hdmi->regmap);
 	}
 
-	hdmi->vpll_clk = devm_clk_get(hdmi->dev, "vpll");
-	if (PTR_ERR(hdmi->vpll_clk) == -ENOENT) {
-		hdmi->vpll_clk = NULL;
-	} else if (PTR_ERR(hdmi->vpll_clk) == -EPROBE_DEFER) {
+	hdmi->ref_clk = devm_clk_get(hdmi->dev, "ref");
+	if (PTR_ERR(hdmi->ref_clk) == -ENOENT)
+		hdmi->ref_clk = devm_clk_get(hdmi->dev, "vpll");
+
+	if (PTR_ERR(hdmi->ref_clk) == -ENOENT) {
+		hdmi->ref_clk = NULL;
+	} else if (PTR_ERR(hdmi->ref_clk) == -EPROBE_DEFER) {
 		return -EPROBE_DEFER;
-	} else if (IS_ERR(hdmi->vpll_clk)) {
-		DRM_DEV_ERROR(hdmi->dev, "failed to get vpll clock\n");
-		return PTR_ERR(hdmi->vpll_clk);
+	} else if (IS_ERR(hdmi->ref_clk)) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to get reference clock\n");
+		return PTR_ERR(hdmi->ref_clk);
 	}
 
 	hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf");
@@ -262,7 +265,7 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
 {
 	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
 
-	clk_set_rate(hdmi->vpll_clk, adj_mode->clock * 1000);
+	clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000);
 }
 
 static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
@@ -542,9 +545,9 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
-	ret = clk_prepare_enable(hdmi->vpll_clk);
+	ret = clk_prepare_enable(hdmi->ref_clk);
 	if (ret) {
-		DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI vpll: %d\n",
+		DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n",
 			      ret);
 		return ret;
 	}
@@ -563,7 +566,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	if (IS_ERR(hdmi->hdmi)) {
 		ret = PTR_ERR(hdmi->hdmi);
 		drm_encoder_cleanup(encoder);
-		clk_disable_unprepare(hdmi->vpll_clk);
+		clk_disable_unprepare(hdmi->ref_clk);
 	}
 
 	return ret;
@@ -575,7 +578,7 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
 	struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
 
 	dw_hdmi_unbind(hdmi->hdmi);
-	clk_disable_unprepare(hdmi->vpll_clk);
+	clk_disable_unprepare(hdmi->ref_clk);
 }
 
 static const struct component_ops dw_hdmi_rockchip_ops = {
-- 
2.30.2


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

* [PATCH v6 02/23] drm/rockchip: dw_hdmi: rename vpll clock to reference clock
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

"vpll" is a misnomer. A clock input to a device should be named after
the usage in the device, not after the clock that drives it. On the
rk3568 the same clock is driven by the HPLL.
To fix that, this patch renames the vpll clock to ref clock. The clock
name "vpll" is left for compatibility to old device trees.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 29 ++++++++++++---------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 06c9ddef6f362..1740d4c953e32 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -69,7 +69,7 @@ struct rockchip_hdmi {
 	struct regmap *regmap;
 	struct rockchip_encoder encoder;
 	const struct rockchip_hdmi_chip_data *chip_data;
-	struct clk *vpll_clk;
+	struct clk *ref_clk;
 	struct clk *grf_clk;
 	struct dw_hdmi *hdmi;
 	struct phy *phy;
@@ -201,14 +201,17 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 		return PTR_ERR(hdmi->regmap);
 	}
 
-	hdmi->vpll_clk = devm_clk_get(hdmi->dev, "vpll");
-	if (PTR_ERR(hdmi->vpll_clk) == -ENOENT) {
-		hdmi->vpll_clk = NULL;
-	} else if (PTR_ERR(hdmi->vpll_clk) == -EPROBE_DEFER) {
+	hdmi->ref_clk = devm_clk_get(hdmi->dev, "ref");
+	if (PTR_ERR(hdmi->ref_clk) == -ENOENT)
+		hdmi->ref_clk = devm_clk_get(hdmi->dev, "vpll");
+
+	if (PTR_ERR(hdmi->ref_clk) == -ENOENT) {
+		hdmi->ref_clk = NULL;
+	} else if (PTR_ERR(hdmi->ref_clk) == -EPROBE_DEFER) {
 		return -EPROBE_DEFER;
-	} else if (IS_ERR(hdmi->vpll_clk)) {
-		DRM_DEV_ERROR(hdmi->dev, "failed to get vpll clock\n");
-		return PTR_ERR(hdmi->vpll_clk);
+	} else if (IS_ERR(hdmi->ref_clk)) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to get reference clock\n");
+		return PTR_ERR(hdmi->ref_clk);
 	}
 
 	hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf");
@@ -262,7 +265,7 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
 {
 	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
 
-	clk_set_rate(hdmi->vpll_clk, adj_mode->clock * 1000);
+	clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000);
 }
 
 static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
@@ -542,9 +545,9 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
-	ret = clk_prepare_enable(hdmi->vpll_clk);
+	ret = clk_prepare_enable(hdmi->ref_clk);
 	if (ret) {
-		DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI vpll: %d\n",
+		DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n",
 			      ret);
 		return ret;
 	}
@@ -563,7 +566,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	if (IS_ERR(hdmi->hdmi)) {
 		ret = PTR_ERR(hdmi->hdmi);
 		drm_encoder_cleanup(encoder);
-		clk_disable_unprepare(hdmi->vpll_clk);
+		clk_disable_unprepare(hdmi->ref_clk);
 	}
 
 	return ret;
@@ -575,7 +578,7 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
 	struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
 
 	dw_hdmi_unbind(hdmi->hdmi);
-	clk_disable_unprepare(hdmi->vpll_clk);
+	clk_disable_unprepare(hdmi->ref_clk);
 }
 
 static const struct component_ops dw_hdmi_rockchip_ops = {
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 02/23] drm/rockchip: dw_hdmi: rename vpll clock to reference clock
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

"vpll" is a misnomer. A clock input to a device should be named after
the usage in the device, not after the clock that drives it. On the
rk3568 the same clock is driven by the HPLL.
To fix that, this patch renames the vpll clock to ref clock. The clock
name "vpll" is left for compatibility to old device trees.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 29 ++++++++++++---------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 06c9ddef6f362..1740d4c953e32 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -69,7 +69,7 @@ struct rockchip_hdmi {
 	struct regmap *regmap;
 	struct rockchip_encoder encoder;
 	const struct rockchip_hdmi_chip_data *chip_data;
-	struct clk *vpll_clk;
+	struct clk *ref_clk;
 	struct clk *grf_clk;
 	struct dw_hdmi *hdmi;
 	struct phy *phy;
@@ -201,14 +201,17 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 		return PTR_ERR(hdmi->regmap);
 	}
 
-	hdmi->vpll_clk = devm_clk_get(hdmi->dev, "vpll");
-	if (PTR_ERR(hdmi->vpll_clk) == -ENOENT) {
-		hdmi->vpll_clk = NULL;
-	} else if (PTR_ERR(hdmi->vpll_clk) == -EPROBE_DEFER) {
+	hdmi->ref_clk = devm_clk_get(hdmi->dev, "ref");
+	if (PTR_ERR(hdmi->ref_clk) == -ENOENT)
+		hdmi->ref_clk = devm_clk_get(hdmi->dev, "vpll");
+
+	if (PTR_ERR(hdmi->ref_clk) == -ENOENT) {
+		hdmi->ref_clk = NULL;
+	} else if (PTR_ERR(hdmi->ref_clk) == -EPROBE_DEFER) {
 		return -EPROBE_DEFER;
-	} else if (IS_ERR(hdmi->vpll_clk)) {
-		DRM_DEV_ERROR(hdmi->dev, "failed to get vpll clock\n");
-		return PTR_ERR(hdmi->vpll_clk);
+	} else if (IS_ERR(hdmi->ref_clk)) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to get reference clock\n");
+		return PTR_ERR(hdmi->ref_clk);
 	}
 
 	hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf");
@@ -262,7 +265,7 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
 {
 	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
 
-	clk_set_rate(hdmi->vpll_clk, adj_mode->clock * 1000);
+	clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000);
 }
 
 static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
@@ -542,9 +545,9 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
-	ret = clk_prepare_enable(hdmi->vpll_clk);
+	ret = clk_prepare_enable(hdmi->ref_clk);
 	if (ret) {
-		DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI vpll: %d\n",
+		DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n",
 			      ret);
 		return ret;
 	}
@@ -563,7 +566,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	if (IS_ERR(hdmi->hdmi)) {
 		ret = PTR_ERR(hdmi->hdmi);
 		drm_encoder_cleanup(encoder);
-		clk_disable_unprepare(hdmi->vpll_clk);
+		clk_disable_unprepare(hdmi->ref_clk);
 	}
 
 	return ret;
@@ -575,7 +578,7 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
 	struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
 
 	dw_hdmi_unbind(hdmi->hdmi);
-	clk_disable_unprepare(hdmi->vpll_clk);
+	clk_disable_unprepare(hdmi->ref_clk);
 }
 
 static const struct component_ops dw_hdmi_rockchip_ops = {
-- 
2.30.2


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

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

* [PATCH v6 03/23] dt-bindings: display: rockchip: dw-hdmi: use "ref" as clock name
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

"vpll" is a misnomer. A clock input to a device should be named after
the usage in the device, not after the clock that drives it. On the
rk3568 the same clock is driven by the HPLL.
This patch adds "ref" as a new alternative clock name for "vpll"

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack
    
    Changes since v3:
    - Keep old clock name for compatibility reasons

 .../bindings/display/rockchip/rockchip,dw-hdmi.yaml      | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index da3b889ad8fcd..0400f67e5f2c9 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -36,7 +36,8 @@ properties:
       # order when present.
       - description: The HDMI CEC controller main clock
       - description: Power for GRF IO
-      - description: External clock for some HDMI PHY
+      - description: External clock for some HDMI PHY (old clock name, deprecated)
+      - description: External clock for some HDMI PHY (new name)
 
   clock-names:
     minItems: 2
@@ -47,10 +48,14 @@ properties:
           - cec
           - grf
           - vpll
+          - ref
       - enum:
           - grf
           - vpll
-      - const: vpll
+          - ref
+      - enum:
+          - vpll
+          - ref
 
   ddc-i2c-bus:
     $ref: /schemas/types.yaml#/definitions/phandle
-- 
2.30.2


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

* [PATCH v6 03/23] dt-bindings: display: rockchip: dw-hdmi: use "ref" as clock name
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

"vpll" is a misnomer. A clock input to a device should be named after
the usage in the device, not after the clock that drives it. On the
rk3568 the same clock is driven by the HPLL.
This patch adds "ref" as a new alternative clock name for "vpll"

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack
    
    Changes since v3:
    - Keep old clock name for compatibility reasons

 .../bindings/display/rockchip/rockchip,dw-hdmi.yaml      | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index da3b889ad8fcd..0400f67e5f2c9 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -36,7 +36,8 @@ properties:
       # order when present.
       - description: The HDMI CEC controller main clock
       - description: Power for GRF IO
-      - description: External clock for some HDMI PHY
+      - description: External clock for some HDMI PHY (old clock name, deprecated)
+      - description: External clock for some HDMI PHY (new name)
 
   clock-names:
     minItems: 2
@@ -47,10 +48,14 @@ properties:
           - cec
           - grf
           - vpll
+          - ref
       - enum:
           - grf
           - vpll
-      - const: vpll
+          - ref
+      - enum:
+          - vpll
+          - ref
 
   ddc-i2c-bus:
     $ref: /schemas/types.yaml#/definitions/phandle
-- 
2.30.2


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

* [PATCH v6 03/23] dt-bindings: display: rockchip: dw-hdmi: use "ref" as clock name
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

"vpll" is a misnomer. A clock input to a device should be named after
the usage in the device, not after the clock that drives it. On the
rk3568 the same clock is driven by the HPLL.
This patch adds "ref" as a new alternative clock name for "vpll"

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack
    
    Changes since v3:
    - Keep old clock name for compatibility reasons

 .../bindings/display/rockchip/rockchip,dw-hdmi.yaml      | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index da3b889ad8fcd..0400f67e5f2c9 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -36,7 +36,8 @@ properties:
       # order when present.
       - description: The HDMI CEC controller main clock
       - description: Power for GRF IO
-      - description: External clock for some HDMI PHY
+      - description: External clock for some HDMI PHY (old clock name, deprecated)
+      - description: External clock for some HDMI PHY (new name)
 
   clock-names:
     minItems: 2
@@ -47,10 +48,14 @@ properties:
           - cec
           - grf
           - vpll
+          - ref
       - enum:
           - grf
           - vpll
-      - const: vpll
+          - ref
+      - enum:
+          - vpll
+          - ref
 
   ddc-i2c-bus:
     $ref: /schemas/types.yaml#/definitions/phandle
-- 
2.30.2


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

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

* [PATCH v6 03/23] dt-bindings: display: rockchip: dw-hdmi: use "ref" as clock name
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

"vpll" is a misnomer. A clock input to a device should be named after
the usage in the device, not after the clock that drives it. On the
rk3568 the same clock is driven by the HPLL.
This patch adds "ref" as a new alternative clock name for "vpll"

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack
    
    Changes since v3:
    - Keep old clock name for compatibility reasons

 .../bindings/display/rockchip/rockchip,dw-hdmi.yaml      | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index da3b889ad8fcd..0400f67e5f2c9 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -36,7 +36,8 @@ properties:
       # order when present.
       - description: The HDMI CEC controller main clock
       - description: Power for GRF IO
-      - description: External clock for some HDMI PHY
+      - description: External clock for some HDMI PHY (old clock name, deprecated)
+      - description: External clock for some HDMI PHY (new name)
 
   clock-names:
     minItems: 2
@@ -47,10 +48,14 @@ properties:
           - cec
           - grf
           - vpll
+          - ref
       - enum:
           - grf
           - vpll
-      - const: vpll
+          - ref
+      - enum:
+          - vpll
+          - ref
 
   ddc-i2c-bus:
     $ref: /schemas/types.yaml#/definitions/phandle
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 04/23] arm64: dts: rockchip: rk3399: rename HDMI ref clock to 'ref'
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

The reference clock for the HDMI controller has been renamed to 'ref',
the previous 'vpll' name is only left for compatibility in the driver.
Rename the clock to the new name.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 080457a68e3c7..d0add619b0d22 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -1884,7 +1884,7 @@ hdmi: hdmi@ff940000 {
 			 <&cru SCLK_HDMI_CEC>,
 			 <&cru PCLK_VIO_GRF>,
 			 <&cru PLL_VPLL>;
-		clock-names = "iahb", "isfr", "cec", "grf", "vpll";
+		clock-names = "iahb", "isfr", "cec", "grf", "ref";
 		power-domains = <&power RK3399_PD_HDCP>;
 		reg-io-width = <4>;
 		rockchip,grf = <&grf>;
-- 
2.30.2


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

* [PATCH v6 04/23] arm64: dts: rockchip: rk3399: rename HDMI ref clock to 'ref'
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

The reference clock for the HDMI controller has been renamed to 'ref',
the previous 'vpll' name is only left for compatibility in the driver.
Rename the clock to the new name.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 080457a68e3c7..d0add619b0d22 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -1884,7 +1884,7 @@ hdmi: hdmi@ff940000 {
 			 <&cru SCLK_HDMI_CEC>,
 			 <&cru PCLK_VIO_GRF>,
 			 <&cru PLL_VPLL>;
-		clock-names = "iahb", "isfr", "cec", "grf", "vpll";
+		clock-names = "iahb", "isfr", "cec", "grf", "ref";
 		power-domains = <&power RK3399_PD_HDCP>;
 		reg-io-width = <4>;
 		rockchip,grf = <&grf>;
-- 
2.30.2


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

* [PATCH v6 04/23] arm64: dts: rockchip: rk3399: rename HDMI ref clock to 'ref'
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

The reference clock for the HDMI controller has been renamed to 'ref',
the previous 'vpll' name is only left for compatibility in the driver.
Rename the clock to the new name.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 080457a68e3c7..d0add619b0d22 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -1884,7 +1884,7 @@ hdmi: hdmi@ff940000 {
 			 <&cru SCLK_HDMI_CEC>,
 			 <&cru PCLK_VIO_GRF>,
 			 <&cru PLL_VPLL>;
-		clock-names = "iahb", "isfr", "cec", "grf", "vpll";
+		clock-names = "iahb", "isfr", "cec", "grf", "ref";
 		power-domains = <&power RK3399_PD_HDCP>;
 		reg-io-width = <4>;
 		rockchip,grf = <&grf>;
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 04/23] arm64: dts: rockchip: rk3399: rename HDMI ref clock to 'ref'
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

The reference clock for the HDMI controller has been renamed to 'ref',
the previous 'vpll' name is only left for compatibility in the driver.
Rename the clock to the new name.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 080457a68e3c7..d0add619b0d22 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -1884,7 +1884,7 @@ hdmi: hdmi@ff940000 {
 			 <&cru SCLK_HDMI_CEC>,
 			 <&cru PCLK_VIO_GRF>,
 			 <&cru PLL_VPLL>;
-		clock-names = "iahb", "isfr", "cec", "grf", "vpll";
+		clock-names = "iahb", "isfr", "cec", "grf", "ref";
 		power-domains = <&power RK3399_PD_HDCP>;
 		reg-io-width = <4>;
 		rockchip,grf = <&grf>;
-- 
2.30.2


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

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

* [PATCH v6 05/23] drm/rockchip: dw_hdmi: add rk3568 support
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

Add a new dw_hdmi_plat_data struct and new compatible for rk3568.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 31 +++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 1740d4c953e32..efaff4017086b 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -50,6 +50,10 @@
 #define RK3399_GRF_SOC_CON20		0x6250
 #define RK3399_HDMI_LCDC_SEL		BIT(6)
 
+#define RK3568_GRF_VO_CON1		0x0364
+#define RK3568_HDMI_SDAIN_MSK		BIT(15)
+#define RK3568_HDMI_SCLIN_MSK		BIT(14)
+
 #define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
 
 /**
@@ -475,6 +479,19 @@ static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
 	.use_drm_infoframe = true,
 };
 
+static struct rockchip_hdmi_chip_data rk3568_chip_data = {
+	.lcdsel_grf_reg = -1,
+};
+
+static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
+	.mode_valid = dw_hdmi_rockchip_mode_valid,
+	.mpll_cfg   = rockchip_mpll_cfg,
+	.cur_ctr    = rockchip_cur_ctr,
+	.phy_config = rockchip_phy_config,
+	.phy_data = &rk3568_chip_data,
+	.use_drm_infoframe = true,
+};
+
 static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
 	{ .compatible = "rockchip,rk3228-dw-hdmi",
 	  .data = &rk3228_hdmi_drv_data
@@ -488,6 +505,9 @@ static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
 	{ .compatible = "rockchip,rk3399-dw-hdmi",
 	  .data = &rk3399_hdmi_drv_data
 	},
+	{ .compatible = "rockchip,rk3568-dw-hdmi",
+	  .data = &rk3568_hdmi_drv_data
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids);
@@ -522,6 +542,9 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	encoder = &hdmi->encoder.encoder;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+
+	hdmi->encoder.port = of_graph_get_port_by_id(dev->of_node, 0);
+
 	/*
 	 * If we failed to find the CRTC(s) which this encoder is
 	 * supposed to be connected to, it's because the CRTC has
@@ -552,6 +575,14 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
+	if (hdmi->chip_data == &rk3568_chip_data) {
+		regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1,
+			     HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK |
+					   RK3568_HDMI_SCLIN_MSK,
+					   RK3568_HDMI_SDAIN_MSK |
+					   RK3568_HDMI_SCLIN_MSK));
+	}
+
 	drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
 	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
 
-- 
2.30.2


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

* [PATCH v6 05/23] drm/rockchip: dw_hdmi: add rk3568 support
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

Add a new dw_hdmi_plat_data struct and new compatible for rk3568.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 31 +++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 1740d4c953e32..efaff4017086b 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -50,6 +50,10 @@
 #define RK3399_GRF_SOC_CON20		0x6250
 #define RK3399_HDMI_LCDC_SEL		BIT(6)
 
+#define RK3568_GRF_VO_CON1		0x0364
+#define RK3568_HDMI_SDAIN_MSK		BIT(15)
+#define RK3568_HDMI_SCLIN_MSK		BIT(14)
+
 #define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
 
 /**
@@ -475,6 +479,19 @@ static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
 	.use_drm_infoframe = true,
 };
 
+static struct rockchip_hdmi_chip_data rk3568_chip_data = {
+	.lcdsel_grf_reg = -1,
+};
+
+static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
+	.mode_valid = dw_hdmi_rockchip_mode_valid,
+	.mpll_cfg   = rockchip_mpll_cfg,
+	.cur_ctr    = rockchip_cur_ctr,
+	.phy_config = rockchip_phy_config,
+	.phy_data = &rk3568_chip_data,
+	.use_drm_infoframe = true,
+};
+
 static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
 	{ .compatible = "rockchip,rk3228-dw-hdmi",
 	  .data = &rk3228_hdmi_drv_data
@@ -488,6 +505,9 @@ static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
 	{ .compatible = "rockchip,rk3399-dw-hdmi",
 	  .data = &rk3399_hdmi_drv_data
 	},
+	{ .compatible = "rockchip,rk3568-dw-hdmi",
+	  .data = &rk3568_hdmi_drv_data
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids);
@@ -522,6 +542,9 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	encoder = &hdmi->encoder.encoder;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+
+	hdmi->encoder.port = of_graph_get_port_by_id(dev->of_node, 0);
+
 	/*
 	 * If we failed to find the CRTC(s) which this encoder is
 	 * supposed to be connected to, it's because the CRTC has
@@ -552,6 +575,14 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
+	if (hdmi->chip_data == &rk3568_chip_data) {
+		regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1,
+			     HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK |
+					   RK3568_HDMI_SCLIN_MSK,
+					   RK3568_HDMI_SDAIN_MSK |
+					   RK3568_HDMI_SCLIN_MSK));
+	}
+
 	drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
 	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
 
-- 
2.30.2


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

* [PATCH v6 05/23] drm/rockchip: dw_hdmi: add rk3568 support
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

Add a new dw_hdmi_plat_data struct and new compatible for rk3568.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 31 +++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 1740d4c953e32..efaff4017086b 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -50,6 +50,10 @@
 #define RK3399_GRF_SOC_CON20		0x6250
 #define RK3399_HDMI_LCDC_SEL		BIT(6)
 
+#define RK3568_GRF_VO_CON1		0x0364
+#define RK3568_HDMI_SDAIN_MSK		BIT(15)
+#define RK3568_HDMI_SCLIN_MSK		BIT(14)
+
 #define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
 
 /**
@@ -475,6 +479,19 @@ static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
 	.use_drm_infoframe = true,
 };
 
+static struct rockchip_hdmi_chip_data rk3568_chip_data = {
+	.lcdsel_grf_reg = -1,
+};
+
+static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
+	.mode_valid = dw_hdmi_rockchip_mode_valid,
+	.mpll_cfg   = rockchip_mpll_cfg,
+	.cur_ctr    = rockchip_cur_ctr,
+	.phy_config = rockchip_phy_config,
+	.phy_data = &rk3568_chip_data,
+	.use_drm_infoframe = true,
+};
+
 static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
 	{ .compatible = "rockchip,rk3228-dw-hdmi",
 	  .data = &rk3228_hdmi_drv_data
@@ -488,6 +505,9 @@ static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
 	{ .compatible = "rockchip,rk3399-dw-hdmi",
 	  .data = &rk3399_hdmi_drv_data
 	},
+	{ .compatible = "rockchip,rk3568-dw-hdmi",
+	  .data = &rk3568_hdmi_drv_data
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids);
@@ -522,6 +542,9 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	encoder = &hdmi->encoder.encoder;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+
+	hdmi->encoder.port = of_graph_get_port_by_id(dev->of_node, 0);
+
 	/*
 	 * If we failed to find the CRTC(s) which this encoder is
 	 * supposed to be connected to, it's because the CRTC has
@@ -552,6 +575,14 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
+	if (hdmi->chip_data == &rk3568_chip_data) {
+		regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1,
+			     HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK |
+					   RK3568_HDMI_SCLIN_MSK,
+					   RK3568_HDMI_SDAIN_MSK |
+					   RK3568_HDMI_SCLIN_MSK));
+	}
+
 	drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
 	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
 
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 05/23] drm/rockchip: dw_hdmi: add rk3568 support
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

Add a new dw_hdmi_plat_data struct and new compatible for rk3568.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 31 +++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 1740d4c953e32..efaff4017086b 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -50,6 +50,10 @@
 #define RK3399_GRF_SOC_CON20		0x6250
 #define RK3399_HDMI_LCDC_SEL		BIT(6)
 
+#define RK3568_GRF_VO_CON1		0x0364
+#define RK3568_HDMI_SDAIN_MSK		BIT(15)
+#define RK3568_HDMI_SCLIN_MSK		BIT(14)
+
 #define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
 
 /**
@@ -475,6 +479,19 @@ static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
 	.use_drm_infoframe = true,
 };
 
+static struct rockchip_hdmi_chip_data rk3568_chip_data = {
+	.lcdsel_grf_reg = -1,
+};
+
+static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
+	.mode_valid = dw_hdmi_rockchip_mode_valid,
+	.mpll_cfg   = rockchip_mpll_cfg,
+	.cur_ctr    = rockchip_cur_ctr,
+	.phy_config = rockchip_phy_config,
+	.phy_data = &rk3568_chip_data,
+	.use_drm_infoframe = true,
+};
+
 static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
 	{ .compatible = "rockchip,rk3228-dw-hdmi",
 	  .data = &rk3228_hdmi_drv_data
@@ -488,6 +505,9 @@ static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
 	{ .compatible = "rockchip,rk3399-dw-hdmi",
 	  .data = &rk3399_hdmi_drv_data
 	},
+	{ .compatible = "rockchip,rk3568-dw-hdmi",
+	  .data = &rk3568_hdmi_drv_data
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids);
@@ -522,6 +542,9 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	encoder = &hdmi->encoder.encoder;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+
+	hdmi->encoder.port = of_graph_get_port_by_id(dev->of_node, 0);
+
 	/*
 	 * If we failed to find the CRTC(s) which this encoder is
 	 * supposed to be connected to, it's because the CRTC has
@@ -552,6 +575,14 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
+	if (hdmi->chip_data == &rk3568_chip_data) {
+		regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1,
+			     HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK |
+					   RK3568_HDMI_SCLIN_MSK,
+					   RK3568_HDMI_SDAIN_MSK |
+					   RK3568_HDMI_SCLIN_MSK));
+	}
+
 	drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
 	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
 
-- 
2.30.2


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

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

* [PATCH v6 06/23] dt-bindings: display: rockchip: dw-hdmi: Add compatible for rk3568 HDMI
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Rob Herring, Sascha Hauer

From: Benjamin Gaignard <benjamin.gaignard@collabora.com>

Define a new compatible for rk3568 HDMI.
This version of HDMI hardware block needs two new clocks hclk_vio and hclk
to provide phy reference clocks.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 .../devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index 0400f67e5f2c9..e6b8437a1e2d1 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -23,6 +23,7 @@ properties:
       - rockchip,rk3288-dw-hdmi
       - rockchip,rk3328-dw-hdmi
       - rockchip,rk3399-dw-hdmi
+      - rockchip,rk3568-dw-hdmi
 
   reg-io-width:
     const: 4
-- 
2.30.2


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

* [PATCH v6 06/23] dt-bindings: display: rockchip: dw-hdmi: Add compatible for rk3568 HDMI
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

From: Benjamin Gaignard <benjamin.gaignard@collabora.com>

Define a new compatible for rk3568 HDMI.
This version of HDMI hardware block needs two new clocks hclk_vio and hclk
to provide phy reference clocks.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 .../devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index 0400f67e5f2c9..e6b8437a1e2d1 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -23,6 +23,7 @@ properties:
       - rockchip,rk3288-dw-hdmi
       - rockchip,rk3328-dw-hdmi
       - rockchip,rk3399-dw-hdmi
+      - rockchip,rk3568-dw-hdmi
 
   reg-io-width:
     const: 4
-- 
2.30.2


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

* [PATCH v6 06/23] dt-bindings: display: rockchip: dw-hdmi: Add compatible for rk3568 HDMI
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Rob Herring, Sascha Hauer

From: Benjamin Gaignard <benjamin.gaignard@collabora.com>

Define a new compatible for rk3568 HDMI.
This version of HDMI hardware block needs two new clocks hclk_vio and hclk
to provide phy reference clocks.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 .../devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index 0400f67e5f2c9..e6b8437a1e2d1 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -23,6 +23,7 @@ properties:
       - rockchip,rk3288-dw-hdmi
       - rockchip,rk3328-dw-hdmi
       - rockchip,rk3399-dw-hdmi
+      - rockchip,rk3568-dw-hdmi
 
   reg-io-width:
     const: 4
-- 
2.30.2


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

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

* [PATCH v6 06/23] dt-bindings: display: rockchip: dw-hdmi: Add compatible for rk3568 HDMI
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Rob Herring, Sascha Hauer

From: Benjamin Gaignard <benjamin.gaignard@collabora.com>

Define a new compatible for rk3568 HDMI.
This version of HDMI hardware block needs two new clocks hclk_vio and hclk
to provide phy reference clocks.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 .../devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index 0400f67e5f2c9..e6b8437a1e2d1 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -23,6 +23,7 @@ properties:
       - rockchip,rk3288-dw-hdmi
       - rockchip,rk3328-dw-hdmi
       - rockchip,rk3399-dw-hdmi
+      - rockchip,rk3568-dw-hdmi
 
   reg-io-width:
     const: 4
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 07/23] drm/rockchip: dw_hdmi: add regulator support
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

The RK3568 has HDMI_TX_AVDD0V9 and HDMI_TX_AVDD_1V8 supply inputs needed
for the HDMI port. add support for these to the driver for boards which
have them supplied by switchable regulators.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 41 +++++++++++++++++++--
 1 file changed, 38 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index efaff4017086b..11acd4668ebef 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -9,6 +9,7 @@
 #include <linux/platform_device.h>
 #include <linux/phy/phy.h>
 #include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
 
 #include <drm/bridge/dw_hdmi.h>
 #include <drm/drm_edid.h>
@@ -76,6 +77,8 @@ struct rockchip_hdmi {
 	struct clk *ref_clk;
 	struct clk *grf_clk;
 	struct dw_hdmi *hdmi;
+	struct regulator *avdd_0v9;
+	struct regulator *avdd_1v8;
 	struct phy *phy;
 };
 
@@ -228,6 +231,14 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 		return PTR_ERR(hdmi->grf_clk);
 	}
 
+	hdmi->avdd_0v9 = devm_regulator_get(hdmi->dev, "avdd-0v9");
+	if (IS_ERR(hdmi->avdd_0v9))
+		return PTR_ERR(hdmi->avdd_0v9);
+
+	hdmi->avdd_1v8 = devm_regulator_get(hdmi->dev, "avdd-1v8");
+	if (IS_ERR(hdmi->avdd_1v8))
+		return PTR_ERR(hdmi->avdd_1v8);
+
 	return 0;
 }
 
@@ -568,11 +579,23 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
+	ret = regulator_enable(hdmi->avdd_0v9);
+	if (ret) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd0v9: %d\n", ret);
+		goto err_avdd_0v9;
+	}
+
+	ret = regulator_enable(hdmi->avdd_1v8);
+	if (ret) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd1v8: %d\n", ret);
+		goto err_avdd_1v8;
+	}
+
 	ret = clk_prepare_enable(hdmi->ref_clk);
 	if (ret) {
 		DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n",
 			      ret);
-		return ret;
+		goto err_clk;
 	}
 
 	if (hdmi->chip_data == &rk3568_chip_data) {
@@ -596,10 +619,19 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	 */
 	if (IS_ERR(hdmi->hdmi)) {
 		ret = PTR_ERR(hdmi->hdmi);
-		drm_encoder_cleanup(encoder);
-		clk_disable_unprepare(hdmi->ref_clk);
+		goto err_bind;
 	}
 
+	return 0;
+
+err_bind:
+	clk_disable_unprepare(hdmi->ref_clk);
+	drm_encoder_cleanup(encoder);
+err_clk:
+	regulator_disable(hdmi->avdd_1v8);
+err_avdd_1v8:
+	regulator_disable(hdmi->avdd_0v9);
+err_avdd_0v9:
 	return ret;
 }
 
@@ -610,6 +642,9 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
 
 	dw_hdmi_unbind(hdmi->hdmi);
 	clk_disable_unprepare(hdmi->ref_clk);
+
+	regulator_disable(hdmi->avdd_1v8);
+	regulator_disable(hdmi->avdd_0v9);
 }
 
 static const struct component_ops dw_hdmi_rockchip_ops = {
-- 
2.30.2


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

* [PATCH v6 07/23] drm/rockchip: dw_hdmi: add regulator support
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

The RK3568 has HDMI_TX_AVDD0V9 and HDMI_TX_AVDD_1V8 supply inputs needed
for the HDMI port. add support for these to the driver for boards which
have them supplied by switchable regulators.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 41 +++++++++++++++++++--
 1 file changed, 38 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index efaff4017086b..11acd4668ebef 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -9,6 +9,7 @@
 #include <linux/platform_device.h>
 #include <linux/phy/phy.h>
 #include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
 
 #include <drm/bridge/dw_hdmi.h>
 #include <drm/drm_edid.h>
@@ -76,6 +77,8 @@ struct rockchip_hdmi {
 	struct clk *ref_clk;
 	struct clk *grf_clk;
 	struct dw_hdmi *hdmi;
+	struct regulator *avdd_0v9;
+	struct regulator *avdd_1v8;
 	struct phy *phy;
 };
 
@@ -228,6 +231,14 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 		return PTR_ERR(hdmi->grf_clk);
 	}
 
+	hdmi->avdd_0v9 = devm_regulator_get(hdmi->dev, "avdd-0v9");
+	if (IS_ERR(hdmi->avdd_0v9))
+		return PTR_ERR(hdmi->avdd_0v9);
+
+	hdmi->avdd_1v8 = devm_regulator_get(hdmi->dev, "avdd-1v8");
+	if (IS_ERR(hdmi->avdd_1v8))
+		return PTR_ERR(hdmi->avdd_1v8);
+
 	return 0;
 }
 
@@ -568,11 +579,23 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
+	ret = regulator_enable(hdmi->avdd_0v9);
+	if (ret) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd0v9: %d\n", ret);
+		goto err_avdd_0v9;
+	}
+
+	ret = regulator_enable(hdmi->avdd_1v8);
+	if (ret) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd1v8: %d\n", ret);
+		goto err_avdd_1v8;
+	}
+
 	ret = clk_prepare_enable(hdmi->ref_clk);
 	if (ret) {
 		DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n",
 			      ret);
-		return ret;
+		goto err_clk;
 	}
 
 	if (hdmi->chip_data == &rk3568_chip_data) {
@@ -596,10 +619,19 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	 */
 	if (IS_ERR(hdmi->hdmi)) {
 		ret = PTR_ERR(hdmi->hdmi);
-		drm_encoder_cleanup(encoder);
-		clk_disable_unprepare(hdmi->ref_clk);
+		goto err_bind;
 	}
 
+	return 0;
+
+err_bind:
+	clk_disable_unprepare(hdmi->ref_clk);
+	drm_encoder_cleanup(encoder);
+err_clk:
+	regulator_disable(hdmi->avdd_1v8);
+err_avdd_1v8:
+	regulator_disable(hdmi->avdd_0v9);
+err_avdd_0v9:
 	return ret;
 }
 
@@ -610,6 +642,9 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
 
 	dw_hdmi_unbind(hdmi->hdmi);
 	clk_disable_unprepare(hdmi->ref_clk);
+
+	regulator_disable(hdmi->avdd_1v8);
+	regulator_disable(hdmi->avdd_0v9);
 }
 
 static const struct component_ops dw_hdmi_rockchip_ops = {
-- 
2.30.2


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

* [PATCH v6 07/23] drm/rockchip: dw_hdmi: add regulator support
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

The RK3568 has HDMI_TX_AVDD0V9 and HDMI_TX_AVDD_1V8 supply inputs needed
for the HDMI port. add support for these to the driver for boards which
have them supplied by switchable regulators.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 41 +++++++++++++++++++--
 1 file changed, 38 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index efaff4017086b..11acd4668ebef 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -9,6 +9,7 @@
 #include <linux/platform_device.h>
 #include <linux/phy/phy.h>
 #include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
 
 #include <drm/bridge/dw_hdmi.h>
 #include <drm/drm_edid.h>
@@ -76,6 +77,8 @@ struct rockchip_hdmi {
 	struct clk *ref_clk;
 	struct clk *grf_clk;
 	struct dw_hdmi *hdmi;
+	struct regulator *avdd_0v9;
+	struct regulator *avdd_1v8;
 	struct phy *phy;
 };
 
@@ -228,6 +231,14 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 		return PTR_ERR(hdmi->grf_clk);
 	}
 
+	hdmi->avdd_0v9 = devm_regulator_get(hdmi->dev, "avdd-0v9");
+	if (IS_ERR(hdmi->avdd_0v9))
+		return PTR_ERR(hdmi->avdd_0v9);
+
+	hdmi->avdd_1v8 = devm_regulator_get(hdmi->dev, "avdd-1v8");
+	if (IS_ERR(hdmi->avdd_1v8))
+		return PTR_ERR(hdmi->avdd_1v8);
+
 	return 0;
 }
 
@@ -568,11 +579,23 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
+	ret = regulator_enable(hdmi->avdd_0v9);
+	if (ret) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd0v9: %d\n", ret);
+		goto err_avdd_0v9;
+	}
+
+	ret = regulator_enable(hdmi->avdd_1v8);
+	if (ret) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd1v8: %d\n", ret);
+		goto err_avdd_1v8;
+	}
+
 	ret = clk_prepare_enable(hdmi->ref_clk);
 	if (ret) {
 		DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n",
 			      ret);
-		return ret;
+		goto err_clk;
 	}
 
 	if (hdmi->chip_data == &rk3568_chip_data) {
@@ -596,10 +619,19 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	 */
 	if (IS_ERR(hdmi->hdmi)) {
 		ret = PTR_ERR(hdmi->hdmi);
-		drm_encoder_cleanup(encoder);
-		clk_disable_unprepare(hdmi->ref_clk);
+		goto err_bind;
 	}
 
+	return 0;
+
+err_bind:
+	clk_disable_unprepare(hdmi->ref_clk);
+	drm_encoder_cleanup(encoder);
+err_clk:
+	regulator_disable(hdmi->avdd_1v8);
+err_avdd_1v8:
+	regulator_disable(hdmi->avdd_0v9);
+err_avdd_0v9:
 	return ret;
 }
 
@@ -610,6 +642,9 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
 
 	dw_hdmi_unbind(hdmi->hdmi);
 	clk_disable_unprepare(hdmi->ref_clk);
+
+	regulator_disable(hdmi->avdd_1v8);
+	regulator_disable(hdmi->avdd_0v9);
 }
 
 static const struct component_ops dw_hdmi_rockchip_ops = {
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 07/23] drm/rockchip: dw_hdmi: add regulator support
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

The RK3568 has HDMI_TX_AVDD0V9 and HDMI_TX_AVDD_1V8 supply inputs needed
for the HDMI port. add support for these to the driver for boards which
have them supplied by switchable regulators.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 41 +++++++++++++++++++--
 1 file changed, 38 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index efaff4017086b..11acd4668ebef 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -9,6 +9,7 @@
 #include <linux/platform_device.h>
 #include <linux/phy/phy.h>
 #include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
 
 #include <drm/bridge/dw_hdmi.h>
 #include <drm/drm_edid.h>
@@ -76,6 +77,8 @@ struct rockchip_hdmi {
 	struct clk *ref_clk;
 	struct clk *grf_clk;
 	struct dw_hdmi *hdmi;
+	struct regulator *avdd_0v9;
+	struct regulator *avdd_1v8;
 	struct phy *phy;
 };
 
@@ -228,6 +231,14 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 		return PTR_ERR(hdmi->grf_clk);
 	}
 
+	hdmi->avdd_0v9 = devm_regulator_get(hdmi->dev, "avdd-0v9");
+	if (IS_ERR(hdmi->avdd_0v9))
+		return PTR_ERR(hdmi->avdd_0v9);
+
+	hdmi->avdd_1v8 = devm_regulator_get(hdmi->dev, "avdd-1v8");
+	if (IS_ERR(hdmi->avdd_1v8))
+		return PTR_ERR(hdmi->avdd_1v8);
+
 	return 0;
 }
 
@@ -568,11 +579,23 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
+	ret = regulator_enable(hdmi->avdd_0v9);
+	if (ret) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd0v9: %d\n", ret);
+		goto err_avdd_0v9;
+	}
+
+	ret = regulator_enable(hdmi->avdd_1v8);
+	if (ret) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd1v8: %d\n", ret);
+		goto err_avdd_1v8;
+	}
+
 	ret = clk_prepare_enable(hdmi->ref_clk);
 	if (ret) {
 		DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n",
 			      ret);
-		return ret;
+		goto err_clk;
 	}
 
 	if (hdmi->chip_data == &rk3568_chip_data) {
@@ -596,10 +619,19 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	 */
 	if (IS_ERR(hdmi->hdmi)) {
 		ret = PTR_ERR(hdmi->hdmi);
-		drm_encoder_cleanup(encoder);
-		clk_disable_unprepare(hdmi->ref_clk);
+		goto err_bind;
 	}
 
+	return 0;
+
+err_bind:
+	clk_disable_unprepare(hdmi->ref_clk);
+	drm_encoder_cleanup(encoder);
+err_clk:
+	regulator_disable(hdmi->avdd_1v8);
+err_avdd_1v8:
+	regulator_disable(hdmi->avdd_0v9);
+err_avdd_0v9:
 	return ret;
 }
 
@@ -610,6 +642,9 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
 
 	dw_hdmi_unbind(hdmi->hdmi);
 	clk_disable_unprepare(hdmi->ref_clk);
+
+	regulator_disable(hdmi->avdd_1v8);
+	regulator_disable(hdmi->avdd_0v9);
 }
 
 static const struct component_ops dw_hdmi_rockchip_ops = {
-- 
2.30.2


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

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

* [PATCH v6 08/23] dt-bindings: display: rockchip: dw-hdmi: Add regulator support
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

The RK3568 has HDMI_TX_AVDD0V9 and HDMI_TX_AVDD_1V8 supply inputs
needed for the HDMI port. Add the binding for these supplies.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack

 .../bindings/display/rockchip/rockchip,dw-hdmi.yaml   | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index e6b8437a1e2d1..38ebb69830287 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -28,6 +28,17 @@ properties:
   reg-io-width:
     const: 4
 
+  avdd-0v9-supply:
+    description:
+      A 0.9V supply that powers up the SoC internal circuitry. The actual pin name
+      varies between the different SoCs and is usually HDMI_TX_AVDD_0V9 or sometimes
+      HDMI_AVDD_1V0.
+
+  avdd-1v8-supply:
+    description:
+      A 1.8V supply that powers up the SoC internal circuitry. The pin name on the
+      SoC usually is HDMI_TX_AVDD_1V8.
+
   clocks:
     minItems: 2
     items:
-- 
2.30.2


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

* [PATCH v6 08/23] dt-bindings: display: rockchip: dw-hdmi: Add regulator support
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

The RK3568 has HDMI_TX_AVDD0V9 and HDMI_TX_AVDD_1V8 supply inputs
needed for the HDMI port. Add the binding for these supplies.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack

 .../bindings/display/rockchip/rockchip,dw-hdmi.yaml   | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index e6b8437a1e2d1..38ebb69830287 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -28,6 +28,17 @@ properties:
   reg-io-width:
     const: 4
 
+  avdd-0v9-supply:
+    description:
+      A 0.9V supply that powers up the SoC internal circuitry. The actual pin name
+      varies between the different SoCs and is usually HDMI_TX_AVDD_0V9 or sometimes
+      HDMI_AVDD_1V0.
+
+  avdd-1v8-supply:
+    description:
+      A 1.8V supply that powers up the SoC internal circuitry. The pin name on the
+      SoC usually is HDMI_TX_AVDD_1V8.
+
   clocks:
     minItems: 2
     items:
-- 
2.30.2


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

* [PATCH v6 08/23] dt-bindings: display: rockchip: dw-hdmi: Add regulator support
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

The RK3568 has HDMI_TX_AVDD0V9 and HDMI_TX_AVDD_1V8 supply inputs
needed for the HDMI port. Add the binding for these supplies.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack

 .../bindings/display/rockchip/rockchip,dw-hdmi.yaml   | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index e6b8437a1e2d1..38ebb69830287 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -28,6 +28,17 @@ properties:
   reg-io-width:
     const: 4
 
+  avdd-0v9-supply:
+    description:
+      A 0.9V supply that powers up the SoC internal circuitry. The actual pin name
+      varies between the different SoCs and is usually HDMI_TX_AVDD_0V9 or sometimes
+      HDMI_AVDD_1V0.
+
+  avdd-1v8-supply:
+    description:
+      A 1.8V supply that powers up the SoC internal circuitry. The pin name on the
+      SoC usually is HDMI_TX_AVDD_1V8.
+
   clocks:
     minItems: 2
     items:
-- 
2.30.2


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

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

* [PATCH v6 08/23] dt-bindings: display: rockchip: dw-hdmi: Add regulator support
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

The RK3568 has HDMI_TX_AVDD0V9 and HDMI_TX_AVDD_1V8 supply inputs
needed for the HDMI port. Add the binding for these supplies.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack

 .../bindings/display/rockchip/rockchip,dw-hdmi.yaml   | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index e6b8437a1e2d1..38ebb69830287 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -28,6 +28,17 @@ properties:
   reg-io-width:
     const: 4
 
+  avdd-0v9-supply:
+    description:
+      A 0.9V supply that powers up the SoC internal circuitry. The actual pin name
+      varies between the different SoCs and is usually HDMI_TX_AVDD_0V9 or sometimes
+      HDMI_AVDD_1V0.
+
+  avdd-1v8-supply:
+    description:
+      A 1.8V supply that powers up the SoC internal circuitry. The pin name on the
+      SoC usually is HDMI_TX_AVDD_1V8.
+
   clocks:
     minItems: 2
     items:
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 09/23] drm/rockchip: dw_hdmi: Add support for hclk
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

The rk3568 HDMI has an additional clock that needs to be enabled for the
HDMI controller to work. The purpose of that clock is not clear. It is
named "hclk" in the downstream driver, so use the same name.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Use devm_clk_get_optional rather than devm_clk_get

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 11acd4668ebef..45f66fd613a6d 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -76,6 +76,7 @@ struct rockchip_hdmi {
 	const struct rockchip_hdmi_chip_data *chip_data;
 	struct clk *ref_clk;
 	struct clk *grf_clk;
+	struct clk *hclk_clk;
 	struct dw_hdmi *hdmi;
 	struct regulator *avdd_0v9;
 	struct regulator *avdd_1v8;
@@ -231,6 +232,14 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 		return PTR_ERR(hdmi->grf_clk);
 	}
 
+	hdmi->hclk_clk = devm_clk_get_optional(hdmi->dev, "hclk");
+	if (PTR_ERR(hdmi->hclk_clk) == -EPROBE_DEFER) {
+		return -EPROBE_DEFER;
+	} else if (IS_ERR(hdmi->hclk_clk)) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to get hclk_clk clock\n");
+		return PTR_ERR(hdmi->hclk_clk);
+	}
+
 	hdmi->avdd_0v9 = devm_regulator_get(hdmi->dev, "avdd-0v9");
 	if (IS_ERR(hdmi->avdd_0v9))
 		return PTR_ERR(hdmi->avdd_0v9);
@@ -598,6 +607,13 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		goto err_clk;
 	}
 
+	ret = clk_prepare_enable(hdmi->hclk_clk);
+	if (ret) {
+		DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI hclk clock: %d\n",
+			      ret);
+		goto err_clk;
+	}
+
 	if (hdmi->chip_data == &rk3568_chip_data) {
 		regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1,
 			     HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK |
-- 
2.30.2


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

* [PATCH v6 09/23] drm/rockchip: dw_hdmi: Add support for hclk
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

The rk3568 HDMI has an additional clock that needs to be enabled for the
HDMI controller to work. The purpose of that clock is not clear. It is
named "hclk" in the downstream driver, so use the same name.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Use devm_clk_get_optional rather than devm_clk_get

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 11acd4668ebef..45f66fd613a6d 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -76,6 +76,7 @@ struct rockchip_hdmi {
 	const struct rockchip_hdmi_chip_data *chip_data;
 	struct clk *ref_clk;
 	struct clk *grf_clk;
+	struct clk *hclk_clk;
 	struct dw_hdmi *hdmi;
 	struct regulator *avdd_0v9;
 	struct regulator *avdd_1v8;
@@ -231,6 +232,14 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 		return PTR_ERR(hdmi->grf_clk);
 	}
 
+	hdmi->hclk_clk = devm_clk_get_optional(hdmi->dev, "hclk");
+	if (PTR_ERR(hdmi->hclk_clk) == -EPROBE_DEFER) {
+		return -EPROBE_DEFER;
+	} else if (IS_ERR(hdmi->hclk_clk)) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to get hclk_clk clock\n");
+		return PTR_ERR(hdmi->hclk_clk);
+	}
+
 	hdmi->avdd_0v9 = devm_regulator_get(hdmi->dev, "avdd-0v9");
 	if (IS_ERR(hdmi->avdd_0v9))
 		return PTR_ERR(hdmi->avdd_0v9);
@@ -598,6 +607,13 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		goto err_clk;
 	}
 
+	ret = clk_prepare_enable(hdmi->hclk_clk);
+	if (ret) {
+		DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI hclk clock: %d\n",
+			      ret);
+		goto err_clk;
+	}
+
 	if (hdmi->chip_data == &rk3568_chip_data) {
 		regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1,
 			     HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK |
-- 
2.30.2


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

* [PATCH v6 09/23] drm/rockchip: dw_hdmi: Add support for hclk
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

The rk3568 HDMI has an additional clock that needs to be enabled for the
HDMI controller to work. The purpose of that clock is not clear. It is
named "hclk" in the downstream driver, so use the same name.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Use devm_clk_get_optional rather than devm_clk_get

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 11acd4668ebef..45f66fd613a6d 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -76,6 +76,7 @@ struct rockchip_hdmi {
 	const struct rockchip_hdmi_chip_data *chip_data;
 	struct clk *ref_clk;
 	struct clk *grf_clk;
+	struct clk *hclk_clk;
 	struct dw_hdmi *hdmi;
 	struct regulator *avdd_0v9;
 	struct regulator *avdd_1v8;
@@ -231,6 +232,14 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 		return PTR_ERR(hdmi->grf_clk);
 	}
 
+	hdmi->hclk_clk = devm_clk_get_optional(hdmi->dev, "hclk");
+	if (PTR_ERR(hdmi->hclk_clk) == -EPROBE_DEFER) {
+		return -EPROBE_DEFER;
+	} else if (IS_ERR(hdmi->hclk_clk)) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to get hclk_clk clock\n");
+		return PTR_ERR(hdmi->hclk_clk);
+	}
+
 	hdmi->avdd_0v9 = devm_regulator_get(hdmi->dev, "avdd-0v9");
 	if (IS_ERR(hdmi->avdd_0v9))
 		return PTR_ERR(hdmi->avdd_0v9);
@@ -598,6 +607,13 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		goto err_clk;
 	}
 
+	ret = clk_prepare_enable(hdmi->hclk_clk);
+	if (ret) {
+		DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI hclk clock: %d\n",
+			      ret);
+		goto err_clk;
+	}
+
 	if (hdmi->chip_data == &rk3568_chip_data) {
 		regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1,
 			     HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK |
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 09/23] drm/rockchip: dw_hdmi: Add support for hclk
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

The rk3568 HDMI has an additional clock that needs to be enabled for the
HDMI controller to work. The purpose of that clock is not clear. It is
named "hclk" in the downstream driver, so use the same name.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Use devm_clk_get_optional rather than devm_clk_get

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 11acd4668ebef..45f66fd613a6d 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -76,6 +76,7 @@ struct rockchip_hdmi {
 	const struct rockchip_hdmi_chip_data *chip_data;
 	struct clk *ref_clk;
 	struct clk *grf_clk;
+	struct clk *hclk_clk;
 	struct dw_hdmi *hdmi;
 	struct regulator *avdd_0v9;
 	struct regulator *avdd_1v8;
@@ -231,6 +232,14 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 		return PTR_ERR(hdmi->grf_clk);
 	}
 
+	hdmi->hclk_clk = devm_clk_get_optional(hdmi->dev, "hclk");
+	if (PTR_ERR(hdmi->hclk_clk) == -EPROBE_DEFER) {
+		return -EPROBE_DEFER;
+	} else if (IS_ERR(hdmi->hclk_clk)) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to get hclk_clk clock\n");
+		return PTR_ERR(hdmi->hclk_clk);
+	}
+
 	hdmi->avdd_0v9 = devm_regulator_get(hdmi->dev, "avdd-0v9");
 	if (IS_ERR(hdmi->avdd_0v9))
 		return PTR_ERR(hdmi->avdd_0v9);
@@ -598,6 +607,13 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		goto err_clk;
 	}
 
+	ret = clk_prepare_enable(hdmi->hclk_clk);
+	if (ret) {
+		DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI hclk clock: %d\n",
+			      ret);
+		goto err_clk;
+	}
+
 	if (hdmi->chip_data == &rk3568_chip_data) {
 		regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1,
 			     HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK |
-- 
2.30.2


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

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

* [PATCH v6 10/23] dt-bindings: display: rockchip: dw-hdmi: Add additional clock
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

The rk3568 HDMI has an additional clock that needs to be enabled for the
HDMI controller to work. The purpose of that clock is not clear. It is
named "hclk" in the downstream driver, so use the same name.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack

 .../bindings/display/rockchip/rockchip,dw-hdmi.yaml        | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index 38ebb69830287..67a76f51637a7 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -44,12 +44,13 @@ properties:
     items:
       - {}
       - {}
-      # The next three clocks are all optional, but shall be specified in this
+      # The next four clocks are all optional, but shall be specified in this
       # order when present.
       - description: The HDMI CEC controller main clock
       - description: Power for GRF IO
       - description: External clock for some HDMI PHY (old clock name, deprecated)
       - description: External clock for some HDMI PHY (new name)
+      - description: hclk
 
   clock-names:
     minItems: 2
@@ -61,13 +62,17 @@ properties:
           - grf
           - vpll
           - ref
+          - hclk
       - enum:
           - grf
           - vpll
           - ref
+          - hclk
       - enum:
           - vpll
           - ref
+          - hclk
+      - const: hclk
 
   ddc-i2c-bus:
     $ref: /schemas/types.yaml#/definitions/phandle
-- 
2.30.2


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

* [PATCH v6 10/23] dt-bindings: display: rockchip: dw-hdmi: Add additional clock
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

The rk3568 HDMI has an additional clock that needs to be enabled for the
HDMI controller to work. The purpose of that clock is not clear. It is
named "hclk" in the downstream driver, so use the same name.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack

 .../bindings/display/rockchip/rockchip,dw-hdmi.yaml        | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index 38ebb69830287..67a76f51637a7 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -44,12 +44,13 @@ properties:
     items:
       - {}
       - {}
-      # The next three clocks are all optional, but shall be specified in this
+      # The next four clocks are all optional, but shall be specified in this
       # order when present.
       - description: The HDMI CEC controller main clock
       - description: Power for GRF IO
       - description: External clock for some HDMI PHY (old clock name, deprecated)
       - description: External clock for some HDMI PHY (new name)
+      - description: hclk
 
   clock-names:
     minItems: 2
@@ -61,13 +62,17 @@ properties:
           - grf
           - vpll
           - ref
+          - hclk
       - enum:
           - grf
           - vpll
           - ref
+          - hclk
       - enum:
           - vpll
           - ref
+          - hclk
+      - const: hclk
 
   ddc-i2c-bus:
     $ref: /schemas/types.yaml#/definitions/phandle
-- 
2.30.2


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

* [PATCH v6 10/23] dt-bindings: display: rockchip: dw-hdmi: Add additional clock
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

The rk3568 HDMI has an additional clock that needs to be enabled for the
HDMI controller to work. The purpose of that clock is not clear. It is
named "hclk" in the downstream driver, so use the same name.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack

 .../bindings/display/rockchip/rockchip,dw-hdmi.yaml        | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index 38ebb69830287..67a76f51637a7 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -44,12 +44,13 @@ properties:
     items:
       - {}
       - {}
-      # The next three clocks are all optional, but shall be specified in this
+      # The next four clocks are all optional, but shall be specified in this
       # order when present.
       - description: The HDMI CEC controller main clock
       - description: Power for GRF IO
       - description: External clock for some HDMI PHY (old clock name, deprecated)
       - description: External clock for some HDMI PHY (new name)
+      - description: hclk
 
   clock-names:
     minItems: 2
@@ -61,13 +62,17 @@ properties:
           - grf
           - vpll
           - ref
+          - hclk
       - enum:
           - grf
           - vpll
           - ref
+          - hclk
       - enum:
           - vpll
           - ref
+          - hclk
+      - const: hclk
 
   ddc-i2c-bus:
     $ref: /schemas/types.yaml#/definitions/phandle
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 10/23] dt-bindings: display: rockchip: dw-hdmi: Add additional clock
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

The rk3568 HDMI has an additional clock that needs to be enabled for the
HDMI controller to work. The purpose of that clock is not clear. It is
named "hclk" in the downstream driver, so use the same name.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack

 .../bindings/display/rockchip/rockchip,dw-hdmi.yaml        | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index 38ebb69830287..67a76f51637a7 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -44,12 +44,13 @@ properties:
     items:
       - {}
       - {}
-      # The next three clocks are all optional, but shall be specified in this
+      # The next four clocks are all optional, but shall be specified in this
       # order when present.
       - description: The HDMI CEC controller main clock
       - description: Power for GRF IO
       - description: External clock for some HDMI PHY (old clock name, deprecated)
       - description: External clock for some HDMI PHY (new name)
+      - description: hclk
 
   clock-names:
     minItems: 2
@@ -61,13 +62,17 @@ properties:
           - grf
           - vpll
           - ref
+          - hclk
       - enum:
           - grf
           - vpll
           - ref
+          - hclk
       - enum:
           - vpll
           - ref
+          - hclk
+      - const: hclk
 
   ddc-i2c-bus:
     $ref: /schemas/types.yaml#/definitions/phandle
-- 
2.30.2


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

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

* [PATCH v6 11/23] drm/rockchip: dw_hdmi: Use auto-generated tables
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Douglas Anderson, Yakir Yang,
	Sascha Hauer

From: Douglas Anderson <dianders@chromium.org>

The previous tables for mpll_cfg and curr_ctrl were created using the
20-pages of example settings provided by the PHY vendor.  Those
example settings weren't particularly dense, so there were places
where we were guessing what the settings would be for 10-bit and
12-bit (not that we use those anyway).  It was also always a lot of
extra work every time we wanted to add a new clock rate since we had
to cross-reference several tables.

In <https://crrev.com/c/285855> I've gone through the work to figure
out how to generate this table automatically.  Let's now use the
automatically generated table and then we'll never need to look at it
again.

We only support 8-bit mode right now and only support a small number
of clock rates and I've verified that the only 8-bit rate that was
affected was 148.5.  That mode appears to have been wrong in the old
table.

Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Yakir Yang <ykk@rock-chips.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Add missing Signed-off-by me
    
    Changes since v3:
    - new patch

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 130 +++++++++++---------
 1 file changed, 69 insertions(+), 61 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 45f66fd613a6d..dfc0fedbcf06c 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -92,80 +92,88 @@ static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
 
 static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 	{
-		27000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+		30666000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2153, 0x0000 },
+			{ 0x40f3, 0x0000 },
 		},
-	}, {
-		36000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+	},  {
+		36800000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2153, 0x0000 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		40000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+	},  {
+		46000000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2142, 0x0001 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		54000000, {
-			{ 0x0072, 0x0001},
-			{ 0x2142, 0x0001},
-			{ 0x40a2, 0x0001},
+	},  {
+		61333000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2142, 0x0001 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		65000000, {
-			{ 0x0072, 0x0001},
-			{ 0x2142, 0x0001},
-			{ 0x40a2, 0x0001},
+	},  {
+		73600000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2142, 0x0001 },
+			{ 0x4061, 0x0002 },
 		},
-	}, {
-		66000000, {
-			{ 0x013e, 0x0003},
-			{ 0x217e, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		92000000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2145, 0x0002 },
+			{ 0x4061, 0x0002 },
 		},
-	}, {
-		74250000, {
-			{ 0x0072, 0x0001},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		122666000, {
+			{ 0x0051, 0x0002 },
+			{ 0x2145, 0x0002 },
+			{ 0x4061, 0x0002 },
 		},
-	}, {
-		83500000, {
-			{ 0x0072, 0x0001},
+	},  {
+		147200000, {
+			{ 0x0051, 0x0002 },
+			{ 0x2145, 0x0002 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		108000000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		184000000, {
+			{ 0x0051, 0x0002 },
+			{ 0x214c, 0x0003 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		106500000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		226666000, {
+			{ 0x0040, 0x0003 },
+			{ 0x214c, 0x0003 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		146250000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		272000000, {
+			{ 0x0040, 0x0003 },
+			{ 0x214c, 0x0003 },
+			{ 0x5a64, 0x0003 },
 		},
-	}, {
-		148500000, {
-			{ 0x0051, 0x0003},
-			{ 0x214c, 0x0003},
-			{ 0x4064, 0x0003}
+	},  {
+		340000000, {
+			{ 0x0040, 0x0003 },
+			{ 0x3b4c, 0x0003 },
+			{ 0x5a64, 0x0003 },
 		},
-	}, {
+	},  {
+		600000000, {
+			{ 0x1a40, 0x0003 },
+			{ 0x3b4c, 0x0003 },
+			{ 0x5a64, 0x0003 },
+		},
+	},  {
 		~0UL, {
-			{ 0x00a0, 0x000a },
-			{ 0x2001, 0x000f },
-			{ 0x4002, 0x000f },
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
 		},
 	}
 };
-- 
2.30.2


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

* [PATCH v6 11/23] drm/rockchip: dw_hdmi: Use auto-generated tables
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, Douglas Anderson, linux-rockchip, Michael Riesch,
	kernel, Yakir Yang, Andy Yan, linux-arm-kernel

From: Douglas Anderson <dianders@chromium.org>

The previous tables for mpll_cfg and curr_ctrl were created using the
20-pages of example settings provided by the PHY vendor.  Those
example settings weren't particularly dense, so there were places
where we were guessing what the settings would be for 10-bit and
12-bit (not that we use those anyway).  It was also always a lot of
extra work every time we wanted to add a new clock rate since we had
to cross-reference several tables.

In <https://crrev.com/c/285855> I've gone through the work to figure
out how to generate this table automatically.  Let's now use the
automatically generated table and then we'll never need to look at it
again.

We only support 8-bit mode right now and only support a small number
of clock rates and I've verified that the only 8-bit rate that was
affected was 148.5.  That mode appears to have been wrong in the old
table.

Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Yakir Yang <ykk@rock-chips.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Add missing Signed-off-by me
    
    Changes since v3:
    - new patch

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 130 +++++++++++---------
 1 file changed, 69 insertions(+), 61 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 45f66fd613a6d..dfc0fedbcf06c 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -92,80 +92,88 @@ static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
 
 static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 	{
-		27000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+		30666000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2153, 0x0000 },
+			{ 0x40f3, 0x0000 },
 		},
-	}, {
-		36000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+	},  {
+		36800000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2153, 0x0000 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		40000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+	},  {
+		46000000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2142, 0x0001 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		54000000, {
-			{ 0x0072, 0x0001},
-			{ 0x2142, 0x0001},
-			{ 0x40a2, 0x0001},
+	},  {
+		61333000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2142, 0x0001 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		65000000, {
-			{ 0x0072, 0x0001},
-			{ 0x2142, 0x0001},
-			{ 0x40a2, 0x0001},
+	},  {
+		73600000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2142, 0x0001 },
+			{ 0x4061, 0x0002 },
 		},
-	}, {
-		66000000, {
-			{ 0x013e, 0x0003},
-			{ 0x217e, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		92000000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2145, 0x0002 },
+			{ 0x4061, 0x0002 },
 		},
-	}, {
-		74250000, {
-			{ 0x0072, 0x0001},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		122666000, {
+			{ 0x0051, 0x0002 },
+			{ 0x2145, 0x0002 },
+			{ 0x4061, 0x0002 },
 		},
-	}, {
-		83500000, {
-			{ 0x0072, 0x0001},
+	},  {
+		147200000, {
+			{ 0x0051, 0x0002 },
+			{ 0x2145, 0x0002 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		108000000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		184000000, {
+			{ 0x0051, 0x0002 },
+			{ 0x214c, 0x0003 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		106500000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		226666000, {
+			{ 0x0040, 0x0003 },
+			{ 0x214c, 0x0003 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		146250000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		272000000, {
+			{ 0x0040, 0x0003 },
+			{ 0x214c, 0x0003 },
+			{ 0x5a64, 0x0003 },
 		},
-	}, {
-		148500000, {
-			{ 0x0051, 0x0003},
-			{ 0x214c, 0x0003},
-			{ 0x4064, 0x0003}
+	},  {
+		340000000, {
+			{ 0x0040, 0x0003 },
+			{ 0x3b4c, 0x0003 },
+			{ 0x5a64, 0x0003 },
 		},
-	}, {
+	},  {
+		600000000, {
+			{ 0x1a40, 0x0003 },
+			{ 0x3b4c, 0x0003 },
+			{ 0x5a64, 0x0003 },
+		},
+	},  {
 		~0UL, {
-			{ 0x00a0, 0x000a },
-			{ 0x2001, 0x000f },
-			{ 0x4002, 0x000f },
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
 		},
 	}
 };
-- 
2.30.2


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

* [PATCH v6 11/23] drm/rockchip: dw_hdmi: Use auto-generated tables
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Douglas Anderson, Yakir Yang,
	Sascha Hauer

From: Douglas Anderson <dianders@chromium.org>

The previous tables for mpll_cfg and curr_ctrl were created using the
20-pages of example settings provided by the PHY vendor.  Those
example settings weren't particularly dense, so there were places
where we were guessing what the settings would be for 10-bit and
12-bit (not that we use those anyway).  It was also always a lot of
extra work every time we wanted to add a new clock rate since we had
to cross-reference several tables.

In <https://crrev.com/c/285855> I've gone through the work to figure
out how to generate this table automatically.  Let's now use the
automatically generated table and then we'll never need to look at it
again.

We only support 8-bit mode right now and only support a small number
of clock rates and I've verified that the only 8-bit rate that was
affected was 148.5.  That mode appears to have been wrong in the old
table.

Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Yakir Yang <ykk@rock-chips.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Add missing Signed-off-by me
    
    Changes since v3:
    - new patch

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 130 +++++++++++---------
 1 file changed, 69 insertions(+), 61 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 45f66fd613a6d..dfc0fedbcf06c 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -92,80 +92,88 @@ static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
 
 static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 	{
-		27000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+		30666000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2153, 0x0000 },
+			{ 0x40f3, 0x0000 },
 		},
-	}, {
-		36000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+	},  {
+		36800000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2153, 0x0000 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		40000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+	},  {
+		46000000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2142, 0x0001 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		54000000, {
-			{ 0x0072, 0x0001},
-			{ 0x2142, 0x0001},
-			{ 0x40a2, 0x0001},
+	},  {
+		61333000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2142, 0x0001 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		65000000, {
-			{ 0x0072, 0x0001},
-			{ 0x2142, 0x0001},
-			{ 0x40a2, 0x0001},
+	},  {
+		73600000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2142, 0x0001 },
+			{ 0x4061, 0x0002 },
 		},
-	}, {
-		66000000, {
-			{ 0x013e, 0x0003},
-			{ 0x217e, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		92000000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2145, 0x0002 },
+			{ 0x4061, 0x0002 },
 		},
-	}, {
-		74250000, {
-			{ 0x0072, 0x0001},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		122666000, {
+			{ 0x0051, 0x0002 },
+			{ 0x2145, 0x0002 },
+			{ 0x4061, 0x0002 },
 		},
-	}, {
-		83500000, {
-			{ 0x0072, 0x0001},
+	},  {
+		147200000, {
+			{ 0x0051, 0x0002 },
+			{ 0x2145, 0x0002 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		108000000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		184000000, {
+			{ 0x0051, 0x0002 },
+			{ 0x214c, 0x0003 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		106500000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		226666000, {
+			{ 0x0040, 0x0003 },
+			{ 0x214c, 0x0003 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		146250000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		272000000, {
+			{ 0x0040, 0x0003 },
+			{ 0x214c, 0x0003 },
+			{ 0x5a64, 0x0003 },
 		},
-	}, {
-		148500000, {
-			{ 0x0051, 0x0003},
-			{ 0x214c, 0x0003},
-			{ 0x4064, 0x0003}
+	},  {
+		340000000, {
+			{ 0x0040, 0x0003 },
+			{ 0x3b4c, 0x0003 },
+			{ 0x5a64, 0x0003 },
 		},
-	}, {
+	},  {
+		600000000, {
+			{ 0x1a40, 0x0003 },
+			{ 0x3b4c, 0x0003 },
+			{ 0x5a64, 0x0003 },
+		},
+	},  {
 		~0UL, {
-			{ 0x00a0, 0x000a },
-			{ 0x2001, 0x000f },
-			{ 0x4002, 0x000f },
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
 		},
 	}
 };
-- 
2.30.2


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

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

* [PATCH v6 11/23] drm/rockchip: dw_hdmi: Use auto-generated tables
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Douglas Anderson, Yakir Yang,
	Sascha Hauer

From: Douglas Anderson <dianders@chromium.org>

The previous tables for mpll_cfg and curr_ctrl were created using the
20-pages of example settings provided by the PHY vendor.  Those
example settings weren't particularly dense, so there were places
where we were guessing what the settings would be for 10-bit and
12-bit (not that we use those anyway).  It was also always a lot of
extra work every time we wanted to add a new clock rate since we had
to cross-reference several tables.

In <https://crrev.com/c/285855> I've gone through the work to figure
out how to generate this table automatically.  Let's now use the
automatically generated table and then we'll never need to look at it
again.

We only support 8-bit mode right now and only support a small number
of clock rates and I've verified that the only 8-bit rate that was
affected was 148.5.  That mode appears to have been wrong in the old
table.

Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Yakir Yang <ykk@rock-chips.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Add missing Signed-off-by me
    
    Changes since v3:
    - new patch

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 130 +++++++++++---------
 1 file changed, 69 insertions(+), 61 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 45f66fd613a6d..dfc0fedbcf06c 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -92,80 +92,88 @@ static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
 
 static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 	{
-		27000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+		30666000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2153, 0x0000 },
+			{ 0x40f3, 0x0000 },
 		},
-	}, {
-		36000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+	},  {
+		36800000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2153, 0x0000 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		40000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+	},  {
+		46000000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2142, 0x0001 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		54000000, {
-			{ 0x0072, 0x0001},
-			{ 0x2142, 0x0001},
-			{ 0x40a2, 0x0001},
+	},  {
+		61333000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2142, 0x0001 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		65000000, {
-			{ 0x0072, 0x0001},
-			{ 0x2142, 0x0001},
-			{ 0x40a2, 0x0001},
+	},  {
+		73600000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2142, 0x0001 },
+			{ 0x4061, 0x0002 },
 		},
-	}, {
-		66000000, {
-			{ 0x013e, 0x0003},
-			{ 0x217e, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		92000000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2145, 0x0002 },
+			{ 0x4061, 0x0002 },
 		},
-	}, {
-		74250000, {
-			{ 0x0072, 0x0001},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		122666000, {
+			{ 0x0051, 0x0002 },
+			{ 0x2145, 0x0002 },
+			{ 0x4061, 0x0002 },
 		},
-	}, {
-		83500000, {
-			{ 0x0072, 0x0001},
+	},  {
+		147200000, {
+			{ 0x0051, 0x0002 },
+			{ 0x2145, 0x0002 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		108000000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		184000000, {
+			{ 0x0051, 0x0002 },
+			{ 0x214c, 0x0003 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		106500000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		226666000, {
+			{ 0x0040, 0x0003 },
+			{ 0x214c, 0x0003 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		146250000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		272000000, {
+			{ 0x0040, 0x0003 },
+			{ 0x214c, 0x0003 },
+			{ 0x5a64, 0x0003 },
 		},
-	}, {
-		148500000, {
-			{ 0x0051, 0x0003},
-			{ 0x214c, 0x0003},
-			{ 0x4064, 0x0003}
+	},  {
+		340000000, {
+			{ 0x0040, 0x0003 },
+			{ 0x3b4c, 0x0003 },
+			{ 0x5a64, 0x0003 },
 		},
-	}, {
+	},  {
+		600000000, {
+			{ 0x1a40, 0x0003 },
+			{ 0x3b4c, 0x0003 },
+			{ 0x5a64, 0x0003 },
+		},
+	},  {
 		~0UL, {
-			{ 0x00a0, 0x000a },
-			{ 0x2001, 0x000f },
-			{ 0x4002, 0x000f },
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
 		},
 	}
 };
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 12/23] drm/rockchip: dw_hdmi: drop mode_valid hook
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

The driver checks if the pixel clock of the given mode matches an entry
in the mpll config table. The frequencies in the mpll table are meant as
a frequency range up to which the entry works, not as a frequency that
must match the pixel clock. The downstream Kernel also does not have
this check, so drop it to allow for more display resolutions.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v3:
    - new patch

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 25 ---------------------
 1 file changed, 25 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index dfc0fedbcf06c..b48f137334ad6 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -259,26 +259,6 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 	return 0;
 }
 
-static enum drm_mode_status
-dw_hdmi_rockchip_mode_valid(struct dw_hdmi *hdmi, void *data,
-			    const struct drm_display_info *info,
-			    const struct drm_display_mode *mode)
-{
-	const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
-	int pclk = mode->clock * 1000;
-	bool valid = false;
-	int i;
-
-	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
-		if (pclk == mpll_cfg[i].mpixelclock) {
-			valid = true;
-			break;
-		}
-	}
-
-	return (valid) ? MODE_OK : MODE_BAD;
-}
-
 static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
 {
 }
@@ -444,7 +424,6 @@ static struct rockchip_hdmi_chip_data rk3228_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg = rockchip_mpll_cfg,
 	.cur_ctr = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -461,7 +440,6 @@ static struct rockchip_hdmi_chip_data rk3288_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -481,7 +459,6 @@ static struct rockchip_hdmi_chip_data rk3328_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg = rockchip_mpll_cfg,
 	.cur_ctr = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -499,7 +476,6 @@ static struct rockchip_hdmi_chip_data rk3399_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -512,7 +488,6 @@ static struct rockchip_hdmi_chip_data rk3568_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
-- 
2.30.2


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

* [PATCH v6 12/23] drm/rockchip: dw_hdmi: drop mode_valid hook
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

The driver checks if the pixel clock of the given mode matches an entry
in the mpll config table. The frequencies in the mpll table are meant as
a frequency range up to which the entry works, not as a frequency that
must match the pixel clock. The downstream Kernel also does not have
this check, so drop it to allow for more display resolutions.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v3:
    - new patch

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 25 ---------------------
 1 file changed, 25 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index dfc0fedbcf06c..b48f137334ad6 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -259,26 +259,6 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 	return 0;
 }
 
-static enum drm_mode_status
-dw_hdmi_rockchip_mode_valid(struct dw_hdmi *hdmi, void *data,
-			    const struct drm_display_info *info,
-			    const struct drm_display_mode *mode)
-{
-	const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
-	int pclk = mode->clock * 1000;
-	bool valid = false;
-	int i;
-
-	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
-		if (pclk == mpll_cfg[i].mpixelclock) {
-			valid = true;
-			break;
-		}
-	}
-
-	return (valid) ? MODE_OK : MODE_BAD;
-}
-
 static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
 {
 }
@@ -444,7 +424,6 @@ static struct rockchip_hdmi_chip_data rk3228_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg = rockchip_mpll_cfg,
 	.cur_ctr = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -461,7 +440,6 @@ static struct rockchip_hdmi_chip_data rk3288_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -481,7 +459,6 @@ static struct rockchip_hdmi_chip_data rk3328_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg = rockchip_mpll_cfg,
 	.cur_ctr = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -499,7 +476,6 @@ static struct rockchip_hdmi_chip_data rk3399_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -512,7 +488,6 @@ static struct rockchip_hdmi_chip_data rk3568_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
-- 
2.30.2


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

* [PATCH v6 12/23] drm/rockchip: dw_hdmi: drop mode_valid hook
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

The driver checks if the pixel clock of the given mode matches an entry
in the mpll config table. The frequencies in the mpll table are meant as
a frequency range up to which the entry works, not as a frequency that
must match the pixel clock. The downstream Kernel also does not have
this check, so drop it to allow for more display resolutions.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v3:
    - new patch

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 25 ---------------------
 1 file changed, 25 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index dfc0fedbcf06c..b48f137334ad6 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -259,26 +259,6 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 	return 0;
 }
 
-static enum drm_mode_status
-dw_hdmi_rockchip_mode_valid(struct dw_hdmi *hdmi, void *data,
-			    const struct drm_display_info *info,
-			    const struct drm_display_mode *mode)
-{
-	const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
-	int pclk = mode->clock * 1000;
-	bool valid = false;
-	int i;
-
-	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
-		if (pclk == mpll_cfg[i].mpixelclock) {
-			valid = true;
-			break;
-		}
-	}
-
-	return (valid) ? MODE_OK : MODE_BAD;
-}
-
 static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
 {
 }
@@ -444,7 +424,6 @@ static struct rockchip_hdmi_chip_data rk3228_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg = rockchip_mpll_cfg,
 	.cur_ctr = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -461,7 +440,6 @@ static struct rockchip_hdmi_chip_data rk3288_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -481,7 +459,6 @@ static struct rockchip_hdmi_chip_data rk3328_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg = rockchip_mpll_cfg,
 	.cur_ctr = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -499,7 +476,6 @@ static struct rockchip_hdmi_chip_data rk3399_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -512,7 +488,6 @@ static struct rockchip_hdmi_chip_data rk3568_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
-- 
2.30.2


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

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

* [PATCH v6 12/23] drm/rockchip: dw_hdmi: drop mode_valid hook
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

The driver checks if the pixel clock of the given mode matches an entry
in the mpll config table. The frequencies in the mpll table are meant as
a frequency range up to which the entry works, not as a frequency that
must match the pixel clock. The downstream Kernel also does not have
this check, so drop it to allow for more display resolutions.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v3:
    - new patch

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 25 ---------------------
 1 file changed, 25 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index dfc0fedbcf06c..b48f137334ad6 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -259,26 +259,6 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 	return 0;
 }
 
-static enum drm_mode_status
-dw_hdmi_rockchip_mode_valid(struct dw_hdmi *hdmi, void *data,
-			    const struct drm_display_info *info,
-			    const struct drm_display_mode *mode)
-{
-	const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
-	int pclk = mode->clock * 1000;
-	bool valid = false;
-	int i;
-
-	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
-		if (pclk == mpll_cfg[i].mpixelclock) {
-			valid = true;
-			break;
-		}
-	}
-
-	return (valid) ? MODE_OK : MODE_BAD;
-}
-
 static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
 {
 }
@@ -444,7 +424,6 @@ static struct rockchip_hdmi_chip_data rk3228_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg = rockchip_mpll_cfg,
 	.cur_ctr = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -461,7 +440,6 @@ static struct rockchip_hdmi_chip_data rk3288_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -481,7 +459,6 @@ static struct rockchip_hdmi_chip_data rk3328_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg = rockchip_mpll_cfg,
 	.cur_ctr = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -499,7 +476,6 @@ static struct rockchip_hdmi_chip_data rk3399_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -512,7 +488,6 @@ static struct rockchip_hdmi_chip_data rk3568_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 13/23] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Douglas Anderson, Yakir Yang,
	Sascha Hauer

From: Douglas Anderson <dianders@chromium.org>

Jitter was improved by lowering the MPLL bandwidth to account for high
frequency noise in the rk3288 PLL.  In each case MPLL bandwidth was
lowered only enough to get us a comfortable margin.  We believe that
lowering the bandwidth like this is safe given sufficient testing.

Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Yakir Yang <ykk@rock-chips.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v3:
    - new patch

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 16 ++--------------
 1 file changed, 2 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index b48f137334ad6..4f2aff4b512d8 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -181,20 +181,8 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
 	/*      pixelclk    bpp8    bpp10   bpp12 */
 	{
-		40000000,  { 0x0018, 0x0018, 0x0018 },
-	}, {
-		65000000,  { 0x0028, 0x0028, 0x0028 },
-	}, {
-		66000000,  { 0x0038, 0x0038, 0x0038 },
-	}, {
-		74250000,  { 0x0028, 0x0038, 0x0038 },
-	}, {
-		83500000,  { 0x0028, 0x0038, 0x0038 },
-	}, {
-		146250000, { 0x0038, 0x0038, 0x0038 },
-	}, {
-		148500000, { 0x0000, 0x0038, 0x0038 },
-	}, {
+		600000000, { 0x0000, 0x0000, 0x0000 },
+	},  {
 		~0UL,      { 0x0000, 0x0000, 0x0000},
 	}
 };
-- 
2.30.2


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

* [PATCH v6 13/23] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, Douglas Anderson, linux-rockchip, Michael Riesch,
	kernel, Yakir Yang, Andy Yan, linux-arm-kernel

From: Douglas Anderson <dianders@chromium.org>

Jitter was improved by lowering the MPLL bandwidth to account for high
frequency noise in the rk3288 PLL.  In each case MPLL bandwidth was
lowered only enough to get us a comfortable margin.  We believe that
lowering the bandwidth like this is safe given sufficient testing.

Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Yakir Yang <ykk@rock-chips.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v3:
    - new patch

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 16 ++--------------
 1 file changed, 2 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index b48f137334ad6..4f2aff4b512d8 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -181,20 +181,8 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
 	/*      pixelclk    bpp8    bpp10   bpp12 */
 	{
-		40000000,  { 0x0018, 0x0018, 0x0018 },
-	}, {
-		65000000,  { 0x0028, 0x0028, 0x0028 },
-	}, {
-		66000000,  { 0x0038, 0x0038, 0x0038 },
-	}, {
-		74250000,  { 0x0028, 0x0038, 0x0038 },
-	}, {
-		83500000,  { 0x0028, 0x0038, 0x0038 },
-	}, {
-		146250000, { 0x0038, 0x0038, 0x0038 },
-	}, {
-		148500000, { 0x0000, 0x0038, 0x0038 },
-	}, {
+		600000000, { 0x0000, 0x0000, 0x0000 },
+	},  {
 		~0UL,      { 0x0000, 0x0000, 0x0000},
 	}
 };
-- 
2.30.2


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

* [PATCH v6 13/23] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Douglas Anderson, Yakir Yang,
	Sascha Hauer

From: Douglas Anderson <dianders@chromium.org>

Jitter was improved by lowering the MPLL bandwidth to account for high
frequency noise in the rk3288 PLL.  In each case MPLL bandwidth was
lowered only enough to get us a comfortable margin.  We believe that
lowering the bandwidth like this is safe given sufficient testing.

Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Yakir Yang <ykk@rock-chips.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v3:
    - new patch

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 16 ++--------------
 1 file changed, 2 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index b48f137334ad6..4f2aff4b512d8 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -181,20 +181,8 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
 	/*      pixelclk    bpp8    bpp10   bpp12 */
 	{
-		40000000,  { 0x0018, 0x0018, 0x0018 },
-	}, {
-		65000000,  { 0x0028, 0x0028, 0x0028 },
-	}, {
-		66000000,  { 0x0038, 0x0038, 0x0038 },
-	}, {
-		74250000,  { 0x0028, 0x0038, 0x0038 },
-	}, {
-		83500000,  { 0x0028, 0x0038, 0x0038 },
-	}, {
-		146250000, { 0x0038, 0x0038, 0x0038 },
-	}, {
-		148500000, { 0x0000, 0x0038, 0x0038 },
-	}, {
+		600000000, { 0x0000, 0x0000, 0x0000 },
+	},  {
 		~0UL,      { 0x0000, 0x0000, 0x0000},
 	}
 };
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 13/23] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Douglas Anderson, Yakir Yang,
	Sascha Hauer

From: Douglas Anderson <dianders@chromium.org>

Jitter was improved by lowering the MPLL bandwidth to account for high
frequency noise in the rk3288 PLL.  In each case MPLL bandwidth was
lowered only enough to get us a comfortable margin.  We believe that
lowering the bandwidth like this is safe given sufficient testing.

Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Yakir Yang <ykk@rock-chips.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v3:
    - new patch

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 16 ++--------------
 1 file changed, 2 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index b48f137334ad6..4f2aff4b512d8 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -181,20 +181,8 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
 	/*      pixelclk    bpp8    bpp10   bpp12 */
 	{
-		40000000,  { 0x0018, 0x0018, 0x0018 },
-	}, {
-		65000000,  { 0x0028, 0x0028, 0x0028 },
-	}, {
-		66000000,  { 0x0038, 0x0038, 0x0038 },
-	}, {
-		74250000,  { 0x0028, 0x0038, 0x0038 },
-	}, {
-		83500000,  { 0x0028, 0x0038, 0x0038 },
-	}, {
-		146250000, { 0x0038, 0x0038, 0x0038 },
-	}, {
-		148500000, { 0x0000, 0x0038, 0x0038 },
-	}, {
+		600000000, { 0x0000, 0x0000, 0x0000 },
+	},  {
 		~0UL,      { 0x0000, 0x0000, 0x0000},
 	}
 };
-- 
2.30.2


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

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

* [PATCH v6 14/23] drm/rockchip: dw_hdmi: add default 594Mhz clk for 4K@60hz
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Nickey Yang, Sascha Hauer

From: Nickey Yang <nickey.yang@rock-chips.com>

add 594Mhz configuration parameters in rockchip_phy_config

Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v3:
    - new patch

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 4f2aff4b512d8..2c9e3d82fbc69 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -192,6 +192,7 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
 	{ 74250000,  0x8009, 0x0004, 0x0272},
 	{ 148500000, 0x802b, 0x0004, 0x028d},
 	{ 297000000, 0x8039, 0x0005, 0x028d},
+	{ 594000000, 0x8039, 0x0000, 0x019d},
 	{ ~0UL,	     0x0000, 0x0000, 0x0000}
 };
 
-- 
2.30.2


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

* [PATCH v6 14/23] drm/rockchip: dw_hdmi: add default 594Mhz clk for 4K@60hz
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Nickey Yang, Michael Riesch, kernel,
	Andy Yan, linux-arm-kernel

From: Nickey Yang <nickey.yang@rock-chips.com>

add 594Mhz configuration parameters in rockchip_phy_config

Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v3:
    - new patch

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 4f2aff4b512d8..2c9e3d82fbc69 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -192,6 +192,7 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
 	{ 74250000,  0x8009, 0x0004, 0x0272},
 	{ 148500000, 0x802b, 0x0004, 0x028d},
 	{ 297000000, 0x8039, 0x0005, 0x028d},
+	{ 594000000, 0x8039, 0x0000, 0x019d},
 	{ ~0UL,	     0x0000, 0x0000, 0x0000}
 };
 
-- 
2.30.2


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

* [PATCH v6 14/23] drm/rockchip: dw_hdmi: add default 594Mhz clk for 4K@60hz
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Nickey Yang, Sascha Hauer

From: Nickey Yang <nickey.yang@rock-chips.com>

add 594Mhz configuration parameters in rockchip_phy_config

Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v3:
    - new patch

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 4f2aff4b512d8..2c9e3d82fbc69 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -192,6 +192,7 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
 	{ 74250000,  0x8009, 0x0004, 0x0272},
 	{ 148500000, 0x802b, 0x0004, 0x028d},
 	{ 297000000, 0x8039, 0x0005, 0x028d},
+	{ 594000000, 0x8039, 0x0000, 0x019d},
 	{ ~0UL,	     0x0000, 0x0000, 0x0000}
 };
 
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 14/23] drm/rockchip: dw_hdmi: add default 594Mhz clk for 4K@60hz
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Nickey Yang, Sascha Hauer

From: Nickey Yang <nickey.yang@rock-chips.com>

add 594Mhz configuration parameters in rockchip_phy_config

Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v3:
    - new patch

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 4f2aff4b512d8..2c9e3d82fbc69 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -192,6 +192,7 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
 	{ 74250000,  0x8009, 0x0004, 0x0272},
 	{ 148500000, 0x802b, 0x0004, 0x028d},
 	{ 297000000, 0x8039, 0x0005, 0x028d},
+	{ 594000000, 0x8039, 0x0000, 0x019d},
 	{ ~0UL,	     0x0000, 0x0000, 0x0000}
 };
 
-- 
2.30.2


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

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

* [PATCH v6 15/23] dt-bindings: display: rockchip: dw-hdmi: Make unwedge pinctrl optional
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

None of the upstream device tree files has a "unwedge" pinctrl
specified. Make it optional.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack

 .../devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index 67a76f51637a7..7dd753630b46a 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -94,6 +94,7 @@ properties:
       The unwedge pinctrl entry shall drive the DDC SDA line low. This is
       intended to work around a hardware errata that can cause the DDC I2C
       bus to be wedged.
+    minItems: 1
     items:
       - const: default
       - const: unwedge
-- 
2.30.2


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

* [PATCH v6 15/23] dt-bindings: display: rockchip: dw-hdmi: Make unwedge pinctrl optional
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

None of the upstream device tree files has a "unwedge" pinctrl
specified. Make it optional.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack

 .../devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index 67a76f51637a7..7dd753630b46a 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -94,6 +94,7 @@ properties:
       The unwedge pinctrl entry shall drive the DDC SDA line low. This is
       intended to work around a hardware errata that can cause the DDC I2C
       bus to be wedged.
+    minItems: 1
     items:
       - const: default
       - const: unwedge
-- 
2.30.2


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

* [PATCH v6 15/23] dt-bindings: display: rockchip: dw-hdmi: Make unwedge pinctrl optional
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

None of the upstream device tree files has a "unwedge" pinctrl
specified. Make it optional.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack

 .../devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index 67a76f51637a7..7dd753630b46a 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -94,6 +94,7 @@ properties:
       The unwedge pinctrl entry shall drive the DDC SDA line low. This is
       intended to work around a hardware errata that can cause the DDC I2C
       bus to be wedged.
+    minItems: 1
     items:
       - const: default
       - const: unwedge
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 15/23] dt-bindings: display: rockchip: dw-hdmi: Make unwedge pinctrl optional
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

None of the upstream device tree files has a "unwedge" pinctrl
specified. Make it optional.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack

 .../devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index 67a76f51637a7..7dd753630b46a 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -94,6 +94,7 @@ properties:
       The unwedge pinctrl entry shall drive the DDC SDA line low. This is
       intended to work around a hardware errata that can cause the DDC I2C
       bus to be wedged.
+    minItems: 1
     items:
       - const: default
       - const: unwedge
-- 
2.30.2


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

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

* [PATCH v6 16/23] arm64: dts: rockchip: rk356x: Add VOP2 nodes
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

The VOP2 is the display output controller on the RK3568. Add the node
for it to the dtsi file along with the required display-subsystem node
and the iommu node.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack
    
    Changes since v3:
    - Bring back gamma_lut regs
    - Drop redundant _vop suffix from clock names

 arch/arm64/boot/dts/rockchip/rk3566.dtsi |  4 ++
 arch/arm64/boot/dts/rockchip/rk3568.dtsi |  4 ++
 arch/arm64/boot/dts/rockchip/rk356x.dtsi | 52 ++++++++++++++++++++++++
 include/dt-bindings/soc/rockchip,vop2.h  | 14 +++++++
 4 files changed, 74 insertions(+)
 create mode 100644 include/dt-bindings/soc/rockchip,vop2.h

diff --git a/arch/arm64/boot/dts/rockchip/rk3566.dtsi b/arch/arm64/boot/dts/rockchip/rk3566.dtsi
index 3839eef5e4f76..595fa2562cb8e 100644
--- a/arch/arm64/boot/dts/rockchip/rk3566.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3566.dtsi
@@ -18,3 +18,7 @@ power-domain@RK3568_PD_PIPE {
 		#power-domain-cells = <0>;
 	};
 };
+
+&vop {
+	compatible = "rockchip,rk3566-vop";
+};
diff --git a/arch/arm64/boot/dts/rockchip/rk3568.dtsi b/arch/arm64/boot/dts/rockchip/rk3568.dtsi
index 2fd313a295f8a..1e55efb6fcfde 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3568.dtsi
@@ -95,3 +95,7 @@ power-domain@RK3568_PD_PIPE {
 		#power-domain-cells = <0>;
 	};
 };
+
+&vop {
+	compatible = "rockchip,rk3568-vop";
+};
diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
index a68033a239750..1c24e4219c75e 100644
--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
@@ -10,6 +10,7 @@
 #include <dt-bindings/pinctrl/rockchip.h>
 #include <dt-bindings/power/rk3568-power.h>
 #include <dt-bindings/soc/rockchip,boot-mode.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
 #include <dt-bindings/thermal/thermal.h>
 
 / {
@@ -129,6 +130,11 @@ opp-1800000000 {
 		};
 	};
 
+	display_subsystem: display-subsystem {
+		compatible = "rockchip,display-subsystem";
+		ports = <&vop_out>;
+	};
+
 	firmware {
 		scmi: scmi {
 			compatible = "arm,scmi-smc";
@@ -451,6 +457,52 @@ gmac1_mtl_tx_setup: tx-queues-config {
 		};
 	};
 
+	vop: vop@fe040000 {
+		reg = <0x0 0xfe040000 0x0 0x3000>, <0x0 0xfe044000 0x0 0x1000>;
+		reg-names = "regs", "gamma_lut";
+		interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru ACLK_VOP>, <&cru HCLK_VOP>, <&cru DCLK_VOP0>,
+			 <&cru DCLK_VOP1>, <&cru DCLK_VOP2>;
+		clock-names = "aclk", "hclk", "dclk_vp0", "dclk_vp1", "dclk_vp2";
+		iommus = <&vop_mmu>;
+		power-domains = <&power RK3568_PD_VO>;
+		rockchip,grf = <&grf>;
+		status = "disabled";
+
+		vop_out: ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			vp0: port@0 {
+				reg = <0>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+			};
+
+			vp1: port@1 {
+				reg = <1>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+			};
+
+			vp2: port@2 {
+				reg = <2>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+			};
+		};
+	};
+
+	vop_mmu: iommu@fe043e00 {
+		compatible = "rockchip,rk3568-iommu";
+		reg = <0x0 0xfe043e00 0x0 0x100>, <0x0 0xfe043f00 0x0 0x100>;
+		interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru ACLK_VOP>, <&cru HCLK_VOP>;
+		clock-names = "aclk", "iface";
+		#iommu-cells = <0>;
+		status = "disabled";
+	};
+
 	qos_gpu: qos@fe128000 {
 		compatible = "rockchip,rk3568-qos", "syscon";
 		reg = <0x0 0xfe128000 0x0 0x20>;
diff --git a/include/dt-bindings/soc/rockchip,vop2.h b/include/dt-bindings/soc/rockchip,vop2.h
new file mode 100644
index 0000000000000..0a87bc90564a7
--- /dev/null
+++ b/include/dt-bindings/soc/rockchip,vop2.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+
+#ifndef __DT_BINDINGS_ROCKCHIP_VOP2_H
+#define __DT_BINDINGS_ROCKCHIP_VOP2_H
+
+#define RK3568_VOP2_EP_RGB	0
+#define RK3568_VOP2_EP_HDMI	1
+#define RK3568_VOP2_EP_EDP	2
+#define RK3568_VOP2_EP_MIPI0	3
+#define RK3568_VOP2_EP_LVDS0	4
+#define RK3568_VOP2_EP_MIPI1	5
+#define RK3568_VOP2_EP_LVDS1	6
+
+#endif /* __DT_BINDINGS_ROCKCHIP_VOP2_H */
-- 
2.30.2


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

* [PATCH v6 16/23] arm64: dts: rockchip: rk356x: Add VOP2 nodes
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

The VOP2 is the display output controller on the RK3568. Add the node
for it to the dtsi file along with the required display-subsystem node
and the iommu node.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack
    
    Changes since v3:
    - Bring back gamma_lut regs
    - Drop redundant _vop suffix from clock names

 arch/arm64/boot/dts/rockchip/rk3566.dtsi |  4 ++
 arch/arm64/boot/dts/rockchip/rk3568.dtsi |  4 ++
 arch/arm64/boot/dts/rockchip/rk356x.dtsi | 52 ++++++++++++++++++++++++
 include/dt-bindings/soc/rockchip,vop2.h  | 14 +++++++
 4 files changed, 74 insertions(+)
 create mode 100644 include/dt-bindings/soc/rockchip,vop2.h

diff --git a/arch/arm64/boot/dts/rockchip/rk3566.dtsi b/arch/arm64/boot/dts/rockchip/rk3566.dtsi
index 3839eef5e4f76..595fa2562cb8e 100644
--- a/arch/arm64/boot/dts/rockchip/rk3566.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3566.dtsi
@@ -18,3 +18,7 @@ power-domain@RK3568_PD_PIPE {
 		#power-domain-cells = <0>;
 	};
 };
+
+&vop {
+	compatible = "rockchip,rk3566-vop";
+};
diff --git a/arch/arm64/boot/dts/rockchip/rk3568.dtsi b/arch/arm64/boot/dts/rockchip/rk3568.dtsi
index 2fd313a295f8a..1e55efb6fcfde 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3568.dtsi
@@ -95,3 +95,7 @@ power-domain@RK3568_PD_PIPE {
 		#power-domain-cells = <0>;
 	};
 };
+
+&vop {
+	compatible = "rockchip,rk3568-vop";
+};
diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
index a68033a239750..1c24e4219c75e 100644
--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
@@ -10,6 +10,7 @@
 #include <dt-bindings/pinctrl/rockchip.h>
 #include <dt-bindings/power/rk3568-power.h>
 #include <dt-bindings/soc/rockchip,boot-mode.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
 #include <dt-bindings/thermal/thermal.h>
 
 / {
@@ -129,6 +130,11 @@ opp-1800000000 {
 		};
 	};
 
+	display_subsystem: display-subsystem {
+		compatible = "rockchip,display-subsystem";
+		ports = <&vop_out>;
+	};
+
 	firmware {
 		scmi: scmi {
 			compatible = "arm,scmi-smc";
@@ -451,6 +457,52 @@ gmac1_mtl_tx_setup: tx-queues-config {
 		};
 	};
 
+	vop: vop@fe040000 {
+		reg = <0x0 0xfe040000 0x0 0x3000>, <0x0 0xfe044000 0x0 0x1000>;
+		reg-names = "regs", "gamma_lut";
+		interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru ACLK_VOP>, <&cru HCLK_VOP>, <&cru DCLK_VOP0>,
+			 <&cru DCLK_VOP1>, <&cru DCLK_VOP2>;
+		clock-names = "aclk", "hclk", "dclk_vp0", "dclk_vp1", "dclk_vp2";
+		iommus = <&vop_mmu>;
+		power-domains = <&power RK3568_PD_VO>;
+		rockchip,grf = <&grf>;
+		status = "disabled";
+
+		vop_out: ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			vp0: port@0 {
+				reg = <0>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+			};
+
+			vp1: port@1 {
+				reg = <1>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+			};
+
+			vp2: port@2 {
+				reg = <2>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+			};
+		};
+	};
+
+	vop_mmu: iommu@fe043e00 {
+		compatible = "rockchip,rk3568-iommu";
+		reg = <0x0 0xfe043e00 0x0 0x100>, <0x0 0xfe043f00 0x0 0x100>;
+		interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru ACLK_VOP>, <&cru HCLK_VOP>;
+		clock-names = "aclk", "iface";
+		#iommu-cells = <0>;
+		status = "disabled";
+	};
+
 	qos_gpu: qos@fe128000 {
 		compatible = "rockchip,rk3568-qos", "syscon";
 		reg = <0x0 0xfe128000 0x0 0x20>;
diff --git a/include/dt-bindings/soc/rockchip,vop2.h b/include/dt-bindings/soc/rockchip,vop2.h
new file mode 100644
index 0000000000000..0a87bc90564a7
--- /dev/null
+++ b/include/dt-bindings/soc/rockchip,vop2.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+
+#ifndef __DT_BINDINGS_ROCKCHIP_VOP2_H
+#define __DT_BINDINGS_ROCKCHIP_VOP2_H
+
+#define RK3568_VOP2_EP_RGB	0
+#define RK3568_VOP2_EP_HDMI	1
+#define RK3568_VOP2_EP_EDP	2
+#define RK3568_VOP2_EP_MIPI0	3
+#define RK3568_VOP2_EP_LVDS0	4
+#define RK3568_VOP2_EP_MIPI1	5
+#define RK3568_VOP2_EP_LVDS1	6
+
+#endif /* __DT_BINDINGS_ROCKCHIP_VOP2_H */
-- 
2.30.2


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

* [PATCH v6 16/23] arm64: dts: rockchip: rk356x: Add VOP2 nodes
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

The VOP2 is the display output controller on the RK3568. Add the node
for it to the dtsi file along with the required display-subsystem node
and the iommu node.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack
    
    Changes since v3:
    - Bring back gamma_lut regs
    - Drop redundant _vop suffix from clock names

 arch/arm64/boot/dts/rockchip/rk3566.dtsi |  4 ++
 arch/arm64/boot/dts/rockchip/rk3568.dtsi |  4 ++
 arch/arm64/boot/dts/rockchip/rk356x.dtsi | 52 ++++++++++++++++++++++++
 include/dt-bindings/soc/rockchip,vop2.h  | 14 +++++++
 4 files changed, 74 insertions(+)
 create mode 100644 include/dt-bindings/soc/rockchip,vop2.h

diff --git a/arch/arm64/boot/dts/rockchip/rk3566.dtsi b/arch/arm64/boot/dts/rockchip/rk3566.dtsi
index 3839eef5e4f76..595fa2562cb8e 100644
--- a/arch/arm64/boot/dts/rockchip/rk3566.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3566.dtsi
@@ -18,3 +18,7 @@ power-domain@RK3568_PD_PIPE {
 		#power-domain-cells = <0>;
 	};
 };
+
+&vop {
+	compatible = "rockchip,rk3566-vop";
+};
diff --git a/arch/arm64/boot/dts/rockchip/rk3568.dtsi b/arch/arm64/boot/dts/rockchip/rk3568.dtsi
index 2fd313a295f8a..1e55efb6fcfde 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3568.dtsi
@@ -95,3 +95,7 @@ power-domain@RK3568_PD_PIPE {
 		#power-domain-cells = <0>;
 	};
 };
+
+&vop {
+	compatible = "rockchip,rk3568-vop";
+};
diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
index a68033a239750..1c24e4219c75e 100644
--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
@@ -10,6 +10,7 @@
 #include <dt-bindings/pinctrl/rockchip.h>
 #include <dt-bindings/power/rk3568-power.h>
 #include <dt-bindings/soc/rockchip,boot-mode.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
 #include <dt-bindings/thermal/thermal.h>
 
 / {
@@ -129,6 +130,11 @@ opp-1800000000 {
 		};
 	};
 
+	display_subsystem: display-subsystem {
+		compatible = "rockchip,display-subsystem";
+		ports = <&vop_out>;
+	};
+
 	firmware {
 		scmi: scmi {
 			compatible = "arm,scmi-smc";
@@ -451,6 +457,52 @@ gmac1_mtl_tx_setup: tx-queues-config {
 		};
 	};
 
+	vop: vop@fe040000 {
+		reg = <0x0 0xfe040000 0x0 0x3000>, <0x0 0xfe044000 0x0 0x1000>;
+		reg-names = "regs", "gamma_lut";
+		interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru ACLK_VOP>, <&cru HCLK_VOP>, <&cru DCLK_VOP0>,
+			 <&cru DCLK_VOP1>, <&cru DCLK_VOP2>;
+		clock-names = "aclk", "hclk", "dclk_vp0", "dclk_vp1", "dclk_vp2";
+		iommus = <&vop_mmu>;
+		power-domains = <&power RK3568_PD_VO>;
+		rockchip,grf = <&grf>;
+		status = "disabled";
+
+		vop_out: ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			vp0: port@0 {
+				reg = <0>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+			};
+
+			vp1: port@1 {
+				reg = <1>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+			};
+
+			vp2: port@2 {
+				reg = <2>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+			};
+		};
+	};
+
+	vop_mmu: iommu@fe043e00 {
+		compatible = "rockchip,rk3568-iommu";
+		reg = <0x0 0xfe043e00 0x0 0x100>, <0x0 0xfe043f00 0x0 0x100>;
+		interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru ACLK_VOP>, <&cru HCLK_VOP>;
+		clock-names = "aclk", "iface";
+		#iommu-cells = <0>;
+		status = "disabled";
+	};
+
 	qos_gpu: qos@fe128000 {
 		compatible = "rockchip,rk3568-qos", "syscon";
 		reg = <0x0 0xfe128000 0x0 0x20>;
diff --git a/include/dt-bindings/soc/rockchip,vop2.h b/include/dt-bindings/soc/rockchip,vop2.h
new file mode 100644
index 0000000000000..0a87bc90564a7
--- /dev/null
+++ b/include/dt-bindings/soc/rockchip,vop2.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+
+#ifndef __DT_BINDINGS_ROCKCHIP_VOP2_H
+#define __DT_BINDINGS_ROCKCHIP_VOP2_H
+
+#define RK3568_VOP2_EP_RGB	0
+#define RK3568_VOP2_EP_HDMI	1
+#define RK3568_VOP2_EP_EDP	2
+#define RK3568_VOP2_EP_MIPI0	3
+#define RK3568_VOP2_EP_LVDS0	4
+#define RK3568_VOP2_EP_MIPI1	5
+#define RK3568_VOP2_EP_LVDS1	6
+
+#endif /* __DT_BINDINGS_ROCKCHIP_VOP2_H */
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 16/23] arm64: dts: rockchip: rk356x: Add VOP2 nodes
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

The VOP2 is the display output controller on the RK3568. Add the node
for it to the dtsi file along with the required display-subsystem node
and the iommu node.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v4:
    - Add Robs Ack
    
    Changes since v3:
    - Bring back gamma_lut regs
    - Drop redundant _vop suffix from clock names

 arch/arm64/boot/dts/rockchip/rk3566.dtsi |  4 ++
 arch/arm64/boot/dts/rockchip/rk3568.dtsi |  4 ++
 arch/arm64/boot/dts/rockchip/rk356x.dtsi | 52 ++++++++++++++++++++++++
 include/dt-bindings/soc/rockchip,vop2.h  | 14 +++++++
 4 files changed, 74 insertions(+)
 create mode 100644 include/dt-bindings/soc/rockchip,vop2.h

diff --git a/arch/arm64/boot/dts/rockchip/rk3566.dtsi b/arch/arm64/boot/dts/rockchip/rk3566.dtsi
index 3839eef5e4f76..595fa2562cb8e 100644
--- a/arch/arm64/boot/dts/rockchip/rk3566.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3566.dtsi
@@ -18,3 +18,7 @@ power-domain@RK3568_PD_PIPE {
 		#power-domain-cells = <0>;
 	};
 };
+
+&vop {
+	compatible = "rockchip,rk3566-vop";
+};
diff --git a/arch/arm64/boot/dts/rockchip/rk3568.dtsi b/arch/arm64/boot/dts/rockchip/rk3568.dtsi
index 2fd313a295f8a..1e55efb6fcfde 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3568.dtsi
@@ -95,3 +95,7 @@ power-domain@RK3568_PD_PIPE {
 		#power-domain-cells = <0>;
 	};
 };
+
+&vop {
+	compatible = "rockchip,rk3568-vop";
+};
diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
index a68033a239750..1c24e4219c75e 100644
--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
@@ -10,6 +10,7 @@
 #include <dt-bindings/pinctrl/rockchip.h>
 #include <dt-bindings/power/rk3568-power.h>
 #include <dt-bindings/soc/rockchip,boot-mode.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
 #include <dt-bindings/thermal/thermal.h>
 
 / {
@@ -129,6 +130,11 @@ opp-1800000000 {
 		};
 	};
 
+	display_subsystem: display-subsystem {
+		compatible = "rockchip,display-subsystem";
+		ports = <&vop_out>;
+	};
+
 	firmware {
 		scmi: scmi {
 			compatible = "arm,scmi-smc";
@@ -451,6 +457,52 @@ gmac1_mtl_tx_setup: tx-queues-config {
 		};
 	};
 
+	vop: vop@fe040000 {
+		reg = <0x0 0xfe040000 0x0 0x3000>, <0x0 0xfe044000 0x0 0x1000>;
+		reg-names = "regs", "gamma_lut";
+		interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru ACLK_VOP>, <&cru HCLK_VOP>, <&cru DCLK_VOP0>,
+			 <&cru DCLK_VOP1>, <&cru DCLK_VOP2>;
+		clock-names = "aclk", "hclk", "dclk_vp0", "dclk_vp1", "dclk_vp2";
+		iommus = <&vop_mmu>;
+		power-domains = <&power RK3568_PD_VO>;
+		rockchip,grf = <&grf>;
+		status = "disabled";
+
+		vop_out: ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			vp0: port@0 {
+				reg = <0>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+			};
+
+			vp1: port@1 {
+				reg = <1>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+			};
+
+			vp2: port@2 {
+				reg = <2>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+			};
+		};
+	};
+
+	vop_mmu: iommu@fe043e00 {
+		compatible = "rockchip,rk3568-iommu";
+		reg = <0x0 0xfe043e00 0x0 0x100>, <0x0 0xfe043f00 0x0 0x100>;
+		interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru ACLK_VOP>, <&cru HCLK_VOP>;
+		clock-names = "aclk", "iface";
+		#iommu-cells = <0>;
+		status = "disabled";
+	};
+
 	qos_gpu: qos@fe128000 {
 		compatible = "rockchip,rk3568-qos", "syscon";
 		reg = <0x0 0xfe128000 0x0 0x20>;
diff --git a/include/dt-bindings/soc/rockchip,vop2.h b/include/dt-bindings/soc/rockchip,vop2.h
new file mode 100644
index 0000000000000..0a87bc90564a7
--- /dev/null
+++ b/include/dt-bindings/soc/rockchip,vop2.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+
+#ifndef __DT_BINDINGS_ROCKCHIP_VOP2_H
+#define __DT_BINDINGS_ROCKCHIP_VOP2_H
+
+#define RK3568_VOP2_EP_RGB	0
+#define RK3568_VOP2_EP_HDMI	1
+#define RK3568_VOP2_EP_EDP	2
+#define RK3568_VOP2_EP_MIPI0	3
+#define RK3568_VOP2_EP_LVDS0	4
+#define RK3568_VOP2_EP_MIPI1	5
+#define RK3568_VOP2_EP_LVDS1	6
+
+#endif /* __DT_BINDINGS_ROCKCHIP_VOP2_H */
-- 
2.30.2


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

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

* [PATCH v6 17/23] arm64: dts: rockchip: rk356x: Add HDMI nodes
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

Add support for the HDMI port found on RK3568.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Drop unnecessary #size-cells/#address-cells from nodes with only single endpoint

 arch/arm64/boot/dts/rockchip/rk356x.dtsi | 33 +++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
index 1c24e4219c75e..229ed7a755f3b 100644
--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
@@ -10,7 +10,6 @@
 #include <dt-bindings/pinctrl/rockchip.h>
 #include <dt-bindings/power/rk3568-power.h>
 #include <dt-bindings/soc/rockchip,boot-mode.h>
-#include <dt-bindings/soc/rockchip,vop2.h>
 #include <dt-bindings/thermal/thermal.h>
 
 / {
@@ -503,6 +502,38 @@ vop_mmu: iommu@fe043e00 {
 		status = "disabled";
 	};
 
+	hdmi: hdmi@fe0a0000 {
+		compatible = "rockchip,rk3568-dw-hdmi";
+		reg = <0x0 0xfe0a0000 0x0 0x20000>;
+		interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru PCLK_HDMI_HOST>,
+			 <&cru CLK_HDMI_SFR>,
+			 <&cru CLK_HDMI_CEC>,
+			 <&pmucru CLK_HDMI_REF>,
+			 <&cru HCLK_VOP>;
+		clock-names = "iahb", "isfr", "cec", "ref", "hclk";
+		pinctrl-names = "default";
+		pinctrl-0 = <&hdmitx_scl &hdmitx_sda &hdmitxm0_cec>;
+		power-domains = <&power RK3568_PD_VO>;
+		reg-io-width = <4>;
+		rockchip,grf = <&grf>;
+		#sound-dai-cells = <0>;
+		status = "disabled";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			hdmi_in: port@0 {
+				reg = <0>;
+			};
+
+			hdmi_out: port@1 {
+				reg = <1>;
+			};
+		};
+	};
+
 	qos_gpu: qos@fe128000 {
 		compatible = "rockchip,rk3568-qos", "syscon";
 		reg = <0x0 0xfe128000 0x0 0x20>;
-- 
2.30.2


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

* [PATCH v6 17/23] arm64: dts: rockchip: rk356x: Add HDMI nodes
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

Add support for the HDMI port found on RK3568.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Drop unnecessary #size-cells/#address-cells from nodes with only single endpoint

 arch/arm64/boot/dts/rockchip/rk356x.dtsi | 33 +++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
index 1c24e4219c75e..229ed7a755f3b 100644
--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
@@ -10,7 +10,6 @@
 #include <dt-bindings/pinctrl/rockchip.h>
 #include <dt-bindings/power/rk3568-power.h>
 #include <dt-bindings/soc/rockchip,boot-mode.h>
-#include <dt-bindings/soc/rockchip,vop2.h>
 #include <dt-bindings/thermal/thermal.h>
 
 / {
@@ -503,6 +502,38 @@ vop_mmu: iommu@fe043e00 {
 		status = "disabled";
 	};
 
+	hdmi: hdmi@fe0a0000 {
+		compatible = "rockchip,rk3568-dw-hdmi";
+		reg = <0x0 0xfe0a0000 0x0 0x20000>;
+		interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru PCLK_HDMI_HOST>,
+			 <&cru CLK_HDMI_SFR>,
+			 <&cru CLK_HDMI_CEC>,
+			 <&pmucru CLK_HDMI_REF>,
+			 <&cru HCLK_VOP>;
+		clock-names = "iahb", "isfr", "cec", "ref", "hclk";
+		pinctrl-names = "default";
+		pinctrl-0 = <&hdmitx_scl &hdmitx_sda &hdmitxm0_cec>;
+		power-domains = <&power RK3568_PD_VO>;
+		reg-io-width = <4>;
+		rockchip,grf = <&grf>;
+		#sound-dai-cells = <0>;
+		status = "disabled";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			hdmi_in: port@0 {
+				reg = <0>;
+			};
+
+			hdmi_out: port@1 {
+				reg = <1>;
+			};
+		};
+	};
+
 	qos_gpu: qos@fe128000 {
 		compatible = "rockchip,rk3568-qos", "syscon";
 		reg = <0x0 0xfe128000 0x0 0x20>;
-- 
2.30.2


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

* [PATCH v6 17/23] arm64: dts: rockchip: rk356x: Add HDMI nodes
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

Add support for the HDMI port found on RK3568.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Drop unnecessary #size-cells/#address-cells from nodes with only single endpoint

 arch/arm64/boot/dts/rockchip/rk356x.dtsi | 33 +++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
index 1c24e4219c75e..229ed7a755f3b 100644
--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
@@ -10,7 +10,6 @@
 #include <dt-bindings/pinctrl/rockchip.h>
 #include <dt-bindings/power/rk3568-power.h>
 #include <dt-bindings/soc/rockchip,boot-mode.h>
-#include <dt-bindings/soc/rockchip,vop2.h>
 #include <dt-bindings/thermal/thermal.h>
 
 / {
@@ -503,6 +502,38 @@ vop_mmu: iommu@fe043e00 {
 		status = "disabled";
 	};
 
+	hdmi: hdmi@fe0a0000 {
+		compatible = "rockchip,rk3568-dw-hdmi";
+		reg = <0x0 0xfe0a0000 0x0 0x20000>;
+		interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru PCLK_HDMI_HOST>,
+			 <&cru CLK_HDMI_SFR>,
+			 <&cru CLK_HDMI_CEC>,
+			 <&pmucru CLK_HDMI_REF>,
+			 <&cru HCLK_VOP>;
+		clock-names = "iahb", "isfr", "cec", "ref", "hclk";
+		pinctrl-names = "default";
+		pinctrl-0 = <&hdmitx_scl &hdmitx_sda &hdmitxm0_cec>;
+		power-domains = <&power RK3568_PD_VO>;
+		reg-io-width = <4>;
+		rockchip,grf = <&grf>;
+		#sound-dai-cells = <0>;
+		status = "disabled";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			hdmi_in: port@0 {
+				reg = <0>;
+			};
+
+			hdmi_out: port@1 {
+				reg = <1>;
+			};
+		};
+	};
+
 	qos_gpu: qos@fe128000 {
 		compatible = "rockchip,rk3568-qos", "syscon";
 		reg = <0x0 0xfe128000 0x0 0x20>;
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 17/23] arm64: dts: rockchip: rk356x: Add HDMI nodes
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

Add support for the HDMI port found on RK3568.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Drop unnecessary #size-cells/#address-cells from nodes with only single endpoint

 arch/arm64/boot/dts/rockchip/rk356x.dtsi | 33 +++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
index 1c24e4219c75e..229ed7a755f3b 100644
--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
@@ -10,7 +10,6 @@
 #include <dt-bindings/pinctrl/rockchip.h>
 #include <dt-bindings/power/rk3568-power.h>
 #include <dt-bindings/soc/rockchip,boot-mode.h>
-#include <dt-bindings/soc/rockchip,vop2.h>
 #include <dt-bindings/thermal/thermal.h>
 
 / {
@@ -503,6 +502,38 @@ vop_mmu: iommu@fe043e00 {
 		status = "disabled";
 	};
 
+	hdmi: hdmi@fe0a0000 {
+		compatible = "rockchip,rk3568-dw-hdmi";
+		reg = <0x0 0xfe0a0000 0x0 0x20000>;
+		interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru PCLK_HDMI_HOST>,
+			 <&cru CLK_HDMI_SFR>,
+			 <&cru CLK_HDMI_CEC>,
+			 <&pmucru CLK_HDMI_REF>,
+			 <&cru HCLK_VOP>;
+		clock-names = "iahb", "isfr", "cec", "ref", "hclk";
+		pinctrl-names = "default";
+		pinctrl-0 = <&hdmitx_scl &hdmitx_sda &hdmitxm0_cec>;
+		power-domains = <&power RK3568_PD_VO>;
+		reg-io-width = <4>;
+		rockchip,grf = <&grf>;
+		#sound-dai-cells = <0>;
+		status = "disabled";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			hdmi_in: port@0 {
+				reg = <0>;
+			};
+
+			hdmi_out: port@1 {
+				reg = <1>;
+			};
+		};
+	};
+
 	qos_gpu: qos@fe128000 {
 		compatible = "rockchip,rk3568-qos", "syscon";
 		reg = <0x0 0xfe128000 0x0 0x20>;
-- 
2.30.2


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

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

* [PATCH v6 18/23] arm64: dts: rockchip: rk3568-evb: Enable VOP2 and hdmi
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

This enabled the VOP2 display controller along with hdmi and the
required port routes which is enough to get a picture out of the
hdmi port of the board.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Drop reg property from single endpoint node
    
    Changes since v4:
    - Sort nodes alphabetically
    
    Changes since v3:
    - Fix HDMI connector type

 .../boot/dts/rockchip/rk3568-evb1-v10.dts     | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts
index 184e2aa2416af..cd4e01c7994ce 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts
@@ -7,6 +7,7 @@
 /dts-v1/;
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/pinctrl/rockchip.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
 #include "rk3568.dtsi"
 
 / {
@@ -33,6 +34,17 @@ dc_12v: dc-12v {
 		regulator-max-microvolt = <12000000>;
 	};
 
+	hdmi-con {
+		compatible = "hdmi-connector";
+		type = "a";
+
+		port {
+			hdmi_con_in: endpoint {
+				remote-endpoint = <&hdmi_out_con>;
+			};
+		};
+	};
+
 	vcc3v3_sys: vcc3v3-sys {
 		compatible = "regulator-fixed";
 		regulator-name = "vcc3v3_sys";
@@ -106,6 +118,24 @@ &gmac1m1_rgmii_clk
 	status = "okay";
 };
 
+&hdmi {
+	avdd-0v9-supply = <&vdda0v9_image>;
+	avdd-1v8-supply = <&vcca1v8_image>;
+	status = "okay";
+};
+
+&hdmi_in {
+	hdmi_in_vp0: endpoint {
+		remote-endpoint = <&vp0_out_hdmi>;
+	};
+};
+
+&hdmi_out {
+	hdmi_out_con: endpoint {
+		remote-endpoint = <&hdmi_con_in>;
+	};
+};
+
 &i2c0 {
 	status = "okay";
 
@@ -390,3 +420,20 @@ &sdmmc0 {
 &uart2 {
 	status = "okay";
 };
+
+&vop {
+	assigned-clocks = <&cru DCLK_VOP0>, <&cru DCLK_VOP1>;
+	assigned-clock-parents = <&pmucru PLL_HPLL>, <&cru PLL_VPLL>;
+	status = "okay";
+};
+
+&vop_mmu {
+	status = "okay";
+};
+
+&vp0 {
+	vp0_out_hdmi: endpoint@RK3568_VOP2_EP_HDMI {
+		reg = <RK3568_VOP2_EP_HDMI>;
+		remote-endpoint = <&hdmi_in_vp0>;
+	};
+};
-- 
2.30.2


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

* [PATCH v6 18/23] arm64: dts: rockchip: rk3568-evb: Enable VOP2 and hdmi
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

This enabled the VOP2 display controller along with hdmi and the
required port routes which is enough to get a picture out of the
hdmi port of the board.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Drop reg property from single endpoint node
    
    Changes since v4:
    - Sort nodes alphabetically
    
    Changes since v3:
    - Fix HDMI connector type

 .../boot/dts/rockchip/rk3568-evb1-v10.dts     | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts
index 184e2aa2416af..cd4e01c7994ce 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts
@@ -7,6 +7,7 @@
 /dts-v1/;
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/pinctrl/rockchip.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
 #include "rk3568.dtsi"
 
 / {
@@ -33,6 +34,17 @@ dc_12v: dc-12v {
 		regulator-max-microvolt = <12000000>;
 	};
 
+	hdmi-con {
+		compatible = "hdmi-connector";
+		type = "a";
+
+		port {
+			hdmi_con_in: endpoint {
+				remote-endpoint = <&hdmi_out_con>;
+			};
+		};
+	};
+
 	vcc3v3_sys: vcc3v3-sys {
 		compatible = "regulator-fixed";
 		regulator-name = "vcc3v3_sys";
@@ -106,6 +118,24 @@ &gmac1m1_rgmii_clk
 	status = "okay";
 };
 
+&hdmi {
+	avdd-0v9-supply = <&vdda0v9_image>;
+	avdd-1v8-supply = <&vcca1v8_image>;
+	status = "okay";
+};
+
+&hdmi_in {
+	hdmi_in_vp0: endpoint {
+		remote-endpoint = <&vp0_out_hdmi>;
+	};
+};
+
+&hdmi_out {
+	hdmi_out_con: endpoint {
+		remote-endpoint = <&hdmi_con_in>;
+	};
+};
+
 &i2c0 {
 	status = "okay";
 
@@ -390,3 +420,20 @@ &sdmmc0 {
 &uart2 {
 	status = "okay";
 };
+
+&vop {
+	assigned-clocks = <&cru DCLK_VOP0>, <&cru DCLK_VOP1>;
+	assigned-clock-parents = <&pmucru PLL_HPLL>, <&cru PLL_VPLL>;
+	status = "okay";
+};
+
+&vop_mmu {
+	status = "okay";
+};
+
+&vp0 {
+	vp0_out_hdmi: endpoint@RK3568_VOP2_EP_HDMI {
+		reg = <RK3568_VOP2_EP_HDMI>;
+		remote-endpoint = <&hdmi_in_vp0>;
+	};
+};
-- 
2.30.2


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

* [PATCH v6 18/23] arm64: dts: rockchip: rk3568-evb: Enable VOP2 and hdmi
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

This enabled the VOP2 display controller along with hdmi and the
required port routes which is enough to get a picture out of the
hdmi port of the board.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Drop reg property from single endpoint node
    
    Changes since v4:
    - Sort nodes alphabetically
    
    Changes since v3:
    - Fix HDMI connector type

 .../boot/dts/rockchip/rk3568-evb1-v10.dts     | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts
index 184e2aa2416af..cd4e01c7994ce 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts
@@ -7,6 +7,7 @@
 /dts-v1/;
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/pinctrl/rockchip.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
 #include "rk3568.dtsi"
 
 / {
@@ -33,6 +34,17 @@ dc_12v: dc-12v {
 		regulator-max-microvolt = <12000000>;
 	};
 
+	hdmi-con {
+		compatible = "hdmi-connector";
+		type = "a";
+
+		port {
+			hdmi_con_in: endpoint {
+				remote-endpoint = <&hdmi_out_con>;
+			};
+		};
+	};
+
 	vcc3v3_sys: vcc3v3-sys {
 		compatible = "regulator-fixed";
 		regulator-name = "vcc3v3_sys";
@@ -106,6 +118,24 @@ &gmac1m1_rgmii_clk
 	status = "okay";
 };
 
+&hdmi {
+	avdd-0v9-supply = <&vdda0v9_image>;
+	avdd-1v8-supply = <&vcca1v8_image>;
+	status = "okay";
+};
+
+&hdmi_in {
+	hdmi_in_vp0: endpoint {
+		remote-endpoint = <&vp0_out_hdmi>;
+	};
+};
+
+&hdmi_out {
+	hdmi_out_con: endpoint {
+		remote-endpoint = <&hdmi_con_in>;
+	};
+};
+
 &i2c0 {
 	status = "okay";
 
@@ -390,3 +420,20 @@ &sdmmc0 {
 &uart2 {
 	status = "okay";
 };
+
+&vop {
+	assigned-clocks = <&cru DCLK_VOP0>, <&cru DCLK_VOP1>;
+	assigned-clock-parents = <&pmucru PLL_HPLL>, <&cru PLL_VPLL>;
+	status = "okay";
+};
+
+&vop_mmu {
+	status = "okay";
+};
+
+&vp0 {
+	vp0_out_hdmi: endpoint@RK3568_VOP2_EP_HDMI {
+		reg = <RK3568_VOP2_EP_HDMI>;
+		remote-endpoint = <&hdmi_in_vp0>;
+	};
+};
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 18/23] arm64: dts: rockchip: rk3568-evb: Enable VOP2 and hdmi
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

This enabled the VOP2 display controller along with hdmi and the
required port routes which is enough to get a picture out of the
hdmi port of the board.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Drop reg property from single endpoint node
    
    Changes since v4:
    - Sort nodes alphabetically
    
    Changes since v3:
    - Fix HDMI connector type

 .../boot/dts/rockchip/rk3568-evb1-v10.dts     | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts
index 184e2aa2416af..cd4e01c7994ce 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3568-evb1-v10.dts
@@ -7,6 +7,7 @@
 /dts-v1/;
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/pinctrl/rockchip.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
 #include "rk3568.dtsi"
 
 / {
@@ -33,6 +34,17 @@ dc_12v: dc-12v {
 		regulator-max-microvolt = <12000000>;
 	};
 
+	hdmi-con {
+		compatible = "hdmi-connector";
+		type = "a";
+
+		port {
+			hdmi_con_in: endpoint {
+				remote-endpoint = <&hdmi_out_con>;
+			};
+		};
+	};
+
 	vcc3v3_sys: vcc3v3-sys {
 		compatible = "regulator-fixed";
 		regulator-name = "vcc3v3_sys";
@@ -106,6 +118,24 @@ &gmac1m1_rgmii_clk
 	status = "okay";
 };
 
+&hdmi {
+	avdd-0v9-supply = <&vdda0v9_image>;
+	avdd-1v8-supply = <&vcca1v8_image>;
+	status = "okay";
+};
+
+&hdmi_in {
+	hdmi_in_vp0: endpoint {
+		remote-endpoint = <&vp0_out_hdmi>;
+	};
+};
+
+&hdmi_out {
+	hdmi_out_con: endpoint {
+		remote-endpoint = <&hdmi_con_in>;
+	};
+};
+
 &i2c0 {
 	status = "okay";
 
@@ -390,3 +420,20 @@ &sdmmc0 {
 &uart2 {
 	status = "okay";
 };
+
+&vop {
+	assigned-clocks = <&cru DCLK_VOP0>, <&cru DCLK_VOP1>;
+	assigned-clock-parents = <&pmucru PLL_HPLL>, <&cru PLL_VPLL>;
+	status = "okay";
+};
+
+&vop_mmu {
+	status = "okay";
+};
+
+&vp0 {
+	vp0_out_hdmi: endpoint@RK3568_VOP2_EP_HDMI {
+		reg = <RK3568_VOP2_EP_HDMI>;
+		remote-endpoint = <&hdmi_in_vp0>;
+	};
+};
-- 
2.30.2


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

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

* [PATCH v6 19/23] arm64: dts: rockchip: enable vop2 and hdmi tx on quartz64a
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

From: Michael Riesch <michael.riesch@wolfvision.net>

Enable the RK356x Video Output Processor (VOP) 2 on the Pine64
Quartz64 Model A.

Signed-off-by: Michael Riesch <michael.riesch@wolfvision.net>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Drop reg property from single endpoint node
    
    Changes since v4:
    - Sort nodes alphabetically
    
    Changes since v3:
    - Fix HDMI connector type

 .../boot/dts/rockchip/rk3566-quartz64-a.dts   | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts
index 166399b7f13f0..5d7035649cda8 100644
--- a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts
@@ -4,6 +4,7 @@
 
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/pinctrl/rockchip.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
 #include "rk3566.dtsi"
 
 / {
@@ -35,6 +36,17 @@ fan: gpio_fan {
 		#cooling-cells = <2>;
 	};
 
+	hdmi-con {
+		compatible = "hdmi-connector";
+		type = "a";
+
+		port {
+			hdmi_con_in: endpoint {
+				remote-endpoint = <&hdmi_out_con>;
+			};
+		};
+	};
+
 	leds {
 		compatible = "gpio-leds";
 
@@ -205,6 +217,24 @@ &gmac1m0_clkinout
 	status = "okay";
 };
 
+&hdmi {
+	avdd-0v9-supply = <&vdda_0v9>;
+	avdd-1v8-supply = <&vcc_1v8>;
+	status = "okay";
+};
+
+&hdmi_in {
+	hdmi_in_vp0: endpoint {
+		remote-endpoint = <&vp0_out_hdmi>;
+	};
+};
+
+&hdmi_out {
+	hdmi_out_con: endpoint {
+		remote-endpoint = <&hdmi_con_in>;
+	};
+};
+
 &i2c0 {
 	status = "okay";
 
@@ -551,3 +581,20 @@ bluetooth {
 &uart2 {
 	status = "okay";
 };
+
+&vop {
+	assigned-clocks = <&cru DCLK_VOP0>, <&cru DCLK_VOP1>;
+	assigned-clock-parents = <&pmucru PLL_HPLL>, <&cru PLL_VPLL>;
+	status = "okay";
+};
+
+&vop_mmu {
+	status = "okay";
+};
+
+&vp0 {
+	vp0_out_hdmi: endpoint@RK3568_VOP2_EP_HDMI {
+		reg = <RK3568_VOP2_EP_HDMI>;
+		remote-endpoint = <&hdmi_in_vp0>;
+	};
+};
-- 
2.30.2


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

* [PATCH v6 19/23] arm64: dts: rockchip: enable vop2 and hdmi tx on quartz64a
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

From: Michael Riesch <michael.riesch@wolfvision.net>

Enable the RK356x Video Output Processor (VOP) 2 on the Pine64
Quartz64 Model A.

Signed-off-by: Michael Riesch <michael.riesch@wolfvision.net>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Drop reg property from single endpoint node
    
    Changes since v4:
    - Sort nodes alphabetically
    
    Changes since v3:
    - Fix HDMI connector type

 .../boot/dts/rockchip/rk3566-quartz64-a.dts   | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts
index 166399b7f13f0..5d7035649cda8 100644
--- a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts
@@ -4,6 +4,7 @@
 
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/pinctrl/rockchip.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
 #include "rk3566.dtsi"
 
 / {
@@ -35,6 +36,17 @@ fan: gpio_fan {
 		#cooling-cells = <2>;
 	};
 
+	hdmi-con {
+		compatible = "hdmi-connector";
+		type = "a";
+
+		port {
+			hdmi_con_in: endpoint {
+				remote-endpoint = <&hdmi_out_con>;
+			};
+		};
+	};
+
 	leds {
 		compatible = "gpio-leds";
 
@@ -205,6 +217,24 @@ &gmac1m0_clkinout
 	status = "okay";
 };
 
+&hdmi {
+	avdd-0v9-supply = <&vdda_0v9>;
+	avdd-1v8-supply = <&vcc_1v8>;
+	status = "okay";
+};
+
+&hdmi_in {
+	hdmi_in_vp0: endpoint {
+		remote-endpoint = <&vp0_out_hdmi>;
+	};
+};
+
+&hdmi_out {
+	hdmi_out_con: endpoint {
+		remote-endpoint = <&hdmi_con_in>;
+	};
+};
+
 &i2c0 {
 	status = "okay";
 
@@ -551,3 +581,20 @@ bluetooth {
 &uart2 {
 	status = "okay";
 };
+
+&vop {
+	assigned-clocks = <&cru DCLK_VOP0>, <&cru DCLK_VOP1>;
+	assigned-clock-parents = <&pmucru PLL_HPLL>, <&cru PLL_VPLL>;
+	status = "okay";
+};
+
+&vop_mmu {
+	status = "okay";
+};
+
+&vp0 {
+	vp0_out_hdmi: endpoint@RK3568_VOP2_EP_HDMI {
+		reg = <RK3568_VOP2_EP_HDMI>;
+		remote-endpoint = <&hdmi_in_vp0>;
+	};
+};
-- 
2.30.2


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

* [PATCH v6 19/23] arm64: dts: rockchip: enable vop2 and hdmi tx on quartz64a
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

From: Michael Riesch <michael.riesch@wolfvision.net>

Enable the RK356x Video Output Processor (VOP) 2 on the Pine64
Quartz64 Model A.

Signed-off-by: Michael Riesch <michael.riesch@wolfvision.net>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Drop reg property from single endpoint node
    
    Changes since v4:
    - Sort nodes alphabetically
    
    Changes since v3:
    - Fix HDMI connector type

 .../boot/dts/rockchip/rk3566-quartz64-a.dts   | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts
index 166399b7f13f0..5d7035649cda8 100644
--- a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts
@@ -4,6 +4,7 @@
 
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/pinctrl/rockchip.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
 #include "rk3566.dtsi"
 
 / {
@@ -35,6 +36,17 @@ fan: gpio_fan {
 		#cooling-cells = <2>;
 	};
 
+	hdmi-con {
+		compatible = "hdmi-connector";
+		type = "a";
+
+		port {
+			hdmi_con_in: endpoint {
+				remote-endpoint = <&hdmi_out_con>;
+			};
+		};
+	};
+
 	leds {
 		compatible = "gpio-leds";
 
@@ -205,6 +217,24 @@ &gmac1m0_clkinout
 	status = "okay";
 };
 
+&hdmi {
+	avdd-0v9-supply = <&vdda_0v9>;
+	avdd-1v8-supply = <&vcc_1v8>;
+	status = "okay";
+};
+
+&hdmi_in {
+	hdmi_in_vp0: endpoint {
+		remote-endpoint = <&vp0_out_hdmi>;
+	};
+};
+
+&hdmi_out {
+	hdmi_out_con: endpoint {
+		remote-endpoint = <&hdmi_con_in>;
+	};
+};
+
 &i2c0 {
 	status = "okay";
 
@@ -551,3 +581,20 @@ bluetooth {
 &uart2 {
 	status = "okay";
 };
+
+&vop {
+	assigned-clocks = <&cru DCLK_VOP0>, <&cru DCLK_VOP1>;
+	assigned-clock-parents = <&pmucru PLL_HPLL>, <&cru PLL_VPLL>;
+	status = "okay";
+};
+
+&vop_mmu {
+	status = "okay";
+};
+
+&vp0 {
+	vp0_out_hdmi: endpoint@RK3568_VOP2_EP_HDMI {
+		reg = <RK3568_VOP2_EP_HDMI>;
+		remote-endpoint = <&hdmi_in_vp0>;
+	};
+};
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 19/23] arm64: dts: rockchip: enable vop2 and hdmi tx on quartz64a
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

From: Michael Riesch <michael.riesch@wolfvision.net>

Enable the RK356x Video Output Processor (VOP) 2 on the Pine64
Quartz64 Model A.

Signed-off-by: Michael Riesch <michael.riesch@wolfvision.net>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - Drop reg property from single endpoint node
    
    Changes since v4:
    - Sort nodes alphabetically
    
    Changes since v3:
    - Fix HDMI connector type

 .../boot/dts/rockchip/rk3566-quartz64-a.dts   | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts
index 166399b7f13f0..5d7035649cda8 100644
--- a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts
@@ -4,6 +4,7 @@
 
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/pinctrl/rockchip.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
 #include "rk3566.dtsi"
 
 / {
@@ -35,6 +36,17 @@ fan: gpio_fan {
 		#cooling-cells = <2>;
 	};
 
+	hdmi-con {
+		compatible = "hdmi-connector";
+		type = "a";
+
+		port {
+			hdmi_con_in: endpoint {
+				remote-endpoint = <&hdmi_out_con>;
+			};
+		};
+	};
+
 	leds {
 		compatible = "gpio-leds";
 
@@ -205,6 +217,24 @@ &gmac1m0_clkinout
 	status = "okay";
 };
 
+&hdmi {
+	avdd-0v9-supply = <&vdda_0v9>;
+	avdd-1v8-supply = <&vcc_1v8>;
+	status = "okay";
+};
+
+&hdmi_in {
+	hdmi_in_vp0: endpoint {
+		remote-endpoint = <&vp0_out_hdmi>;
+	};
+};
+
+&hdmi_out {
+	hdmi_out_con: endpoint {
+		remote-endpoint = <&hdmi_con_in>;
+	};
+};
+
 &i2c0 {
 	status = "okay";
 
@@ -551,3 +581,20 @@ bluetooth {
 &uart2 {
 	status = "okay";
 };
+
+&vop {
+	assigned-clocks = <&cru DCLK_VOP0>, <&cru DCLK_VOP1>;
+	assigned-clock-parents = <&pmucru PLL_HPLL>, <&cru PLL_VPLL>;
+	status = "okay";
+};
+
+&vop_mmu {
+	status = "okay";
+};
+
+&vp0 {
+	vp0_out_hdmi: endpoint@RK3568_VOP2_EP_HDMI {
+		reg = <RK3568_VOP2_EP_HDMI>;
+		remote-endpoint = <&hdmi_in_vp0>;
+	};
+};
-- 
2.30.2


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

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

* [PATCH v6 20/23] drm/rockchip: Make VOP driver optional
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

With upcoming VOP2 support VOP won't be the only choice anymore, so make
the VOP driver optional.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/rockchip/Kconfig            | 8 ++++++++
 drivers/gpu/drm/rockchip/Makefile           | 3 ++-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 +-
 3 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 9f1ecefc39332..b9b156308460a 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -21,8 +21,16 @@ config DRM_ROCKCHIP
 
 if DRM_ROCKCHIP
 
+config ROCKCHIP_VOP
+	bool "Rockchip VOP driver"
+	default y
+	help
+	  This selects support for the VOP driver. You should enable it
+	  on all older SoCs up to RK3399.
+
 config ROCKCHIP_ANALOGIX_DP
 	bool "Rockchip specific extensions for Analogix DP driver"
+	depends on ROCKCHIP_VOP
 	help
 	  This selects support for Rockchip SoC specific extensions
 	  for the Analogix Core DP driver. If you want to enable DP
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 1a56f696558ca..dfc5512fdb9f1 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -4,8 +4,9 @@
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
-		rockchip_drm_gem.o rockchip_drm_vop.o rockchip_vop_reg.o
+		rockchip_drm_gem.o
 
+rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
 rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
 rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
 rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index bec207de45440..82c8faf1fb6b8 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -458,7 +458,7 @@ static int __init rockchip_drm_init(void)
 	int ret;
 
 	num_rockchip_sub_drivers = 0;
-	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_DRM_ROCKCHIP);
+	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
 				CONFIG_ROCKCHIP_LVDS);
 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
-- 
2.30.2


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

* [PATCH v6 20/23] drm/rockchip: Make VOP driver optional
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

With upcoming VOP2 support VOP won't be the only choice anymore, so make
the VOP driver optional.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/rockchip/Kconfig            | 8 ++++++++
 drivers/gpu/drm/rockchip/Makefile           | 3 ++-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 +-
 3 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 9f1ecefc39332..b9b156308460a 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -21,8 +21,16 @@ config DRM_ROCKCHIP
 
 if DRM_ROCKCHIP
 
+config ROCKCHIP_VOP
+	bool "Rockchip VOP driver"
+	default y
+	help
+	  This selects support for the VOP driver. You should enable it
+	  on all older SoCs up to RK3399.
+
 config ROCKCHIP_ANALOGIX_DP
 	bool "Rockchip specific extensions for Analogix DP driver"
+	depends on ROCKCHIP_VOP
 	help
 	  This selects support for Rockchip SoC specific extensions
 	  for the Analogix Core DP driver. If you want to enable DP
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 1a56f696558ca..dfc5512fdb9f1 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -4,8 +4,9 @@
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
-		rockchip_drm_gem.o rockchip_drm_vop.o rockchip_vop_reg.o
+		rockchip_drm_gem.o
 
+rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
 rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
 rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
 rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index bec207de45440..82c8faf1fb6b8 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -458,7 +458,7 @@ static int __init rockchip_drm_init(void)
 	int ret;
 
 	num_rockchip_sub_drivers = 0;
-	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_DRM_ROCKCHIP);
+	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
 				CONFIG_ROCKCHIP_LVDS);
 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
-- 
2.30.2


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

* [PATCH v6 20/23] drm/rockchip: Make VOP driver optional
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

With upcoming VOP2 support VOP won't be the only choice anymore, so make
the VOP driver optional.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/rockchip/Kconfig            | 8 ++++++++
 drivers/gpu/drm/rockchip/Makefile           | 3 ++-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 +-
 3 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 9f1ecefc39332..b9b156308460a 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -21,8 +21,16 @@ config DRM_ROCKCHIP
 
 if DRM_ROCKCHIP
 
+config ROCKCHIP_VOP
+	bool "Rockchip VOP driver"
+	default y
+	help
+	  This selects support for the VOP driver. You should enable it
+	  on all older SoCs up to RK3399.
+
 config ROCKCHIP_ANALOGIX_DP
 	bool "Rockchip specific extensions for Analogix DP driver"
+	depends on ROCKCHIP_VOP
 	help
 	  This selects support for Rockchip SoC specific extensions
 	  for the Analogix Core DP driver. If you want to enable DP
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 1a56f696558ca..dfc5512fdb9f1 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -4,8 +4,9 @@
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
-		rockchip_drm_gem.o rockchip_drm_vop.o rockchip_vop_reg.o
+		rockchip_drm_gem.o
 
+rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
 rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
 rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
 rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index bec207de45440..82c8faf1fb6b8 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -458,7 +458,7 @@ static int __init rockchip_drm_init(void)
 	int ret;
 
 	num_rockchip_sub_drivers = 0;
-	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_DRM_ROCKCHIP);
+	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
 				CONFIG_ROCKCHIP_LVDS);
 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
-- 
2.30.2


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

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

* [PATCH v6 20/23] drm/rockchip: Make VOP driver optional
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

With upcoming VOP2 support VOP won't be the only choice anymore, so make
the VOP driver optional.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/rockchip/Kconfig            | 8 ++++++++
 drivers/gpu/drm/rockchip/Makefile           | 3 ++-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 +-
 3 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 9f1ecefc39332..b9b156308460a 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -21,8 +21,16 @@ config DRM_ROCKCHIP
 
 if DRM_ROCKCHIP
 
+config ROCKCHIP_VOP
+	bool "Rockchip VOP driver"
+	default y
+	help
+	  This selects support for the VOP driver. You should enable it
+	  on all older SoCs up to RK3399.
+
 config ROCKCHIP_ANALOGIX_DP
 	bool "Rockchip specific extensions for Analogix DP driver"
+	depends on ROCKCHIP_VOP
 	help
 	  This selects support for Rockchip SoC specific extensions
 	  for the Analogix Core DP driver. If you want to enable DP
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 1a56f696558ca..dfc5512fdb9f1 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -4,8 +4,9 @@
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
-		rockchip_drm_gem.o rockchip_drm_vop.o rockchip_vop_reg.o
+		rockchip_drm_gem.o
 
+rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
 rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
 rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
 rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index bec207de45440..82c8faf1fb6b8 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -458,7 +458,7 @@ static int __init rockchip_drm_init(void)
 	int ret;
 
 	num_rockchip_sub_drivers = 0;
-	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_DRM_ROCKCHIP);
+	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
 				CONFIG_ROCKCHIP_LVDS);
 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
  2022-02-17  8:29 ` Sascha Hauer
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

From: Andy Yan <andy.yan@rock-chips.com>

The VOP2 unit is found on Rockchip SoCs beginning with rk3566/rk3568.
It replaces the VOP unit found in the older Rockchip SoCs.

This driver has been derived from the downstream Rockchip Kernel and
heavily modified:

- All nonstandard DRM properties have been removed
- dropped struct vop2_plane_state and pass around less data between
  functions
- Dropped all DRM_FORMAT_* not known on upstream
- rework register access to get rid of excessively used macros
- Drop all waiting for framesyncs

The driver is tested with HDMI and MIPI-DSI display on a RK3568-EVB
board. Overlay support is tested with the modetest utility. AFBC support
on the cluster windows is tested with weston-simple-dmabuf-egl on
weston using the (yet to be upstreamed) panfrost driver support.

Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - consistently use u8/u16/u32 rather than uint8_t/uint16_t/uint32_t
    - Use spin_lock rather than spin_lock_irqsave
    - replace printk with drm_dbg
    - break some overlong lines
    
    Changes since v4:
    - Avoid stack frame overflow by not allocating big array on the stack
    
    Changes since v3:
    - Sort includes
    - fix typos
    - Drop spinlock
    - Use regmap_set_bits()/regmap_clear_bits()
    - simplify vop2_scale_factor()
    - simplify vop2_afbc_transform_offset()
    
    Changes since v4:
    - Sort nodes alphabetically
    
    Changes since v3:
    - Fix HDMI connector type

 drivers/gpu/drm/rockchip/Kconfig             |    6 +
 drivers/gpu/drm/rockchip/Makefile            |    1 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c  |    1 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h  |    6 +-
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c   |    2 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h  |   15 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2708 ++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h |  477 +++
 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c |  281 ++
 9 files changed, 3496 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index b9b156308460a..4ff0043f0ee70 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -28,6 +28,12 @@ config ROCKCHIP_VOP
 	  This selects support for the VOP driver. You should enable it
 	  on all older SoCs up to RK3399.
 
+config ROCKCHIP_VOP2
+	bool "Rockchip VOP2 driver"
+	help
+	  This selects support for the VOP2 driver. You should enable it
+	  on all newer SoCs beginning form RK3568.
+
 config ROCKCHIP_ANALOGIX_DP
 	bool "Rockchip specific extensions for Analogix DP driver"
 	depends on ROCKCHIP_VOP
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index dfc5512fdb9f1..3ff7b21c04149 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -6,6 +6,7 @@
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
 		rockchip_drm_gem.o
 
+rockchipdrm-$(CONFIG_ROCKCHIP_VOP2) += rockchip_drm_vop2.o rockchip_vop2_reg.o
 rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
 rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
 rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 82c8faf1fb6b8..95f6c5985fdd7 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -459,6 +459,7 @@ static int __init rockchip_drm_init(void)
 
 	num_rockchip_sub_drivers = 0;
 	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
+	ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2);
 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
 				CONFIG_ROCKCHIP_LVDS);
 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index d3e42410ae5da..5cb207aaaadd0 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -18,7 +18,7 @@
 
 #define ROCKCHIP_MAX_FB_BUFFER	3
 #define ROCKCHIP_MAX_CONNECTOR	2
-#define ROCKCHIP_MAX_CRTC	2
+#define ROCKCHIP_MAX_CRTC	4
 
 struct drm_device;
 struct drm_connector;
@@ -31,6 +31,9 @@ struct rockchip_crtc_state {
 	int output_bpc;
 	int output_flags;
 	bool enable_afbc;
+	u32 bus_format;
+	u32 bus_flags;
+	int color_space;
 };
 #define to_rockchip_crtc_state(s) \
 		container_of(s, struct rockchip_crtc_state, base)
@@ -63,6 +66,7 @@ extern struct platform_driver rockchip_dp_driver;
 extern struct platform_driver rockchip_lvds_driver;
 extern struct platform_driver vop_platform_driver;
 extern struct platform_driver rk3066_hdmi_driver;
+extern struct platform_driver vop2_platform_driver;
 
 struct rockchip_encoder {
 	struct device_node *port;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 3aa37e177667e..0d2cb4f3922b8 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -134,4 +134,6 @@ void rockchip_drm_mode_config_init(struct drm_device *dev)
 
 	dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
 	dev->mode_config.helper_private = &rockchip_mode_config_helpers;
+
+	dev->mode_config.normalize_zpos = true;
 }
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index 857d97cdc67c6..1e364d7b50e69 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -54,9 +54,23 @@ struct vop_afbc {
 	struct vop_reg enable;
 	struct vop_reg win_sel;
 	struct vop_reg format;
+	struct vop_reg rb_swap;
+	struct vop_reg uv_swap;
+	struct vop_reg auto_gating_en;
+	struct vop_reg block_split_en;
+	struct vop_reg pic_vir_width;
+	struct vop_reg tile_num;
 	struct vop_reg hreg_block_split;
+	struct vop_reg pic_offset;
 	struct vop_reg pic_size;
+	struct vop_reg dsp_offset;
+	struct vop_reg transform_offset;
 	struct vop_reg hdr_ptr;
+	struct vop_reg half_block_en;
+	struct vop_reg xmirror;
+	struct vop_reg ymirror;
+	struct vop_reg rotate_270;
+	struct vop_reg rotate_90;
 	struct vop_reg rstn;
 };
 
@@ -410,4 +424,5 @@ static inline int scl_vop_cal_lb_mode(int width, bool is_yuv)
 }
 
 extern const struct component_ops vop_component_ops;
+
 #endif /* _ROCKCHIP_DRM_VOP_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
new file mode 100644
index 0000000000000..394dd6c583682
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -0,0 +1,2708 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
+ * Author: Andy Yan <andy.yan@rock-chips.com>
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/swab.h>
+
+#include <drm/drm.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_flip_work.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+
+#include <uapi/linux/videodev2.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_vop2.h"
+
+/*
+ * VOP2 architecture
+ *
+ +----------+   +-------------+                                                        +-----------+
+ |  Cluster |   | Sel 1 from 6|                                                        | 1 from 3  |
+ |  window0 |   |    Layer0   |                                                        |    RGB    |
+ +----------+   +-------------+              +---------------+    +-------------+      +-----------+
+ +----------+   +-------------+              |N from 6 layers|    |             |
+ |  Cluster |   | Sel 1 from 6|              |   Overlay0    +--->| Video Port0 |      +-----------+
+ |  window1 |   |    Layer1   |              |               |    |             |      | 1 from 3  |
+ +----------+   +-------------+              +---------------+    +-------------+      |   LVDS    |
+ +----------+   +-------------+                                                        +-----------+
+ |  Esmart  |   | Sel 1 from 6|
+ |  window0 |   |   Layer2    |              +---------------+    +-------------+      +-----------+
+ +----------+   +-------------+              |N from 6 Layers|    |             | +--> | 1 from 3  |
+ +----------+   +-------------+   -------->  |   Overlay1    +--->| Video Port1 |      |   MIPI    |
+ |  Esmart  |   | Sel 1 from 6|   -------->  |               |    |             |      +-----------+
+ |  Window1 |   |   Layer3    |              +---------------+    +-------------+
+ +----------+   +-------------+                                                        +-----------+
+ +----------+   +-------------+                                                        | 1 from 3  |
+ |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |   HDMI    |
+ |  Window0 |   |    Layer4   |              |N from 6 Layers|    |             |      +-----------+
+ +----------+   +-------------+              |   Overlay2    +--->| Video Port2 |
+ +----------+   +-------------+              |               |    |             |      +-----------+
+ |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |  1 from 3 |
+ |  Window1 |   |    Layer5   |                                                        |    eDP    |
+ +----------+   +-------------+                                                        +-----------+
+ *
+ */
+
+enum vop2_data_format {
+	VOP2_FMT_ARGB8888 = 0,
+	VOP2_FMT_RGB888,
+	VOP2_FMT_RGB565,
+	VOP2_FMT_XRGB101010,
+	VOP2_FMT_YUV420SP,
+	VOP2_FMT_YUV422SP,
+	VOP2_FMT_YUV444SP,
+	VOP2_FMT_YUYV422 = 8,
+	VOP2_FMT_YUYV420,
+	VOP2_FMT_VYUY422,
+	VOP2_FMT_VYUY420,
+	VOP2_FMT_YUV420SP_TILE_8x4 = 0x10,
+	VOP2_FMT_YUV420SP_TILE_16x2,
+	VOP2_FMT_YUV422SP_TILE_8x4,
+	VOP2_FMT_YUV422SP_TILE_16x2,
+	VOP2_FMT_YUV420SP_10,
+	VOP2_FMT_YUV422SP_10,
+	VOP2_FMT_YUV444SP_10,
+};
+
+enum vop2_afbc_format {
+	VOP2_AFBC_FMT_RGB565,
+	VOP2_AFBC_FMT_ARGB2101010 = 2,
+	VOP2_AFBC_FMT_YUV420_10BIT,
+	VOP2_AFBC_FMT_RGB888,
+	VOP2_AFBC_FMT_ARGB8888,
+	VOP2_AFBC_FMT_YUV420 = 9,
+	VOP2_AFBC_FMT_YUV422 = 0xb,
+	VOP2_AFBC_FMT_YUV422_10BIT = 0xe,
+	VOP2_AFBC_FMT_INVALID = -1,
+};
+
+union vop2_alpha_ctrl {
+	u32 val;
+	struct {
+		/* [0:1] */
+		u32 color_mode:1;
+		u32 alpha_mode:1;
+		/* [2:3] */
+		u32 blend_mode:2;
+		u32 alpha_cal_mode:1;
+		/* [5:7] */
+		u32 factor_mode:3;
+		/* [8:9] */
+		u32 alpha_en:1;
+		u32 src_dst_swap:1;
+		u32 reserved:6;
+		/* [16:23] */
+		u32 glb_alpha:8;
+	} bits;
+};
+
+struct vop2_alpha {
+	union vop2_alpha_ctrl src_color_ctrl;
+	union vop2_alpha_ctrl dst_color_ctrl;
+	union vop2_alpha_ctrl src_alpha_ctrl;
+	union vop2_alpha_ctrl dst_alpha_ctrl;
+};
+
+struct vop2_alpha_config {
+	bool src_premulti_en;
+	bool dst_premulti_en;
+	bool src_pixel_alpha_en;
+	bool dst_pixel_alpha_en;
+	u16 src_glb_alpha_value;
+	u16 dst_glb_alpha_value;
+};
+
+struct vop2_win {
+	struct vop2 *vop2;
+	struct drm_plane base;
+	const struct vop2_win_data *data;
+	struct regmap_field *reg[VOP2_WIN_MAX_REG];
+
+	/**
+	 * @win_id: graphic window id, a cluster may be split into two
+	 * graphics windows.
+	 */
+	u8 win_id;
+	u8 delay;
+	u32 offset;
+
+	enum drm_plane_type type;
+};
+
+struct vop2_video_port {
+	struct drm_crtc crtc;
+	struct vop2 *vop2;
+	struct clk *dclk;
+	unsigned int id;
+	const struct vop2_video_port_regs *regs;
+	const struct vop2_video_port_data *data;
+
+	struct completion dsp_hold_completion;
+
+	/**
+	 * @win_mask: Bitmask of windows attached to the video port;
+	 */
+	u32 win_mask;
+
+	struct vop2_win *primary_plane;
+	struct drm_pending_vblank_event *event;
+
+	unsigned int nlayers;
+};
+
+struct vop2 {
+	struct device *dev;
+	struct drm_device *drm;
+	struct vop2_video_port vps[ROCKCHIP_MAX_CRTC];
+
+	const struct vop2_data *data;
+	/*
+	 * Number of windows that are registered as plane, may be less than the
+	 * total number of hardware windows.
+	 */
+	u32 registered_num_wins;
+
+	void __iomem *regs;
+	struct regmap *map;
+
+	struct regmap *grf;
+
+	/* physical map length of vop2 register */
+	u32 len;
+
+	void __iomem *lut_regs;
+
+	/* protects crtc enable/disable */
+	struct mutex vop2_lock;
+
+	int irq;
+
+	/*
+	 * Some global resources are shared between all video ports(crtcs), so
+	 * we need a ref counter here.
+	 */
+	unsigned int enable_count;
+	struct clk *hclk;
+	struct clk *aclk;
+
+	/* must be put at the end of the struct */
+	struct vop2_win win[];
+};
+
+static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
+{
+	return container_of(crtc, struct vop2_video_port, crtc);
+}
+
+static struct vop2_win *to_vop2_win(struct drm_plane *p)
+{
+	return container_of(p, struct vop2_win, base);
+}
+
+static void vop2_lock(struct vop2 *vop2)
+{
+	mutex_lock(&vop2->vop2_lock);
+}
+
+static void vop2_unlock(struct vop2 *vop2)
+{
+	mutex_unlock(&vop2->vop2_lock);
+}
+
+static void vop2_writel(struct vop2 *vop2, u32 offset, u32 v)
+{
+	regmap_write(vop2->map, offset, v);
+}
+
+static void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
+{
+	regmap_write(vp->vop2->map, vp->data->offset + offset, v);
+}
+
+static u32 vop2_readl(struct vop2 *vop2, u32 offset)
+{
+	u32 val;
+
+	regmap_read(vop2->map, offset, &val);
+
+	return val;
+}
+
+static void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v)
+{
+	regmap_field_write(win->reg[reg], v);
+}
+
+static bool vop2_cluster_window(const struct vop2_win *win)
+{
+	return win->data->feature & WIN_FEATURE_CLUSTER;
+}
+
+static void vop2_cfg_done(struct vop2_video_port *vp)
+{
+	struct vop2 *vop2 = vp->vop2;
+
+	regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE,
+			BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
+}
+
+static void vop2_win_disable(struct vop2_win *win)
+{
+	vop2_win_write(win, VOP2_WIN_ENABLE, 0);
+
+	if (vop2_cluster_window(win))
+		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0);
+}
+
+static enum vop2_data_format vop2_convert_format(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+		return VOP2_FMT_ARGB8888;
+	case DRM_FORMAT_RGB888:
+	case DRM_FORMAT_BGR888:
+		return VOP2_FMT_RGB888;
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_BGR565:
+		return VOP2_FMT_RGB565;
+	case DRM_FORMAT_NV12:
+		return VOP2_FMT_YUV420SP;
+	case DRM_FORMAT_NV16:
+		return VOP2_FMT_YUV422SP;
+	case DRM_FORMAT_NV24:
+		return VOP2_FMT_YUV444SP;
+	case DRM_FORMAT_YUYV:
+	case DRM_FORMAT_YVYU:
+		return VOP2_FMT_VYUY422;
+	case DRM_FORMAT_VYUY:
+	case DRM_FORMAT_UYVY:
+		return VOP2_FMT_YUYV422;
+	default:
+		DRM_ERROR("unsupported format[%08x]\n", format);
+		return -EINVAL;
+	}
+}
+
+static enum vop2_afbc_format vop2_convert_afbc_format(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+		return VOP2_AFBC_FMT_ARGB8888;
+	case DRM_FORMAT_RGB888:
+	case DRM_FORMAT_BGR888:
+		return VOP2_AFBC_FMT_RGB888;
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_BGR565:
+		return VOP2_AFBC_FMT_RGB565;
+	case DRM_FORMAT_NV12:
+		return VOP2_AFBC_FMT_YUV420;
+	case DRM_FORMAT_NV16:
+		return VOP2_AFBC_FMT_YUV422;
+	default:
+		return VOP2_AFBC_FMT_INVALID;
+	}
+
+	return VOP2_AFBC_FMT_INVALID;
+}
+
+static bool vop2_win_rb_swap(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_BGR888:
+	case DRM_FORMAT_BGR565:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool vop2_afbc_rb_swap(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_NV24:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool vop2_afbc_uv_swap(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_NV12:
+	case DRM_FORMAT_NV16:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool vop2_win_uv_swap(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_NV12:
+	case DRM_FORMAT_NV16:
+	case DRM_FORMAT_NV24:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool vop2_win_dither_up(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_BGR565:
+	case DRM_FORMAT_RGB565:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
+{
+	/*
+	 * FIXME:
+	 *
+	 * There is no media type for YUV444 output,
+	 * so when out_mode is AAAA or P888, assume output is YUV444 on
+	 * yuv format.
+	 *
+	 * From H/W testing, YUV444 mode need a rb swap.
+	 */
+	if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 ||
+	    bus_format == MEDIA_BUS_FMT_VYUY8_1X16 ||
+	    bus_format == MEDIA_BUS_FMT_YVYU8_2X8 ||
+	    bus_format == MEDIA_BUS_FMT_VYUY8_2X8 ||
+	    ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
+	      bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
+	     (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
+	      output_mode == ROCKCHIP_OUT_MODE_P888)))
+		return true;
+	else
+		return false;
+}
+
+static bool is_yuv_output(u32 bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+	case MEDIA_BUS_FMT_YUYV8_2X8:
+	case MEDIA_BUS_FMT_YVYU8_2X8:
+	case MEDIA_BUS_FMT_UYVY8_2X8:
+	case MEDIA_BUS_FMT_VYUY8_2X8:
+	case MEDIA_BUS_FMT_YUYV8_1X16:
+	case MEDIA_BUS_FMT_YVYU8_1X16:
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+	case MEDIA_BUS_FMT_VYUY8_1X16:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool rockchip_afbc(struct drm_plane *plane, u64 modifier)
+{
+	int i;
+
+	if (modifier == DRM_FORMAT_MOD_LINEAR)
+		return false;
+
+	for (i = 0 ; i < plane->modifier_count; i++)
+		if (plane->modifiers[i] == modifier)
+			return true;
+
+	return false;
+
+}
+
+static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
+					u64 modifier)
+{
+	struct vop2_win *win = to_vop2_win(plane);
+	struct vop2 *vop2 = win->vop2;
+
+	if (modifier == DRM_FORMAT_MOD_INVALID)
+		return false;
+
+	if (modifier == DRM_FORMAT_MOD_LINEAR)
+		return true;
+
+	if (!rockchip_afbc(plane, modifier)) {
+		drm_err(vop2->drm, "Unsupported format modifier 0x%llx\n",
+			modifier);
+
+		return false;
+	}
+
+	return vop2_convert_afbc_format(format) >= 0;
+}
+
+static u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate,
+				      bool afbc_half_block_en)
+{
+	struct drm_rect *src = &pstate->src;
+	struct drm_framebuffer *fb = pstate->fb;
+	u32 bpp = fb->format->cpp[0] * 8;
+	u32 vir_width = (fb->pitches[0] << 3) / bpp;
+	u32 width = drm_rect_width(src) >> 16;
+	u32 height = drm_rect_height(src) >> 16;
+	u32 act_xoffset = src->x1 >> 16;
+	u32 act_yoffset = src->y1 >> 16;
+	u32 align16_crop = 0;
+	u32 align64_crop = 0;
+	u32 height_tmp;
+	u8 tx, ty;
+	u8 bottom_crop_line_num = 0;
+
+	/* 16 pixel align */
+	if (height & 0xf)
+		align16_crop = 16 - (height & 0xf);
+
+	height_tmp = height + align16_crop;
+
+	/* 64 pixel align */
+	if (height_tmp & 0x3f)
+		align64_crop = 64 - (height_tmp & 0x3f);
+
+	bottom_crop_line_num = align16_crop + align64_crop;
+
+	switch (pstate->rotation &
+		(DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y |
+		 DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) {
+	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
+		tx = 16 - ((act_xoffset + width) & 0xf);
+		ty = bottom_crop_line_num - act_yoffset;
+		break;
+	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90:
+		tx = bottom_crop_line_num - act_yoffset;
+		ty = vir_width - width - act_xoffset;
+		break;
+	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270:
+		tx = act_yoffset;
+		ty = act_xoffset;
+		break;
+	case DRM_MODE_REFLECT_X:
+		tx = 16 - ((act_xoffset + width) & 0xf);
+		ty = act_yoffset;
+		break;
+	case DRM_MODE_REFLECT_Y:
+		tx = act_xoffset;
+		ty = bottom_crop_line_num - act_yoffset;
+		break;
+	case DRM_MODE_ROTATE_90:
+		tx = bottom_crop_line_num - act_yoffset;
+		ty = act_xoffset;
+		break;
+	case DRM_MODE_ROTATE_270:
+		tx = act_yoffset;
+		ty = vir_width - width - act_xoffset;
+		break;
+	case 0:
+		tx = act_xoffset;
+		ty = act_yoffset;
+		break;
+	}
+
+	if (afbc_half_block_en)
+		ty &= 0x7f;
+
+#define TRANSFORM_XOFFSET GENMASK(7, 0)
+#define TRANSFORM_YOFFSET GENMASK(23, 16)
+	return FIELD_PREP(TRANSFORM_XOFFSET, tx) |
+		FIELD_PREP(TRANSFORM_YOFFSET, ty);
+}
+
+/*
+ * A Cluster window has 2048 x 16 line buffer, which can
+ * works at 2048 x 16(Full) or 4096 x 8 (Half) mode.
+ * for Cluster_lb_mode register:
+ * 0: half mode, for plane input width range 2048 ~ 4096
+ * 1: half mode, for cluster work at 2 * 2048 plane mode
+ * 2: half mode, for rotate_90/270 mode
+ *
+ */
+static int vop2_get_cluster_lb_mode(struct vop2_win *win,
+				    struct drm_plane_state *pstate)
+{
+	if ((pstate->rotation & DRM_MODE_ROTATE_270) ||
+	    (pstate->rotation & DRM_MODE_ROTATE_90))
+		return 2;
+	else
+		return 0;
+}
+
+static u16 vop2_scale_factor(u32 src, u32 dst)
+{
+	u32 fac;
+	int shift;
+
+	if (src == dst)
+		return 0;
+
+	if (dst < 2)
+		return U16_MAX;
+
+	if (src < 2)
+		return 0;
+
+	if (src > dst)
+		shift = 12;
+	else
+		shift = 16;
+
+	src--;
+	dst--;
+
+	fac = DIV_ROUND_UP(src << shift, dst) - 1;
+
+	if (fac > U16_MAX)
+		return U16_MAX;
+
+	return fac;
+}
+
+static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
+			     u32 src_w, u32 src_h, u32 dst_w,
+			     u32 dst_h, u32 pixel_format)
+{
+	const struct drm_format_info *info;
+	u16 hor_scl_mode, ver_scl_mode;
+	u16 hscl_filter_mode, vscl_filter_mode;
+	u8 gt2 = 0;
+	u8 gt4 = 0;
+	u32 val;
+
+	info = drm_format_info(pixel_format);
+
+	if (src_h >= (4 * dst_h)) {
+		gt4 = 1;
+		src_h >>= 2;
+	} else if (src_h >= (2 * dst_h)) {
+		gt2 = 1;
+		src_h >>= 1;
+	}
+
+	hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
+	ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
+
+	if (hor_scl_mode == SCALE_UP)
+		hscl_filter_mode = VOP2_SCALE_UP_BIC;
+	else
+		hscl_filter_mode = VOP2_SCALE_DOWN_BIL;
+
+	if (ver_scl_mode == SCALE_UP)
+		vscl_filter_mode = VOP2_SCALE_UP_BIL;
+	else
+		vscl_filter_mode = VOP2_SCALE_DOWN_BIL;
+
+	/*
+	 * RK3568 VOP Esmart/Smart dsp_w should be even pixel
+	 * at scale down mode
+	 */
+	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
+		if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) {
+			drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n",
+				win->data->name, dst_w);
+			dst_w++;
+		}
+	}
+
+	val = vop2_scale_factor(src_w, dst_w);
+	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val);
+	val = vop2_scale_factor(src_h, dst_h);
+	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val);
+
+	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4);
+	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2);
+
+	vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode);
+	vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode);
+
+	if (vop2_cluster_window(win))
+		return;
+
+	vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode);
+	vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode);
+
+	if (info->is_yuv) {
+		src_w /= info->hsub;
+		src_h /= info->vsub;
+
+		gt4 = gt2 = 0;
+
+		if (src_h >= (4 * dst_h)) {
+			gt4 = 1;
+			src_h >>= 2;
+		} else if (src_h >= (2 * dst_h)) {
+			gt2 = 1;
+			src_h >>= 1;
+		}
+
+		hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
+		ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
+
+		val = vop2_scale_factor(src_w, dst_w);
+		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val);
+
+		val = vop2_scale_factor(src_h, dst_h);
+		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val);
+
+		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4);
+		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2);
+		vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode);
+		vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode);
+		vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode);
+		vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode);
+	}
+}
+
+static int vop2_convert_csc_mode(int csc_mode)
+{
+	switch (csc_mode) {
+	case V4L2_COLORSPACE_SMPTE170M:
+	case V4L2_COLORSPACE_470_SYSTEM_M:
+	case V4L2_COLORSPACE_470_SYSTEM_BG:
+		return CSC_BT601L;
+	case V4L2_COLORSPACE_REC709:
+	case V4L2_COLORSPACE_SMPTE240M:
+	case V4L2_COLORSPACE_DEFAULT:
+		return CSC_BT709L;
+	case V4L2_COLORSPACE_JPEG:
+		return CSC_BT601F;
+	case V4L2_COLORSPACE_BT2020:
+		return CSC_BT2020;
+	default:
+		return CSC_BT709L;
+	}
+}
+
+/*
+ * colorspace path:
+ *      Input        Win csc                     Output
+ * 1. YUV(2020)  --> Y2R->2020To709->R2Y   --> YUV_OUTPUT(601/709)
+ *    RGB        --> R2Y                  __/
+ *
+ * 2. YUV(2020)  --> bypasss               --> YUV_OUTPUT(2020)
+ *    RGB        --> 709To2020->R2Y       __/
+ *
+ * 3. YUV(2020)  --> Y2R->2020To709        --> RGB_OUTPUT(709)
+ *    RGB        --> R2Y                  __/
+ *
+ * 4. YUV(601/709)-> Y2R->709To2020->R2Y   --> YUV_OUTPUT(2020)
+ *    RGB        --> 709To2020->R2Y       __/
+ *
+ * 5. YUV(601/709)-> bypass                --> YUV_OUTPUT(709)
+ *    RGB        --> R2Y                  __/
+ *
+ * 6. YUV(601/709)-> bypass                --> YUV_OUTPUT(601)
+ *    RGB        --> R2Y(601)             __/
+ *
+ * 7. YUV        --> Y2R(709)              --> RGB_OUTPUT(709)
+ *    RGB        --> bypass               __/
+ *
+ * 8. RGB        --> 709To2020->R2Y        --> YUV_OUTPUT(2020)
+ *
+ * 9. RGB        --> R2Y(709)              --> YUV_OUTPUT(709)
+ *
+ * 10. RGB       --> R2Y(601)              --> YUV_OUTPUT(601)
+ *
+ * 11. RGB       --> bypass                --> RGB_OUTPUT(709)
+ */
+
+static void vop2_setup_csc_mode(struct vop2_video_port *vp,
+				struct vop2_win *win,
+				struct drm_plane_state *pstate)
+{
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
+	int is_input_yuv = pstate->fb->format->is_yuv;
+	int is_output_yuv = is_yuv_output(vcstate->bus_format);
+	int input_csc = V4L2_COLORSPACE_DEFAULT;
+	int output_csc = vcstate->color_space;
+	bool r2y_en, y2r_en;
+	int csc_mode;
+
+	if (is_input_yuv && !is_output_yuv) {
+		y2r_en = true;
+		r2y_en = false;
+		csc_mode = vop2_convert_csc_mode(input_csc);
+	} else if (!is_input_yuv && is_output_yuv) {
+		y2r_en = false;
+		r2y_en = true;
+		csc_mode = vop2_convert_csc_mode(output_csc);
+	} else {
+		y2r_en = false;
+		r2y_en = false;
+		csc_mode = false;
+	}
+
+	vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en);
+	vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en);
+	vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
+}
+
+static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
+{
+	struct vop2 *vop2 = vp->vop2;
+
+	vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq);
+	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq);
+}
+
+static void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq)
+{
+	struct vop2 *vop2 = vp->vop2;
+
+	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16);
+}
+
+static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
+{
+	int ret;
+
+	ret = clk_prepare_enable(vop2->hclk);
+	if (ret < 0) {
+		drm_err(vop2->drm, "failed to enable hclk - %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(vop2->aclk);
+	if (ret < 0) {
+		drm_err(vop2->drm, "failed to enable aclk - %d\n", ret);
+		goto err;
+	}
+
+	return 0;
+err:
+	clk_disable_unprepare(vop2->hclk);
+
+	return ret;
+}
+
+static void vop2_enable(struct vop2 *vop2)
+{
+	int ret;
+
+	ret = pm_runtime_get_sync(vop2->dev);
+	if (ret < 0) {
+		drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret);
+		return;
+	}
+
+	ret = vop2_core_clks_prepare_enable(vop2);
+	if (ret) {
+		pm_runtime_put_sync(vop2->dev);
+		return;
+	}
+
+	if (vop2->data->soc_id == 3566)
+		vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
+
+	vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
+
+	/*
+	 * Disable auto gating, this is a workaround to
+	 * avoid display image shift when a window enabled.
+	 */
+	regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL,
+			  RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN);
+
+	vop2_writel(vop2, RK3568_SYS0_INT_CLR,
+		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
+	vop2_writel(vop2, RK3568_SYS0_INT_EN,
+		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
+	vop2_writel(vop2, RK3568_SYS1_INT_CLR,
+		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
+	vop2_writel(vop2, RK3568_SYS1_INT_EN,
+		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
+}
+
+static void vop2_disable(struct vop2 *vop2)
+{
+	pm_runtime_put_sync(vop2->dev);
+
+	clk_disable_unprepare(vop2->aclk);
+	clk_disable_unprepare(vop2->hclk);
+}
+
+static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
+				     struct drm_atomic_state *state)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct vop2 *vop2 = vp->vop2;
+	int ret;
+
+	vop2_lock(vop2);
+
+	drm_crtc_vblank_off(crtc);
+
+	/*
+	 * Vop standby will take effect at end of current frame,
+	 * if dsp hold valid irq happen, it means standby complete.
+	 *
+	 * we must wait standby complete when we want to disable aclk,
+	 * if not, memory bus maybe dead.
+	 */
+	reinit_completion(&vp->dsp_hold_completion);
+
+	vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
+
+	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
+
+	ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
+					  msecs_to_jiffies(50));
+	if (!ret)
+		drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
+
+	vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
+
+	clk_disable_unprepare(vp->dclk);
+
+	vop2->enable_count--;
+
+	if (!vop2->enable_count)
+		vop2_disable(vop2);
+
+	vop2_unlock(vop2);
+
+	if (crtc->state->event && !crtc->state->active) {
+		spin_lock_irq(&crtc->dev->event_lock);
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+
+		crtc->state->event = NULL;
+	}
+}
+
+static int vop2_plane_atomic_check(struct drm_plane *plane,
+				   struct drm_atomic_state *astate)
+{
+	struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane);
+	struct drm_framebuffer *fb = pstate->fb;
+	struct drm_crtc *crtc = pstate->crtc;
+	struct drm_crtc_state *cstate;
+	struct vop2_video_port *vp;
+	struct vop2 *vop2;
+	const struct vop2_data *vop2_data;
+	struct drm_rect *dest = &pstate->dst;
+	struct drm_rect *src = &pstate->src;
+	int min_scale = FRAC_16_16(1, 8);
+	int max_scale = FRAC_16_16(8, 1);
+	int format;
+	int ret;
+
+	if (!crtc)
+		return 0;
+
+	vp = to_vop2_video_port(crtc);
+	vop2 = vp->vop2;
+	vop2_data = vop2->data;
+
+	cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc);
+	if (WARN_ON(!cstate))
+		return -EINVAL;
+
+	ret = drm_atomic_helper_check_plane_state(pstate, cstate,
+						  min_scale, max_scale,
+						  true, true);
+	if (ret)
+		return ret;
+
+	if (!pstate->visible)
+		return 0;
+
+	format = vop2_convert_format(fb->format->format);
+	if (format < 0)
+		return format;
+
+	if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 ||
+	    drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) {
+		drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
+			  drm_rect_width(src) >> 16, drm_rect_height(src) >> 16,
+			  drm_rect_width(dest), drm_rect_height(dest));
+		pstate->visible = false;
+		return 0;
+	}
+
+	if (drm_rect_width(src) >> 16 > vop2_data->max_input.width ||
+	    drm_rect_height(src) >> 16 > vop2_data->max_input.height) {
+		drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
+			  drm_rect_width(src) >> 16,
+			  drm_rect_height(src) >> 16,
+			  vop2_data->max_input.width,
+			  vop2_data->max_input.height);
+		return -EINVAL;
+	}
+
+	/*
+	 * Src.x1 can be odd when do clip, but yuv plane start point
+	 * need align with 2 pixel.
+	 */
+	if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
+		drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void vop2_plane_atomic_disable(struct drm_plane *plane,
+				      struct drm_atomic_state *state)
+{
+	struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state, plane);
+	struct vop2_win *win = to_vop2_win(plane);
+	struct vop2 *vop2 = win->vop2;
+
+	drm_dbg(vop2->drm, "%s disable\n", win->data->name);
+
+	if (!old_pstate->crtc)
+		return;
+
+	vop2_win_disable(win);
+	vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0);
+}
+
+/*
+ * The color key is 10 bit, so all format should
+ * convert to 10 bit here.
+ */
+static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
+{
+	struct drm_plane_state *pstate = plane->state;
+	struct drm_framebuffer *fb = pstate->fb;
+	struct vop2_win *win = to_vop2_win(plane);
+	u32 color_key_en = 0;
+	u32 r = 0;
+	u32 g = 0;
+	u32 b = 0;
+
+	if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) {
+		vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0);
+		return;
+	}
+
+	switch (fb->format->format) {
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_BGR565:
+		r = (color_key & 0xf800) >> 11;
+		g = (color_key & 0x7e0) >> 5;
+		b = (color_key & 0x1f);
+		r <<= 5;
+		g <<= 4;
+		b <<= 5;
+		color_key_en = 1;
+		break;
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_RGB888:
+	case DRM_FORMAT_BGR888:
+		r = (color_key & 0xff0000) >> 16;
+		g = (color_key & 0xff00) >> 8;
+		b = (color_key & 0xff);
+		r <<= 2;
+		g <<= 2;
+		b <<= 2;
+		color_key_en = 1;
+		break;
+	}
+
+	vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en);
+	vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b);
+}
+
+static void vop2_plane_atomic_update(struct drm_plane *plane,
+				     struct drm_atomic_state *state)
+{
+	struct drm_plane_state *pstate = plane->state;
+	struct drm_crtc *crtc = pstate->crtc;
+	struct vop2_win *win = to_vop2_win(plane);
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
+	struct vop2 *vop2 = win->vop2;
+	struct drm_framebuffer *fb = pstate->fb;
+	u32 bpp = fb->format->cpp[0] * 8;
+	u32 actual_w, actual_h, dsp_w, dsp_h;
+	u32 act_info, dsp_info;
+	u32 format;
+	u32 afbc_format;
+	u32 rb_swap;
+	u32 uv_swap;
+	struct drm_rect *src = &pstate->src;
+	struct drm_rect *dest = &pstate->dst;
+	u32 afbc_tile_num;
+	u32 transform_offset;
+	bool dither_up;
+	bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
+	bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
+	bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
+	bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
+	struct rockchip_gem_object *rk_obj;
+	unsigned long offset;
+	bool afbc_en;
+	dma_addr_t yrgb_mst;
+	dma_addr_t uv_mst;
+
+	/*
+	 * can't update plane when vop2 is disabled.
+	 */
+	if (WARN_ON(!crtc))
+		return;
+
+	if (!pstate->visible) {
+		vop2_plane_atomic_disable(plane, state);
+		return;
+	}
+
+	afbc_en = rockchip_afbc(plane, fb->modifier);
+
+	offset = (src->x1 >> 16) * fb->format->cpp[0];
+
+	/*
+	 * AFBC HDR_PTR must set to the zero offset of the framebuffer.
+	 */
+	if (afbc_en)
+		offset = 0;
+	else if (pstate->rotation & DRM_MODE_REFLECT_Y)
+		offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
+	else
+		offset += (src->y1 >> 16) * fb->pitches[0];
+
+	rk_obj = to_rockchip_obj(fb->obj[0]);
+
+	yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
+	if (fb->format->is_yuv) {
+		int hsub = fb->format->hsub;
+		int vsub = fb->format->vsub;
+
+		offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub;
+		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
+
+		if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en)
+			offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub;
+
+		rk_obj = to_rockchip_obj(fb->obj[0]);
+		uv_mst = rk_obj->dma_addr + offset + fb->offsets[1];
+	}
+
+	actual_w = drm_rect_width(src) >> 16;
+	actual_h = drm_rect_height(src) >> 16;
+	dsp_w = drm_rect_width(dest);
+
+	if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
+		drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
+			  vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
+		dsp_w = adjusted_mode->hdisplay - dest->x1;
+		if (dsp_w < 4)
+			dsp_w = 4;
+		actual_w = dsp_w * actual_w / drm_rect_width(dest);
+	}
+
+	dsp_h = drm_rect_height(dest);
+
+	if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
+		drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
+			  vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
+		dsp_h = adjusted_mode->vdisplay - dest->y1;
+		if (dsp_h < 4)
+			dsp_h = 4;
+		actual_h = dsp_h * actual_h / drm_rect_height(dest);
+	}
+
+	/*
+	 * This is workaround solution for IC design:
+	 * esmart can't support scale down when actual_w % 16 == 1.
+	 */
+	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
+		if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
+			drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
+				vp->id, win->data->name, actual_w);
+			actual_w -= 1;
+		}
+	}
+
+	if (afbc_en && actual_w % 4) {
+		drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
+			  vp->id, win->data->name, actual_w);
+		actual_w = ALIGN_DOWN(actual_w, 4);
+	}
+
+	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
+	dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff);
+
+	format = vop2_convert_format(fb->format->format);
+
+	drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n",
+		      vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h,
+		      dest->x1, dest->y1,
+		      &fb->format->format,
+		      afbc_en ? "AFBC" : "", &yrgb_mst);
+
+	if (afbc_en) {
+		u32 stride;
+
+		/* the afbc superblock is 16 x 16 */
+		afbc_format = vop2_convert_afbc_format(fb->format->format);
+
+		/* Enable color transform for YTR */
+		if (fb->modifier & AFBC_FORMAT_MOD_YTR)
+			afbc_format |= (1 << 4);
+
+		afbc_tile_num = ALIGN(actual_w, 16) >> 4;
+
+		/*
+		 * AFBC pic_vir_width is count by pixel, this is different
+		 * with WIN_VIR_STRIDE.
+		 */
+		stride = (fb->pitches[0] << 3) / bpp;
+		if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
+			drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligened\n",
+				  vp->id, win->data->name, stride);
+
+		rb_swap = vop2_afbc_rb_swap(fb->format->format);
+		uv_swap = vop2_afbc_uv_swap(fb->format->format);
+		/*
+		 * This is a workaround for crazy IC design, Cluster
+		 * and Esmart/Smart use different format configuration map:
+		 * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart.
+		 *
+		 * This is one thing we can make the convert simple:
+		 * AFBCD decode all the YUV data to YUV444. So we just
+		 * set all the yuv 10 bit to YUV444_10.
+		 */
+		if (fb->format->is_yuv && (bpp == 10))
+			format = VOP2_CLUSTER_YUV444_10;
+
+		if (vop2_cluster_window(win))
+			vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
+		vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
+		vop2_win_write(win, VOP2_WIN_AFBC_RB_SWAP, rb_swap);
+		vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap);
+		vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
+		vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
+		if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) {
+			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0);
+			transform_offset = vop2_afbc_transform_offset(pstate, false);
+		} else {
+			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 1);
+			transform_offset = vop2_afbc_transform_offset(pstate, true);
+		}
+		vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
+		vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
+		vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset);
+		vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1));
+		vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16)));
+		vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride);
+		vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num);
+		vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror);
+		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270);
+		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90);
+	} else {
+		vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4));
+	}
+
+	vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror);
+
+	if (rotate_90 || rotate_270) {
+		act_info = swahw32(act_info);
+		actual_w = drm_rect_height(src) >> 16;
+		actual_h = drm_rect_width(src) >> 16;
+	}
+
+	vop2_win_write(win, VOP2_WIN_FORMAT, format);
+	vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst);
+
+	rb_swap = vop2_win_rb_swap(fb->format->format);
+	vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap);
+	if (!vop2_cluster_window(win)) {
+		uv_swap = vop2_win_uv_swap(fb->format->format);
+		vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
+	}
+
+	if (fb->format->is_yuv) {
+		vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4));
+		vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst);
+	}
+
+	vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format);
+	if (!vop2_cluster_window(win))
+		vop2_plane_setup_color_key(plane, 0);
+	vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info);
+	vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info);
+	vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff));
+
+	vop2_setup_csc_mode(vp, win, pstate);
+
+	dither_up = vop2_win_dither_up(fb->format->format);
+	vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up);
+
+	vop2_win_write(win, VOP2_WIN_ENABLE, 1);
+
+	if (vop2_cluster_window(win)) {
+		int lb_mode = vop2_get_cluster_lb_mode(win, pstate);
+
+		vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode);
+		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1);
+	}
+}
+
+static const struct drm_plane_helper_funcs vop2_plane_helper_funcs = {
+	.atomic_check = vop2_plane_atomic_check,
+	.atomic_update = vop2_plane_atomic_update,
+	.atomic_disable = vop2_plane_atomic_disable,
+};
+
+static const struct drm_plane_funcs vop2_plane_funcs = {
+	.update_plane	= drm_atomic_helper_update_plane,
+	.disable_plane	= drm_atomic_helper_disable_plane,
+	.destroy = drm_plane_cleanup,
+	.reset = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+	.format_mod_supported = rockchip_vop2_mod_supported,
+};
+
+static int vop2_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+
+	vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD);
+
+	return 0;
+}
+
+static void vop2_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+
+	vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD);
+}
+
+static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
+				 const struct drm_display_mode *mode,
+				 struct drm_display_mode *adj_mode)
+{
+	drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V |
+					CRTC_STEREO_DOUBLE);
+
+	return true;
+}
+
+static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
+{
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
+
+	switch (vcstate->bus_format) {
+	case MEDIA_BUS_FMT_RGB565_1X16:
+		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
+		break;
+	case MEDIA_BUS_FMT_RGB666_1X18:
+	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
+		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
+		*dsp_ctrl |= RGB888_TO_RGB666;
+		break;
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
+		break;
+	default:
+		break;
+	}
+
+	if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA)
+		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
+
+	*dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL,
+				DITHER_DOWN_ALLEGRO);
+}
+
+static void vop2_post_config(struct drm_crtc *crtc)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+	u16 vtotal = mode->crtc_vtotal;
+	u16 hdisplay = mode->crtc_hdisplay;
+	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
+	u16 vdisplay = mode->crtc_vdisplay;
+	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
+	u32 left_margin = 100, right_margin = 100;
+	u32 top_margin = 100, bottom_margin = 100;
+	u16 hsize = hdisplay * (left_margin + right_margin) / 200;
+	u16 vsize = vdisplay * (top_margin + bottom_margin) / 200;
+	u16 hact_end, vact_end;
+	u32 val;
+
+	vsize = rounddown(vsize, 2);
+	hsize = rounddown(hsize, 2);
+	hact_st += hdisplay * (100 - left_margin) / 200;
+	hact_end = hact_st + hsize;
+	val = hact_st << 16;
+	val |= hact_end;
+	vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val);
+	vact_st += vdisplay * (100 - top_margin) / 200;
+	vact_end = vact_st + vsize;
+	val = vact_st << 16;
+	val |= vact_end;
+	vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val);
+	val = scl_cal_scale2(vdisplay, vsize) << 16;
+	val |= scl_cal_scale2(hdisplay, hsize);
+	vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val);
+
+	val = 0;
+	if (hdisplay != hsize)
+		val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN;
+	if (vdisplay != vsize)
+		val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN;
+	vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val);
+
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		u16 vact_st_f1 = vtotal + vact_st + 1;
+		u16 vact_end_f1 = vact_st_f1 + vsize;
+
+		val = vact_st_f1 << 16 | vact_end_f1;
+		vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
+	}
+
+	vop2_vp_write(vp, RK3568_VP_DSP_BG, 0);
+}
+
+static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
+				u32 polflags)
+{
+	struct vop2 *vop2 = vp->vop2;
+	u32 die, dip;
+
+	die = vop2_readl(vop2, RK3568_DSP_IF_EN);
+	dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
+
+	switch (id) {
+	case RK3568_VOP2_EP_RGB:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_RGB |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
+		if (polflags & POLFLAG_DCLK_INV)
+			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
+		else
+			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16));
+		break;
+	case RK3568_VOP2_EP_HDMI:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_HDMI |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id);
+		break;
+	case RK3568_VOP2_EP_EDP:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_EDP |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id);
+		break;
+	case RK3568_VOP2_EP_MIPI0:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id);
+		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
+		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
+		break;
+	case RK3568_VOP2_EP_MIPI1:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
+		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
+		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
+		break;
+	case RK3568_VOP2_EP_LVDS0:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id);
+		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
+		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
+		break;
+	case RK3568_VOP2_EP_LVDS1:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id);
+		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
+		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
+		break;
+	default:
+		return;
+	};
+
+	dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
+
+	vop2_writel(vop2, RK3568_DSP_IF_EN, die);
+	vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
+}
+
+static int us_to_vertical_line(struct drm_display_mode *mode, int us)
+{
+	return us * mode->clock / mode->htotal / 1000;
+}
+
+static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
+				    struct drm_atomic_state *state)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct vop2 *vop2 = vp->vop2;
+	const struct vop2_data *vop2_data = vop2->data;
+	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
+	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+	unsigned long clock = mode->crtc_clock * 1000;
+	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
+	u16 hdisplay = mode->crtc_hdisplay;
+	u16 htotal = mode->crtc_htotal;
+	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
+	u16 hact_end = hact_st + hdisplay;
+	u16 vdisplay = mode->crtc_vdisplay;
+	u16 vtotal = mode->crtc_vtotal;
+	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
+	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
+	u16 vact_end = vact_st + vdisplay;
+	u8 out_mode;
+	u32 dsp_ctrl = 0;
+	int act_end;
+	u32 val, polflags;
+	int ret;
+	struct drm_encoder *encoder;
+
+	drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n",
+		hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
+		drm_mode_vrefresh(mode), vcstate->output_type, vp->id);
+
+	vop2_lock(vop2);
+
+	ret = clk_prepare_enable(vp->dclk);
+	if (ret < 0) {
+		drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
+			      vp->id, ret);
+		return;
+	}
+
+	if (!vop2->enable_count)
+		vop2_enable(vop2);
+
+	vop2->enable_count++;
+
+	vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY);
+
+	polflags = 0;
+	if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
+		polflags |= POLFLAG_DCLK_INV;
+	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+		polflags |= BIT(HSYNC_POSITIVE);
+	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+		polflags |= BIT(VSYNC_POSITIVE);
+
+	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
+		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+		struct device_node *node, *parent;
+
+		parent = of_get_parent(rkencoder->port);
+
+		for_each_endpoint_of_node(parent, node) {
+			struct device_node *crtc_port = of_graph_get_remote_port(node);
+			struct device_node *epn;
+			struct of_endpoint endpoint;
+
+			if (crtc->port != crtc_port) {
+				of_node_put(crtc_port);
+				continue;
+			}
+
+			of_node_put(crtc_port);
+
+			epn = of_graph_get_remote_endpoint(node);
+			of_graph_parse_endpoint(epn, &endpoint);
+			of_node_put(epn);
+
+			drm_dbg(vop2->drm, "vp%d is connected to %s, id %d\n",
+					   vp->id, encoder->name, endpoint.id);
+			rk3568_set_intf_mux(vp, endpoint.id, polflags);
+		}
+		of_node_put(parent);
+	}
+
+	if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
+	     !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT))
+		out_mode = ROCKCHIP_OUT_MODE_P888;
+	else
+		out_mode = vcstate->output_mode;
+
+	dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode);
+
+	if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP;
+
+	if (is_yuv_output(vcstate->bus_format))
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y;
+
+	vop2_dither_setup(crtc, &dsp_ctrl);
+
+	vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len);
+	val = hact_st << 16;
+	val |= hact_end;
+	vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val);
+
+	val = vact_st << 16;
+	val |= vact_end;
+	vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val);
+
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		u16 vact_st_f1 = vtotal + vact_st + 1;
+		u16 vact_end_f1 = vact_st_f1 + vdisplay;
+
+		val = vact_st_f1 << 16 | vact_end_f1;
+		vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val);
+
+		val = vtotal << 16 | (vtotal + vsync_len);
+		vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val);
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE;
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL;
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN;
+		vtotal += vtotal + 1;
+		act_end = vact_end_f1;
+	} else {
+		act_end = vact_end;
+	}
+
+	vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id),
+		    (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end);
+
+	vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len);
+
+	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
+		clock *= 2;
+	}
+
+	vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
+
+	clk_set_rate(vp->dclk, clock);
+
+	vop2_post_config(crtc);
+
+	vop2_cfg_done(vp);
+
+	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
+
+	drm_crtc_vblank_on(crtc);
+
+	vop2_unlock(vop2);
+}
+
+static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
+				  struct drm_atomic_state *state)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct drm_plane *plane;
+	int nplanes = 0;
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+
+	drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
+		nplanes++;
+
+	if (nplanes > vp->nlayers)
+		return -EINVAL;
+
+	return 0;
+}
+
+static bool is_opaque(u16 alpha)
+{
+	return (alpha >> 8) == 0xff;
+}
+
+static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config,
+			     struct vop2_alpha *alpha)
+{
+	int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1;
+	int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1;
+	int src_color_mode = alpha_config->src_premulti_en ?
+				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
+	int dst_color_mode = alpha_config->dst_premulti_en ?
+				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
+
+	alpha->src_color_ctrl.val = 0;
+	alpha->dst_color_ctrl.val = 0;
+	alpha->src_alpha_ctrl.val = 0;
+	alpha->dst_alpha_ctrl.val = 0;
+
+	if (!alpha_config->src_pixel_alpha_en)
+		alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
+	else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en)
+		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX;
+	else
+		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
+
+	alpha->src_color_ctrl.bits.alpha_en = 1;
+
+	if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) {
+		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
+		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
+	} else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) {
+		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
+		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE;
+	} else {
+		alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL;
+		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
+	}
+	alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8;
+	alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
+	alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
+
+	alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
+	alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
+	alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
+	alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8;
+	alpha->dst_color_ctrl.bits.color_mode = dst_color_mode;
+	alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
+
+	alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
+	alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode;
+	alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
+	alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE;
+
+	alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
+	if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en)
+		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX;
+	else
+		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
+	alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION;
+	alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
+}
+
+static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id)
+{
+	struct vop2_video_port *vp;
+	int used_layer = 0;
+	int i;
+
+	for (i = 0; i < port_id; i++) {
+		vp = &vop2->vps[i];
+		used_layer += hweight32(vp->win_mask);
+	}
+
+	return used_layer;
+}
+
+static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win)
+{
+	u32 offset = (main_win->data->phys_id * 0x10);
+	struct vop2_alpha_config alpha_config;
+	struct vop2_alpha alpha;
+	struct drm_plane_state *bottom_win_pstate;
+	bool src_pixel_alpha_en = false;
+	u16 src_glb_alpha_val, dst_glb_alpha_val;
+	bool premulti_en = false;
+	bool swap = false;
+
+	/* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */
+	bottom_win_pstate = main_win->base.state;
+	src_glb_alpha_val = 0;
+	dst_glb_alpha_val = main_win->base.state->alpha;
+
+	if (!bottom_win_pstate->fb)
+		return;
+
+	alpha_config.src_premulti_en = premulti_en;
+	alpha_config.dst_premulti_en = false;
+	alpha_config.src_pixel_alpha_en = src_pixel_alpha_en;
+	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
+	alpha_config.src_glb_alpha_value = src_glb_alpha_val;
+	alpha_config.dst_glb_alpha_value = dst_glb_alpha_val;
+	vop2_parse_alpha(&alpha_config, &alpha);
+
+	alpha.src_color_ctrl.bits.src_dst_swap = swap;
+	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset,
+		    alpha.src_color_ctrl.val);
+	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset,
+		    alpha.dst_color_ctrl.val);
+	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset,
+		    alpha.src_alpha_ctrl.val);
+	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset,
+		    alpha.dst_alpha_ctrl.val);
+}
+
+static void vop2_setup_alpha(struct vop2_video_port *vp)
+{
+	struct vop2 *vop2 = vp->vop2;
+	struct drm_framebuffer *fb;
+	struct vop2_alpha_config alpha_config;
+	struct vop2_alpha alpha;
+	struct drm_plane *plane;
+	int pixel_alpha_en;
+	int premulti_en, gpremulti_en = 0;
+	int mixer_id;
+	u32 offset;
+	bool bottom_layer_alpha_en = false;
+	u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE;
+
+	mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id);
+	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
+
+	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
+		struct vop2_win *win = to_vop2_win(plane);
+
+		if (plane->state->normalized_zpos == 0 &&
+		    !is_opaque(plane->state->alpha) &&
+		    !vop2_cluster_window(win)) {
+			/*
+			 * If bottom layer have global alpha effect [except cluster layer,
+			 * because cluster have deal with bottom layer global alpha value
+			 * at cluster mix], bottom layer mix need deal with global alpha.
+			 */
+			bottom_layer_alpha_en = true;
+			dst_global_alpha = plane->state->alpha;
+		}
+	}
+
+	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
+		struct vop2_win *win = to_vop2_win(plane);
+		int zpos = plane->state->normalized_zpos;
+
+		if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
+			premulti_en = 1;
+		else
+			premulti_en = 0;
+
+		plane = &win->base;
+		fb = plane->state->fb;
+
+		pixel_alpha_en = fb->format->has_alpha;
+
+		alpha_config.src_premulti_en = premulti_en;
+
+		if (bottom_layer_alpha_en && zpos == 1) {
+			gpremulti_en = premulti_en;
+			/* Cd = Cs + (1 - As) * Cd * Agd */
+			alpha_config.dst_premulti_en = false;
+			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
+			alpha_config.src_glb_alpha_value = plane->state->alpha;
+			alpha_config.dst_glb_alpha_value = dst_global_alpha;
+		} else if (vop2_cluster_window(win)) {
+			/* Mix output data only have pixel alpha */
+			alpha_config.dst_premulti_en = true;
+			alpha_config.src_pixel_alpha_en = true;
+			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+		} else {
+			/* Cd = Cs + (1 - As) * Cd */
+			alpha_config.dst_premulti_en = true;
+			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
+			alpha_config.src_glb_alpha_value = plane->state->alpha;
+			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+		}
+
+		vop2_parse_alpha(&alpha_config, &alpha);
+
+		offset = (mixer_id + zpos - 1) * 0x10;
+		vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset,
+			    alpha.src_color_ctrl.val);
+		vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset,
+			    alpha.dst_color_ctrl.val);
+		vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset,
+			    alpha.src_alpha_ctrl.val);
+		vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset,
+			    alpha.dst_alpha_ctrl.val);
+	}
+
+	if (vp->id == 0) {
+		if (bottom_layer_alpha_en) {
+			/* Transfer pixel alpha to hdr mix */
+			alpha_config.src_premulti_en = gpremulti_en;
+			alpha_config.dst_premulti_en = true;
+			alpha_config.src_pixel_alpha_en = true;
+			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+			vop2_parse_alpha(&alpha_config, &alpha);
+
+			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL,
+				alpha.src_color_ctrl.val);
+			vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL,
+				alpha.dst_color_ctrl.val);
+			vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL,
+				alpha.src_alpha_ctrl.val);
+			vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL,
+				alpha.dst_alpha_ctrl.val);
+		} else {
+			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0);
+		}
+	}
+}
+
+static void vop2_setup_layer_mixer(struct vop2_video_port *vp)
+{
+	struct vop2 *vop2 = vp->vop2;
+	struct drm_plane *plane;
+	u32 layer_sel = 0;
+	u32 port_sel;
+	unsigned int nlayer, ofs;
+	struct drm_display_mode *adjusted_mode;
+	u16 hsync_len;
+	u16 hdisplay;
+	u32 bg_dly;
+	u32 pre_scan_dly;
+	int i;
+	struct vop2_video_port *vp0 = &vop2->vps[0];
+	struct vop2_video_port *vp1 = &vop2->vps[1];
+	struct vop2_video_port *vp2 = &vop2->vps[2];
+
+	adjusted_mode = &vp->crtc.state->adjusted_mode;
+	hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
+	hdisplay = adjusted_mode->crtc_hdisplay;
+
+	bg_dly = vp->data->pre_scan_max_dly[3];
+	vop2_writel(vop2, RK3568_VP_BG_MIX_CTRL(vp->id),
+			    FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly));
+
+	pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len;
+	vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly);
+
+	vop2_writel(vop2, RK3568_OVL_CTRL, 0);
+	port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
+	port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT;
+
+	if (vp0->nlayers)
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX,
+				     vp0->nlayers - 1);
+	else
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8);
+
+	if (vp1->nlayers)
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX,
+				     (vp0->nlayers + vp1->nlayers - 1));
+	else
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
+
+	if (vp2->nlayers)
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX,
+			(vp2->nlayers + vp1->nlayers + vp0->nlayers - 1));
+	else
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
+
+	layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
+
+	ofs = 0;
+	for (i = 0; i < vp->id; i++)
+		ofs += vop2->vps[i].nlayers;
+
+	nlayer = 0;
+	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
+		struct vop2_win *win = to_vop2_win(plane);
+
+		switch (win->data->phys_id) {
+		case ROCKCHIP_VOP2_CLUSTER0:
+			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id);
+			break;
+		case ROCKCHIP_VOP2_CLUSTER1:
+			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id);
+			break;
+		case ROCKCHIP_VOP2_ESMART0:
+			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id);
+			break;
+		case ROCKCHIP_VOP2_ESMART1:
+			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id);
+			break;
+		case ROCKCHIP_VOP2_SMART0:
+			port_sel &= ~RK3568_OVL_PORT_SEL__SMART0;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id);
+			break;
+		case ROCKCHIP_VOP2_SMART1:
+			port_sel &= ~RK3568_OVL_PORT_SEL__SMART1;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id);
+			break;
+		}
+
+		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
+		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, win->data->layer_sel_id);
+		nlayer++;
+	}
+
+	/* configure unused layers to 0x5 (reserved) */
+	for (; nlayer < 3; nlayer++) {
+		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
+		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5);
+	}
+
+	vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel);
+	vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
+	vop2_writel(vop2, RK3568_OVL_CTRL, RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD);
+}
+
+static void vop2_setup_dly_for_windows(struct vop2 *vop2)
+{
+	struct vop2_win *win;
+	int i = 0;
+	u32 cdly = 0, sdly = 0;
+
+	for (i = 0; i < vop2->data->win_size; i++) {
+		u32 dly;
+
+		win = &vop2->win[i];
+		dly = win->delay;
+
+		switch (win->data->phys_id) {
+		case ROCKCHIP_VOP2_CLUSTER0:
+			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly);
+			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly);
+			break;
+		case ROCKCHIP_VOP2_CLUSTER1:
+			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly);
+			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly);
+			break;
+		case ROCKCHIP_VOP2_ESMART0:
+			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly);
+			break;
+		case ROCKCHIP_VOP2_ESMART1:
+			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly);
+			break;
+		case ROCKCHIP_VOP2_SMART0:
+			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly);
+			break;
+		case ROCKCHIP_VOP2_SMART1:
+			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly);
+			break;
+		}
+	}
+
+	vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly);
+	vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly);
+}
+
+static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
+				   struct drm_atomic_state *state)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct vop2 *vop2 = vp->vop2;
+	struct drm_plane *plane;
+
+	vp->win_mask = 0;
+
+	drm_atomic_crtc_for_each_plane(plane, crtc) {
+		struct vop2_win *win = to_vop2_win(plane);
+
+		win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT];
+
+		vp->win_mask |= BIT(win->data->phys_id);
+
+		if (vop2_cluster_window(win))
+			vop2_setup_cluster_alpha(vop2, win);
+	}
+
+	if (!vp->win_mask)
+		return;
+
+	vop2_setup_layer_mixer(vp);
+	vop2_setup_alpha(vp);
+	vop2_setup_dly_for_windows(vop2);
+}
+
+static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
+				   struct drm_atomic_state *state)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+
+	vop2_post_config(crtc);
+
+	vop2_cfg_done(vp);
+
+	spin_lock_irq(&crtc->dev->event_lock);
+
+	if (crtc->state->event) {
+		WARN_ON(drm_crtc_vblank_get(crtc));
+		vp->event = crtc->state->event;
+		crtc->state->event = NULL;
+	}
+
+	spin_unlock_irq(&crtc->dev->event_lock);
+}
+
+static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
+	.mode_fixup = vop2_crtc_mode_fixup,
+	.atomic_check = vop2_crtc_atomic_check,
+	.atomic_begin = vop2_crtc_atomic_begin,
+	.atomic_flush = vop2_crtc_atomic_flush,
+	.atomic_enable = vop2_crtc_atomic_enable,
+	.atomic_disable = vop2_crtc_atomic_disable,
+};
+
+static void vop2_crtc_reset(struct drm_crtc *crtc)
+{
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
+
+	if (crtc->state) {
+		__drm_atomic_helper_crtc_destroy_state(crtc->state);
+		kfree(vcstate);
+	}
+
+	vcstate = kzalloc(sizeof(*vcstate), GFP_KERNEL);
+	if (!vcstate)
+		return;
+
+	crtc->state = &vcstate->base;
+	crtc->state->crtc = crtc;
+}
+
+static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc)
+{
+	struct rockchip_crtc_state *vcstate, *old_vcstate;
+
+	old_vcstate = to_rockchip_crtc_state(crtc->state);
+
+	vcstate = kmemdup(old_vcstate, sizeof(*old_vcstate), GFP_KERNEL);
+	if (!vcstate)
+		return NULL;
+
+	__drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
+
+	return &vcstate->base;
+}
+
+static void vop2_crtc_destroy_state(struct drm_crtc *crtc,
+				    struct drm_crtc_state *state)
+{
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state);
+
+	__drm_atomic_helper_crtc_destroy_state(&vcstate->base);
+	kfree(vcstate);
+}
+
+static const struct drm_crtc_funcs vop2_crtc_funcs = {
+	.set_config = drm_atomic_helper_set_config,
+	.page_flip = drm_atomic_helper_page_flip,
+	.destroy = drm_crtc_cleanup,
+	.reset = vop2_crtc_reset,
+	.atomic_duplicate_state = vop2_crtc_duplicate_state,
+	.atomic_destroy_state = vop2_crtc_destroy_state,
+	.enable_vblank = vop2_crtc_enable_vblank,
+	.disable_vblank = vop2_crtc_disable_vblank,
+};
+
+static irqreturn_t vop2_isr(int irq, void *data)
+{
+	struct vop2 *vop2 = data;
+	const struct vop2_data *vop2_data = vop2->data;
+	u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM];
+	int ret = IRQ_NONE;
+	int i;
+
+	/*
+	 * The irq is shared with the iommu. If the runtime-pm state of the
+	 * vop2-device is disabled the irq has to be targeted at the iommu.
+	 */
+	if (!pm_runtime_get_if_in_use(vop2->dev))
+		return IRQ_NONE;
+
+	for (i = 0; i < vop2_data->nr_vps; i++) {
+		struct vop2_video_port *vp = &vop2->vps[i];
+		struct drm_crtc *crtc = &vp->crtc;
+		u32 irqs;
+
+		irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
+		vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
+
+		if (irqs & VP_INT_DSP_HOLD_VALID) {
+			complete(&vp->dsp_hold_completion);
+			ret = IRQ_HANDLED;
+		}
+
+		if (irqs & VP_INT_FS_FIELD) {
+			drm_crtc_handle_vblank(crtc);
+			spin_lock(&crtc->dev->event_lock);
+			if (vp->event) {
+				u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
+
+				if (!(val & BIT(vp->id))) {
+					drm_crtc_send_vblank_event(crtc, vp->event);
+					vp->event = NULL;
+					drm_crtc_vblank_put(crtc);
+				}
+			}
+			spin_unlock(&crtc->dev->event_lock);
+
+			ret = IRQ_HANDLED;
+		}
+
+		if (irqs & VP_INT_POST_BUF_EMPTY) {
+			drm_err_ratelimited(vop2->drm,
+					    "POST_BUF_EMPTY irq err at vp%d\n",
+					    vp->id);
+			ret = IRQ_HANDLED;
+		}
+	}
+
+	axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS);
+	vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]);
+	axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS);
+	vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]);
+
+	for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) {
+		if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) {
+			drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n");
+			ret = IRQ_HANDLED;
+		}
+	}
+
+	pm_runtime_put(vop2->dev);
+
+	return ret;
+}
+
+static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win,
+			   unsigned long possible_crtcs)
+{
+	const struct vop2_win_data *win_data = win->data;
+	unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+				  BIT(DRM_MODE_BLEND_PREMULTI) |
+				  BIT(DRM_MODE_BLEND_COVERAGE);
+	int ret;
+
+	ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs,
+				       &vop2_plane_funcs, win_data->formats,
+				       win_data->nformats,
+				       win_data->format_modifiers,
+				       win->type, win_data->name);
+	if (ret) {
+		drm_err(vop2->drm, "failed to initialize plane %d\n", ret);
+		return ret;
+	}
+
+	drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs);
+
+	if (win->data->supported_rotations)
+		drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0,
+						   DRM_MODE_ROTATE_0 |
+						   win->data->supported_rotations);
+	drm_plane_create_alpha_property(&win->base);
+	drm_plane_create_blend_mode_property(&win->base, blend_caps);
+	drm_plane_create_zpos_property(&win->base, win->win_id, 0,
+				       vop2->registered_num_wins - 1);
+
+	return 0;
+}
+
+static struct vop2_video_port *get_activated_vp(struct vop2 *vop2, int n)
+{
+	int i, id = 0;
+
+	for (i = 0; i < vop2->data->nr_vps; i++) {
+		struct vop2_video_port *vp = &vop2->vps[i];
+
+		if (!vp->crtc.port)
+			continue;
+
+		if (n == id)
+			return vp;
+		id++;
+	}
+
+	return NULL;
+}
+
+#define NR_LAYERS 6
+
+static int vop2_create_crtc(struct vop2 *vop2)
+{
+	const struct vop2_data *vop2_data = vop2->data;
+	struct drm_device *drm = vop2->drm;
+	struct device *dev = vop2->dev;
+	struct drm_plane *plane;
+	struct device_node *port;
+	struct vop2_video_port *vp;
+	u32 possible_crtcs;
+	int i, nvp, nvps = 0;
+	int ret;
+
+	for (i = 0; i < vop2_data->nr_vps; i++) {
+		const struct vop2_video_port_data *vp_data;
+		struct device_node *np;
+		char dclk_name[9];
+
+		vp_data = &vop2_data->vp[i];
+		vp = &vop2->vps[i];
+		vp->vop2 = vop2;
+		vp->id = vp_data->id;
+		vp->regs = vp_data->regs;
+		vp->data = vp_data;
+
+		snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
+		vp->dclk = devm_clk_get(vop2->dev, dclk_name);
+		if (IS_ERR(vp->dclk)) {
+			drm_err(vop2->drm, "failed to get %s\n", dclk_name);
+			return PTR_ERR(vp->dclk);
+		}
+
+		np = of_graph_get_remote_node(dev->of_node, i, -1);
+		if (!np) {
+			drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
+			continue;
+		}
+		of_node_put(np);
+
+		port = of_graph_get_port_by_id(dev->of_node, i);
+		if (!port) {
+			drm_err(vop2->drm, "no port node found for video_port%d\n", i);
+			return -ENOENT;
+		}
+
+		vp->crtc.port = port;
+		nvps++;
+	}
+
+	nvp = 0;
+	for (i = 0; i < vop2->registered_num_wins; i++) {
+		struct vop2_win *win = &vop2->win[i];
+
+		if (win->type == DRM_PLANE_TYPE_PRIMARY) {
+			vp = get_activated_vp(vop2, nvp);
+
+			if (vp) {
+				possible_crtcs = BIT(nvp);
+				vp->primary_plane = win;
+
+				nvp++;
+			} else {
+				/* change the unused primary window to overlay window */
+				win->type = DRM_PLANE_TYPE_OVERLAY;
+			}
+		}
+
+		if (win->type == DRM_PLANE_TYPE_OVERLAY)
+			possible_crtcs = (1 << vop2_data->nr_vps) - 1;
+
+		ret = vop2_plane_init(vop2, win, possible_crtcs);
+
+		if (ret) {
+			drm_err(vop2->drm, "failed to init plane %s: %d\n",
+				win->data->name, ret);
+			return ret;
+		}
+	}
+
+	for (i = 0; i < vop2_data->nr_vps; i++) {
+		vp = &vop2->vps[i];
+
+		if (!vp->crtc.port)
+			continue;
+
+		plane = &vp->primary_plane->base;
+
+		ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL,
+						&vop2_crtc_funcs,
+						"video_port%d", vp->id);
+		if (ret) {
+			drm_err(vop2->drm, "crtc init for video_port%d failed\n", i);
+			return ret;
+		}
+
+		drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs);
+
+		init_completion(&vp->dsp_hold_completion);
+	}
+
+	for (i = 0; i < vop2->data->nr_vps; i++) {
+		struct vop2_video_port *vp = &vop2->vps[i];
+
+		if (vp->crtc.port)
+			vp->nlayers = NR_LAYERS / nvps;
+	}
+
+	return 0;
+}
+
+static void vop2_destroy_crtc(struct drm_crtc *crtc)
+{
+	of_node_put(crtc->port);
+
+	/*
+	 * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane()
+	 * references the CRTC.
+	 */
+	drm_crtc_cleanup(crtc);
+}
+
+static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = {
+	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0),
+	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5),
+	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14),
+	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18),
+	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31),
+	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31),
+	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31),
+	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31),
+	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31),
+	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19),
+	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15),
+	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31),
+	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8),
+	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9),
+	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11),
+
+	/* Scale */
+	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15),
+	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31),
+	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15),
+	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13),
+	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3),
+	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28),
+	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29),
+
+	/* cluster regs */
+	[VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1),
+	[VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0),
+	[VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7),
+
+	/* afbc regs */
+	[VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6),
+	[VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9),
+	[VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10),
+	[VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4),
+	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7),
+	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8),
+	[VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31),
+	[VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31),
+	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15),
+	[VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31),
+	[VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31),
+	[VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31),
+	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31),
+	[VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0),
+	[VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1),
+	[VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2),
+	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3),
+	[VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff },
+	[VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff },
+	[VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff },
+	[VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
+	[VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
+	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff },
+	[VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff },
+};
+
+static int vop2_cluster_init(struct vop2_win *win)
+{
+	struct vop2 *vop2 = win->vop2;
+	struct reg_field *cluster_regs;
+	int ret, i;
+
+	cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs),
+			       GFP_KERNEL);
+	if (!cluster_regs)
+		return -ENOMEM;
+
+	for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++)
+		if (cluster_regs[i].reg != 0xffffffff)
+			cluster_regs[i].reg += win->offset;
+
+	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
+					    cluster_regs,
+					    ARRAY_SIZE(vop2_cluster_regs));
+
+	kfree(cluster_regs);
+
+	return ret;
+};
+
+static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = {
+	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0),
+	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5),
+	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12),
+	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14),
+	[VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16),
+	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31),
+	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31),
+	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28),
+	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31),
+	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31),
+	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17),
+	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15),
+	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31),
+	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0),
+	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1),
+	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3),
+	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31),
+	[VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29),
+	[VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31),
+
+	/* Scale */
+	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15),
+	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31),
+	[VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15),
+	[VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31),
+	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1),
+	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3),
+	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5),
+	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7),
+	[VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9),
+	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11),
+	[VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13),
+	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15),
+	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17),
+	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8),
+	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9),
+	[VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10),
+	[VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11),
+	[VOP2_WIN_XMIRROR] = { .reg = 0xffffffff },
+	[VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff },
+	[VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff },
+};
+
+static int vop2_esmart_init(struct vop2_win *win)
+{
+	struct vop2 *vop2 = win->vop2;
+	struct reg_field *esmart_regs;
+	int ret, i;
+
+	esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs),
+			       GFP_KERNEL);
+	if (!esmart_regs)
+		return -ENOMEM;
+
+	for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++)
+		if (esmart_regs[i].reg != 0xffffffff)
+			esmart_regs[i].reg += win->offset;
+
+	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
+					    esmart_regs,
+					    ARRAY_SIZE(vop2_esmart_regs));
+
+	kfree(esmart_regs);
+
+	return ret;
+};
+
+static int vop2_win_init(struct vop2 *vop2)
+{
+	const struct vop2_data *vop2_data = vop2->data;
+	struct vop2_win *win;
+	int i, ret;
+
+	for (i = 0; i < vop2_data->win_size; i++) {
+		const struct vop2_win_data *win_data = &vop2_data->win[i];
+
+		win = &vop2->win[i];
+		win->data = win_data;
+		win->type = win_data->type;
+		win->offset = win_data->base;
+		win->win_id = i;
+		win->vop2 = vop2;
+		if (vop2_cluster_window(win))
+			ret = vop2_cluster_init(win);
+		else
+			ret = vop2_esmart_init(win);
+		if (ret)
+			return ret;
+	}
+
+	vop2->registered_num_wins = vop2_data->win_size;
+
+	return 0;
+}
+
+/*
+ * The window registers are only updated when config done is written.
+ * Until that they read back the old value. As we read-modify-write
+ * these registers mark them as non-volatile. This makes sure we read
+ * the new values from the regmap register cache.
+ */
+static const struct regmap_range vop2_nonvolatile_range[] = {
+	regmap_reg_range(0x1000, 0x23ff),
+};
+
+static const struct regmap_access_table vop2_volatile_table = {
+	.no_ranges = vop2_nonvolatile_range,
+	.n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range),
+};
+
+static const struct regmap_config vop2_regmap_config = {
+	.reg_bits	= 32,
+	.val_bits	= 32,
+	.reg_stride	= 4,
+	.max_register	= 0x3000,
+	.name		= "vop2",
+	.volatile_table	= &vop2_volatile_table,
+	.cache_type	= REGCACHE_RBTREE,
+};
+
+static int vop2_bind(struct device *dev, struct device *master, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	const struct vop2_data *vop2_data;
+	struct drm_device *drm = data;
+	struct vop2 *vop2;
+	struct resource *res;
+	size_t alloc_size;
+	int ret;
+
+	vop2_data = of_device_get_match_data(dev);
+	if (!vop2_data)
+		return -ENODEV;
+
+	/* Allocate vop2 struct and its vop2_win array */
+	alloc_size = sizeof(*vop2) + sizeof(*vop2->win) * vop2_data->win_size;
+	vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
+	if (!vop2)
+		return -ENOMEM;
+
+	vop2->dev = dev;
+	vop2->data = vop2_data;
+	vop2->drm = drm;
+
+	dev_set_drvdata(dev, vop2);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+	if (!res) {
+		drm_err(vop2->drm, "failed to get vop2 register byname\n");
+		return -EINVAL;
+	}
+
+	vop2->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(vop2->regs))
+		return PTR_ERR(vop2->regs);
+	vop2->len = resource_size(res);
+
+	vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config);
+
+	ret = vop2_win_init(vop2);
+	if (ret)
+		return ret;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma_lut");
+	if (res) {
+		vop2->lut_regs = devm_ioremap_resource(dev, res);
+		if (IS_ERR(vop2->lut_regs))
+			return PTR_ERR(vop2->lut_regs);
+	}
+
+	vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
+
+	vop2->hclk = devm_clk_get(vop2->dev, "hclk");
+	if (IS_ERR(vop2->hclk)) {
+		drm_err(vop2->drm, "failed to get hclk source\n");
+		return PTR_ERR(vop2->hclk);
+	}
+
+	vop2->aclk = devm_clk_get(vop2->dev, "aclk");
+	if (IS_ERR(vop2->aclk)) {
+		drm_err(vop2->drm, "failed to get aclk source\n");
+		return PTR_ERR(vop2->aclk);
+	}
+
+	vop2->irq = platform_get_irq(pdev, 0);
+	if (vop2->irq < 0) {
+		drm_err(vop2->drm, "cannot find irq for vop2\n");
+		return vop2->irq;
+	}
+
+	mutex_init(&vop2->vop2_lock);
+
+	ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
+	if (ret)
+		return ret;
+
+	ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev);
+	if (ret) {
+		drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret);
+		return ret;
+	}
+
+	ret = vop2_create_crtc(vop2);
+	if (ret)
+		return ret;
+
+	pm_runtime_enable(&pdev->dev);
+
+	return 0;
+}
+
+static void vop2_unbind(struct device *dev, struct device *master, void *data)
+{
+	struct vop2 *vop2 = dev_get_drvdata(dev);
+	struct drm_device *drm = vop2->drm;
+	struct list_head *plane_list = &drm->mode_config.plane_list;
+	struct list_head *crtc_list = &drm->mode_config.crtc_list;
+	struct drm_crtc *crtc, *tmpc;
+	struct drm_plane *plane, *tmpp;
+
+	rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
+
+	pm_runtime_disable(dev);
+
+	list_for_each_entry_safe(plane, tmpp, plane_list, head)
+		drm_plane_cleanup(plane);
+
+	list_for_each_entry_safe(crtc, tmpc, crtc_list, head)
+		vop2_destroy_crtc(crtc);
+}
+
+const struct component_ops vop2_component_ops = {
+	.bind = vop2_bind,
+	.unbind = vop2_unbind,
+};
+EXPORT_SYMBOL_GPL(vop2_component_ops);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
new file mode 100644
index 0000000000000..c727093a06d68
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
@@ -0,0 +1,477 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ */
+
+#ifndef _ROCKCHIP_DRM_VOP2_H
+#define _ROCKCHIP_DRM_VOP2_H
+
+#include "rockchip_drm_vop.h"
+
+#include <linux/regmap.h>
+#include <drm/drm_modes.h>
+
+#define VOP_FEATURE_OUTPUT_10BIT        BIT(0)
+
+#define WIN_FEATURE_AFBDC		BIT(0)
+#define WIN_FEATURE_CLUSTER		BIT(1)
+
+/*
+ *  the delay number of a window in different mode.
+ */
+enum win_dly_mode {
+	VOP2_DLY_MODE_DEFAULT,   /**< default mode */
+	VOP2_DLY_MODE_HISO_S,    /** HDR in SDR out mode, as a SDR window */
+	VOP2_DLY_MODE_HIHO_H,    /** HDR in HDR out mode, as a HDR window */
+	VOP2_DLY_MODE_MAX,
+};
+
+struct vop_rect {
+	int width;
+	int height;
+};
+
+enum vop2_scale_up_mode {
+	VOP2_SCALE_UP_NRST_NBOR,
+	VOP2_SCALE_UP_BIL,
+	VOP2_SCALE_UP_BIC,
+};
+
+enum vop2_scale_down_mode {
+	VOP2_SCALE_DOWN_NRST_NBOR,
+	VOP2_SCALE_DOWN_BIL,
+	VOP2_SCALE_DOWN_AVG,
+};
+
+enum vop2_win_regs {
+	VOP2_WIN_ENABLE,
+	VOP2_WIN_FORMAT,
+	VOP2_WIN_CSC_MODE,
+	VOP2_WIN_XMIRROR,
+	VOP2_WIN_YMIRROR,
+	VOP2_WIN_RB_SWAP,
+	VOP2_WIN_UV_SWAP,
+	VOP2_WIN_ACT_INFO,
+	VOP2_WIN_DSP_INFO,
+	VOP2_WIN_DSP_ST,
+	VOP2_WIN_YRGB_MST,
+	VOP2_WIN_UV_MST,
+	VOP2_WIN_YRGB_VIR,
+	VOP2_WIN_UV_VIR,
+	VOP2_WIN_YUV_CLIP,
+	VOP2_WIN_Y2R_EN,
+	VOP2_WIN_R2Y_EN,
+	VOP2_WIN_COLOR_KEY,
+	VOP2_WIN_COLOR_KEY_EN,
+	VOP2_WIN_DITHER_UP,
+
+	/* scale regs */
+	VOP2_WIN_SCALE_YRGB_X,
+	VOP2_WIN_SCALE_YRGB_Y,
+	VOP2_WIN_SCALE_CBCR_X,
+	VOP2_WIN_SCALE_CBCR_Y,
+	VOP2_WIN_YRGB_HOR_SCL_MODE,
+	VOP2_WIN_YRGB_HSCL_FILTER_MODE,
+	VOP2_WIN_YRGB_VER_SCL_MODE,
+	VOP2_WIN_YRGB_VSCL_FILTER_MODE,
+	VOP2_WIN_CBCR_VER_SCL_MODE,
+	VOP2_WIN_CBCR_HSCL_FILTER_MODE,
+	VOP2_WIN_CBCR_HOR_SCL_MODE,
+	VOP2_WIN_CBCR_VSCL_FILTER_MODE,
+	VOP2_WIN_VSD_CBCR_GT2,
+	VOP2_WIN_VSD_CBCR_GT4,
+	VOP2_WIN_VSD_YRGB_GT2,
+	VOP2_WIN_VSD_YRGB_GT4,
+	VOP2_WIN_BIC_COE_SEL,
+
+	/* cluster regs */
+	VOP2_WIN_CLUSTER_ENABLE,
+	VOP2_WIN_AFBC_ENABLE,
+	VOP2_WIN_CLUSTER_LB_MODE,
+
+	/* afbc regs */
+	VOP2_WIN_AFBC_FORMAT,
+	VOP2_WIN_AFBC_RB_SWAP,
+	VOP2_WIN_AFBC_UV_SWAP,
+	VOP2_WIN_AFBC_AUTO_GATING_EN,
+	VOP2_WIN_AFBC_BLOCK_SPLIT_EN,
+	VOP2_WIN_AFBC_PIC_VIR_WIDTH,
+	VOP2_WIN_AFBC_TILE_NUM,
+	VOP2_WIN_AFBC_PIC_OFFSET,
+	VOP2_WIN_AFBC_PIC_SIZE,
+	VOP2_WIN_AFBC_DSP_OFFSET,
+	VOP2_WIN_AFBC_TRANSFORM_OFFSET,
+	VOP2_WIN_AFBC_HDR_PTR,
+	VOP2_WIN_AFBC_HALF_BLOCK_EN,
+	VOP2_WIN_AFBC_ROTATE_270,
+	VOP2_WIN_AFBC_ROTATE_90,
+	VOP2_WIN_MAX_REG,
+};
+
+struct vop2_win_data {
+	const char *name;
+	unsigned int phys_id;
+
+	u32 base;
+	enum drm_plane_type type;
+
+	u32 nformats;
+	const u32 *formats;
+	const uint64_t *format_modifiers;
+	const unsigned int supported_rotations;
+
+	/**
+	 * @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2
+	 */
+	unsigned int layer_sel_id;
+	uint64_t feature;
+
+	unsigned int max_upscale_factor;
+	unsigned int max_downscale_factor;
+	const u8 dly[VOP2_DLY_MODE_MAX];
+};
+
+struct vop2_video_port_data {
+	unsigned int id;
+	u32 feature;
+	u16 gamma_lut_len;
+	u16 cubic_lut_len;
+	struct vop_rect max_output;
+	const u8 pre_scan_max_dly[4];
+	const struct vop2_video_port_regs *regs;
+	unsigned int offset;
+};
+
+struct vop2_data {
+	u8 nr_vps;
+	const struct vop2_ctrl *ctrl;
+	const struct vop2_win_data *win;
+	const struct vop2_video_port_data *vp;
+	const struct vop_csc_table *csc_table;
+	struct vop_rect max_input;
+	struct vop_rect max_output;
+
+	unsigned int win_size;
+	unsigned int soc_id;
+};
+
+/* interrupt define */
+#define FS_NEW_INTR			BIT(4)
+#define ADDR_SAME_INTR			BIT(5)
+#define LINE_FLAG1_INTR			BIT(6)
+#define WIN0_EMPTY_INTR			BIT(7)
+#define WIN1_EMPTY_INTR			BIT(8)
+#define WIN2_EMPTY_INTR			BIT(9)
+#define WIN3_EMPTY_INTR			BIT(10)
+#define HWC_EMPTY_INTR			BIT(11)
+#define POST_BUF_EMPTY_INTR		BIT(12)
+#define PWM_GEN_INTR			BIT(13)
+#define DMA_FINISH_INTR			BIT(14)
+#define FS_FIELD_INTR			BIT(15)
+#define FE_INTR				BIT(16)
+#define WB_UV_FIFO_FULL_INTR		BIT(17)
+#define WB_YRGB_FIFO_FULL_INTR		BIT(18)
+#define WB_COMPLETE_INTR		BIT(19)
+
+/*
+ * display output interface supported by rockchip lcdc
+ */
+#define ROCKCHIP_OUT_MODE_P888		0
+#define ROCKCHIP_OUT_MODE_BT1120	0
+#define ROCKCHIP_OUT_MODE_P666		1
+#define ROCKCHIP_OUT_MODE_P565		2
+#define ROCKCHIP_OUT_MODE_BT656		5
+#define ROCKCHIP_OUT_MODE_S888		8
+#define ROCKCHIP_OUT_MODE_S888_DUMMY	12
+#define ROCKCHIP_OUT_MODE_YUV420	14
+/* for use special outface */
+#define ROCKCHIP_OUT_MODE_AAAA		15
+
+enum vop_csc_format {
+	CSC_BT601L,
+	CSC_BT709L,
+	CSC_BT601F,
+	CSC_BT2020,
+};
+
+enum src_factor_mode {
+	SRC_FAC_ALPHA_ZERO,
+	SRC_FAC_ALPHA_ONE,
+	SRC_FAC_ALPHA_DST,
+	SRC_FAC_ALPHA_DST_INVERSE,
+	SRC_FAC_ALPHA_SRC,
+	SRC_FAC_ALPHA_SRC_GLOBAL,
+};
+
+enum dst_factor_mode {
+	DST_FAC_ALPHA_ZERO,
+	DST_FAC_ALPHA_ONE,
+	DST_FAC_ALPHA_SRC,
+	DST_FAC_ALPHA_SRC_INVERSE,
+	DST_FAC_ALPHA_DST,
+	DST_FAC_ALPHA_DST_GLOBAL,
+};
+
+#define RK3568_GRF_VO_CON1			0x0364
+/* System registers definition */
+#define RK3568_REG_CFG_DONE			0x000
+#define RK3568_VERSION_INFO			0x004
+#define RK3568_SYS_AUTO_GATING_CTRL		0x008
+#define RK3568_SYS_AXI_LUT_CTRL			0x024
+#define RK3568_DSP_IF_EN			0x028
+#define RK3568_DSP_IF_CTRL			0x02c
+#define RK3568_DSP_IF_POL			0x030
+#define RK3568_WB_CTRL				0x40
+#define RK3568_WB_XSCAL_FACTOR			0x44
+#define RK3568_WB_YRGB_MST			0x48
+#define RK3568_WB_CBR_MST			0x4C
+#define RK3568_OTP_WIN_EN			0x050
+#define RK3568_LUT_PORT_SEL			0x058
+#define RK3568_SYS_STATUS0			0x060
+#define RK3568_VP_LINE_FLAG(vp)			(0x70 + (vp) * 0x4)
+#define RK3568_SYS0_INT_EN			0x80
+#define RK3568_SYS0_INT_CLR			0x84
+#define RK3568_SYS0_INT_STATUS			0x88
+#define RK3568_SYS1_INT_EN			0x90
+#define RK3568_SYS1_INT_CLR			0x94
+#define RK3568_SYS1_INT_STATUS			0x98
+#define RK3568_VP_INT_EN(vp)			(0xA0 + (vp) * 0x10)
+#define RK3568_VP_INT_CLR(vp)			(0xA4 + (vp) * 0x10)
+#define RK3568_VP_INT_STATUS(vp)		(0xA8 + (vp) * 0x10)
+#define RK3568_VP_INT_RAW_STATUS(vp)		(0xAC + (vp) * 0x10)
+
+/* Video Port registers definition */
+#define RK3568_VP_DSP_CTRL			0x00
+#define RK3568_VP_MIPI_CTRL			0x04
+#define RK3568_VP_COLOR_BAR_CTRL		0x08
+#define RK3568_VP_3D_LUT_CTRL			0x10
+#define RK3568_VP_3D_LUT_MST			0x20
+#define RK3568_VP_DSP_BG			0x2C
+#define RK3568_VP_PRE_SCAN_HTIMING		0x30
+#define RK3568_VP_POST_DSP_HACT_INFO		0x34
+#define RK3568_VP_POST_DSP_VACT_INFO		0x38
+#define RK3568_VP_POST_SCL_FACTOR_YRGB		0x3C
+#define RK3568_VP_POST_SCL_CTRL			0x40
+#define RK3568_VP_POST_DSP_VACT_INFO_F1		0x44
+#define RK3568_VP_DSP_HTOTAL_HS_END		0x48
+#define RK3568_VP_DSP_HACT_ST_END		0x4C
+#define RK3568_VP_DSP_VTOTAL_VS_END		0x50
+#define RK3568_VP_DSP_VACT_ST_END		0x54
+#define RK3568_VP_DSP_VS_ST_END_F1		0x58
+#define RK3568_VP_DSP_VACT_ST_END_F1		0x5C
+#define RK3568_VP_BCSH_CTRL			0x60
+#define RK3568_VP_BCSH_BCS			0x64
+#define RK3568_VP_BCSH_H			0x68
+#define RK3568_VP_BCSH_COLOR_BAR		0x6C
+
+/* Overlay registers definition    */
+#define RK3568_OVL_CTRL				0x600
+#define RK3568_OVL_LAYER_SEL			0x604
+#define RK3568_OVL_PORT_SEL			0x608
+#define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL	0x610
+#define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL	0x614
+#define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL	0x618
+#define RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL	0x61C
+#define RK3568_MIX0_SRC_COLOR_CTRL		0x650
+#define RK3568_MIX0_DST_COLOR_CTRL		0x654
+#define RK3568_MIX0_SRC_ALPHA_CTRL		0x658
+#define RK3568_MIX0_DST_ALPHA_CTRL		0x65C
+#define RK3568_HDR0_SRC_COLOR_CTRL		0x6C0
+#define RK3568_HDR0_DST_COLOR_CTRL		0x6C4
+#define RK3568_HDR0_SRC_ALPHA_CTRL		0x6C8
+#define RK3568_HDR0_DST_ALPHA_CTRL		0x6CC
+#define RK3568_VP_BG_MIX_CTRL(vp)		(0x6E0 + (vp) * 4)
+#define RK3568_CLUSTER_DLY_NUM			0x6F0
+#define RK3568_SMART_DLY_NUM			0x6F8
+
+/* Cluster register definition, offset relative to window base */
+#define RK3568_CLUSTER_WIN_CTRL0		0x00
+#define RK3568_CLUSTER_WIN_CTRL1		0x04
+#define RK3568_CLUSTER_WIN_YRGB_MST		0x10
+#define RK3568_CLUSTER_WIN_CBR_MST		0x14
+#define RK3568_CLUSTER_WIN_VIR			0x18
+#define RK3568_CLUSTER_WIN_ACT_INFO		0x20
+#define RK3568_CLUSTER_WIN_DSP_INFO		0x24
+#define RK3568_CLUSTER_WIN_DSP_ST		0x28
+#define RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB	0x30
+#define RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET	0x3C
+#define RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL	0x50
+#define RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE	0x54
+#define RK3568_CLUSTER_WIN_AFBCD_HDR_PTR	0x58
+#define RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH	0x5C
+#define RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE	0x60
+#define RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET	0x64
+#define RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET	0x68
+#define RK3568_CLUSTER_WIN_AFBCD_CTRL		0x6C
+
+#define RK3568_CLUSTER_CTRL			0x100
+
+/* (E)smart register definition, offset relative to window base */
+#define RK3568_SMART_CTRL0			0x00
+#define RK3568_SMART_CTRL1			0x04
+#define RK3568_SMART_REGION0_CTRL		0x10
+#define RK3568_SMART_REGION0_YRGB_MST		0x14
+#define RK3568_SMART_REGION0_CBR_MST		0x18
+#define RK3568_SMART_REGION0_VIR		0x1C
+#define RK3568_SMART_REGION0_ACT_INFO		0x20
+#define RK3568_SMART_REGION0_DSP_INFO		0x24
+#define RK3568_SMART_REGION0_DSP_ST		0x28
+#define RK3568_SMART_REGION0_SCL_CTRL		0x30
+#define RK3568_SMART_REGION0_SCL_FACTOR_YRGB	0x34
+#define RK3568_SMART_REGION0_SCL_FACTOR_CBR	0x38
+#define RK3568_SMART_REGION0_SCL_OFFSET		0x3C
+#define RK3568_SMART_REGION1_CTRL		0x40
+#define RK3568_SMART_REGION1_YRGB_MST		0x44
+#define RK3568_SMART_REGION1_CBR_MST		0x48
+#define RK3568_SMART_REGION1_VIR		0x4C
+#define RK3568_SMART_REGION1_ACT_INFO		0x50
+#define RK3568_SMART_REGION1_DSP_INFO		0x54
+#define RK3568_SMART_REGION1_DSP_ST		0x58
+#define RK3568_SMART_REGION1_SCL_CTRL		0x60
+#define RK3568_SMART_REGION1_SCL_FACTOR_YRGB	0x64
+#define RK3568_SMART_REGION1_SCL_FACTOR_CBR	0x68
+#define RK3568_SMART_REGION1_SCL_OFFSET		0x6C
+#define RK3568_SMART_REGION2_CTRL		0x70
+#define RK3568_SMART_REGION2_YRGB_MST		0x74
+#define RK3568_SMART_REGION2_CBR_MST		0x78
+#define RK3568_SMART_REGION2_VIR		0x7C
+#define RK3568_SMART_REGION2_ACT_INFO		0x80
+#define RK3568_SMART_REGION2_DSP_INFO		0x84
+#define RK3568_SMART_REGION2_DSP_ST		0x88
+#define RK3568_SMART_REGION2_SCL_CTRL		0x90
+#define RK3568_SMART_REGION2_SCL_FACTOR_YRGB	0x94
+#define RK3568_SMART_REGION2_SCL_FACTOR_CBR	0x98
+#define RK3568_SMART_REGION2_SCL_OFFSET		0x9C
+#define RK3568_SMART_REGION3_CTRL		0xA0
+#define RK3568_SMART_REGION3_YRGB_MST		0xA4
+#define RK3568_SMART_REGION3_CBR_MST		0xA8
+#define RK3568_SMART_REGION3_VIR		0xAC
+#define RK3568_SMART_REGION3_ACT_INFO		0xB0
+#define RK3568_SMART_REGION3_DSP_INFO		0xB4
+#define RK3568_SMART_REGION3_DSP_ST		0xB8
+#define RK3568_SMART_REGION3_SCL_CTRL		0xC0
+#define RK3568_SMART_REGION3_SCL_FACTOR_YRGB	0xC4
+#define RK3568_SMART_REGION3_SCL_FACTOR_CBR	0xC8
+#define RK3568_SMART_REGION3_SCL_OFFSET		0xCC
+#define RK3568_SMART_COLOR_KEY_CTRL		0xD0
+
+/* HDR register definition */
+#define RK3568_HDR_LUT_CTRL			0x2000
+#define RK3568_HDR_LUT_MST			0x2004
+#define RK3568_SDR2HDR_CTRL			0x2010
+#define RK3568_HDR2SDR_CTRL			0x2020
+#define RK3568_HDR2SDR_SRC_RANGE		0x2024
+#define RK3568_HDR2SDR_NORMFACEETF		0x2028
+#define RK3568_HDR2SDR_DST_RANGE		0x202C
+#define RK3568_HDR2SDR_NORMFACCGAMMA		0x2030
+#define RK3568_HDR_EETF_OETF_Y0			0x203C
+#define RK3568_HDR_SAT_Y0			0x20C0
+#define RK3568_HDR_EOTF_OETF_Y0			0x20F0
+#define RK3568_HDR_OETF_DX_POW1			0x2200
+#define RK3568_HDR_OETF_XN1			0x2300
+
+#define RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN		BIT(15)
+
+#define RK3568_VP_DSP_CTRL__STANDBY			BIT(31)
+#define RK3568_VP_DSP_CTRL__DITHER_DOWN_MODE		BIT(20)
+#define RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL		GENMASK(19, 18)
+#define RK3568_VP_DSP_CTRL__DITHER_DOWN_EN		BIT(17)
+#define RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN		BIT(16)
+#define RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y		BIT(15)
+#define RK3568_VP_DSP_CTRL__DSP_RB_SWAP			BIT(9)
+#define RK3568_VP_DSP_CTRL__DSP_INTERLACE		BIT(7)
+#define RK3568_VP_DSP_CTRL__DSP_FILED_POL		BIT(6)
+#define RK3568_VP_DSP_CTRL__P2I_EN			BIT(5)
+#define RK3568_VP_DSP_CTRL__CORE_DCLK_DIV		BIT(4)
+#define RK3568_VP_DSP_CTRL__OUT_MODE			GENMASK(3, 0)
+
+#define RK3568_VP_POST_SCL_CTRL__VSCALEDOWN		BIT(1)
+#define RK3568_VP_POST_SCL_CTRL__HSCALEDOWN		BIT(0)
+
+#define RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX		GENMASK(26, 25)
+#define RK3568_SYS_DSP_INFACE_EN_LVDS1			BIT(24)
+#define RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX		GENMASK(22, 21)
+#define RK3568_SYS_DSP_INFACE_EN_MIPI1			BIT(20)
+#define RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX		GENMASK(19, 18)
+#define RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX		GENMASK(17, 16)
+#define RK3568_SYS_DSP_INFACE_EN_EDP_MUX		GENMASK(15, 14)
+#define RK3568_SYS_DSP_INFACE_EN_HDMI_MUX		GENMASK(11, 10)
+#define RK3568_SYS_DSP_INFACE_EN_RGB_MUX		GENMASK(9, 8)
+#define RK3568_SYS_DSP_INFACE_EN_LVDS0			BIT(5)
+#define RK3568_SYS_DSP_INFACE_EN_MIPI0			BIT(4)
+#define RK3568_SYS_DSP_INFACE_EN_EDP			BIT(3)
+#define RK3568_SYS_DSP_INFACE_EN_HDMI			BIT(1)
+#define RK3568_SYS_DSP_INFACE_EN_RGB			BIT(0)
+
+#define RK3568_DSP_IF_POL__MIPI_PIN_POL			GENMASK(19, 16)
+#define RK3568_DSP_IF_POL__EDP_PIN_POL			GENMASK(15, 12)
+#define RK3568_DSP_IF_POL__HDMI_PIN_POL			GENMASK(7, 4)
+#define RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL		GENMASK(3, 0)
+
+#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2_PHASE_LOCK	BIT(5)
+#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2			BIT(4)
+
+#define RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN	BIT(31)
+
+#define RK3568_DSP_IF_POL__CFG_DONE_IMD			BIT(28)
+
+#define VOP2_SYS_AXI_BUS_NUM				2
+
+#define VOP2_CLUSTER_YUV444_10				0x12
+
+#define VOP2_COLOR_KEY_MASK				BIT(31)
+
+#define RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD		BIT(28)
+
+#define RK3568_VP_BG_MIX_CTRL__BG_DLY			GENMASK(31, 24)
+
+#define RK3568_OVL_PORT_SEL__SEL_PORT			GENMASK(31, 16)
+#define RK3568_OVL_PORT_SEL__SMART1			GENMASK(31, 30)
+#define RK3568_OVL_PORT_SEL__SMART0			GENMASK(29, 28)
+#define RK3568_OVL_PORT_SEL__ESMART1			GENMASK(27, 26)
+#define RK3568_OVL_PORT_SEL__ESMART0			GENMASK(25, 24)
+#define RK3568_OVL_PORT_SEL__CLUSTER1			GENMASK(19, 18)
+#define RK3568_OVL_PORT_SEL__CLUSTER0			GENMASK(17, 16)
+#define RK3568_OVL_PORT_SET__PORT2_MUX			GENMASK(11, 8)
+#define RK3568_OVL_PORT_SET__PORT1_MUX			GENMASK(7, 4)
+#define RK3568_OVL_PORT_SET__PORT0_MUX			GENMASK(3, 0)
+#define RK3568_OVL_LAYER_SEL__LAYER(layer, x)		((x) << ((layer) * 4))
+
+#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_1		GENMASK(31, 24)
+#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_0		GENMASK(23, 16)
+#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_1		GENMASK(15, 8)
+#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_0		GENMASK(7, 0)
+
+#define RK3568_SMART_DLY_NUM__SMART1			GENMASK(31, 24)
+#define RK3568_SMART_DLY_NUM__SMART0			GENMASK(23, 16)
+#define RK3568_SMART_DLY_NUM__ESMART1			GENMASK(15, 8)
+#define RK3568_SMART_DLY_NUM__ESMART0			GENMASK(7, 0)
+
+#define VP_INT_DSP_HOLD_VALID	BIT(6)
+#define VP_INT_FS_FIELD		BIT(5)
+#define VP_INT_POST_BUF_EMPTY	BIT(4)
+#define VP_INT_LINE_FLAG1	BIT(3)
+#define VP_INT_LINE_FLAG0	BIT(2)
+#define VOP2_INT_BUS_ERRPR	BIT(1)
+#define VP_INT_FS		BIT(0)
+
+#define POLFLAG_DCLK_INV	BIT(3)
+
+enum vop2_layer_phy_id {
+	ROCKCHIP_VOP2_CLUSTER0 = 0,
+	ROCKCHIP_VOP2_CLUSTER1,
+	ROCKCHIP_VOP2_ESMART0,
+	ROCKCHIP_VOP2_ESMART1,
+	ROCKCHIP_VOP2_SMART0,
+	ROCKCHIP_VOP2_SMART1,
+	ROCKCHIP_VOP2_CLUSTER2,
+	ROCKCHIP_VOP2_CLUSTER3,
+	ROCKCHIP_VOP2_ESMART2,
+	ROCKCHIP_VOP2_ESMART3,
+	ROCKCHIP_VOP2_PHY_ID_INVALID = -1,
+};
+
+extern const struct component_ops vop2_component_ops;
+
+#endif /* _ROCKCHIP_DRM_VOP2_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
new file mode 100644
index 0000000000000..9bf0637bf8e26
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) Rockchip Electronics Co.Ltd
+ * Author: Andy Yan <andy.yan@rock-chips.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/component.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_print.h>
+
+#include "rockchip_drm_vop2.h"
+
+static const uint32_t formats_win_full_10bit[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV16,
+	DRM_FORMAT_NV24,
+};
+
+static const uint32_t formats_win_full_10bit_yuyv[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV16,
+	DRM_FORMAT_NV24,
+	DRM_FORMAT_YVYU,
+	DRM_FORMAT_VYUY,
+};
+
+static const uint32_t formats_win_lite[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+};
+
+static const uint64_t format_modifiers[] = {
+	DRM_FORMAT_MOD_LINEAR,
+	DRM_FORMAT_MOD_INVALID,
+};
+
+static const uint64_t format_modifiers_afbc[] = {
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_SPARSE),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_CBR),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR |
+				AFBC_FORMAT_MOD_SPARSE),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_CBR |
+				AFBC_FORMAT_MOD_SPARSE),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR |
+				AFBC_FORMAT_MOD_CBR),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR |
+				AFBC_FORMAT_MOD_CBR |
+				AFBC_FORMAT_MOD_SPARSE),
+
+	/* SPLIT mandates SPARSE, RGB modes mandates YTR */
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR |
+				AFBC_FORMAT_MOD_SPARSE |
+				AFBC_FORMAT_MOD_SPLIT),
+	DRM_FORMAT_MOD_INVALID,
+};
+
+static const struct vop2_video_port_data rk3568_vop_video_ports[] = {
+	{
+		.id = 0,
+		.feature = VOP_FEATURE_OUTPUT_10BIT,
+		.gamma_lut_len = 1024,
+		.cubic_lut_len = 9 * 9 * 9,
+		.max_output = { 4096, 2304 },
+		.pre_scan_max_dly = { 69, 53, 53, 42 },
+		.offset = 0xc00,
+	}, {
+		.id = 1,
+		.gamma_lut_len = 1024,
+		.max_output = { 2048, 1536 },
+		.pre_scan_max_dly = { 40, 40, 40, 40 },
+		.offset = 0xd00,
+	}, {
+		.id = 2,
+		.gamma_lut_len = 1024,
+		.max_output = { 1920, 1080 },
+		.pre_scan_max_dly = { 40, 40, 40, 40 },
+		.offset = 0xe00,
+	},
+};
+
+/*
+ * rk3568 vop with 2 cluster, 2 esmart win, 2 smart win.
+ * Every cluster can work as 4K win or split into two win.
+ * All win in cluster support AFBCD.
+ *
+ * Every esmart win and smart win support 4 Multi-region.
+ *
+ * Scale filter mode:
+ *
+ * * Cluster:  bicubic for horizontal scale up, others use bilinear
+ * * ESmart:
+ *    * nearest-neighbor/bilinear/bicubic for scale up
+ *    * nearest-neighbor/bilinear/average for scale down
+ *
+ *
+ * @TODO describe the wind like cpu-map dt nodes;
+ */
+static const struct vop2_win_data rk3568_vop_win_data[] = {
+	{
+		.name = "Smart0-win0",
+		.phys_id = ROCKCHIP_VOP2_SMART0,
+		.base = 0x1c00,
+		.formats = formats_win_lite,
+		.nformats = ARRAY_SIZE(formats_win_lite),
+		.format_modifiers = format_modifiers,
+		.layer_sel_id = 3,
+		.supported_rotations = DRM_MODE_REFLECT_Y,
+		.type = DRM_PLANE_TYPE_PRIMARY,
+		.max_upscale_factor = 8,
+		.max_downscale_factor = 8,
+		.dly = { 20, 47, 41 },
+	}, {
+		.name = "Smart1-win0",
+		.phys_id = ROCKCHIP_VOP2_SMART1,
+		.formats = formats_win_lite,
+		.nformats = ARRAY_SIZE(formats_win_lite),
+		.format_modifiers = format_modifiers,
+		.base = 0x1e00,
+		.layer_sel_id = 7,
+		.supported_rotations = DRM_MODE_REFLECT_Y,
+		.type = DRM_PLANE_TYPE_PRIMARY,
+		.max_upscale_factor = 8,
+		.max_downscale_factor = 8,
+		.dly = { 20, 47, 41 },
+	}, {
+		.name = "Esmart1-win0",
+		.phys_id = ROCKCHIP_VOP2_ESMART1,
+		.formats = formats_win_full_10bit_yuyv,
+		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
+		.format_modifiers = format_modifiers,
+		.base = 0x1a00,
+		.layer_sel_id = 6,
+		.supported_rotations = DRM_MODE_REFLECT_Y,
+		.type = DRM_PLANE_TYPE_PRIMARY,
+		.max_upscale_factor = 8,
+		.max_downscale_factor = 8,
+		.dly = { 20, 47, 41 },
+	}, {
+		.name = "Esmart0-win0",
+		.phys_id = ROCKCHIP_VOP2_ESMART0,
+		.formats = formats_win_full_10bit_yuyv,
+		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
+		.format_modifiers = format_modifiers,
+		.base = 0x1800,
+		.layer_sel_id = 2,
+		.supported_rotations = DRM_MODE_REFLECT_Y,
+		.type = DRM_PLANE_TYPE_OVERLAY,
+		.max_upscale_factor = 8,
+		.max_downscale_factor = 8,
+		.dly = { 20, 47, 41 },
+	}, {
+		.name = "Cluster0-win0",
+		.phys_id = ROCKCHIP_VOP2_CLUSTER0,
+		.base = 0x1000,
+		.formats = formats_win_full_10bit,
+		.nformats = ARRAY_SIZE(formats_win_full_10bit),
+		.format_modifiers = format_modifiers_afbc,
+		.layer_sel_id = 0,
+		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
+					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
+		.max_upscale_factor = 4,
+		.max_downscale_factor = 4,
+		.dly = { 0, 27, 21 },
+		.type = DRM_PLANE_TYPE_OVERLAY,
+		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
+	}, {
+		.name = "Cluster1-win0",
+		.phys_id = ROCKCHIP_VOP2_CLUSTER1,
+		.base = 0x1200,
+		.formats = formats_win_full_10bit,
+		.nformats = ARRAY_SIZE(formats_win_full_10bit),
+		.format_modifiers = format_modifiers_afbc,
+		.layer_sel_id = 1,
+		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
+					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
+		.type = DRM_PLANE_TYPE_OVERLAY,
+		.max_upscale_factor = 4,
+		.max_downscale_factor = 4,
+		.dly = { 0, 27, 21 },
+		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
+	},
+};
+
+static const struct vop2_data rk3566_vop = {
+	.nr_vps = 3,
+	.max_input = { 4096, 2304 },
+	.max_output = { 4096, 2304 },
+	.vp = rk3568_vop_video_ports,
+	.win = rk3568_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
+	.soc_id = 3566,
+};
+
+static const struct vop2_data rk3568_vop = {
+	.nr_vps = 3,
+	.max_input = { 4096, 2304 },
+	.max_output = { 4096, 2304 },
+	.vp = rk3568_vop_video_ports,
+	.win = rk3568_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
+	.soc_id = 3568,
+};
+
+static const struct of_device_id vop2_dt_match[] = {
+	{
+		.compatible = "rockchip,rk3566-vop",
+		.data = &rk3566_vop,
+	}, {
+		.compatible = "rockchip,rk3568-vop",
+		.data = &rk3568_vop,
+	}, {
+	},
+};
+MODULE_DEVICE_TABLE(of, vop2_dt_match);
+
+static int vop2_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	return component_add(dev, &vop2_component_ops);
+}
+
+static int vop2_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &vop2_component_ops);
+
+	return 0;
+}
+
+struct platform_driver vop2_platform_driver = {
+	.probe = vop2_probe,
+	.remove = vop2_remove,
+	.driver = {
+		.name = "rockchip-vop2",
+		.of_match_table = of_match_ptr(vop2_dt_match),
+	},
+};
-- 
2.30.2


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

* [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

From: Andy Yan <andy.yan@rock-chips.com>

The VOP2 unit is found on Rockchip SoCs beginning with rk3566/rk3568.
It replaces the VOP unit found in the older Rockchip SoCs.

This driver has been derived from the downstream Rockchip Kernel and
heavily modified:

- All nonstandard DRM properties have been removed
- dropped struct vop2_plane_state and pass around less data between
  functions
- Dropped all DRM_FORMAT_* not known on upstream
- rework register access to get rid of excessively used macros
- Drop all waiting for framesyncs

The driver is tested with HDMI and MIPI-DSI display on a RK3568-EVB
board. Overlay support is tested with the modetest utility. AFBC support
on the cluster windows is tested with weston-simple-dmabuf-egl on
weston using the (yet to be upstreamed) panfrost driver support.

Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - consistently use u8/u16/u32 rather than uint8_t/uint16_t/uint32_t
    - Use spin_lock rather than spin_lock_irqsave
    - replace printk with drm_dbg
    - break some overlong lines
    
    Changes since v4:
    - Avoid stack frame overflow by not allocating big array on the stack
    
    Changes since v3:
    - Sort includes
    - fix typos
    - Drop spinlock
    - Use regmap_set_bits()/regmap_clear_bits()
    - simplify vop2_scale_factor()
    - simplify vop2_afbc_transform_offset()
    
    Changes since v4:
    - Sort nodes alphabetically
    
    Changes since v3:
    - Fix HDMI connector type

 drivers/gpu/drm/rockchip/Kconfig             |    6 +
 drivers/gpu/drm/rockchip/Makefile            |    1 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c  |    1 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h  |    6 +-
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c   |    2 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h  |   15 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2708 ++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h |  477 +++
 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c |  281 ++
 9 files changed, 3496 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index b9b156308460a..4ff0043f0ee70 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -28,6 +28,12 @@ config ROCKCHIP_VOP
 	  This selects support for the VOP driver. You should enable it
 	  on all older SoCs up to RK3399.
 
+config ROCKCHIP_VOP2
+	bool "Rockchip VOP2 driver"
+	help
+	  This selects support for the VOP2 driver. You should enable it
+	  on all newer SoCs beginning form RK3568.
+
 config ROCKCHIP_ANALOGIX_DP
 	bool "Rockchip specific extensions for Analogix DP driver"
 	depends on ROCKCHIP_VOP
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index dfc5512fdb9f1..3ff7b21c04149 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -6,6 +6,7 @@
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
 		rockchip_drm_gem.o
 
+rockchipdrm-$(CONFIG_ROCKCHIP_VOP2) += rockchip_drm_vop2.o rockchip_vop2_reg.o
 rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
 rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
 rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 82c8faf1fb6b8..95f6c5985fdd7 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -459,6 +459,7 @@ static int __init rockchip_drm_init(void)
 
 	num_rockchip_sub_drivers = 0;
 	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
+	ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2);
 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
 				CONFIG_ROCKCHIP_LVDS);
 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index d3e42410ae5da..5cb207aaaadd0 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -18,7 +18,7 @@
 
 #define ROCKCHIP_MAX_FB_BUFFER	3
 #define ROCKCHIP_MAX_CONNECTOR	2
-#define ROCKCHIP_MAX_CRTC	2
+#define ROCKCHIP_MAX_CRTC	4
 
 struct drm_device;
 struct drm_connector;
@@ -31,6 +31,9 @@ struct rockchip_crtc_state {
 	int output_bpc;
 	int output_flags;
 	bool enable_afbc;
+	u32 bus_format;
+	u32 bus_flags;
+	int color_space;
 };
 #define to_rockchip_crtc_state(s) \
 		container_of(s, struct rockchip_crtc_state, base)
@@ -63,6 +66,7 @@ extern struct platform_driver rockchip_dp_driver;
 extern struct platform_driver rockchip_lvds_driver;
 extern struct platform_driver vop_platform_driver;
 extern struct platform_driver rk3066_hdmi_driver;
+extern struct platform_driver vop2_platform_driver;
 
 struct rockchip_encoder {
 	struct device_node *port;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 3aa37e177667e..0d2cb4f3922b8 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -134,4 +134,6 @@ void rockchip_drm_mode_config_init(struct drm_device *dev)
 
 	dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
 	dev->mode_config.helper_private = &rockchip_mode_config_helpers;
+
+	dev->mode_config.normalize_zpos = true;
 }
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index 857d97cdc67c6..1e364d7b50e69 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -54,9 +54,23 @@ struct vop_afbc {
 	struct vop_reg enable;
 	struct vop_reg win_sel;
 	struct vop_reg format;
+	struct vop_reg rb_swap;
+	struct vop_reg uv_swap;
+	struct vop_reg auto_gating_en;
+	struct vop_reg block_split_en;
+	struct vop_reg pic_vir_width;
+	struct vop_reg tile_num;
 	struct vop_reg hreg_block_split;
+	struct vop_reg pic_offset;
 	struct vop_reg pic_size;
+	struct vop_reg dsp_offset;
+	struct vop_reg transform_offset;
 	struct vop_reg hdr_ptr;
+	struct vop_reg half_block_en;
+	struct vop_reg xmirror;
+	struct vop_reg ymirror;
+	struct vop_reg rotate_270;
+	struct vop_reg rotate_90;
 	struct vop_reg rstn;
 };
 
@@ -410,4 +424,5 @@ static inline int scl_vop_cal_lb_mode(int width, bool is_yuv)
 }
 
 extern const struct component_ops vop_component_ops;
+
 #endif /* _ROCKCHIP_DRM_VOP_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
new file mode 100644
index 0000000000000..394dd6c583682
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -0,0 +1,2708 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
+ * Author: Andy Yan <andy.yan@rock-chips.com>
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/swab.h>
+
+#include <drm/drm.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_flip_work.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+
+#include <uapi/linux/videodev2.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_vop2.h"
+
+/*
+ * VOP2 architecture
+ *
+ +----------+   +-------------+                                                        +-----------+
+ |  Cluster |   | Sel 1 from 6|                                                        | 1 from 3  |
+ |  window0 |   |    Layer0   |                                                        |    RGB    |
+ +----------+   +-------------+              +---------------+    +-------------+      +-----------+
+ +----------+   +-------------+              |N from 6 layers|    |             |
+ |  Cluster |   | Sel 1 from 6|              |   Overlay0    +--->| Video Port0 |      +-----------+
+ |  window1 |   |    Layer1   |              |               |    |             |      | 1 from 3  |
+ +----------+   +-------------+              +---------------+    +-------------+      |   LVDS    |
+ +----------+   +-------------+                                                        +-----------+
+ |  Esmart  |   | Sel 1 from 6|
+ |  window0 |   |   Layer2    |              +---------------+    +-------------+      +-----------+
+ +----------+   +-------------+              |N from 6 Layers|    |             | +--> | 1 from 3  |
+ +----------+   +-------------+   -------->  |   Overlay1    +--->| Video Port1 |      |   MIPI    |
+ |  Esmart  |   | Sel 1 from 6|   -------->  |               |    |             |      +-----------+
+ |  Window1 |   |   Layer3    |              +---------------+    +-------------+
+ +----------+   +-------------+                                                        +-----------+
+ +----------+   +-------------+                                                        | 1 from 3  |
+ |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |   HDMI    |
+ |  Window0 |   |    Layer4   |              |N from 6 Layers|    |             |      +-----------+
+ +----------+   +-------------+              |   Overlay2    +--->| Video Port2 |
+ +----------+   +-------------+              |               |    |             |      +-----------+
+ |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |  1 from 3 |
+ |  Window1 |   |    Layer5   |                                                        |    eDP    |
+ +----------+   +-------------+                                                        +-----------+
+ *
+ */
+
+enum vop2_data_format {
+	VOP2_FMT_ARGB8888 = 0,
+	VOP2_FMT_RGB888,
+	VOP2_FMT_RGB565,
+	VOP2_FMT_XRGB101010,
+	VOP2_FMT_YUV420SP,
+	VOP2_FMT_YUV422SP,
+	VOP2_FMT_YUV444SP,
+	VOP2_FMT_YUYV422 = 8,
+	VOP2_FMT_YUYV420,
+	VOP2_FMT_VYUY422,
+	VOP2_FMT_VYUY420,
+	VOP2_FMT_YUV420SP_TILE_8x4 = 0x10,
+	VOP2_FMT_YUV420SP_TILE_16x2,
+	VOP2_FMT_YUV422SP_TILE_8x4,
+	VOP2_FMT_YUV422SP_TILE_16x2,
+	VOP2_FMT_YUV420SP_10,
+	VOP2_FMT_YUV422SP_10,
+	VOP2_FMT_YUV444SP_10,
+};
+
+enum vop2_afbc_format {
+	VOP2_AFBC_FMT_RGB565,
+	VOP2_AFBC_FMT_ARGB2101010 = 2,
+	VOP2_AFBC_FMT_YUV420_10BIT,
+	VOP2_AFBC_FMT_RGB888,
+	VOP2_AFBC_FMT_ARGB8888,
+	VOP2_AFBC_FMT_YUV420 = 9,
+	VOP2_AFBC_FMT_YUV422 = 0xb,
+	VOP2_AFBC_FMT_YUV422_10BIT = 0xe,
+	VOP2_AFBC_FMT_INVALID = -1,
+};
+
+union vop2_alpha_ctrl {
+	u32 val;
+	struct {
+		/* [0:1] */
+		u32 color_mode:1;
+		u32 alpha_mode:1;
+		/* [2:3] */
+		u32 blend_mode:2;
+		u32 alpha_cal_mode:1;
+		/* [5:7] */
+		u32 factor_mode:3;
+		/* [8:9] */
+		u32 alpha_en:1;
+		u32 src_dst_swap:1;
+		u32 reserved:6;
+		/* [16:23] */
+		u32 glb_alpha:8;
+	} bits;
+};
+
+struct vop2_alpha {
+	union vop2_alpha_ctrl src_color_ctrl;
+	union vop2_alpha_ctrl dst_color_ctrl;
+	union vop2_alpha_ctrl src_alpha_ctrl;
+	union vop2_alpha_ctrl dst_alpha_ctrl;
+};
+
+struct vop2_alpha_config {
+	bool src_premulti_en;
+	bool dst_premulti_en;
+	bool src_pixel_alpha_en;
+	bool dst_pixel_alpha_en;
+	u16 src_glb_alpha_value;
+	u16 dst_glb_alpha_value;
+};
+
+struct vop2_win {
+	struct vop2 *vop2;
+	struct drm_plane base;
+	const struct vop2_win_data *data;
+	struct regmap_field *reg[VOP2_WIN_MAX_REG];
+
+	/**
+	 * @win_id: graphic window id, a cluster may be split into two
+	 * graphics windows.
+	 */
+	u8 win_id;
+	u8 delay;
+	u32 offset;
+
+	enum drm_plane_type type;
+};
+
+struct vop2_video_port {
+	struct drm_crtc crtc;
+	struct vop2 *vop2;
+	struct clk *dclk;
+	unsigned int id;
+	const struct vop2_video_port_regs *regs;
+	const struct vop2_video_port_data *data;
+
+	struct completion dsp_hold_completion;
+
+	/**
+	 * @win_mask: Bitmask of windows attached to the video port;
+	 */
+	u32 win_mask;
+
+	struct vop2_win *primary_plane;
+	struct drm_pending_vblank_event *event;
+
+	unsigned int nlayers;
+};
+
+struct vop2 {
+	struct device *dev;
+	struct drm_device *drm;
+	struct vop2_video_port vps[ROCKCHIP_MAX_CRTC];
+
+	const struct vop2_data *data;
+	/*
+	 * Number of windows that are registered as plane, may be less than the
+	 * total number of hardware windows.
+	 */
+	u32 registered_num_wins;
+
+	void __iomem *regs;
+	struct regmap *map;
+
+	struct regmap *grf;
+
+	/* physical map length of vop2 register */
+	u32 len;
+
+	void __iomem *lut_regs;
+
+	/* protects crtc enable/disable */
+	struct mutex vop2_lock;
+
+	int irq;
+
+	/*
+	 * Some global resources are shared between all video ports(crtcs), so
+	 * we need a ref counter here.
+	 */
+	unsigned int enable_count;
+	struct clk *hclk;
+	struct clk *aclk;
+
+	/* must be put at the end of the struct */
+	struct vop2_win win[];
+};
+
+static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
+{
+	return container_of(crtc, struct vop2_video_port, crtc);
+}
+
+static struct vop2_win *to_vop2_win(struct drm_plane *p)
+{
+	return container_of(p, struct vop2_win, base);
+}
+
+static void vop2_lock(struct vop2 *vop2)
+{
+	mutex_lock(&vop2->vop2_lock);
+}
+
+static void vop2_unlock(struct vop2 *vop2)
+{
+	mutex_unlock(&vop2->vop2_lock);
+}
+
+static void vop2_writel(struct vop2 *vop2, u32 offset, u32 v)
+{
+	regmap_write(vop2->map, offset, v);
+}
+
+static void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
+{
+	regmap_write(vp->vop2->map, vp->data->offset + offset, v);
+}
+
+static u32 vop2_readl(struct vop2 *vop2, u32 offset)
+{
+	u32 val;
+
+	regmap_read(vop2->map, offset, &val);
+
+	return val;
+}
+
+static void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v)
+{
+	regmap_field_write(win->reg[reg], v);
+}
+
+static bool vop2_cluster_window(const struct vop2_win *win)
+{
+	return win->data->feature & WIN_FEATURE_CLUSTER;
+}
+
+static void vop2_cfg_done(struct vop2_video_port *vp)
+{
+	struct vop2 *vop2 = vp->vop2;
+
+	regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE,
+			BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
+}
+
+static void vop2_win_disable(struct vop2_win *win)
+{
+	vop2_win_write(win, VOP2_WIN_ENABLE, 0);
+
+	if (vop2_cluster_window(win))
+		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0);
+}
+
+static enum vop2_data_format vop2_convert_format(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+		return VOP2_FMT_ARGB8888;
+	case DRM_FORMAT_RGB888:
+	case DRM_FORMAT_BGR888:
+		return VOP2_FMT_RGB888;
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_BGR565:
+		return VOP2_FMT_RGB565;
+	case DRM_FORMAT_NV12:
+		return VOP2_FMT_YUV420SP;
+	case DRM_FORMAT_NV16:
+		return VOP2_FMT_YUV422SP;
+	case DRM_FORMAT_NV24:
+		return VOP2_FMT_YUV444SP;
+	case DRM_FORMAT_YUYV:
+	case DRM_FORMAT_YVYU:
+		return VOP2_FMT_VYUY422;
+	case DRM_FORMAT_VYUY:
+	case DRM_FORMAT_UYVY:
+		return VOP2_FMT_YUYV422;
+	default:
+		DRM_ERROR("unsupported format[%08x]\n", format);
+		return -EINVAL;
+	}
+}
+
+static enum vop2_afbc_format vop2_convert_afbc_format(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+		return VOP2_AFBC_FMT_ARGB8888;
+	case DRM_FORMAT_RGB888:
+	case DRM_FORMAT_BGR888:
+		return VOP2_AFBC_FMT_RGB888;
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_BGR565:
+		return VOP2_AFBC_FMT_RGB565;
+	case DRM_FORMAT_NV12:
+		return VOP2_AFBC_FMT_YUV420;
+	case DRM_FORMAT_NV16:
+		return VOP2_AFBC_FMT_YUV422;
+	default:
+		return VOP2_AFBC_FMT_INVALID;
+	}
+
+	return VOP2_AFBC_FMT_INVALID;
+}
+
+static bool vop2_win_rb_swap(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_BGR888:
+	case DRM_FORMAT_BGR565:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool vop2_afbc_rb_swap(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_NV24:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool vop2_afbc_uv_swap(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_NV12:
+	case DRM_FORMAT_NV16:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool vop2_win_uv_swap(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_NV12:
+	case DRM_FORMAT_NV16:
+	case DRM_FORMAT_NV24:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool vop2_win_dither_up(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_BGR565:
+	case DRM_FORMAT_RGB565:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
+{
+	/*
+	 * FIXME:
+	 *
+	 * There is no media type for YUV444 output,
+	 * so when out_mode is AAAA or P888, assume output is YUV444 on
+	 * yuv format.
+	 *
+	 * From H/W testing, YUV444 mode need a rb swap.
+	 */
+	if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 ||
+	    bus_format == MEDIA_BUS_FMT_VYUY8_1X16 ||
+	    bus_format == MEDIA_BUS_FMT_YVYU8_2X8 ||
+	    bus_format == MEDIA_BUS_FMT_VYUY8_2X8 ||
+	    ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
+	      bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
+	     (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
+	      output_mode == ROCKCHIP_OUT_MODE_P888)))
+		return true;
+	else
+		return false;
+}
+
+static bool is_yuv_output(u32 bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+	case MEDIA_BUS_FMT_YUYV8_2X8:
+	case MEDIA_BUS_FMT_YVYU8_2X8:
+	case MEDIA_BUS_FMT_UYVY8_2X8:
+	case MEDIA_BUS_FMT_VYUY8_2X8:
+	case MEDIA_BUS_FMT_YUYV8_1X16:
+	case MEDIA_BUS_FMT_YVYU8_1X16:
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+	case MEDIA_BUS_FMT_VYUY8_1X16:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool rockchip_afbc(struct drm_plane *plane, u64 modifier)
+{
+	int i;
+
+	if (modifier == DRM_FORMAT_MOD_LINEAR)
+		return false;
+
+	for (i = 0 ; i < plane->modifier_count; i++)
+		if (plane->modifiers[i] == modifier)
+			return true;
+
+	return false;
+
+}
+
+static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
+					u64 modifier)
+{
+	struct vop2_win *win = to_vop2_win(plane);
+	struct vop2 *vop2 = win->vop2;
+
+	if (modifier == DRM_FORMAT_MOD_INVALID)
+		return false;
+
+	if (modifier == DRM_FORMAT_MOD_LINEAR)
+		return true;
+
+	if (!rockchip_afbc(plane, modifier)) {
+		drm_err(vop2->drm, "Unsupported format modifier 0x%llx\n",
+			modifier);
+
+		return false;
+	}
+
+	return vop2_convert_afbc_format(format) >= 0;
+}
+
+static u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate,
+				      bool afbc_half_block_en)
+{
+	struct drm_rect *src = &pstate->src;
+	struct drm_framebuffer *fb = pstate->fb;
+	u32 bpp = fb->format->cpp[0] * 8;
+	u32 vir_width = (fb->pitches[0] << 3) / bpp;
+	u32 width = drm_rect_width(src) >> 16;
+	u32 height = drm_rect_height(src) >> 16;
+	u32 act_xoffset = src->x1 >> 16;
+	u32 act_yoffset = src->y1 >> 16;
+	u32 align16_crop = 0;
+	u32 align64_crop = 0;
+	u32 height_tmp;
+	u8 tx, ty;
+	u8 bottom_crop_line_num = 0;
+
+	/* 16 pixel align */
+	if (height & 0xf)
+		align16_crop = 16 - (height & 0xf);
+
+	height_tmp = height + align16_crop;
+
+	/* 64 pixel align */
+	if (height_tmp & 0x3f)
+		align64_crop = 64 - (height_tmp & 0x3f);
+
+	bottom_crop_line_num = align16_crop + align64_crop;
+
+	switch (pstate->rotation &
+		(DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y |
+		 DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) {
+	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
+		tx = 16 - ((act_xoffset + width) & 0xf);
+		ty = bottom_crop_line_num - act_yoffset;
+		break;
+	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90:
+		tx = bottom_crop_line_num - act_yoffset;
+		ty = vir_width - width - act_xoffset;
+		break;
+	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270:
+		tx = act_yoffset;
+		ty = act_xoffset;
+		break;
+	case DRM_MODE_REFLECT_X:
+		tx = 16 - ((act_xoffset + width) & 0xf);
+		ty = act_yoffset;
+		break;
+	case DRM_MODE_REFLECT_Y:
+		tx = act_xoffset;
+		ty = bottom_crop_line_num - act_yoffset;
+		break;
+	case DRM_MODE_ROTATE_90:
+		tx = bottom_crop_line_num - act_yoffset;
+		ty = act_xoffset;
+		break;
+	case DRM_MODE_ROTATE_270:
+		tx = act_yoffset;
+		ty = vir_width - width - act_xoffset;
+		break;
+	case 0:
+		tx = act_xoffset;
+		ty = act_yoffset;
+		break;
+	}
+
+	if (afbc_half_block_en)
+		ty &= 0x7f;
+
+#define TRANSFORM_XOFFSET GENMASK(7, 0)
+#define TRANSFORM_YOFFSET GENMASK(23, 16)
+	return FIELD_PREP(TRANSFORM_XOFFSET, tx) |
+		FIELD_PREP(TRANSFORM_YOFFSET, ty);
+}
+
+/*
+ * A Cluster window has 2048 x 16 line buffer, which can
+ * works at 2048 x 16(Full) or 4096 x 8 (Half) mode.
+ * for Cluster_lb_mode register:
+ * 0: half mode, for plane input width range 2048 ~ 4096
+ * 1: half mode, for cluster work at 2 * 2048 plane mode
+ * 2: half mode, for rotate_90/270 mode
+ *
+ */
+static int vop2_get_cluster_lb_mode(struct vop2_win *win,
+				    struct drm_plane_state *pstate)
+{
+	if ((pstate->rotation & DRM_MODE_ROTATE_270) ||
+	    (pstate->rotation & DRM_MODE_ROTATE_90))
+		return 2;
+	else
+		return 0;
+}
+
+static u16 vop2_scale_factor(u32 src, u32 dst)
+{
+	u32 fac;
+	int shift;
+
+	if (src == dst)
+		return 0;
+
+	if (dst < 2)
+		return U16_MAX;
+
+	if (src < 2)
+		return 0;
+
+	if (src > dst)
+		shift = 12;
+	else
+		shift = 16;
+
+	src--;
+	dst--;
+
+	fac = DIV_ROUND_UP(src << shift, dst) - 1;
+
+	if (fac > U16_MAX)
+		return U16_MAX;
+
+	return fac;
+}
+
+static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
+			     u32 src_w, u32 src_h, u32 dst_w,
+			     u32 dst_h, u32 pixel_format)
+{
+	const struct drm_format_info *info;
+	u16 hor_scl_mode, ver_scl_mode;
+	u16 hscl_filter_mode, vscl_filter_mode;
+	u8 gt2 = 0;
+	u8 gt4 = 0;
+	u32 val;
+
+	info = drm_format_info(pixel_format);
+
+	if (src_h >= (4 * dst_h)) {
+		gt4 = 1;
+		src_h >>= 2;
+	} else if (src_h >= (2 * dst_h)) {
+		gt2 = 1;
+		src_h >>= 1;
+	}
+
+	hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
+	ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
+
+	if (hor_scl_mode == SCALE_UP)
+		hscl_filter_mode = VOP2_SCALE_UP_BIC;
+	else
+		hscl_filter_mode = VOP2_SCALE_DOWN_BIL;
+
+	if (ver_scl_mode == SCALE_UP)
+		vscl_filter_mode = VOP2_SCALE_UP_BIL;
+	else
+		vscl_filter_mode = VOP2_SCALE_DOWN_BIL;
+
+	/*
+	 * RK3568 VOP Esmart/Smart dsp_w should be even pixel
+	 * at scale down mode
+	 */
+	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
+		if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) {
+			drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n",
+				win->data->name, dst_w);
+			dst_w++;
+		}
+	}
+
+	val = vop2_scale_factor(src_w, dst_w);
+	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val);
+	val = vop2_scale_factor(src_h, dst_h);
+	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val);
+
+	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4);
+	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2);
+
+	vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode);
+	vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode);
+
+	if (vop2_cluster_window(win))
+		return;
+
+	vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode);
+	vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode);
+
+	if (info->is_yuv) {
+		src_w /= info->hsub;
+		src_h /= info->vsub;
+
+		gt4 = gt2 = 0;
+
+		if (src_h >= (4 * dst_h)) {
+			gt4 = 1;
+			src_h >>= 2;
+		} else if (src_h >= (2 * dst_h)) {
+			gt2 = 1;
+			src_h >>= 1;
+		}
+
+		hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
+		ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
+
+		val = vop2_scale_factor(src_w, dst_w);
+		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val);
+
+		val = vop2_scale_factor(src_h, dst_h);
+		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val);
+
+		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4);
+		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2);
+		vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode);
+		vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode);
+		vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode);
+		vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode);
+	}
+}
+
+static int vop2_convert_csc_mode(int csc_mode)
+{
+	switch (csc_mode) {
+	case V4L2_COLORSPACE_SMPTE170M:
+	case V4L2_COLORSPACE_470_SYSTEM_M:
+	case V4L2_COLORSPACE_470_SYSTEM_BG:
+		return CSC_BT601L;
+	case V4L2_COLORSPACE_REC709:
+	case V4L2_COLORSPACE_SMPTE240M:
+	case V4L2_COLORSPACE_DEFAULT:
+		return CSC_BT709L;
+	case V4L2_COLORSPACE_JPEG:
+		return CSC_BT601F;
+	case V4L2_COLORSPACE_BT2020:
+		return CSC_BT2020;
+	default:
+		return CSC_BT709L;
+	}
+}
+
+/*
+ * colorspace path:
+ *      Input        Win csc                     Output
+ * 1. YUV(2020)  --> Y2R->2020To709->R2Y   --> YUV_OUTPUT(601/709)
+ *    RGB        --> R2Y                  __/
+ *
+ * 2. YUV(2020)  --> bypasss               --> YUV_OUTPUT(2020)
+ *    RGB        --> 709To2020->R2Y       __/
+ *
+ * 3. YUV(2020)  --> Y2R->2020To709        --> RGB_OUTPUT(709)
+ *    RGB        --> R2Y                  __/
+ *
+ * 4. YUV(601/709)-> Y2R->709To2020->R2Y   --> YUV_OUTPUT(2020)
+ *    RGB        --> 709To2020->R2Y       __/
+ *
+ * 5. YUV(601/709)-> bypass                --> YUV_OUTPUT(709)
+ *    RGB        --> R2Y                  __/
+ *
+ * 6. YUV(601/709)-> bypass                --> YUV_OUTPUT(601)
+ *    RGB        --> R2Y(601)             __/
+ *
+ * 7. YUV        --> Y2R(709)              --> RGB_OUTPUT(709)
+ *    RGB        --> bypass               __/
+ *
+ * 8. RGB        --> 709To2020->R2Y        --> YUV_OUTPUT(2020)
+ *
+ * 9. RGB        --> R2Y(709)              --> YUV_OUTPUT(709)
+ *
+ * 10. RGB       --> R2Y(601)              --> YUV_OUTPUT(601)
+ *
+ * 11. RGB       --> bypass                --> RGB_OUTPUT(709)
+ */
+
+static void vop2_setup_csc_mode(struct vop2_video_port *vp,
+				struct vop2_win *win,
+				struct drm_plane_state *pstate)
+{
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
+	int is_input_yuv = pstate->fb->format->is_yuv;
+	int is_output_yuv = is_yuv_output(vcstate->bus_format);
+	int input_csc = V4L2_COLORSPACE_DEFAULT;
+	int output_csc = vcstate->color_space;
+	bool r2y_en, y2r_en;
+	int csc_mode;
+
+	if (is_input_yuv && !is_output_yuv) {
+		y2r_en = true;
+		r2y_en = false;
+		csc_mode = vop2_convert_csc_mode(input_csc);
+	} else if (!is_input_yuv && is_output_yuv) {
+		y2r_en = false;
+		r2y_en = true;
+		csc_mode = vop2_convert_csc_mode(output_csc);
+	} else {
+		y2r_en = false;
+		r2y_en = false;
+		csc_mode = false;
+	}
+
+	vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en);
+	vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en);
+	vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
+}
+
+static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
+{
+	struct vop2 *vop2 = vp->vop2;
+
+	vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq);
+	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq);
+}
+
+static void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq)
+{
+	struct vop2 *vop2 = vp->vop2;
+
+	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16);
+}
+
+static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
+{
+	int ret;
+
+	ret = clk_prepare_enable(vop2->hclk);
+	if (ret < 0) {
+		drm_err(vop2->drm, "failed to enable hclk - %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(vop2->aclk);
+	if (ret < 0) {
+		drm_err(vop2->drm, "failed to enable aclk - %d\n", ret);
+		goto err;
+	}
+
+	return 0;
+err:
+	clk_disable_unprepare(vop2->hclk);
+
+	return ret;
+}
+
+static void vop2_enable(struct vop2 *vop2)
+{
+	int ret;
+
+	ret = pm_runtime_get_sync(vop2->dev);
+	if (ret < 0) {
+		drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret);
+		return;
+	}
+
+	ret = vop2_core_clks_prepare_enable(vop2);
+	if (ret) {
+		pm_runtime_put_sync(vop2->dev);
+		return;
+	}
+
+	if (vop2->data->soc_id == 3566)
+		vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
+
+	vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
+
+	/*
+	 * Disable auto gating, this is a workaround to
+	 * avoid display image shift when a window enabled.
+	 */
+	regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL,
+			  RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN);
+
+	vop2_writel(vop2, RK3568_SYS0_INT_CLR,
+		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
+	vop2_writel(vop2, RK3568_SYS0_INT_EN,
+		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
+	vop2_writel(vop2, RK3568_SYS1_INT_CLR,
+		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
+	vop2_writel(vop2, RK3568_SYS1_INT_EN,
+		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
+}
+
+static void vop2_disable(struct vop2 *vop2)
+{
+	pm_runtime_put_sync(vop2->dev);
+
+	clk_disable_unprepare(vop2->aclk);
+	clk_disable_unprepare(vop2->hclk);
+}
+
+static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
+				     struct drm_atomic_state *state)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct vop2 *vop2 = vp->vop2;
+	int ret;
+
+	vop2_lock(vop2);
+
+	drm_crtc_vblank_off(crtc);
+
+	/*
+	 * Vop standby will take effect at end of current frame,
+	 * if dsp hold valid irq happen, it means standby complete.
+	 *
+	 * we must wait standby complete when we want to disable aclk,
+	 * if not, memory bus maybe dead.
+	 */
+	reinit_completion(&vp->dsp_hold_completion);
+
+	vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
+
+	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
+
+	ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
+					  msecs_to_jiffies(50));
+	if (!ret)
+		drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
+
+	vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
+
+	clk_disable_unprepare(vp->dclk);
+
+	vop2->enable_count--;
+
+	if (!vop2->enable_count)
+		vop2_disable(vop2);
+
+	vop2_unlock(vop2);
+
+	if (crtc->state->event && !crtc->state->active) {
+		spin_lock_irq(&crtc->dev->event_lock);
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+
+		crtc->state->event = NULL;
+	}
+}
+
+static int vop2_plane_atomic_check(struct drm_plane *plane,
+				   struct drm_atomic_state *astate)
+{
+	struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane);
+	struct drm_framebuffer *fb = pstate->fb;
+	struct drm_crtc *crtc = pstate->crtc;
+	struct drm_crtc_state *cstate;
+	struct vop2_video_port *vp;
+	struct vop2 *vop2;
+	const struct vop2_data *vop2_data;
+	struct drm_rect *dest = &pstate->dst;
+	struct drm_rect *src = &pstate->src;
+	int min_scale = FRAC_16_16(1, 8);
+	int max_scale = FRAC_16_16(8, 1);
+	int format;
+	int ret;
+
+	if (!crtc)
+		return 0;
+
+	vp = to_vop2_video_port(crtc);
+	vop2 = vp->vop2;
+	vop2_data = vop2->data;
+
+	cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc);
+	if (WARN_ON(!cstate))
+		return -EINVAL;
+
+	ret = drm_atomic_helper_check_plane_state(pstate, cstate,
+						  min_scale, max_scale,
+						  true, true);
+	if (ret)
+		return ret;
+
+	if (!pstate->visible)
+		return 0;
+
+	format = vop2_convert_format(fb->format->format);
+	if (format < 0)
+		return format;
+
+	if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 ||
+	    drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) {
+		drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
+			  drm_rect_width(src) >> 16, drm_rect_height(src) >> 16,
+			  drm_rect_width(dest), drm_rect_height(dest));
+		pstate->visible = false;
+		return 0;
+	}
+
+	if (drm_rect_width(src) >> 16 > vop2_data->max_input.width ||
+	    drm_rect_height(src) >> 16 > vop2_data->max_input.height) {
+		drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
+			  drm_rect_width(src) >> 16,
+			  drm_rect_height(src) >> 16,
+			  vop2_data->max_input.width,
+			  vop2_data->max_input.height);
+		return -EINVAL;
+	}
+
+	/*
+	 * Src.x1 can be odd when do clip, but yuv plane start point
+	 * need align with 2 pixel.
+	 */
+	if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
+		drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void vop2_plane_atomic_disable(struct drm_plane *plane,
+				      struct drm_atomic_state *state)
+{
+	struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state, plane);
+	struct vop2_win *win = to_vop2_win(plane);
+	struct vop2 *vop2 = win->vop2;
+
+	drm_dbg(vop2->drm, "%s disable\n", win->data->name);
+
+	if (!old_pstate->crtc)
+		return;
+
+	vop2_win_disable(win);
+	vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0);
+}
+
+/*
+ * The color key is 10 bit, so all format should
+ * convert to 10 bit here.
+ */
+static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
+{
+	struct drm_plane_state *pstate = plane->state;
+	struct drm_framebuffer *fb = pstate->fb;
+	struct vop2_win *win = to_vop2_win(plane);
+	u32 color_key_en = 0;
+	u32 r = 0;
+	u32 g = 0;
+	u32 b = 0;
+
+	if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) {
+		vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0);
+		return;
+	}
+
+	switch (fb->format->format) {
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_BGR565:
+		r = (color_key & 0xf800) >> 11;
+		g = (color_key & 0x7e0) >> 5;
+		b = (color_key & 0x1f);
+		r <<= 5;
+		g <<= 4;
+		b <<= 5;
+		color_key_en = 1;
+		break;
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_RGB888:
+	case DRM_FORMAT_BGR888:
+		r = (color_key & 0xff0000) >> 16;
+		g = (color_key & 0xff00) >> 8;
+		b = (color_key & 0xff);
+		r <<= 2;
+		g <<= 2;
+		b <<= 2;
+		color_key_en = 1;
+		break;
+	}
+
+	vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en);
+	vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b);
+}
+
+static void vop2_plane_atomic_update(struct drm_plane *plane,
+				     struct drm_atomic_state *state)
+{
+	struct drm_plane_state *pstate = plane->state;
+	struct drm_crtc *crtc = pstate->crtc;
+	struct vop2_win *win = to_vop2_win(plane);
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
+	struct vop2 *vop2 = win->vop2;
+	struct drm_framebuffer *fb = pstate->fb;
+	u32 bpp = fb->format->cpp[0] * 8;
+	u32 actual_w, actual_h, dsp_w, dsp_h;
+	u32 act_info, dsp_info;
+	u32 format;
+	u32 afbc_format;
+	u32 rb_swap;
+	u32 uv_swap;
+	struct drm_rect *src = &pstate->src;
+	struct drm_rect *dest = &pstate->dst;
+	u32 afbc_tile_num;
+	u32 transform_offset;
+	bool dither_up;
+	bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
+	bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
+	bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
+	bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
+	struct rockchip_gem_object *rk_obj;
+	unsigned long offset;
+	bool afbc_en;
+	dma_addr_t yrgb_mst;
+	dma_addr_t uv_mst;
+
+	/*
+	 * can't update plane when vop2 is disabled.
+	 */
+	if (WARN_ON(!crtc))
+		return;
+
+	if (!pstate->visible) {
+		vop2_plane_atomic_disable(plane, state);
+		return;
+	}
+
+	afbc_en = rockchip_afbc(plane, fb->modifier);
+
+	offset = (src->x1 >> 16) * fb->format->cpp[0];
+
+	/*
+	 * AFBC HDR_PTR must set to the zero offset of the framebuffer.
+	 */
+	if (afbc_en)
+		offset = 0;
+	else if (pstate->rotation & DRM_MODE_REFLECT_Y)
+		offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
+	else
+		offset += (src->y1 >> 16) * fb->pitches[0];
+
+	rk_obj = to_rockchip_obj(fb->obj[0]);
+
+	yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
+	if (fb->format->is_yuv) {
+		int hsub = fb->format->hsub;
+		int vsub = fb->format->vsub;
+
+		offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub;
+		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
+
+		if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en)
+			offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub;
+
+		rk_obj = to_rockchip_obj(fb->obj[0]);
+		uv_mst = rk_obj->dma_addr + offset + fb->offsets[1];
+	}
+
+	actual_w = drm_rect_width(src) >> 16;
+	actual_h = drm_rect_height(src) >> 16;
+	dsp_w = drm_rect_width(dest);
+
+	if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
+		drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
+			  vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
+		dsp_w = adjusted_mode->hdisplay - dest->x1;
+		if (dsp_w < 4)
+			dsp_w = 4;
+		actual_w = dsp_w * actual_w / drm_rect_width(dest);
+	}
+
+	dsp_h = drm_rect_height(dest);
+
+	if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
+		drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
+			  vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
+		dsp_h = adjusted_mode->vdisplay - dest->y1;
+		if (dsp_h < 4)
+			dsp_h = 4;
+		actual_h = dsp_h * actual_h / drm_rect_height(dest);
+	}
+
+	/*
+	 * This is workaround solution for IC design:
+	 * esmart can't support scale down when actual_w % 16 == 1.
+	 */
+	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
+		if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
+			drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
+				vp->id, win->data->name, actual_w);
+			actual_w -= 1;
+		}
+	}
+
+	if (afbc_en && actual_w % 4) {
+		drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
+			  vp->id, win->data->name, actual_w);
+		actual_w = ALIGN_DOWN(actual_w, 4);
+	}
+
+	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
+	dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff);
+
+	format = vop2_convert_format(fb->format->format);
+
+	drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n",
+		      vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h,
+		      dest->x1, dest->y1,
+		      &fb->format->format,
+		      afbc_en ? "AFBC" : "", &yrgb_mst);
+
+	if (afbc_en) {
+		u32 stride;
+
+		/* the afbc superblock is 16 x 16 */
+		afbc_format = vop2_convert_afbc_format(fb->format->format);
+
+		/* Enable color transform for YTR */
+		if (fb->modifier & AFBC_FORMAT_MOD_YTR)
+			afbc_format |= (1 << 4);
+
+		afbc_tile_num = ALIGN(actual_w, 16) >> 4;
+
+		/*
+		 * AFBC pic_vir_width is count by pixel, this is different
+		 * with WIN_VIR_STRIDE.
+		 */
+		stride = (fb->pitches[0] << 3) / bpp;
+		if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
+			drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligened\n",
+				  vp->id, win->data->name, stride);
+
+		rb_swap = vop2_afbc_rb_swap(fb->format->format);
+		uv_swap = vop2_afbc_uv_swap(fb->format->format);
+		/*
+		 * This is a workaround for crazy IC design, Cluster
+		 * and Esmart/Smart use different format configuration map:
+		 * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart.
+		 *
+		 * This is one thing we can make the convert simple:
+		 * AFBCD decode all the YUV data to YUV444. So we just
+		 * set all the yuv 10 bit to YUV444_10.
+		 */
+		if (fb->format->is_yuv && (bpp == 10))
+			format = VOP2_CLUSTER_YUV444_10;
+
+		if (vop2_cluster_window(win))
+			vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
+		vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
+		vop2_win_write(win, VOP2_WIN_AFBC_RB_SWAP, rb_swap);
+		vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap);
+		vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
+		vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
+		if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) {
+			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0);
+			transform_offset = vop2_afbc_transform_offset(pstate, false);
+		} else {
+			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 1);
+			transform_offset = vop2_afbc_transform_offset(pstate, true);
+		}
+		vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
+		vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
+		vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset);
+		vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1));
+		vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16)));
+		vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride);
+		vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num);
+		vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror);
+		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270);
+		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90);
+	} else {
+		vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4));
+	}
+
+	vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror);
+
+	if (rotate_90 || rotate_270) {
+		act_info = swahw32(act_info);
+		actual_w = drm_rect_height(src) >> 16;
+		actual_h = drm_rect_width(src) >> 16;
+	}
+
+	vop2_win_write(win, VOP2_WIN_FORMAT, format);
+	vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst);
+
+	rb_swap = vop2_win_rb_swap(fb->format->format);
+	vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap);
+	if (!vop2_cluster_window(win)) {
+		uv_swap = vop2_win_uv_swap(fb->format->format);
+		vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
+	}
+
+	if (fb->format->is_yuv) {
+		vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4));
+		vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst);
+	}
+
+	vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format);
+	if (!vop2_cluster_window(win))
+		vop2_plane_setup_color_key(plane, 0);
+	vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info);
+	vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info);
+	vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff));
+
+	vop2_setup_csc_mode(vp, win, pstate);
+
+	dither_up = vop2_win_dither_up(fb->format->format);
+	vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up);
+
+	vop2_win_write(win, VOP2_WIN_ENABLE, 1);
+
+	if (vop2_cluster_window(win)) {
+		int lb_mode = vop2_get_cluster_lb_mode(win, pstate);
+
+		vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode);
+		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1);
+	}
+}
+
+static const struct drm_plane_helper_funcs vop2_plane_helper_funcs = {
+	.atomic_check = vop2_plane_atomic_check,
+	.atomic_update = vop2_plane_atomic_update,
+	.atomic_disable = vop2_plane_atomic_disable,
+};
+
+static const struct drm_plane_funcs vop2_plane_funcs = {
+	.update_plane	= drm_atomic_helper_update_plane,
+	.disable_plane	= drm_atomic_helper_disable_plane,
+	.destroy = drm_plane_cleanup,
+	.reset = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+	.format_mod_supported = rockchip_vop2_mod_supported,
+};
+
+static int vop2_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+
+	vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD);
+
+	return 0;
+}
+
+static void vop2_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+
+	vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD);
+}
+
+static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
+				 const struct drm_display_mode *mode,
+				 struct drm_display_mode *adj_mode)
+{
+	drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V |
+					CRTC_STEREO_DOUBLE);
+
+	return true;
+}
+
+static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
+{
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
+
+	switch (vcstate->bus_format) {
+	case MEDIA_BUS_FMT_RGB565_1X16:
+		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
+		break;
+	case MEDIA_BUS_FMT_RGB666_1X18:
+	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
+		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
+		*dsp_ctrl |= RGB888_TO_RGB666;
+		break;
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
+		break;
+	default:
+		break;
+	}
+
+	if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA)
+		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
+
+	*dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL,
+				DITHER_DOWN_ALLEGRO);
+}
+
+static void vop2_post_config(struct drm_crtc *crtc)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+	u16 vtotal = mode->crtc_vtotal;
+	u16 hdisplay = mode->crtc_hdisplay;
+	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
+	u16 vdisplay = mode->crtc_vdisplay;
+	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
+	u32 left_margin = 100, right_margin = 100;
+	u32 top_margin = 100, bottom_margin = 100;
+	u16 hsize = hdisplay * (left_margin + right_margin) / 200;
+	u16 vsize = vdisplay * (top_margin + bottom_margin) / 200;
+	u16 hact_end, vact_end;
+	u32 val;
+
+	vsize = rounddown(vsize, 2);
+	hsize = rounddown(hsize, 2);
+	hact_st += hdisplay * (100 - left_margin) / 200;
+	hact_end = hact_st + hsize;
+	val = hact_st << 16;
+	val |= hact_end;
+	vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val);
+	vact_st += vdisplay * (100 - top_margin) / 200;
+	vact_end = vact_st + vsize;
+	val = vact_st << 16;
+	val |= vact_end;
+	vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val);
+	val = scl_cal_scale2(vdisplay, vsize) << 16;
+	val |= scl_cal_scale2(hdisplay, hsize);
+	vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val);
+
+	val = 0;
+	if (hdisplay != hsize)
+		val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN;
+	if (vdisplay != vsize)
+		val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN;
+	vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val);
+
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		u16 vact_st_f1 = vtotal + vact_st + 1;
+		u16 vact_end_f1 = vact_st_f1 + vsize;
+
+		val = vact_st_f1 << 16 | vact_end_f1;
+		vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
+	}
+
+	vop2_vp_write(vp, RK3568_VP_DSP_BG, 0);
+}
+
+static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
+				u32 polflags)
+{
+	struct vop2 *vop2 = vp->vop2;
+	u32 die, dip;
+
+	die = vop2_readl(vop2, RK3568_DSP_IF_EN);
+	dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
+
+	switch (id) {
+	case RK3568_VOP2_EP_RGB:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_RGB |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
+		if (polflags & POLFLAG_DCLK_INV)
+			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
+		else
+			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16));
+		break;
+	case RK3568_VOP2_EP_HDMI:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_HDMI |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id);
+		break;
+	case RK3568_VOP2_EP_EDP:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_EDP |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id);
+		break;
+	case RK3568_VOP2_EP_MIPI0:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id);
+		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
+		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
+		break;
+	case RK3568_VOP2_EP_MIPI1:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
+		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
+		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
+		break;
+	case RK3568_VOP2_EP_LVDS0:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id);
+		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
+		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
+		break;
+	case RK3568_VOP2_EP_LVDS1:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id);
+		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
+		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
+		break;
+	default:
+		return;
+	};
+
+	dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
+
+	vop2_writel(vop2, RK3568_DSP_IF_EN, die);
+	vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
+}
+
+static int us_to_vertical_line(struct drm_display_mode *mode, int us)
+{
+	return us * mode->clock / mode->htotal / 1000;
+}
+
+static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
+				    struct drm_atomic_state *state)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct vop2 *vop2 = vp->vop2;
+	const struct vop2_data *vop2_data = vop2->data;
+	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
+	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+	unsigned long clock = mode->crtc_clock * 1000;
+	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
+	u16 hdisplay = mode->crtc_hdisplay;
+	u16 htotal = mode->crtc_htotal;
+	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
+	u16 hact_end = hact_st + hdisplay;
+	u16 vdisplay = mode->crtc_vdisplay;
+	u16 vtotal = mode->crtc_vtotal;
+	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
+	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
+	u16 vact_end = vact_st + vdisplay;
+	u8 out_mode;
+	u32 dsp_ctrl = 0;
+	int act_end;
+	u32 val, polflags;
+	int ret;
+	struct drm_encoder *encoder;
+
+	drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n",
+		hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
+		drm_mode_vrefresh(mode), vcstate->output_type, vp->id);
+
+	vop2_lock(vop2);
+
+	ret = clk_prepare_enable(vp->dclk);
+	if (ret < 0) {
+		drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
+			      vp->id, ret);
+		return;
+	}
+
+	if (!vop2->enable_count)
+		vop2_enable(vop2);
+
+	vop2->enable_count++;
+
+	vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY);
+
+	polflags = 0;
+	if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
+		polflags |= POLFLAG_DCLK_INV;
+	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+		polflags |= BIT(HSYNC_POSITIVE);
+	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+		polflags |= BIT(VSYNC_POSITIVE);
+
+	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
+		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+		struct device_node *node, *parent;
+
+		parent = of_get_parent(rkencoder->port);
+
+		for_each_endpoint_of_node(parent, node) {
+			struct device_node *crtc_port = of_graph_get_remote_port(node);
+			struct device_node *epn;
+			struct of_endpoint endpoint;
+
+			if (crtc->port != crtc_port) {
+				of_node_put(crtc_port);
+				continue;
+			}
+
+			of_node_put(crtc_port);
+
+			epn = of_graph_get_remote_endpoint(node);
+			of_graph_parse_endpoint(epn, &endpoint);
+			of_node_put(epn);
+
+			drm_dbg(vop2->drm, "vp%d is connected to %s, id %d\n",
+					   vp->id, encoder->name, endpoint.id);
+			rk3568_set_intf_mux(vp, endpoint.id, polflags);
+		}
+		of_node_put(parent);
+	}
+
+	if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
+	     !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT))
+		out_mode = ROCKCHIP_OUT_MODE_P888;
+	else
+		out_mode = vcstate->output_mode;
+
+	dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode);
+
+	if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP;
+
+	if (is_yuv_output(vcstate->bus_format))
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y;
+
+	vop2_dither_setup(crtc, &dsp_ctrl);
+
+	vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len);
+	val = hact_st << 16;
+	val |= hact_end;
+	vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val);
+
+	val = vact_st << 16;
+	val |= vact_end;
+	vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val);
+
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		u16 vact_st_f1 = vtotal + vact_st + 1;
+		u16 vact_end_f1 = vact_st_f1 + vdisplay;
+
+		val = vact_st_f1 << 16 | vact_end_f1;
+		vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val);
+
+		val = vtotal << 16 | (vtotal + vsync_len);
+		vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val);
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE;
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL;
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN;
+		vtotal += vtotal + 1;
+		act_end = vact_end_f1;
+	} else {
+		act_end = vact_end;
+	}
+
+	vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id),
+		    (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end);
+
+	vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len);
+
+	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
+		clock *= 2;
+	}
+
+	vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
+
+	clk_set_rate(vp->dclk, clock);
+
+	vop2_post_config(crtc);
+
+	vop2_cfg_done(vp);
+
+	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
+
+	drm_crtc_vblank_on(crtc);
+
+	vop2_unlock(vop2);
+}
+
+static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
+				  struct drm_atomic_state *state)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct drm_plane *plane;
+	int nplanes = 0;
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+
+	drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
+		nplanes++;
+
+	if (nplanes > vp->nlayers)
+		return -EINVAL;
+
+	return 0;
+}
+
+static bool is_opaque(u16 alpha)
+{
+	return (alpha >> 8) == 0xff;
+}
+
+static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config,
+			     struct vop2_alpha *alpha)
+{
+	int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1;
+	int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1;
+	int src_color_mode = alpha_config->src_premulti_en ?
+				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
+	int dst_color_mode = alpha_config->dst_premulti_en ?
+				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
+
+	alpha->src_color_ctrl.val = 0;
+	alpha->dst_color_ctrl.val = 0;
+	alpha->src_alpha_ctrl.val = 0;
+	alpha->dst_alpha_ctrl.val = 0;
+
+	if (!alpha_config->src_pixel_alpha_en)
+		alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
+	else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en)
+		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX;
+	else
+		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
+
+	alpha->src_color_ctrl.bits.alpha_en = 1;
+
+	if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) {
+		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
+		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
+	} else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) {
+		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
+		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE;
+	} else {
+		alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL;
+		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
+	}
+	alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8;
+	alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
+	alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
+
+	alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
+	alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
+	alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
+	alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8;
+	alpha->dst_color_ctrl.bits.color_mode = dst_color_mode;
+	alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
+
+	alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
+	alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode;
+	alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
+	alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE;
+
+	alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
+	if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en)
+		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX;
+	else
+		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
+	alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION;
+	alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
+}
+
+static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id)
+{
+	struct vop2_video_port *vp;
+	int used_layer = 0;
+	int i;
+
+	for (i = 0; i < port_id; i++) {
+		vp = &vop2->vps[i];
+		used_layer += hweight32(vp->win_mask);
+	}
+
+	return used_layer;
+}
+
+static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win)
+{
+	u32 offset = (main_win->data->phys_id * 0x10);
+	struct vop2_alpha_config alpha_config;
+	struct vop2_alpha alpha;
+	struct drm_plane_state *bottom_win_pstate;
+	bool src_pixel_alpha_en = false;
+	u16 src_glb_alpha_val, dst_glb_alpha_val;
+	bool premulti_en = false;
+	bool swap = false;
+
+	/* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */
+	bottom_win_pstate = main_win->base.state;
+	src_glb_alpha_val = 0;
+	dst_glb_alpha_val = main_win->base.state->alpha;
+
+	if (!bottom_win_pstate->fb)
+		return;
+
+	alpha_config.src_premulti_en = premulti_en;
+	alpha_config.dst_premulti_en = false;
+	alpha_config.src_pixel_alpha_en = src_pixel_alpha_en;
+	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
+	alpha_config.src_glb_alpha_value = src_glb_alpha_val;
+	alpha_config.dst_glb_alpha_value = dst_glb_alpha_val;
+	vop2_parse_alpha(&alpha_config, &alpha);
+
+	alpha.src_color_ctrl.bits.src_dst_swap = swap;
+	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset,
+		    alpha.src_color_ctrl.val);
+	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset,
+		    alpha.dst_color_ctrl.val);
+	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset,
+		    alpha.src_alpha_ctrl.val);
+	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset,
+		    alpha.dst_alpha_ctrl.val);
+}
+
+static void vop2_setup_alpha(struct vop2_video_port *vp)
+{
+	struct vop2 *vop2 = vp->vop2;
+	struct drm_framebuffer *fb;
+	struct vop2_alpha_config alpha_config;
+	struct vop2_alpha alpha;
+	struct drm_plane *plane;
+	int pixel_alpha_en;
+	int premulti_en, gpremulti_en = 0;
+	int mixer_id;
+	u32 offset;
+	bool bottom_layer_alpha_en = false;
+	u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE;
+
+	mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id);
+	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
+
+	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
+		struct vop2_win *win = to_vop2_win(plane);
+
+		if (plane->state->normalized_zpos == 0 &&
+		    !is_opaque(plane->state->alpha) &&
+		    !vop2_cluster_window(win)) {
+			/*
+			 * If bottom layer have global alpha effect [except cluster layer,
+			 * because cluster have deal with bottom layer global alpha value
+			 * at cluster mix], bottom layer mix need deal with global alpha.
+			 */
+			bottom_layer_alpha_en = true;
+			dst_global_alpha = plane->state->alpha;
+		}
+	}
+
+	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
+		struct vop2_win *win = to_vop2_win(plane);
+		int zpos = plane->state->normalized_zpos;
+
+		if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
+			premulti_en = 1;
+		else
+			premulti_en = 0;
+
+		plane = &win->base;
+		fb = plane->state->fb;
+
+		pixel_alpha_en = fb->format->has_alpha;
+
+		alpha_config.src_premulti_en = premulti_en;
+
+		if (bottom_layer_alpha_en && zpos == 1) {
+			gpremulti_en = premulti_en;
+			/* Cd = Cs + (1 - As) * Cd * Agd */
+			alpha_config.dst_premulti_en = false;
+			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
+			alpha_config.src_glb_alpha_value = plane->state->alpha;
+			alpha_config.dst_glb_alpha_value = dst_global_alpha;
+		} else if (vop2_cluster_window(win)) {
+			/* Mix output data only have pixel alpha */
+			alpha_config.dst_premulti_en = true;
+			alpha_config.src_pixel_alpha_en = true;
+			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+		} else {
+			/* Cd = Cs + (1 - As) * Cd */
+			alpha_config.dst_premulti_en = true;
+			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
+			alpha_config.src_glb_alpha_value = plane->state->alpha;
+			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+		}
+
+		vop2_parse_alpha(&alpha_config, &alpha);
+
+		offset = (mixer_id + zpos - 1) * 0x10;
+		vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset,
+			    alpha.src_color_ctrl.val);
+		vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset,
+			    alpha.dst_color_ctrl.val);
+		vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset,
+			    alpha.src_alpha_ctrl.val);
+		vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset,
+			    alpha.dst_alpha_ctrl.val);
+	}
+
+	if (vp->id == 0) {
+		if (bottom_layer_alpha_en) {
+			/* Transfer pixel alpha to hdr mix */
+			alpha_config.src_premulti_en = gpremulti_en;
+			alpha_config.dst_premulti_en = true;
+			alpha_config.src_pixel_alpha_en = true;
+			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+			vop2_parse_alpha(&alpha_config, &alpha);
+
+			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL,
+				alpha.src_color_ctrl.val);
+			vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL,
+				alpha.dst_color_ctrl.val);
+			vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL,
+				alpha.src_alpha_ctrl.val);
+			vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL,
+				alpha.dst_alpha_ctrl.val);
+		} else {
+			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0);
+		}
+	}
+}
+
+static void vop2_setup_layer_mixer(struct vop2_video_port *vp)
+{
+	struct vop2 *vop2 = vp->vop2;
+	struct drm_plane *plane;
+	u32 layer_sel = 0;
+	u32 port_sel;
+	unsigned int nlayer, ofs;
+	struct drm_display_mode *adjusted_mode;
+	u16 hsync_len;
+	u16 hdisplay;
+	u32 bg_dly;
+	u32 pre_scan_dly;
+	int i;
+	struct vop2_video_port *vp0 = &vop2->vps[0];
+	struct vop2_video_port *vp1 = &vop2->vps[1];
+	struct vop2_video_port *vp2 = &vop2->vps[2];
+
+	adjusted_mode = &vp->crtc.state->adjusted_mode;
+	hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
+	hdisplay = adjusted_mode->crtc_hdisplay;
+
+	bg_dly = vp->data->pre_scan_max_dly[3];
+	vop2_writel(vop2, RK3568_VP_BG_MIX_CTRL(vp->id),
+			    FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly));
+
+	pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len;
+	vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly);
+
+	vop2_writel(vop2, RK3568_OVL_CTRL, 0);
+	port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
+	port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT;
+
+	if (vp0->nlayers)
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX,
+				     vp0->nlayers - 1);
+	else
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8);
+
+	if (vp1->nlayers)
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX,
+				     (vp0->nlayers + vp1->nlayers - 1));
+	else
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
+
+	if (vp2->nlayers)
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX,
+			(vp2->nlayers + vp1->nlayers + vp0->nlayers - 1));
+	else
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
+
+	layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
+
+	ofs = 0;
+	for (i = 0; i < vp->id; i++)
+		ofs += vop2->vps[i].nlayers;
+
+	nlayer = 0;
+	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
+		struct vop2_win *win = to_vop2_win(plane);
+
+		switch (win->data->phys_id) {
+		case ROCKCHIP_VOP2_CLUSTER0:
+			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id);
+			break;
+		case ROCKCHIP_VOP2_CLUSTER1:
+			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id);
+			break;
+		case ROCKCHIP_VOP2_ESMART0:
+			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id);
+			break;
+		case ROCKCHIP_VOP2_ESMART1:
+			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id);
+			break;
+		case ROCKCHIP_VOP2_SMART0:
+			port_sel &= ~RK3568_OVL_PORT_SEL__SMART0;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id);
+			break;
+		case ROCKCHIP_VOP2_SMART1:
+			port_sel &= ~RK3568_OVL_PORT_SEL__SMART1;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id);
+			break;
+		}
+
+		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
+		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, win->data->layer_sel_id);
+		nlayer++;
+	}
+
+	/* configure unused layers to 0x5 (reserved) */
+	for (; nlayer < 3; nlayer++) {
+		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
+		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5);
+	}
+
+	vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel);
+	vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
+	vop2_writel(vop2, RK3568_OVL_CTRL, RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD);
+}
+
+static void vop2_setup_dly_for_windows(struct vop2 *vop2)
+{
+	struct vop2_win *win;
+	int i = 0;
+	u32 cdly = 0, sdly = 0;
+
+	for (i = 0; i < vop2->data->win_size; i++) {
+		u32 dly;
+
+		win = &vop2->win[i];
+		dly = win->delay;
+
+		switch (win->data->phys_id) {
+		case ROCKCHIP_VOP2_CLUSTER0:
+			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly);
+			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly);
+			break;
+		case ROCKCHIP_VOP2_CLUSTER1:
+			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly);
+			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly);
+			break;
+		case ROCKCHIP_VOP2_ESMART0:
+			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly);
+			break;
+		case ROCKCHIP_VOP2_ESMART1:
+			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly);
+			break;
+		case ROCKCHIP_VOP2_SMART0:
+			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly);
+			break;
+		case ROCKCHIP_VOP2_SMART1:
+			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly);
+			break;
+		}
+	}
+
+	vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly);
+	vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly);
+}
+
+static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
+				   struct drm_atomic_state *state)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct vop2 *vop2 = vp->vop2;
+	struct drm_plane *plane;
+
+	vp->win_mask = 0;
+
+	drm_atomic_crtc_for_each_plane(plane, crtc) {
+		struct vop2_win *win = to_vop2_win(plane);
+
+		win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT];
+
+		vp->win_mask |= BIT(win->data->phys_id);
+
+		if (vop2_cluster_window(win))
+			vop2_setup_cluster_alpha(vop2, win);
+	}
+
+	if (!vp->win_mask)
+		return;
+
+	vop2_setup_layer_mixer(vp);
+	vop2_setup_alpha(vp);
+	vop2_setup_dly_for_windows(vop2);
+}
+
+static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
+				   struct drm_atomic_state *state)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+
+	vop2_post_config(crtc);
+
+	vop2_cfg_done(vp);
+
+	spin_lock_irq(&crtc->dev->event_lock);
+
+	if (crtc->state->event) {
+		WARN_ON(drm_crtc_vblank_get(crtc));
+		vp->event = crtc->state->event;
+		crtc->state->event = NULL;
+	}
+
+	spin_unlock_irq(&crtc->dev->event_lock);
+}
+
+static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
+	.mode_fixup = vop2_crtc_mode_fixup,
+	.atomic_check = vop2_crtc_atomic_check,
+	.atomic_begin = vop2_crtc_atomic_begin,
+	.atomic_flush = vop2_crtc_atomic_flush,
+	.atomic_enable = vop2_crtc_atomic_enable,
+	.atomic_disable = vop2_crtc_atomic_disable,
+};
+
+static void vop2_crtc_reset(struct drm_crtc *crtc)
+{
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
+
+	if (crtc->state) {
+		__drm_atomic_helper_crtc_destroy_state(crtc->state);
+		kfree(vcstate);
+	}
+
+	vcstate = kzalloc(sizeof(*vcstate), GFP_KERNEL);
+	if (!vcstate)
+		return;
+
+	crtc->state = &vcstate->base;
+	crtc->state->crtc = crtc;
+}
+
+static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc)
+{
+	struct rockchip_crtc_state *vcstate, *old_vcstate;
+
+	old_vcstate = to_rockchip_crtc_state(crtc->state);
+
+	vcstate = kmemdup(old_vcstate, sizeof(*old_vcstate), GFP_KERNEL);
+	if (!vcstate)
+		return NULL;
+
+	__drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
+
+	return &vcstate->base;
+}
+
+static void vop2_crtc_destroy_state(struct drm_crtc *crtc,
+				    struct drm_crtc_state *state)
+{
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state);
+
+	__drm_atomic_helper_crtc_destroy_state(&vcstate->base);
+	kfree(vcstate);
+}
+
+static const struct drm_crtc_funcs vop2_crtc_funcs = {
+	.set_config = drm_atomic_helper_set_config,
+	.page_flip = drm_atomic_helper_page_flip,
+	.destroy = drm_crtc_cleanup,
+	.reset = vop2_crtc_reset,
+	.atomic_duplicate_state = vop2_crtc_duplicate_state,
+	.atomic_destroy_state = vop2_crtc_destroy_state,
+	.enable_vblank = vop2_crtc_enable_vblank,
+	.disable_vblank = vop2_crtc_disable_vblank,
+};
+
+static irqreturn_t vop2_isr(int irq, void *data)
+{
+	struct vop2 *vop2 = data;
+	const struct vop2_data *vop2_data = vop2->data;
+	u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM];
+	int ret = IRQ_NONE;
+	int i;
+
+	/*
+	 * The irq is shared with the iommu. If the runtime-pm state of the
+	 * vop2-device is disabled the irq has to be targeted at the iommu.
+	 */
+	if (!pm_runtime_get_if_in_use(vop2->dev))
+		return IRQ_NONE;
+
+	for (i = 0; i < vop2_data->nr_vps; i++) {
+		struct vop2_video_port *vp = &vop2->vps[i];
+		struct drm_crtc *crtc = &vp->crtc;
+		u32 irqs;
+
+		irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
+		vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
+
+		if (irqs & VP_INT_DSP_HOLD_VALID) {
+			complete(&vp->dsp_hold_completion);
+			ret = IRQ_HANDLED;
+		}
+
+		if (irqs & VP_INT_FS_FIELD) {
+			drm_crtc_handle_vblank(crtc);
+			spin_lock(&crtc->dev->event_lock);
+			if (vp->event) {
+				u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
+
+				if (!(val & BIT(vp->id))) {
+					drm_crtc_send_vblank_event(crtc, vp->event);
+					vp->event = NULL;
+					drm_crtc_vblank_put(crtc);
+				}
+			}
+			spin_unlock(&crtc->dev->event_lock);
+
+			ret = IRQ_HANDLED;
+		}
+
+		if (irqs & VP_INT_POST_BUF_EMPTY) {
+			drm_err_ratelimited(vop2->drm,
+					    "POST_BUF_EMPTY irq err at vp%d\n",
+					    vp->id);
+			ret = IRQ_HANDLED;
+		}
+	}
+
+	axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS);
+	vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]);
+	axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS);
+	vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]);
+
+	for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) {
+		if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) {
+			drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n");
+			ret = IRQ_HANDLED;
+		}
+	}
+
+	pm_runtime_put(vop2->dev);
+
+	return ret;
+}
+
+static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win,
+			   unsigned long possible_crtcs)
+{
+	const struct vop2_win_data *win_data = win->data;
+	unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+				  BIT(DRM_MODE_BLEND_PREMULTI) |
+				  BIT(DRM_MODE_BLEND_COVERAGE);
+	int ret;
+
+	ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs,
+				       &vop2_plane_funcs, win_data->formats,
+				       win_data->nformats,
+				       win_data->format_modifiers,
+				       win->type, win_data->name);
+	if (ret) {
+		drm_err(vop2->drm, "failed to initialize plane %d\n", ret);
+		return ret;
+	}
+
+	drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs);
+
+	if (win->data->supported_rotations)
+		drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0,
+						   DRM_MODE_ROTATE_0 |
+						   win->data->supported_rotations);
+	drm_plane_create_alpha_property(&win->base);
+	drm_plane_create_blend_mode_property(&win->base, blend_caps);
+	drm_plane_create_zpos_property(&win->base, win->win_id, 0,
+				       vop2->registered_num_wins - 1);
+
+	return 0;
+}
+
+static struct vop2_video_port *get_activated_vp(struct vop2 *vop2, int n)
+{
+	int i, id = 0;
+
+	for (i = 0; i < vop2->data->nr_vps; i++) {
+		struct vop2_video_port *vp = &vop2->vps[i];
+
+		if (!vp->crtc.port)
+			continue;
+
+		if (n == id)
+			return vp;
+		id++;
+	}
+
+	return NULL;
+}
+
+#define NR_LAYERS 6
+
+static int vop2_create_crtc(struct vop2 *vop2)
+{
+	const struct vop2_data *vop2_data = vop2->data;
+	struct drm_device *drm = vop2->drm;
+	struct device *dev = vop2->dev;
+	struct drm_plane *plane;
+	struct device_node *port;
+	struct vop2_video_port *vp;
+	u32 possible_crtcs;
+	int i, nvp, nvps = 0;
+	int ret;
+
+	for (i = 0; i < vop2_data->nr_vps; i++) {
+		const struct vop2_video_port_data *vp_data;
+		struct device_node *np;
+		char dclk_name[9];
+
+		vp_data = &vop2_data->vp[i];
+		vp = &vop2->vps[i];
+		vp->vop2 = vop2;
+		vp->id = vp_data->id;
+		vp->regs = vp_data->regs;
+		vp->data = vp_data;
+
+		snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
+		vp->dclk = devm_clk_get(vop2->dev, dclk_name);
+		if (IS_ERR(vp->dclk)) {
+			drm_err(vop2->drm, "failed to get %s\n", dclk_name);
+			return PTR_ERR(vp->dclk);
+		}
+
+		np = of_graph_get_remote_node(dev->of_node, i, -1);
+		if (!np) {
+			drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
+			continue;
+		}
+		of_node_put(np);
+
+		port = of_graph_get_port_by_id(dev->of_node, i);
+		if (!port) {
+			drm_err(vop2->drm, "no port node found for video_port%d\n", i);
+			return -ENOENT;
+		}
+
+		vp->crtc.port = port;
+		nvps++;
+	}
+
+	nvp = 0;
+	for (i = 0; i < vop2->registered_num_wins; i++) {
+		struct vop2_win *win = &vop2->win[i];
+
+		if (win->type == DRM_PLANE_TYPE_PRIMARY) {
+			vp = get_activated_vp(vop2, nvp);
+
+			if (vp) {
+				possible_crtcs = BIT(nvp);
+				vp->primary_plane = win;
+
+				nvp++;
+			} else {
+				/* change the unused primary window to overlay window */
+				win->type = DRM_PLANE_TYPE_OVERLAY;
+			}
+		}
+
+		if (win->type == DRM_PLANE_TYPE_OVERLAY)
+			possible_crtcs = (1 << vop2_data->nr_vps) - 1;
+
+		ret = vop2_plane_init(vop2, win, possible_crtcs);
+
+		if (ret) {
+			drm_err(vop2->drm, "failed to init plane %s: %d\n",
+				win->data->name, ret);
+			return ret;
+		}
+	}
+
+	for (i = 0; i < vop2_data->nr_vps; i++) {
+		vp = &vop2->vps[i];
+
+		if (!vp->crtc.port)
+			continue;
+
+		plane = &vp->primary_plane->base;
+
+		ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL,
+						&vop2_crtc_funcs,
+						"video_port%d", vp->id);
+		if (ret) {
+			drm_err(vop2->drm, "crtc init for video_port%d failed\n", i);
+			return ret;
+		}
+
+		drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs);
+
+		init_completion(&vp->dsp_hold_completion);
+	}
+
+	for (i = 0; i < vop2->data->nr_vps; i++) {
+		struct vop2_video_port *vp = &vop2->vps[i];
+
+		if (vp->crtc.port)
+			vp->nlayers = NR_LAYERS / nvps;
+	}
+
+	return 0;
+}
+
+static void vop2_destroy_crtc(struct drm_crtc *crtc)
+{
+	of_node_put(crtc->port);
+
+	/*
+	 * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane()
+	 * references the CRTC.
+	 */
+	drm_crtc_cleanup(crtc);
+}
+
+static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = {
+	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0),
+	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5),
+	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14),
+	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18),
+	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31),
+	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31),
+	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31),
+	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31),
+	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31),
+	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19),
+	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15),
+	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31),
+	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8),
+	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9),
+	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11),
+
+	/* Scale */
+	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15),
+	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31),
+	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15),
+	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13),
+	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3),
+	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28),
+	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29),
+
+	/* cluster regs */
+	[VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1),
+	[VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0),
+	[VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7),
+
+	/* afbc regs */
+	[VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6),
+	[VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9),
+	[VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10),
+	[VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4),
+	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7),
+	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8),
+	[VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31),
+	[VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31),
+	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15),
+	[VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31),
+	[VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31),
+	[VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31),
+	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31),
+	[VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0),
+	[VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1),
+	[VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2),
+	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3),
+	[VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff },
+	[VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff },
+	[VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff },
+	[VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
+	[VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
+	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff },
+	[VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff },
+};
+
+static int vop2_cluster_init(struct vop2_win *win)
+{
+	struct vop2 *vop2 = win->vop2;
+	struct reg_field *cluster_regs;
+	int ret, i;
+
+	cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs),
+			       GFP_KERNEL);
+	if (!cluster_regs)
+		return -ENOMEM;
+
+	for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++)
+		if (cluster_regs[i].reg != 0xffffffff)
+			cluster_regs[i].reg += win->offset;
+
+	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
+					    cluster_regs,
+					    ARRAY_SIZE(vop2_cluster_regs));
+
+	kfree(cluster_regs);
+
+	return ret;
+};
+
+static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = {
+	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0),
+	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5),
+	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12),
+	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14),
+	[VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16),
+	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31),
+	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31),
+	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28),
+	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31),
+	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31),
+	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17),
+	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15),
+	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31),
+	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0),
+	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1),
+	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3),
+	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31),
+	[VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29),
+	[VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31),
+
+	/* Scale */
+	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15),
+	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31),
+	[VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15),
+	[VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31),
+	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1),
+	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3),
+	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5),
+	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7),
+	[VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9),
+	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11),
+	[VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13),
+	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15),
+	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17),
+	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8),
+	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9),
+	[VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10),
+	[VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11),
+	[VOP2_WIN_XMIRROR] = { .reg = 0xffffffff },
+	[VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff },
+	[VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff },
+};
+
+static int vop2_esmart_init(struct vop2_win *win)
+{
+	struct vop2 *vop2 = win->vop2;
+	struct reg_field *esmart_regs;
+	int ret, i;
+
+	esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs),
+			       GFP_KERNEL);
+	if (!esmart_regs)
+		return -ENOMEM;
+
+	for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++)
+		if (esmart_regs[i].reg != 0xffffffff)
+			esmart_regs[i].reg += win->offset;
+
+	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
+					    esmart_regs,
+					    ARRAY_SIZE(vop2_esmart_regs));
+
+	kfree(esmart_regs);
+
+	return ret;
+};
+
+static int vop2_win_init(struct vop2 *vop2)
+{
+	const struct vop2_data *vop2_data = vop2->data;
+	struct vop2_win *win;
+	int i, ret;
+
+	for (i = 0; i < vop2_data->win_size; i++) {
+		const struct vop2_win_data *win_data = &vop2_data->win[i];
+
+		win = &vop2->win[i];
+		win->data = win_data;
+		win->type = win_data->type;
+		win->offset = win_data->base;
+		win->win_id = i;
+		win->vop2 = vop2;
+		if (vop2_cluster_window(win))
+			ret = vop2_cluster_init(win);
+		else
+			ret = vop2_esmart_init(win);
+		if (ret)
+			return ret;
+	}
+
+	vop2->registered_num_wins = vop2_data->win_size;
+
+	return 0;
+}
+
+/*
+ * The window registers are only updated when config done is written.
+ * Until that they read back the old value. As we read-modify-write
+ * these registers mark them as non-volatile. This makes sure we read
+ * the new values from the regmap register cache.
+ */
+static const struct regmap_range vop2_nonvolatile_range[] = {
+	regmap_reg_range(0x1000, 0x23ff),
+};
+
+static const struct regmap_access_table vop2_volatile_table = {
+	.no_ranges = vop2_nonvolatile_range,
+	.n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range),
+};
+
+static const struct regmap_config vop2_regmap_config = {
+	.reg_bits	= 32,
+	.val_bits	= 32,
+	.reg_stride	= 4,
+	.max_register	= 0x3000,
+	.name		= "vop2",
+	.volatile_table	= &vop2_volatile_table,
+	.cache_type	= REGCACHE_RBTREE,
+};
+
+static int vop2_bind(struct device *dev, struct device *master, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	const struct vop2_data *vop2_data;
+	struct drm_device *drm = data;
+	struct vop2 *vop2;
+	struct resource *res;
+	size_t alloc_size;
+	int ret;
+
+	vop2_data = of_device_get_match_data(dev);
+	if (!vop2_data)
+		return -ENODEV;
+
+	/* Allocate vop2 struct and its vop2_win array */
+	alloc_size = sizeof(*vop2) + sizeof(*vop2->win) * vop2_data->win_size;
+	vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
+	if (!vop2)
+		return -ENOMEM;
+
+	vop2->dev = dev;
+	vop2->data = vop2_data;
+	vop2->drm = drm;
+
+	dev_set_drvdata(dev, vop2);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+	if (!res) {
+		drm_err(vop2->drm, "failed to get vop2 register byname\n");
+		return -EINVAL;
+	}
+
+	vop2->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(vop2->regs))
+		return PTR_ERR(vop2->regs);
+	vop2->len = resource_size(res);
+
+	vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config);
+
+	ret = vop2_win_init(vop2);
+	if (ret)
+		return ret;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma_lut");
+	if (res) {
+		vop2->lut_regs = devm_ioremap_resource(dev, res);
+		if (IS_ERR(vop2->lut_regs))
+			return PTR_ERR(vop2->lut_regs);
+	}
+
+	vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
+
+	vop2->hclk = devm_clk_get(vop2->dev, "hclk");
+	if (IS_ERR(vop2->hclk)) {
+		drm_err(vop2->drm, "failed to get hclk source\n");
+		return PTR_ERR(vop2->hclk);
+	}
+
+	vop2->aclk = devm_clk_get(vop2->dev, "aclk");
+	if (IS_ERR(vop2->aclk)) {
+		drm_err(vop2->drm, "failed to get aclk source\n");
+		return PTR_ERR(vop2->aclk);
+	}
+
+	vop2->irq = platform_get_irq(pdev, 0);
+	if (vop2->irq < 0) {
+		drm_err(vop2->drm, "cannot find irq for vop2\n");
+		return vop2->irq;
+	}
+
+	mutex_init(&vop2->vop2_lock);
+
+	ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
+	if (ret)
+		return ret;
+
+	ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev);
+	if (ret) {
+		drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret);
+		return ret;
+	}
+
+	ret = vop2_create_crtc(vop2);
+	if (ret)
+		return ret;
+
+	pm_runtime_enable(&pdev->dev);
+
+	return 0;
+}
+
+static void vop2_unbind(struct device *dev, struct device *master, void *data)
+{
+	struct vop2 *vop2 = dev_get_drvdata(dev);
+	struct drm_device *drm = vop2->drm;
+	struct list_head *plane_list = &drm->mode_config.plane_list;
+	struct list_head *crtc_list = &drm->mode_config.crtc_list;
+	struct drm_crtc *crtc, *tmpc;
+	struct drm_plane *plane, *tmpp;
+
+	rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
+
+	pm_runtime_disable(dev);
+
+	list_for_each_entry_safe(plane, tmpp, plane_list, head)
+		drm_plane_cleanup(plane);
+
+	list_for_each_entry_safe(crtc, tmpc, crtc_list, head)
+		vop2_destroy_crtc(crtc);
+}
+
+const struct component_ops vop2_component_ops = {
+	.bind = vop2_bind,
+	.unbind = vop2_unbind,
+};
+EXPORT_SYMBOL_GPL(vop2_component_ops);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
new file mode 100644
index 0000000000000..c727093a06d68
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
@@ -0,0 +1,477 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ */
+
+#ifndef _ROCKCHIP_DRM_VOP2_H
+#define _ROCKCHIP_DRM_VOP2_H
+
+#include "rockchip_drm_vop.h"
+
+#include <linux/regmap.h>
+#include <drm/drm_modes.h>
+
+#define VOP_FEATURE_OUTPUT_10BIT        BIT(0)
+
+#define WIN_FEATURE_AFBDC		BIT(0)
+#define WIN_FEATURE_CLUSTER		BIT(1)
+
+/*
+ *  the delay number of a window in different mode.
+ */
+enum win_dly_mode {
+	VOP2_DLY_MODE_DEFAULT,   /**< default mode */
+	VOP2_DLY_MODE_HISO_S,    /** HDR in SDR out mode, as a SDR window */
+	VOP2_DLY_MODE_HIHO_H,    /** HDR in HDR out mode, as a HDR window */
+	VOP2_DLY_MODE_MAX,
+};
+
+struct vop_rect {
+	int width;
+	int height;
+};
+
+enum vop2_scale_up_mode {
+	VOP2_SCALE_UP_NRST_NBOR,
+	VOP2_SCALE_UP_BIL,
+	VOP2_SCALE_UP_BIC,
+};
+
+enum vop2_scale_down_mode {
+	VOP2_SCALE_DOWN_NRST_NBOR,
+	VOP2_SCALE_DOWN_BIL,
+	VOP2_SCALE_DOWN_AVG,
+};
+
+enum vop2_win_regs {
+	VOP2_WIN_ENABLE,
+	VOP2_WIN_FORMAT,
+	VOP2_WIN_CSC_MODE,
+	VOP2_WIN_XMIRROR,
+	VOP2_WIN_YMIRROR,
+	VOP2_WIN_RB_SWAP,
+	VOP2_WIN_UV_SWAP,
+	VOP2_WIN_ACT_INFO,
+	VOP2_WIN_DSP_INFO,
+	VOP2_WIN_DSP_ST,
+	VOP2_WIN_YRGB_MST,
+	VOP2_WIN_UV_MST,
+	VOP2_WIN_YRGB_VIR,
+	VOP2_WIN_UV_VIR,
+	VOP2_WIN_YUV_CLIP,
+	VOP2_WIN_Y2R_EN,
+	VOP2_WIN_R2Y_EN,
+	VOP2_WIN_COLOR_KEY,
+	VOP2_WIN_COLOR_KEY_EN,
+	VOP2_WIN_DITHER_UP,
+
+	/* scale regs */
+	VOP2_WIN_SCALE_YRGB_X,
+	VOP2_WIN_SCALE_YRGB_Y,
+	VOP2_WIN_SCALE_CBCR_X,
+	VOP2_WIN_SCALE_CBCR_Y,
+	VOP2_WIN_YRGB_HOR_SCL_MODE,
+	VOP2_WIN_YRGB_HSCL_FILTER_MODE,
+	VOP2_WIN_YRGB_VER_SCL_MODE,
+	VOP2_WIN_YRGB_VSCL_FILTER_MODE,
+	VOP2_WIN_CBCR_VER_SCL_MODE,
+	VOP2_WIN_CBCR_HSCL_FILTER_MODE,
+	VOP2_WIN_CBCR_HOR_SCL_MODE,
+	VOP2_WIN_CBCR_VSCL_FILTER_MODE,
+	VOP2_WIN_VSD_CBCR_GT2,
+	VOP2_WIN_VSD_CBCR_GT4,
+	VOP2_WIN_VSD_YRGB_GT2,
+	VOP2_WIN_VSD_YRGB_GT4,
+	VOP2_WIN_BIC_COE_SEL,
+
+	/* cluster regs */
+	VOP2_WIN_CLUSTER_ENABLE,
+	VOP2_WIN_AFBC_ENABLE,
+	VOP2_WIN_CLUSTER_LB_MODE,
+
+	/* afbc regs */
+	VOP2_WIN_AFBC_FORMAT,
+	VOP2_WIN_AFBC_RB_SWAP,
+	VOP2_WIN_AFBC_UV_SWAP,
+	VOP2_WIN_AFBC_AUTO_GATING_EN,
+	VOP2_WIN_AFBC_BLOCK_SPLIT_EN,
+	VOP2_WIN_AFBC_PIC_VIR_WIDTH,
+	VOP2_WIN_AFBC_TILE_NUM,
+	VOP2_WIN_AFBC_PIC_OFFSET,
+	VOP2_WIN_AFBC_PIC_SIZE,
+	VOP2_WIN_AFBC_DSP_OFFSET,
+	VOP2_WIN_AFBC_TRANSFORM_OFFSET,
+	VOP2_WIN_AFBC_HDR_PTR,
+	VOP2_WIN_AFBC_HALF_BLOCK_EN,
+	VOP2_WIN_AFBC_ROTATE_270,
+	VOP2_WIN_AFBC_ROTATE_90,
+	VOP2_WIN_MAX_REG,
+};
+
+struct vop2_win_data {
+	const char *name;
+	unsigned int phys_id;
+
+	u32 base;
+	enum drm_plane_type type;
+
+	u32 nformats;
+	const u32 *formats;
+	const uint64_t *format_modifiers;
+	const unsigned int supported_rotations;
+
+	/**
+	 * @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2
+	 */
+	unsigned int layer_sel_id;
+	uint64_t feature;
+
+	unsigned int max_upscale_factor;
+	unsigned int max_downscale_factor;
+	const u8 dly[VOP2_DLY_MODE_MAX];
+};
+
+struct vop2_video_port_data {
+	unsigned int id;
+	u32 feature;
+	u16 gamma_lut_len;
+	u16 cubic_lut_len;
+	struct vop_rect max_output;
+	const u8 pre_scan_max_dly[4];
+	const struct vop2_video_port_regs *regs;
+	unsigned int offset;
+};
+
+struct vop2_data {
+	u8 nr_vps;
+	const struct vop2_ctrl *ctrl;
+	const struct vop2_win_data *win;
+	const struct vop2_video_port_data *vp;
+	const struct vop_csc_table *csc_table;
+	struct vop_rect max_input;
+	struct vop_rect max_output;
+
+	unsigned int win_size;
+	unsigned int soc_id;
+};
+
+/* interrupt define */
+#define FS_NEW_INTR			BIT(4)
+#define ADDR_SAME_INTR			BIT(5)
+#define LINE_FLAG1_INTR			BIT(6)
+#define WIN0_EMPTY_INTR			BIT(7)
+#define WIN1_EMPTY_INTR			BIT(8)
+#define WIN2_EMPTY_INTR			BIT(9)
+#define WIN3_EMPTY_INTR			BIT(10)
+#define HWC_EMPTY_INTR			BIT(11)
+#define POST_BUF_EMPTY_INTR		BIT(12)
+#define PWM_GEN_INTR			BIT(13)
+#define DMA_FINISH_INTR			BIT(14)
+#define FS_FIELD_INTR			BIT(15)
+#define FE_INTR				BIT(16)
+#define WB_UV_FIFO_FULL_INTR		BIT(17)
+#define WB_YRGB_FIFO_FULL_INTR		BIT(18)
+#define WB_COMPLETE_INTR		BIT(19)
+
+/*
+ * display output interface supported by rockchip lcdc
+ */
+#define ROCKCHIP_OUT_MODE_P888		0
+#define ROCKCHIP_OUT_MODE_BT1120	0
+#define ROCKCHIP_OUT_MODE_P666		1
+#define ROCKCHIP_OUT_MODE_P565		2
+#define ROCKCHIP_OUT_MODE_BT656		5
+#define ROCKCHIP_OUT_MODE_S888		8
+#define ROCKCHIP_OUT_MODE_S888_DUMMY	12
+#define ROCKCHIP_OUT_MODE_YUV420	14
+/* for use special outface */
+#define ROCKCHIP_OUT_MODE_AAAA		15
+
+enum vop_csc_format {
+	CSC_BT601L,
+	CSC_BT709L,
+	CSC_BT601F,
+	CSC_BT2020,
+};
+
+enum src_factor_mode {
+	SRC_FAC_ALPHA_ZERO,
+	SRC_FAC_ALPHA_ONE,
+	SRC_FAC_ALPHA_DST,
+	SRC_FAC_ALPHA_DST_INVERSE,
+	SRC_FAC_ALPHA_SRC,
+	SRC_FAC_ALPHA_SRC_GLOBAL,
+};
+
+enum dst_factor_mode {
+	DST_FAC_ALPHA_ZERO,
+	DST_FAC_ALPHA_ONE,
+	DST_FAC_ALPHA_SRC,
+	DST_FAC_ALPHA_SRC_INVERSE,
+	DST_FAC_ALPHA_DST,
+	DST_FAC_ALPHA_DST_GLOBAL,
+};
+
+#define RK3568_GRF_VO_CON1			0x0364
+/* System registers definition */
+#define RK3568_REG_CFG_DONE			0x000
+#define RK3568_VERSION_INFO			0x004
+#define RK3568_SYS_AUTO_GATING_CTRL		0x008
+#define RK3568_SYS_AXI_LUT_CTRL			0x024
+#define RK3568_DSP_IF_EN			0x028
+#define RK3568_DSP_IF_CTRL			0x02c
+#define RK3568_DSP_IF_POL			0x030
+#define RK3568_WB_CTRL				0x40
+#define RK3568_WB_XSCAL_FACTOR			0x44
+#define RK3568_WB_YRGB_MST			0x48
+#define RK3568_WB_CBR_MST			0x4C
+#define RK3568_OTP_WIN_EN			0x050
+#define RK3568_LUT_PORT_SEL			0x058
+#define RK3568_SYS_STATUS0			0x060
+#define RK3568_VP_LINE_FLAG(vp)			(0x70 + (vp) * 0x4)
+#define RK3568_SYS0_INT_EN			0x80
+#define RK3568_SYS0_INT_CLR			0x84
+#define RK3568_SYS0_INT_STATUS			0x88
+#define RK3568_SYS1_INT_EN			0x90
+#define RK3568_SYS1_INT_CLR			0x94
+#define RK3568_SYS1_INT_STATUS			0x98
+#define RK3568_VP_INT_EN(vp)			(0xA0 + (vp) * 0x10)
+#define RK3568_VP_INT_CLR(vp)			(0xA4 + (vp) * 0x10)
+#define RK3568_VP_INT_STATUS(vp)		(0xA8 + (vp) * 0x10)
+#define RK3568_VP_INT_RAW_STATUS(vp)		(0xAC + (vp) * 0x10)
+
+/* Video Port registers definition */
+#define RK3568_VP_DSP_CTRL			0x00
+#define RK3568_VP_MIPI_CTRL			0x04
+#define RK3568_VP_COLOR_BAR_CTRL		0x08
+#define RK3568_VP_3D_LUT_CTRL			0x10
+#define RK3568_VP_3D_LUT_MST			0x20
+#define RK3568_VP_DSP_BG			0x2C
+#define RK3568_VP_PRE_SCAN_HTIMING		0x30
+#define RK3568_VP_POST_DSP_HACT_INFO		0x34
+#define RK3568_VP_POST_DSP_VACT_INFO		0x38
+#define RK3568_VP_POST_SCL_FACTOR_YRGB		0x3C
+#define RK3568_VP_POST_SCL_CTRL			0x40
+#define RK3568_VP_POST_DSP_VACT_INFO_F1		0x44
+#define RK3568_VP_DSP_HTOTAL_HS_END		0x48
+#define RK3568_VP_DSP_HACT_ST_END		0x4C
+#define RK3568_VP_DSP_VTOTAL_VS_END		0x50
+#define RK3568_VP_DSP_VACT_ST_END		0x54
+#define RK3568_VP_DSP_VS_ST_END_F1		0x58
+#define RK3568_VP_DSP_VACT_ST_END_F1		0x5C
+#define RK3568_VP_BCSH_CTRL			0x60
+#define RK3568_VP_BCSH_BCS			0x64
+#define RK3568_VP_BCSH_H			0x68
+#define RK3568_VP_BCSH_COLOR_BAR		0x6C
+
+/* Overlay registers definition    */
+#define RK3568_OVL_CTRL				0x600
+#define RK3568_OVL_LAYER_SEL			0x604
+#define RK3568_OVL_PORT_SEL			0x608
+#define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL	0x610
+#define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL	0x614
+#define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL	0x618
+#define RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL	0x61C
+#define RK3568_MIX0_SRC_COLOR_CTRL		0x650
+#define RK3568_MIX0_DST_COLOR_CTRL		0x654
+#define RK3568_MIX0_SRC_ALPHA_CTRL		0x658
+#define RK3568_MIX0_DST_ALPHA_CTRL		0x65C
+#define RK3568_HDR0_SRC_COLOR_CTRL		0x6C0
+#define RK3568_HDR0_DST_COLOR_CTRL		0x6C4
+#define RK3568_HDR0_SRC_ALPHA_CTRL		0x6C8
+#define RK3568_HDR0_DST_ALPHA_CTRL		0x6CC
+#define RK3568_VP_BG_MIX_CTRL(vp)		(0x6E0 + (vp) * 4)
+#define RK3568_CLUSTER_DLY_NUM			0x6F0
+#define RK3568_SMART_DLY_NUM			0x6F8
+
+/* Cluster register definition, offset relative to window base */
+#define RK3568_CLUSTER_WIN_CTRL0		0x00
+#define RK3568_CLUSTER_WIN_CTRL1		0x04
+#define RK3568_CLUSTER_WIN_YRGB_MST		0x10
+#define RK3568_CLUSTER_WIN_CBR_MST		0x14
+#define RK3568_CLUSTER_WIN_VIR			0x18
+#define RK3568_CLUSTER_WIN_ACT_INFO		0x20
+#define RK3568_CLUSTER_WIN_DSP_INFO		0x24
+#define RK3568_CLUSTER_WIN_DSP_ST		0x28
+#define RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB	0x30
+#define RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET	0x3C
+#define RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL	0x50
+#define RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE	0x54
+#define RK3568_CLUSTER_WIN_AFBCD_HDR_PTR	0x58
+#define RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH	0x5C
+#define RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE	0x60
+#define RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET	0x64
+#define RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET	0x68
+#define RK3568_CLUSTER_WIN_AFBCD_CTRL		0x6C
+
+#define RK3568_CLUSTER_CTRL			0x100
+
+/* (E)smart register definition, offset relative to window base */
+#define RK3568_SMART_CTRL0			0x00
+#define RK3568_SMART_CTRL1			0x04
+#define RK3568_SMART_REGION0_CTRL		0x10
+#define RK3568_SMART_REGION0_YRGB_MST		0x14
+#define RK3568_SMART_REGION0_CBR_MST		0x18
+#define RK3568_SMART_REGION0_VIR		0x1C
+#define RK3568_SMART_REGION0_ACT_INFO		0x20
+#define RK3568_SMART_REGION0_DSP_INFO		0x24
+#define RK3568_SMART_REGION0_DSP_ST		0x28
+#define RK3568_SMART_REGION0_SCL_CTRL		0x30
+#define RK3568_SMART_REGION0_SCL_FACTOR_YRGB	0x34
+#define RK3568_SMART_REGION0_SCL_FACTOR_CBR	0x38
+#define RK3568_SMART_REGION0_SCL_OFFSET		0x3C
+#define RK3568_SMART_REGION1_CTRL		0x40
+#define RK3568_SMART_REGION1_YRGB_MST		0x44
+#define RK3568_SMART_REGION1_CBR_MST		0x48
+#define RK3568_SMART_REGION1_VIR		0x4C
+#define RK3568_SMART_REGION1_ACT_INFO		0x50
+#define RK3568_SMART_REGION1_DSP_INFO		0x54
+#define RK3568_SMART_REGION1_DSP_ST		0x58
+#define RK3568_SMART_REGION1_SCL_CTRL		0x60
+#define RK3568_SMART_REGION1_SCL_FACTOR_YRGB	0x64
+#define RK3568_SMART_REGION1_SCL_FACTOR_CBR	0x68
+#define RK3568_SMART_REGION1_SCL_OFFSET		0x6C
+#define RK3568_SMART_REGION2_CTRL		0x70
+#define RK3568_SMART_REGION2_YRGB_MST		0x74
+#define RK3568_SMART_REGION2_CBR_MST		0x78
+#define RK3568_SMART_REGION2_VIR		0x7C
+#define RK3568_SMART_REGION2_ACT_INFO		0x80
+#define RK3568_SMART_REGION2_DSP_INFO		0x84
+#define RK3568_SMART_REGION2_DSP_ST		0x88
+#define RK3568_SMART_REGION2_SCL_CTRL		0x90
+#define RK3568_SMART_REGION2_SCL_FACTOR_YRGB	0x94
+#define RK3568_SMART_REGION2_SCL_FACTOR_CBR	0x98
+#define RK3568_SMART_REGION2_SCL_OFFSET		0x9C
+#define RK3568_SMART_REGION3_CTRL		0xA0
+#define RK3568_SMART_REGION3_YRGB_MST		0xA4
+#define RK3568_SMART_REGION3_CBR_MST		0xA8
+#define RK3568_SMART_REGION3_VIR		0xAC
+#define RK3568_SMART_REGION3_ACT_INFO		0xB0
+#define RK3568_SMART_REGION3_DSP_INFO		0xB4
+#define RK3568_SMART_REGION3_DSP_ST		0xB8
+#define RK3568_SMART_REGION3_SCL_CTRL		0xC0
+#define RK3568_SMART_REGION3_SCL_FACTOR_YRGB	0xC4
+#define RK3568_SMART_REGION3_SCL_FACTOR_CBR	0xC8
+#define RK3568_SMART_REGION3_SCL_OFFSET		0xCC
+#define RK3568_SMART_COLOR_KEY_CTRL		0xD0
+
+/* HDR register definition */
+#define RK3568_HDR_LUT_CTRL			0x2000
+#define RK3568_HDR_LUT_MST			0x2004
+#define RK3568_SDR2HDR_CTRL			0x2010
+#define RK3568_HDR2SDR_CTRL			0x2020
+#define RK3568_HDR2SDR_SRC_RANGE		0x2024
+#define RK3568_HDR2SDR_NORMFACEETF		0x2028
+#define RK3568_HDR2SDR_DST_RANGE		0x202C
+#define RK3568_HDR2SDR_NORMFACCGAMMA		0x2030
+#define RK3568_HDR_EETF_OETF_Y0			0x203C
+#define RK3568_HDR_SAT_Y0			0x20C0
+#define RK3568_HDR_EOTF_OETF_Y0			0x20F0
+#define RK3568_HDR_OETF_DX_POW1			0x2200
+#define RK3568_HDR_OETF_XN1			0x2300
+
+#define RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN		BIT(15)
+
+#define RK3568_VP_DSP_CTRL__STANDBY			BIT(31)
+#define RK3568_VP_DSP_CTRL__DITHER_DOWN_MODE		BIT(20)
+#define RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL		GENMASK(19, 18)
+#define RK3568_VP_DSP_CTRL__DITHER_DOWN_EN		BIT(17)
+#define RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN		BIT(16)
+#define RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y		BIT(15)
+#define RK3568_VP_DSP_CTRL__DSP_RB_SWAP			BIT(9)
+#define RK3568_VP_DSP_CTRL__DSP_INTERLACE		BIT(7)
+#define RK3568_VP_DSP_CTRL__DSP_FILED_POL		BIT(6)
+#define RK3568_VP_DSP_CTRL__P2I_EN			BIT(5)
+#define RK3568_VP_DSP_CTRL__CORE_DCLK_DIV		BIT(4)
+#define RK3568_VP_DSP_CTRL__OUT_MODE			GENMASK(3, 0)
+
+#define RK3568_VP_POST_SCL_CTRL__VSCALEDOWN		BIT(1)
+#define RK3568_VP_POST_SCL_CTRL__HSCALEDOWN		BIT(0)
+
+#define RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX		GENMASK(26, 25)
+#define RK3568_SYS_DSP_INFACE_EN_LVDS1			BIT(24)
+#define RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX		GENMASK(22, 21)
+#define RK3568_SYS_DSP_INFACE_EN_MIPI1			BIT(20)
+#define RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX		GENMASK(19, 18)
+#define RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX		GENMASK(17, 16)
+#define RK3568_SYS_DSP_INFACE_EN_EDP_MUX		GENMASK(15, 14)
+#define RK3568_SYS_DSP_INFACE_EN_HDMI_MUX		GENMASK(11, 10)
+#define RK3568_SYS_DSP_INFACE_EN_RGB_MUX		GENMASK(9, 8)
+#define RK3568_SYS_DSP_INFACE_EN_LVDS0			BIT(5)
+#define RK3568_SYS_DSP_INFACE_EN_MIPI0			BIT(4)
+#define RK3568_SYS_DSP_INFACE_EN_EDP			BIT(3)
+#define RK3568_SYS_DSP_INFACE_EN_HDMI			BIT(1)
+#define RK3568_SYS_DSP_INFACE_EN_RGB			BIT(0)
+
+#define RK3568_DSP_IF_POL__MIPI_PIN_POL			GENMASK(19, 16)
+#define RK3568_DSP_IF_POL__EDP_PIN_POL			GENMASK(15, 12)
+#define RK3568_DSP_IF_POL__HDMI_PIN_POL			GENMASK(7, 4)
+#define RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL		GENMASK(3, 0)
+
+#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2_PHASE_LOCK	BIT(5)
+#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2			BIT(4)
+
+#define RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN	BIT(31)
+
+#define RK3568_DSP_IF_POL__CFG_DONE_IMD			BIT(28)
+
+#define VOP2_SYS_AXI_BUS_NUM				2
+
+#define VOP2_CLUSTER_YUV444_10				0x12
+
+#define VOP2_COLOR_KEY_MASK				BIT(31)
+
+#define RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD		BIT(28)
+
+#define RK3568_VP_BG_MIX_CTRL__BG_DLY			GENMASK(31, 24)
+
+#define RK3568_OVL_PORT_SEL__SEL_PORT			GENMASK(31, 16)
+#define RK3568_OVL_PORT_SEL__SMART1			GENMASK(31, 30)
+#define RK3568_OVL_PORT_SEL__SMART0			GENMASK(29, 28)
+#define RK3568_OVL_PORT_SEL__ESMART1			GENMASK(27, 26)
+#define RK3568_OVL_PORT_SEL__ESMART0			GENMASK(25, 24)
+#define RK3568_OVL_PORT_SEL__CLUSTER1			GENMASK(19, 18)
+#define RK3568_OVL_PORT_SEL__CLUSTER0			GENMASK(17, 16)
+#define RK3568_OVL_PORT_SET__PORT2_MUX			GENMASK(11, 8)
+#define RK3568_OVL_PORT_SET__PORT1_MUX			GENMASK(7, 4)
+#define RK3568_OVL_PORT_SET__PORT0_MUX			GENMASK(3, 0)
+#define RK3568_OVL_LAYER_SEL__LAYER(layer, x)		((x) << ((layer) * 4))
+
+#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_1		GENMASK(31, 24)
+#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_0		GENMASK(23, 16)
+#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_1		GENMASK(15, 8)
+#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_0		GENMASK(7, 0)
+
+#define RK3568_SMART_DLY_NUM__SMART1			GENMASK(31, 24)
+#define RK3568_SMART_DLY_NUM__SMART0			GENMASK(23, 16)
+#define RK3568_SMART_DLY_NUM__ESMART1			GENMASK(15, 8)
+#define RK3568_SMART_DLY_NUM__ESMART0			GENMASK(7, 0)
+
+#define VP_INT_DSP_HOLD_VALID	BIT(6)
+#define VP_INT_FS_FIELD		BIT(5)
+#define VP_INT_POST_BUF_EMPTY	BIT(4)
+#define VP_INT_LINE_FLAG1	BIT(3)
+#define VP_INT_LINE_FLAG0	BIT(2)
+#define VOP2_INT_BUS_ERRPR	BIT(1)
+#define VP_INT_FS		BIT(0)
+
+#define POLFLAG_DCLK_INV	BIT(3)
+
+enum vop2_layer_phy_id {
+	ROCKCHIP_VOP2_CLUSTER0 = 0,
+	ROCKCHIP_VOP2_CLUSTER1,
+	ROCKCHIP_VOP2_ESMART0,
+	ROCKCHIP_VOP2_ESMART1,
+	ROCKCHIP_VOP2_SMART0,
+	ROCKCHIP_VOP2_SMART1,
+	ROCKCHIP_VOP2_CLUSTER2,
+	ROCKCHIP_VOP2_CLUSTER3,
+	ROCKCHIP_VOP2_ESMART2,
+	ROCKCHIP_VOP2_ESMART3,
+	ROCKCHIP_VOP2_PHY_ID_INVALID = -1,
+};
+
+extern const struct component_ops vop2_component_ops;
+
+#endif /* _ROCKCHIP_DRM_VOP2_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
new file mode 100644
index 0000000000000..9bf0637bf8e26
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) Rockchip Electronics Co.Ltd
+ * Author: Andy Yan <andy.yan@rock-chips.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/component.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_print.h>
+
+#include "rockchip_drm_vop2.h"
+
+static const uint32_t formats_win_full_10bit[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV16,
+	DRM_FORMAT_NV24,
+};
+
+static const uint32_t formats_win_full_10bit_yuyv[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV16,
+	DRM_FORMAT_NV24,
+	DRM_FORMAT_YVYU,
+	DRM_FORMAT_VYUY,
+};
+
+static const uint32_t formats_win_lite[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+};
+
+static const uint64_t format_modifiers[] = {
+	DRM_FORMAT_MOD_LINEAR,
+	DRM_FORMAT_MOD_INVALID,
+};
+
+static const uint64_t format_modifiers_afbc[] = {
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_SPARSE),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_CBR),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR |
+				AFBC_FORMAT_MOD_SPARSE),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_CBR |
+				AFBC_FORMAT_MOD_SPARSE),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR |
+				AFBC_FORMAT_MOD_CBR),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR |
+				AFBC_FORMAT_MOD_CBR |
+				AFBC_FORMAT_MOD_SPARSE),
+
+	/* SPLIT mandates SPARSE, RGB modes mandates YTR */
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR |
+				AFBC_FORMAT_MOD_SPARSE |
+				AFBC_FORMAT_MOD_SPLIT),
+	DRM_FORMAT_MOD_INVALID,
+};
+
+static const struct vop2_video_port_data rk3568_vop_video_ports[] = {
+	{
+		.id = 0,
+		.feature = VOP_FEATURE_OUTPUT_10BIT,
+		.gamma_lut_len = 1024,
+		.cubic_lut_len = 9 * 9 * 9,
+		.max_output = { 4096, 2304 },
+		.pre_scan_max_dly = { 69, 53, 53, 42 },
+		.offset = 0xc00,
+	}, {
+		.id = 1,
+		.gamma_lut_len = 1024,
+		.max_output = { 2048, 1536 },
+		.pre_scan_max_dly = { 40, 40, 40, 40 },
+		.offset = 0xd00,
+	}, {
+		.id = 2,
+		.gamma_lut_len = 1024,
+		.max_output = { 1920, 1080 },
+		.pre_scan_max_dly = { 40, 40, 40, 40 },
+		.offset = 0xe00,
+	},
+};
+
+/*
+ * rk3568 vop with 2 cluster, 2 esmart win, 2 smart win.
+ * Every cluster can work as 4K win or split into two win.
+ * All win in cluster support AFBCD.
+ *
+ * Every esmart win and smart win support 4 Multi-region.
+ *
+ * Scale filter mode:
+ *
+ * * Cluster:  bicubic for horizontal scale up, others use bilinear
+ * * ESmart:
+ *    * nearest-neighbor/bilinear/bicubic for scale up
+ *    * nearest-neighbor/bilinear/average for scale down
+ *
+ *
+ * @TODO describe the wind like cpu-map dt nodes;
+ */
+static const struct vop2_win_data rk3568_vop_win_data[] = {
+	{
+		.name = "Smart0-win0",
+		.phys_id = ROCKCHIP_VOP2_SMART0,
+		.base = 0x1c00,
+		.formats = formats_win_lite,
+		.nformats = ARRAY_SIZE(formats_win_lite),
+		.format_modifiers = format_modifiers,
+		.layer_sel_id = 3,
+		.supported_rotations = DRM_MODE_REFLECT_Y,
+		.type = DRM_PLANE_TYPE_PRIMARY,
+		.max_upscale_factor = 8,
+		.max_downscale_factor = 8,
+		.dly = { 20, 47, 41 },
+	}, {
+		.name = "Smart1-win0",
+		.phys_id = ROCKCHIP_VOP2_SMART1,
+		.formats = formats_win_lite,
+		.nformats = ARRAY_SIZE(formats_win_lite),
+		.format_modifiers = format_modifiers,
+		.base = 0x1e00,
+		.layer_sel_id = 7,
+		.supported_rotations = DRM_MODE_REFLECT_Y,
+		.type = DRM_PLANE_TYPE_PRIMARY,
+		.max_upscale_factor = 8,
+		.max_downscale_factor = 8,
+		.dly = { 20, 47, 41 },
+	}, {
+		.name = "Esmart1-win0",
+		.phys_id = ROCKCHIP_VOP2_ESMART1,
+		.formats = formats_win_full_10bit_yuyv,
+		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
+		.format_modifiers = format_modifiers,
+		.base = 0x1a00,
+		.layer_sel_id = 6,
+		.supported_rotations = DRM_MODE_REFLECT_Y,
+		.type = DRM_PLANE_TYPE_PRIMARY,
+		.max_upscale_factor = 8,
+		.max_downscale_factor = 8,
+		.dly = { 20, 47, 41 },
+	}, {
+		.name = "Esmart0-win0",
+		.phys_id = ROCKCHIP_VOP2_ESMART0,
+		.formats = formats_win_full_10bit_yuyv,
+		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
+		.format_modifiers = format_modifiers,
+		.base = 0x1800,
+		.layer_sel_id = 2,
+		.supported_rotations = DRM_MODE_REFLECT_Y,
+		.type = DRM_PLANE_TYPE_OVERLAY,
+		.max_upscale_factor = 8,
+		.max_downscale_factor = 8,
+		.dly = { 20, 47, 41 },
+	}, {
+		.name = "Cluster0-win0",
+		.phys_id = ROCKCHIP_VOP2_CLUSTER0,
+		.base = 0x1000,
+		.formats = formats_win_full_10bit,
+		.nformats = ARRAY_SIZE(formats_win_full_10bit),
+		.format_modifiers = format_modifiers_afbc,
+		.layer_sel_id = 0,
+		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
+					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
+		.max_upscale_factor = 4,
+		.max_downscale_factor = 4,
+		.dly = { 0, 27, 21 },
+		.type = DRM_PLANE_TYPE_OVERLAY,
+		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
+	}, {
+		.name = "Cluster1-win0",
+		.phys_id = ROCKCHIP_VOP2_CLUSTER1,
+		.base = 0x1200,
+		.formats = formats_win_full_10bit,
+		.nformats = ARRAY_SIZE(formats_win_full_10bit),
+		.format_modifiers = format_modifiers_afbc,
+		.layer_sel_id = 1,
+		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
+					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
+		.type = DRM_PLANE_TYPE_OVERLAY,
+		.max_upscale_factor = 4,
+		.max_downscale_factor = 4,
+		.dly = { 0, 27, 21 },
+		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
+	},
+};
+
+static const struct vop2_data rk3566_vop = {
+	.nr_vps = 3,
+	.max_input = { 4096, 2304 },
+	.max_output = { 4096, 2304 },
+	.vp = rk3568_vop_video_ports,
+	.win = rk3568_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
+	.soc_id = 3566,
+};
+
+static const struct vop2_data rk3568_vop = {
+	.nr_vps = 3,
+	.max_input = { 4096, 2304 },
+	.max_output = { 4096, 2304 },
+	.vp = rk3568_vop_video_ports,
+	.win = rk3568_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
+	.soc_id = 3568,
+};
+
+static const struct of_device_id vop2_dt_match[] = {
+	{
+		.compatible = "rockchip,rk3566-vop",
+		.data = &rk3566_vop,
+	}, {
+		.compatible = "rockchip,rk3568-vop",
+		.data = &rk3568_vop,
+	}, {
+	},
+};
+MODULE_DEVICE_TABLE(of, vop2_dt_match);
+
+static int vop2_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	return component_add(dev, &vop2_component_ops);
+}
+
+static int vop2_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &vop2_component_ops);
+
+	return 0;
+}
+
+struct platform_driver vop2_platform_driver = {
+	.probe = vop2_probe,
+	.remove = vop2_remove,
+	.driver = {
+		.name = "rockchip-vop2",
+		.of_match_table = of_match_ptr(vop2_dt_match),
+	},
+};
-- 
2.30.2


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

* [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

From: Andy Yan <andy.yan@rock-chips.com>

The VOP2 unit is found on Rockchip SoCs beginning with rk3566/rk3568.
It replaces the VOP unit found in the older Rockchip SoCs.

This driver has been derived from the downstream Rockchip Kernel and
heavily modified:

- All nonstandard DRM properties have been removed
- dropped struct vop2_plane_state and pass around less data between
  functions
- Dropped all DRM_FORMAT_* not known on upstream
- rework register access to get rid of excessively used macros
- Drop all waiting for framesyncs

The driver is tested with HDMI and MIPI-DSI display on a RK3568-EVB
board. Overlay support is tested with the modetest utility. AFBC support
on the cluster windows is tested with weston-simple-dmabuf-egl on
weston using the (yet to be upstreamed) panfrost driver support.

Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - consistently use u8/u16/u32 rather than uint8_t/uint16_t/uint32_t
    - Use spin_lock rather than spin_lock_irqsave
    - replace printk with drm_dbg
    - break some overlong lines
    
    Changes since v4:
    - Avoid stack frame overflow by not allocating big array on the stack
    
    Changes since v3:
    - Sort includes
    - fix typos
    - Drop spinlock
    - Use regmap_set_bits()/regmap_clear_bits()
    - simplify vop2_scale_factor()
    - simplify vop2_afbc_transform_offset()
    
    Changes since v4:
    - Sort nodes alphabetically
    
    Changes since v3:
    - Fix HDMI connector type

 drivers/gpu/drm/rockchip/Kconfig             |    6 +
 drivers/gpu/drm/rockchip/Makefile            |    1 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c  |    1 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h  |    6 +-
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c   |    2 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h  |   15 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2708 ++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h |  477 +++
 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c |  281 ++
 9 files changed, 3496 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index b9b156308460a..4ff0043f0ee70 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -28,6 +28,12 @@ config ROCKCHIP_VOP
 	  This selects support for the VOP driver. You should enable it
 	  on all older SoCs up to RK3399.
 
+config ROCKCHIP_VOP2
+	bool "Rockchip VOP2 driver"
+	help
+	  This selects support for the VOP2 driver. You should enable it
+	  on all newer SoCs beginning form RK3568.
+
 config ROCKCHIP_ANALOGIX_DP
 	bool "Rockchip specific extensions for Analogix DP driver"
 	depends on ROCKCHIP_VOP
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index dfc5512fdb9f1..3ff7b21c04149 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -6,6 +6,7 @@
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
 		rockchip_drm_gem.o
 
+rockchipdrm-$(CONFIG_ROCKCHIP_VOP2) += rockchip_drm_vop2.o rockchip_vop2_reg.o
 rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
 rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
 rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 82c8faf1fb6b8..95f6c5985fdd7 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -459,6 +459,7 @@ static int __init rockchip_drm_init(void)
 
 	num_rockchip_sub_drivers = 0;
 	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
+	ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2);
 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
 				CONFIG_ROCKCHIP_LVDS);
 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index d3e42410ae5da..5cb207aaaadd0 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -18,7 +18,7 @@
 
 #define ROCKCHIP_MAX_FB_BUFFER	3
 #define ROCKCHIP_MAX_CONNECTOR	2
-#define ROCKCHIP_MAX_CRTC	2
+#define ROCKCHIP_MAX_CRTC	4
 
 struct drm_device;
 struct drm_connector;
@@ -31,6 +31,9 @@ struct rockchip_crtc_state {
 	int output_bpc;
 	int output_flags;
 	bool enable_afbc;
+	u32 bus_format;
+	u32 bus_flags;
+	int color_space;
 };
 #define to_rockchip_crtc_state(s) \
 		container_of(s, struct rockchip_crtc_state, base)
@@ -63,6 +66,7 @@ extern struct platform_driver rockchip_dp_driver;
 extern struct platform_driver rockchip_lvds_driver;
 extern struct platform_driver vop_platform_driver;
 extern struct platform_driver rk3066_hdmi_driver;
+extern struct platform_driver vop2_platform_driver;
 
 struct rockchip_encoder {
 	struct device_node *port;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 3aa37e177667e..0d2cb4f3922b8 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -134,4 +134,6 @@ void rockchip_drm_mode_config_init(struct drm_device *dev)
 
 	dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
 	dev->mode_config.helper_private = &rockchip_mode_config_helpers;
+
+	dev->mode_config.normalize_zpos = true;
 }
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index 857d97cdc67c6..1e364d7b50e69 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -54,9 +54,23 @@ struct vop_afbc {
 	struct vop_reg enable;
 	struct vop_reg win_sel;
 	struct vop_reg format;
+	struct vop_reg rb_swap;
+	struct vop_reg uv_swap;
+	struct vop_reg auto_gating_en;
+	struct vop_reg block_split_en;
+	struct vop_reg pic_vir_width;
+	struct vop_reg tile_num;
 	struct vop_reg hreg_block_split;
+	struct vop_reg pic_offset;
 	struct vop_reg pic_size;
+	struct vop_reg dsp_offset;
+	struct vop_reg transform_offset;
 	struct vop_reg hdr_ptr;
+	struct vop_reg half_block_en;
+	struct vop_reg xmirror;
+	struct vop_reg ymirror;
+	struct vop_reg rotate_270;
+	struct vop_reg rotate_90;
 	struct vop_reg rstn;
 };
 
@@ -410,4 +424,5 @@ static inline int scl_vop_cal_lb_mode(int width, bool is_yuv)
 }
 
 extern const struct component_ops vop_component_ops;
+
 #endif /* _ROCKCHIP_DRM_VOP_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
new file mode 100644
index 0000000000000..394dd6c583682
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -0,0 +1,2708 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
+ * Author: Andy Yan <andy.yan@rock-chips.com>
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/swab.h>
+
+#include <drm/drm.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_flip_work.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+
+#include <uapi/linux/videodev2.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_vop2.h"
+
+/*
+ * VOP2 architecture
+ *
+ +----------+   +-------------+                                                        +-----------+
+ |  Cluster |   | Sel 1 from 6|                                                        | 1 from 3  |
+ |  window0 |   |    Layer0   |                                                        |    RGB    |
+ +----------+   +-------------+              +---------------+    +-------------+      +-----------+
+ +----------+   +-------------+              |N from 6 layers|    |             |
+ |  Cluster |   | Sel 1 from 6|              |   Overlay0    +--->| Video Port0 |      +-----------+
+ |  window1 |   |    Layer1   |              |               |    |             |      | 1 from 3  |
+ +----------+   +-------------+              +---------------+    +-------------+      |   LVDS    |
+ +----------+   +-------------+                                                        +-----------+
+ |  Esmart  |   | Sel 1 from 6|
+ |  window0 |   |   Layer2    |              +---------------+    +-------------+      +-----------+
+ +----------+   +-------------+              |N from 6 Layers|    |             | +--> | 1 from 3  |
+ +----------+   +-------------+   -------->  |   Overlay1    +--->| Video Port1 |      |   MIPI    |
+ |  Esmart  |   | Sel 1 from 6|   -------->  |               |    |             |      +-----------+
+ |  Window1 |   |   Layer3    |              +---------------+    +-------------+
+ +----------+   +-------------+                                                        +-----------+
+ +----------+   +-------------+                                                        | 1 from 3  |
+ |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |   HDMI    |
+ |  Window0 |   |    Layer4   |              |N from 6 Layers|    |             |      +-----------+
+ +----------+   +-------------+              |   Overlay2    +--->| Video Port2 |
+ +----------+   +-------------+              |               |    |             |      +-----------+
+ |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |  1 from 3 |
+ |  Window1 |   |    Layer5   |                                                        |    eDP    |
+ +----------+   +-------------+                                                        +-----------+
+ *
+ */
+
+enum vop2_data_format {
+	VOP2_FMT_ARGB8888 = 0,
+	VOP2_FMT_RGB888,
+	VOP2_FMT_RGB565,
+	VOP2_FMT_XRGB101010,
+	VOP2_FMT_YUV420SP,
+	VOP2_FMT_YUV422SP,
+	VOP2_FMT_YUV444SP,
+	VOP2_FMT_YUYV422 = 8,
+	VOP2_FMT_YUYV420,
+	VOP2_FMT_VYUY422,
+	VOP2_FMT_VYUY420,
+	VOP2_FMT_YUV420SP_TILE_8x4 = 0x10,
+	VOP2_FMT_YUV420SP_TILE_16x2,
+	VOP2_FMT_YUV422SP_TILE_8x4,
+	VOP2_FMT_YUV422SP_TILE_16x2,
+	VOP2_FMT_YUV420SP_10,
+	VOP2_FMT_YUV422SP_10,
+	VOP2_FMT_YUV444SP_10,
+};
+
+enum vop2_afbc_format {
+	VOP2_AFBC_FMT_RGB565,
+	VOP2_AFBC_FMT_ARGB2101010 = 2,
+	VOP2_AFBC_FMT_YUV420_10BIT,
+	VOP2_AFBC_FMT_RGB888,
+	VOP2_AFBC_FMT_ARGB8888,
+	VOP2_AFBC_FMT_YUV420 = 9,
+	VOP2_AFBC_FMT_YUV422 = 0xb,
+	VOP2_AFBC_FMT_YUV422_10BIT = 0xe,
+	VOP2_AFBC_FMT_INVALID = -1,
+};
+
+union vop2_alpha_ctrl {
+	u32 val;
+	struct {
+		/* [0:1] */
+		u32 color_mode:1;
+		u32 alpha_mode:1;
+		/* [2:3] */
+		u32 blend_mode:2;
+		u32 alpha_cal_mode:1;
+		/* [5:7] */
+		u32 factor_mode:3;
+		/* [8:9] */
+		u32 alpha_en:1;
+		u32 src_dst_swap:1;
+		u32 reserved:6;
+		/* [16:23] */
+		u32 glb_alpha:8;
+	} bits;
+};
+
+struct vop2_alpha {
+	union vop2_alpha_ctrl src_color_ctrl;
+	union vop2_alpha_ctrl dst_color_ctrl;
+	union vop2_alpha_ctrl src_alpha_ctrl;
+	union vop2_alpha_ctrl dst_alpha_ctrl;
+};
+
+struct vop2_alpha_config {
+	bool src_premulti_en;
+	bool dst_premulti_en;
+	bool src_pixel_alpha_en;
+	bool dst_pixel_alpha_en;
+	u16 src_glb_alpha_value;
+	u16 dst_glb_alpha_value;
+};
+
+struct vop2_win {
+	struct vop2 *vop2;
+	struct drm_plane base;
+	const struct vop2_win_data *data;
+	struct regmap_field *reg[VOP2_WIN_MAX_REG];
+
+	/**
+	 * @win_id: graphic window id, a cluster may be split into two
+	 * graphics windows.
+	 */
+	u8 win_id;
+	u8 delay;
+	u32 offset;
+
+	enum drm_plane_type type;
+};
+
+struct vop2_video_port {
+	struct drm_crtc crtc;
+	struct vop2 *vop2;
+	struct clk *dclk;
+	unsigned int id;
+	const struct vop2_video_port_regs *regs;
+	const struct vop2_video_port_data *data;
+
+	struct completion dsp_hold_completion;
+
+	/**
+	 * @win_mask: Bitmask of windows attached to the video port;
+	 */
+	u32 win_mask;
+
+	struct vop2_win *primary_plane;
+	struct drm_pending_vblank_event *event;
+
+	unsigned int nlayers;
+};
+
+struct vop2 {
+	struct device *dev;
+	struct drm_device *drm;
+	struct vop2_video_port vps[ROCKCHIP_MAX_CRTC];
+
+	const struct vop2_data *data;
+	/*
+	 * Number of windows that are registered as plane, may be less than the
+	 * total number of hardware windows.
+	 */
+	u32 registered_num_wins;
+
+	void __iomem *regs;
+	struct regmap *map;
+
+	struct regmap *grf;
+
+	/* physical map length of vop2 register */
+	u32 len;
+
+	void __iomem *lut_regs;
+
+	/* protects crtc enable/disable */
+	struct mutex vop2_lock;
+
+	int irq;
+
+	/*
+	 * Some global resources are shared between all video ports(crtcs), so
+	 * we need a ref counter here.
+	 */
+	unsigned int enable_count;
+	struct clk *hclk;
+	struct clk *aclk;
+
+	/* must be put at the end of the struct */
+	struct vop2_win win[];
+};
+
+static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
+{
+	return container_of(crtc, struct vop2_video_port, crtc);
+}
+
+static struct vop2_win *to_vop2_win(struct drm_plane *p)
+{
+	return container_of(p, struct vop2_win, base);
+}
+
+static void vop2_lock(struct vop2 *vop2)
+{
+	mutex_lock(&vop2->vop2_lock);
+}
+
+static void vop2_unlock(struct vop2 *vop2)
+{
+	mutex_unlock(&vop2->vop2_lock);
+}
+
+static void vop2_writel(struct vop2 *vop2, u32 offset, u32 v)
+{
+	regmap_write(vop2->map, offset, v);
+}
+
+static void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
+{
+	regmap_write(vp->vop2->map, vp->data->offset + offset, v);
+}
+
+static u32 vop2_readl(struct vop2 *vop2, u32 offset)
+{
+	u32 val;
+
+	regmap_read(vop2->map, offset, &val);
+
+	return val;
+}
+
+static void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v)
+{
+	regmap_field_write(win->reg[reg], v);
+}
+
+static bool vop2_cluster_window(const struct vop2_win *win)
+{
+	return win->data->feature & WIN_FEATURE_CLUSTER;
+}
+
+static void vop2_cfg_done(struct vop2_video_port *vp)
+{
+	struct vop2 *vop2 = vp->vop2;
+
+	regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE,
+			BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
+}
+
+static void vop2_win_disable(struct vop2_win *win)
+{
+	vop2_win_write(win, VOP2_WIN_ENABLE, 0);
+
+	if (vop2_cluster_window(win))
+		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0);
+}
+
+static enum vop2_data_format vop2_convert_format(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+		return VOP2_FMT_ARGB8888;
+	case DRM_FORMAT_RGB888:
+	case DRM_FORMAT_BGR888:
+		return VOP2_FMT_RGB888;
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_BGR565:
+		return VOP2_FMT_RGB565;
+	case DRM_FORMAT_NV12:
+		return VOP2_FMT_YUV420SP;
+	case DRM_FORMAT_NV16:
+		return VOP2_FMT_YUV422SP;
+	case DRM_FORMAT_NV24:
+		return VOP2_FMT_YUV444SP;
+	case DRM_FORMAT_YUYV:
+	case DRM_FORMAT_YVYU:
+		return VOP2_FMT_VYUY422;
+	case DRM_FORMAT_VYUY:
+	case DRM_FORMAT_UYVY:
+		return VOP2_FMT_YUYV422;
+	default:
+		DRM_ERROR("unsupported format[%08x]\n", format);
+		return -EINVAL;
+	}
+}
+
+static enum vop2_afbc_format vop2_convert_afbc_format(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+		return VOP2_AFBC_FMT_ARGB8888;
+	case DRM_FORMAT_RGB888:
+	case DRM_FORMAT_BGR888:
+		return VOP2_AFBC_FMT_RGB888;
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_BGR565:
+		return VOP2_AFBC_FMT_RGB565;
+	case DRM_FORMAT_NV12:
+		return VOP2_AFBC_FMT_YUV420;
+	case DRM_FORMAT_NV16:
+		return VOP2_AFBC_FMT_YUV422;
+	default:
+		return VOP2_AFBC_FMT_INVALID;
+	}
+
+	return VOP2_AFBC_FMT_INVALID;
+}
+
+static bool vop2_win_rb_swap(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_BGR888:
+	case DRM_FORMAT_BGR565:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool vop2_afbc_rb_swap(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_NV24:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool vop2_afbc_uv_swap(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_NV12:
+	case DRM_FORMAT_NV16:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool vop2_win_uv_swap(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_NV12:
+	case DRM_FORMAT_NV16:
+	case DRM_FORMAT_NV24:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool vop2_win_dither_up(u32 format)
+{
+	switch (format) {
+	case DRM_FORMAT_BGR565:
+	case DRM_FORMAT_RGB565:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
+{
+	/*
+	 * FIXME:
+	 *
+	 * There is no media type for YUV444 output,
+	 * so when out_mode is AAAA or P888, assume output is YUV444 on
+	 * yuv format.
+	 *
+	 * From H/W testing, YUV444 mode need a rb swap.
+	 */
+	if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 ||
+	    bus_format == MEDIA_BUS_FMT_VYUY8_1X16 ||
+	    bus_format == MEDIA_BUS_FMT_YVYU8_2X8 ||
+	    bus_format == MEDIA_BUS_FMT_VYUY8_2X8 ||
+	    ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
+	      bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
+	     (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
+	      output_mode == ROCKCHIP_OUT_MODE_P888)))
+		return true;
+	else
+		return false;
+}
+
+static bool is_yuv_output(u32 bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+	case MEDIA_BUS_FMT_YUYV8_2X8:
+	case MEDIA_BUS_FMT_YVYU8_2X8:
+	case MEDIA_BUS_FMT_UYVY8_2X8:
+	case MEDIA_BUS_FMT_VYUY8_2X8:
+	case MEDIA_BUS_FMT_YUYV8_1X16:
+	case MEDIA_BUS_FMT_YVYU8_1X16:
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+	case MEDIA_BUS_FMT_VYUY8_1X16:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool rockchip_afbc(struct drm_plane *plane, u64 modifier)
+{
+	int i;
+
+	if (modifier == DRM_FORMAT_MOD_LINEAR)
+		return false;
+
+	for (i = 0 ; i < plane->modifier_count; i++)
+		if (plane->modifiers[i] == modifier)
+			return true;
+
+	return false;
+
+}
+
+static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
+					u64 modifier)
+{
+	struct vop2_win *win = to_vop2_win(plane);
+	struct vop2 *vop2 = win->vop2;
+
+	if (modifier == DRM_FORMAT_MOD_INVALID)
+		return false;
+
+	if (modifier == DRM_FORMAT_MOD_LINEAR)
+		return true;
+
+	if (!rockchip_afbc(plane, modifier)) {
+		drm_err(vop2->drm, "Unsupported format modifier 0x%llx\n",
+			modifier);
+
+		return false;
+	}
+
+	return vop2_convert_afbc_format(format) >= 0;
+}
+
+static u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate,
+				      bool afbc_half_block_en)
+{
+	struct drm_rect *src = &pstate->src;
+	struct drm_framebuffer *fb = pstate->fb;
+	u32 bpp = fb->format->cpp[0] * 8;
+	u32 vir_width = (fb->pitches[0] << 3) / bpp;
+	u32 width = drm_rect_width(src) >> 16;
+	u32 height = drm_rect_height(src) >> 16;
+	u32 act_xoffset = src->x1 >> 16;
+	u32 act_yoffset = src->y1 >> 16;
+	u32 align16_crop = 0;
+	u32 align64_crop = 0;
+	u32 height_tmp;
+	u8 tx, ty;
+	u8 bottom_crop_line_num = 0;
+
+	/* 16 pixel align */
+	if (height & 0xf)
+		align16_crop = 16 - (height & 0xf);
+
+	height_tmp = height + align16_crop;
+
+	/* 64 pixel align */
+	if (height_tmp & 0x3f)
+		align64_crop = 64 - (height_tmp & 0x3f);
+
+	bottom_crop_line_num = align16_crop + align64_crop;
+
+	switch (pstate->rotation &
+		(DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y |
+		 DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) {
+	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
+		tx = 16 - ((act_xoffset + width) & 0xf);
+		ty = bottom_crop_line_num - act_yoffset;
+		break;
+	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90:
+		tx = bottom_crop_line_num - act_yoffset;
+		ty = vir_width - width - act_xoffset;
+		break;
+	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270:
+		tx = act_yoffset;
+		ty = act_xoffset;
+		break;
+	case DRM_MODE_REFLECT_X:
+		tx = 16 - ((act_xoffset + width) & 0xf);
+		ty = act_yoffset;
+		break;
+	case DRM_MODE_REFLECT_Y:
+		tx = act_xoffset;
+		ty = bottom_crop_line_num - act_yoffset;
+		break;
+	case DRM_MODE_ROTATE_90:
+		tx = bottom_crop_line_num - act_yoffset;
+		ty = act_xoffset;
+		break;
+	case DRM_MODE_ROTATE_270:
+		tx = act_yoffset;
+		ty = vir_width - width - act_xoffset;
+		break;
+	case 0:
+		tx = act_xoffset;
+		ty = act_yoffset;
+		break;
+	}
+
+	if (afbc_half_block_en)
+		ty &= 0x7f;
+
+#define TRANSFORM_XOFFSET GENMASK(7, 0)
+#define TRANSFORM_YOFFSET GENMASK(23, 16)
+	return FIELD_PREP(TRANSFORM_XOFFSET, tx) |
+		FIELD_PREP(TRANSFORM_YOFFSET, ty);
+}
+
+/*
+ * A Cluster window has 2048 x 16 line buffer, which can
+ * works at 2048 x 16(Full) or 4096 x 8 (Half) mode.
+ * for Cluster_lb_mode register:
+ * 0: half mode, for plane input width range 2048 ~ 4096
+ * 1: half mode, for cluster work at 2 * 2048 plane mode
+ * 2: half mode, for rotate_90/270 mode
+ *
+ */
+static int vop2_get_cluster_lb_mode(struct vop2_win *win,
+				    struct drm_plane_state *pstate)
+{
+	if ((pstate->rotation & DRM_MODE_ROTATE_270) ||
+	    (pstate->rotation & DRM_MODE_ROTATE_90))
+		return 2;
+	else
+		return 0;
+}
+
+static u16 vop2_scale_factor(u32 src, u32 dst)
+{
+	u32 fac;
+	int shift;
+
+	if (src == dst)
+		return 0;
+
+	if (dst < 2)
+		return U16_MAX;
+
+	if (src < 2)
+		return 0;
+
+	if (src > dst)
+		shift = 12;
+	else
+		shift = 16;
+
+	src--;
+	dst--;
+
+	fac = DIV_ROUND_UP(src << shift, dst) - 1;
+
+	if (fac > U16_MAX)
+		return U16_MAX;
+
+	return fac;
+}
+
+static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
+			     u32 src_w, u32 src_h, u32 dst_w,
+			     u32 dst_h, u32 pixel_format)
+{
+	const struct drm_format_info *info;
+	u16 hor_scl_mode, ver_scl_mode;
+	u16 hscl_filter_mode, vscl_filter_mode;
+	u8 gt2 = 0;
+	u8 gt4 = 0;
+	u32 val;
+
+	info = drm_format_info(pixel_format);
+
+	if (src_h >= (4 * dst_h)) {
+		gt4 = 1;
+		src_h >>= 2;
+	} else if (src_h >= (2 * dst_h)) {
+		gt2 = 1;
+		src_h >>= 1;
+	}
+
+	hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
+	ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
+
+	if (hor_scl_mode == SCALE_UP)
+		hscl_filter_mode = VOP2_SCALE_UP_BIC;
+	else
+		hscl_filter_mode = VOP2_SCALE_DOWN_BIL;
+
+	if (ver_scl_mode == SCALE_UP)
+		vscl_filter_mode = VOP2_SCALE_UP_BIL;
+	else
+		vscl_filter_mode = VOP2_SCALE_DOWN_BIL;
+
+	/*
+	 * RK3568 VOP Esmart/Smart dsp_w should be even pixel
+	 * at scale down mode
+	 */
+	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
+		if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) {
+			drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n",
+				win->data->name, dst_w);
+			dst_w++;
+		}
+	}
+
+	val = vop2_scale_factor(src_w, dst_w);
+	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val);
+	val = vop2_scale_factor(src_h, dst_h);
+	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val);
+
+	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4);
+	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2);
+
+	vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode);
+	vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode);
+
+	if (vop2_cluster_window(win))
+		return;
+
+	vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode);
+	vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode);
+
+	if (info->is_yuv) {
+		src_w /= info->hsub;
+		src_h /= info->vsub;
+
+		gt4 = gt2 = 0;
+
+		if (src_h >= (4 * dst_h)) {
+			gt4 = 1;
+			src_h >>= 2;
+		} else if (src_h >= (2 * dst_h)) {
+			gt2 = 1;
+			src_h >>= 1;
+		}
+
+		hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
+		ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
+
+		val = vop2_scale_factor(src_w, dst_w);
+		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val);
+
+		val = vop2_scale_factor(src_h, dst_h);
+		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val);
+
+		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4);
+		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2);
+		vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode);
+		vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode);
+		vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode);
+		vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode);
+	}
+}
+
+static int vop2_convert_csc_mode(int csc_mode)
+{
+	switch (csc_mode) {
+	case V4L2_COLORSPACE_SMPTE170M:
+	case V4L2_COLORSPACE_470_SYSTEM_M:
+	case V4L2_COLORSPACE_470_SYSTEM_BG:
+		return CSC_BT601L;
+	case V4L2_COLORSPACE_REC709:
+	case V4L2_COLORSPACE_SMPTE240M:
+	case V4L2_COLORSPACE_DEFAULT:
+		return CSC_BT709L;
+	case V4L2_COLORSPACE_JPEG:
+		return CSC_BT601F;
+	case V4L2_COLORSPACE_BT2020:
+		return CSC_BT2020;
+	default:
+		return CSC_BT709L;
+	}
+}
+
+/*
+ * colorspace path:
+ *      Input        Win csc                     Output
+ * 1. YUV(2020)  --> Y2R->2020To709->R2Y   --> YUV_OUTPUT(601/709)
+ *    RGB        --> R2Y                  __/
+ *
+ * 2. YUV(2020)  --> bypasss               --> YUV_OUTPUT(2020)
+ *    RGB        --> 709To2020->R2Y       __/
+ *
+ * 3. YUV(2020)  --> Y2R->2020To709        --> RGB_OUTPUT(709)
+ *    RGB        --> R2Y                  __/
+ *
+ * 4. YUV(601/709)-> Y2R->709To2020->R2Y   --> YUV_OUTPUT(2020)
+ *    RGB        --> 709To2020->R2Y       __/
+ *
+ * 5. YUV(601/709)-> bypass                --> YUV_OUTPUT(709)
+ *    RGB        --> R2Y                  __/
+ *
+ * 6. YUV(601/709)-> bypass                --> YUV_OUTPUT(601)
+ *    RGB        --> R2Y(601)             __/
+ *
+ * 7. YUV        --> Y2R(709)              --> RGB_OUTPUT(709)
+ *    RGB        --> bypass               __/
+ *
+ * 8. RGB        --> 709To2020->R2Y        --> YUV_OUTPUT(2020)
+ *
+ * 9. RGB        --> R2Y(709)              --> YUV_OUTPUT(709)
+ *
+ * 10. RGB       --> R2Y(601)              --> YUV_OUTPUT(601)
+ *
+ * 11. RGB       --> bypass                --> RGB_OUTPUT(709)
+ */
+
+static void vop2_setup_csc_mode(struct vop2_video_port *vp,
+				struct vop2_win *win,
+				struct drm_plane_state *pstate)
+{
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
+	int is_input_yuv = pstate->fb->format->is_yuv;
+	int is_output_yuv = is_yuv_output(vcstate->bus_format);
+	int input_csc = V4L2_COLORSPACE_DEFAULT;
+	int output_csc = vcstate->color_space;
+	bool r2y_en, y2r_en;
+	int csc_mode;
+
+	if (is_input_yuv && !is_output_yuv) {
+		y2r_en = true;
+		r2y_en = false;
+		csc_mode = vop2_convert_csc_mode(input_csc);
+	} else if (!is_input_yuv && is_output_yuv) {
+		y2r_en = false;
+		r2y_en = true;
+		csc_mode = vop2_convert_csc_mode(output_csc);
+	} else {
+		y2r_en = false;
+		r2y_en = false;
+		csc_mode = false;
+	}
+
+	vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en);
+	vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en);
+	vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
+}
+
+static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
+{
+	struct vop2 *vop2 = vp->vop2;
+
+	vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq);
+	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq);
+}
+
+static void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq)
+{
+	struct vop2 *vop2 = vp->vop2;
+
+	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16);
+}
+
+static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
+{
+	int ret;
+
+	ret = clk_prepare_enable(vop2->hclk);
+	if (ret < 0) {
+		drm_err(vop2->drm, "failed to enable hclk - %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(vop2->aclk);
+	if (ret < 0) {
+		drm_err(vop2->drm, "failed to enable aclk - %d\n", ret);
+		goto err;
+	}
+
+	return 0;
+err:
+	clk_disable_unprepare(vop2->hclk);
+
+	return ret;
+}
+
+static void vop2_enable(struct vop2 *vop2)
+{
+	int ret;
+
+	ret = pm_runtime_get_sync(vop2->dev);
+	if (ret < 0) {
+		drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret);
+		return;
+	}
+
+	ret = vop2_core_clks_prepare_enable(vop2);
+	if (ret) {
+		pm_runtime_put_sync(vop2->dev);
+		return;
+	}
+
+	if (vop2->data->soc_id == 3566)
+		vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
+
+	vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
+
+	/*
+	 * Disable auto gating, this is a workaround to
+	 * avoid display image shift when a window enabled.
+	 */
+	regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL,
+			  RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN);
+
+	vop2_writel(vop2, RK3568_SYS0_INT_CLR,
+		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
+	vop2_writel(vop2, RK3568_SYS0_INT_EN,
+		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
+	vop2_writel(vop2, RK3568_SYS1_INT_CLR,
+		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
+	vop2_writel(vop2, RK3568_SYS1_INT_EN,
+		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
+}
+
+static void vop2_disable(struct vop2 *vop2)
+{
+	pm_runtime_put_sync(vop2->dev);
+
+	clk_disable_unprepare(vop2->aclk);
+	clk_disable_unprepare(vop2->hclk);
+}
+
+static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
+				     struct drm_atomic_state *state)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct vop2 *vop2 = vp->vop2;
+	int ret;
+
+	vop2_lock(vop2);
+
+	drm_crtc_vblank_off(crtc);
+
+	/*
+	 * Vop standby will take effect at end of current frame,
+	 * if dsp hold valid irq happen, it means standby complete.
+	 *
+	 * we must wait standby complete when we want to disable aclk,
+	 * if not, memory bus maybe dead.
+	 */
+	reinit_completion(&vp->dsp_hold_completion);
+
+	vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
+
+	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
+
+	ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
+					  msecs_to_jiffies(50));
+	if (!ret)
+		drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
+
+	vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
+
+	clk_disable_unprepare(vp->dclk);
+
+	vop2->enable_count--;
+
+	if (!vop2->enable_count)
+		vop2_disable(vop2);
+
+	vop2_unlock(vop2);
+
+	if (crtc->state->event && !crtc->state->active) {
+		spin_lock_irq(&crtc->dev->event_lock);
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+
+		crtc->state->event = NULL;
+	}
+}
+
+static int vop2_plane_atomic_check(struct drm_plane *plane,
+				   struct drm_atomic_state *astate)
+{
+	struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane);
+	struct drm_framebuffer *fb = pstate->fb;
+	struct drm_crtc *crtc = pstate->crtc;
+	struct drm_crtc_state *cstate;
+	struct vop2_video_port *vp;
+	struct vop2 *vop2;
+	const struct vop2_data *vop2_data;
+	struct drm_rect *dest = &pstate->dst;
+	struct drm_rect *src = &pstate->src;
+	int min_scale = FRAC_16_16(1, 8);
+	int max_scale = FRAC_16_16(8, 1);
+	int format;
+	int ret;
+
+	if (!crtc)
+		return 0;
+
+	vp = to_vop2_video_port(crtc);
+	vop2 = vp->vop2;
+	vop2_data = vop2->data;
+
+	cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc);
+	if (WARN_ON(!cstate))
+		return -EINVAL;
+
+	ret = drm_atomic_helper_check_plane_state(pstate, cstate,
+						  min_scale, max_scale,
+						  true, true);
+	if (ret)
+		return ret;
+
+	if (!pstate->visible)
+		return 0;
+
+	format = vop2_convert_format(fb->format->format);
+	if (format < 0)
+		return format;
+
+	if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 ||
+	    drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) {
+		drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
+			  drm_rect_width(src) >> 16, drm_rect_height(src) >> 16,
+			  drm_rect_width(dest), drm_rect_height(dest));
+		pstate->visible = false;
+		return 0;
+	}
+
+	if (drm_rect_width(src) >> 16 > vop2_data->max_input.width ||
+	    drm_rect_height(src) >> 16 > vop2_data->max_input.height) {
+		drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
+			  drm_rect_width(src) >> 16,
+			  drm_rect_height(src) >> 16,
+			  vop2_data->max_input.width,
+			  vop2_data->max_input.height);
+		return -EINVAL;
+	}
+
+	/*
+	 * Src.x1 can be odd when do clip, but yuv plane start point
+	 * need align with 2 pixel.
+	 */
+	if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
+		drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void vop2_plane_atomic_disable(struct drm_plane *plane,
+				      struct drm_atomic_state *state)
+{
+	struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state, plane);
+	struct vop2_win *win = to_vop2_win(plane);
+	struct vop2 *vop2 = win->vop2;
+
+	drm_dbg(vop2->drm, "%s disable\n", win->data->name);
+
+	if (!old_pstate->crtc)
+		return;
+
+	vop2_win_disable(win);
+	vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0);
+}
+
+/*
+ * The color key is 10 bit, so all format should
+ * convert to 10 bit here.
+ */
+static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
+{
+	struct drm_plane_state *pstate = plane->state;
+	struct drm_framebuffer *fb = pstate->fb;
+	struct vop2_win *win = to_vop2_win(plane);
+	u32 color_key_en = 0;
+	u32 r = 0;
+	u32 g = 0;
+	u32 b = 0;
+
+	if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) {
+		vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0);
+		return;
+	}
+
+	switch (fb->format->format) {
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_BGR565:
+		r = (color_key & 0xf800) >> 11;
+		g = (color_key & 0x7e0) >> 5;
+		b = (color_key & 0x1f);
+		r <<= 5;
+		g <<= 4;
+		b <<= 5;
+		color_key_en = 1;
+		break;
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_RGB888:
+	case DRM_FORMAT_BGR888:
+		r = (color_key & 0xff0000) >> 16;
+		g = (color_key & 0xff00) >> 8;
+		b = (color_key & 0xff);
+		r <<= 2;
+		g <<= 2;
+		b <<= 2;
+		color_key_en = 1;
+		break;
+	}
+
+	vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en);
+	vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b);
+}
+
+static void vop2_plane_atomic_update(struct drm_plane *plane,
+				     struct drm_atomic_state *state)
+{
+	struct drm_plane_state *pstate = plane->state;
+	struct drm_crtc *crtc = pstate->crtc;
+	struct vop2_win *win = to_vop2_win(plane);
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
+	struct vop2 *vop2 = win->vop2;
+	struct drm_framebuffer *fb = pstate->fb;
+	u32 bpp = fb->format->cpp[0] * 8;
+	u32 actual_w, actual_h, dsp_w, dsp_h;
+	u32 act_info, dsp_info;
+	u32 format;
+	u32 afbc_format;
+	u32 rb_swap;
+	u32 uv_swap;
+	struct drm_rect *src = &pstate->src;
+	struct drm_rect *dest = &pstate->dst;
+	u32 afbc_tile_num;
+	u32 transform_offset;
+	bool dither_up;
+	bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
+	bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
+	bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
+	bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
+	struct rockchip_gem_object *rk_obj;
+	unsigned long offset;
+	bool afbc_en;
+	dma_addr_t yrgb_mst;
+	dma_addr_t uv_mst;
+
+	/*
+	 * can't update plane when vop2 is disabled.
+	 */
+	if (WARN_ON(!crtc))
+		return;
+
+	if (!pstate->visible) {
+		vop2_plane_atomic_disable(plane, state);
+		return;
+	}
+
+	afbc_en = rockchip_afbc(plane, fb->modifier);
+
+	offset = (src->x1 >> 16) * fb->format->cpp[0];
+
+	/*
+	 * AFBC HDR_PTR must set to the zero offset of the framebuffer.
+	 */
+	if (afbc_en)
+		offset = 0;
+	else if (pstate->rotation & DRM_MODE_REFLECT_Y)
+		offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
+	else
+		offset += (src->y1 >> 16) * fb->pitches[0];
+
+	rk_obj = to_rockchip_obj(fb->obj[0]);
+
+	yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
+	if (fb->format->is_yuv) {
+		int hsub = fb->format->hsub;
+		int vsub = fb->format->vsub;
+
+		offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub;
+		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
+
+		if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en)
+			offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub;
+
+		rk_obj = to_rockchip_obj(fb->obj[0]);
+		uv_mst = rk_obj->dma_addr + offset + fb->offsets[1];
+	}
+
+	actual_w = drm_rect_width(src) >> 16;
+	actual_h = drm_rect_height(src) >> 16;
+	dsp_w = drm_rect_width(dest);
+
+	if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
+		drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
+			  vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
+		dsp_w = adjusted_mode->hdisplay - dest->x1;
+		if (dsp_w < 4)
+			dsp_w = 4;
+		actual_w = dsp_w * actual_w / drm_rect_width(dest);
+	}
+
+	dsp_h = drm_rect_height(dest);
+
+	if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
+		drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
+			  vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
+		dsp_h = adjusted_mode->vdisplay - dest->y1;
+		if (dsp_h < 4)
+			dsp_h = 4;
+		actual_h = dsp_h * actual_h / drm_rect_height(dest);
+	}
+
+	/*
+	 * This is workaround solution for IC design:
+	 * esmart can't support scale down when actual_w % 16 == 1.
+	 */
+	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
+		if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
+			drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
+				vp->id, win->data->name, actual_w);
+			actual_w -= 1;
+		}
+	}
+
+	if (afbc_en && actual_w % 4) {
+		drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
+			  vp->id, win->data->name, actual_w);
+		actual_w = ALIGN_DOWN(actual_w, 4);
+	}
+
+	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
+	dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff);
+
+	format = vop2_convert_format(fb->format->format);
+
+	drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n",
+		      vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h,
+		      dest->x1, dest->y1,
+		      &fb->format->format,
+		      afbc_en ? "AFBC" : "", &yrgb_mst);
+
+	if (afbc_en) {
+		u32 stride;
+
+		/* the afbc superblock is 16 x 16 */
+		afbc_format = vop2_convert_afbc_format(fb->format->format);
+
+		/* Enable color transform for YTR */
+		if (fb->modifier & AFBC_FORMAT_MOD_YTR)
+			afbc_format |= (1 << 4);
+
+		afbc_tile_num = ALIGN(actual_w, 16) >> 4;
+
+		/*
+		 * AFBC pic_vir_width is count by pixel, this is different
+		 * with WIN_VIR_STRIDE.
+		 */
+		stride = (fb->pitches[0] << 3) / bpp;
+		if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
+			drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligened\n",
+				  vp->id, win->data->name, stride);
+
+		rb_swap = vop2_afbc_rb_swap(fb->format->format);
+		uv_swap = vop2_afbc_uv_swap(fb->format->format);
+		/*
+		 * This is a workaround for crazy IC design, Cluster
+		 * and Esmart/Smart use different format configuration map:
+		 * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart.
+		 *
+		 * This is one thing we can make the convert simple:
+		 * AFBCD decode all the YUV data to YUV444. So we just
+		 * set all the yuv 10 bit to YUV444_10.
+		 */
+		if (fb->format->is_yuv && (bpp == 10))
+			format = VOP2_CLUSTER_YUV444_10;
+
+		if (vop2_cluster_window(win))
+			vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
+		vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
+		vop2_win_write(win, VOP2_WIN_AFBC_RB_SWAP, rb_swap);
+		vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap);
+		vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
+		vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
+		if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) {
+			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0);
+			transform_offset = vop2_afbc_transform_offset(pstate, false);
+		} else {
+			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 1);
+			transform_offset = vop2_afbc_transform_offset(pstate, true);
+		}
+		vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
+		vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
+		vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset);
+		vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1));
+		vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16)));
+		vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride);
+		vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num);
+		vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror);
+		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270);
+		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90);
+	} else {
+		vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4));
+	}
+
+	vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror);
+
+	if (rotate_90 || rotate_270) {
+		act_info = swahw32(act_info);
+		actual_w = drm_rect_height(src) >> 16;
+		actual_h = drm_rect_width(src) >> 16;
+	}
+
+	vop2_win_write(win, VOP2_WIN_FORMAT, format);
+	vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst);
+
+	rb_swap = vop2_win_rb_swap(fb->format->format);
+	vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap);
+	if (!vop2_cluster_window(win)) {
+		uv_swap = vop2_win_uv_swap(fb->format->format);
+		vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
+	}
+
+	if (fb->format->is_yuv) {
+		vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4));
+		vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst);
+	}
+
+	vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format);
+	if (!vop2_cluster_window(win))
+		vop2_plane_setup_color_key(plane, 0);
+	vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info);
+	vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info);
+	vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff));
+
+	vop2_setup_csc_mode(vp, win, pstate);
+
+	dither_up = vop2_win_dither_up(fb->format->format);
+	vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up);
+
+	vop2_win_write(win, VOP2_WIN_ENABLE, 1);
+
+	if (vop2_cluster_window(win)) {
+		int lb_mode = vop2_get_cluster_lb_mode(win, pstate);
+
+		vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode);
+		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1);
+	}
+}
+
+static const struct drm_plane_helper_funcs vop2_plane_helper_funcs = {
+	.atomic_check = vop2_plane_atomic_check,
+	.atomic_update = vop2_plane_atomic_update,
+	.atomic_disable = vop2_plane_atomic_disable,
+};
+
+static const struct drm_plane_funcs vop2_plane_funcs = {
+	.update_plane	= drm_atomic_helper_update_plane,
+	.disable_plane	= drm_atomic_helper_disable_plane,
+	.destroy = drm_plane_cleanup,
+	.reset = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+	.format_mod_supported = rockchip_vop2_mod_supported,
+};
+
+static int vop2_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+
+	vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD);
+
+	return 0;
+}
+
+static void vop2_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+
+	vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD);
+}
+
+static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
+				 const struct drm_display_mode *mode,
+				 struct drm_display_mode *adj_mode)
+{
+	drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V |
+					CRTC_STEREO_DOUBLE);
+
+	return true;
+}
+
+static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
+{
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
+
+	switch (vcstate->bus_format) {
+	case MEDIA_BUS_FMT_RGB565_1X16:
+		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
+		break;
+	case MEDIA_BUS_FMT_RGB666_1X18:
+	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
+		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
+		*dsp_ctrl |= RGB888_TO_RGB666;
+		break;
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
+		break;
+	default:
+		break;
+	}
+
+	if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA)
+		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
+
+	*dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL,
+				DITHER_DOWN_ALLEGRO);
+}
+
+static void vop2_post_config(struct drm_crtc *crtc)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+	u16 vtotal = mode->crtc_vtotal;
+	u16 hdisplay = mode->crtc_hdisplay;
+	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
+	u16 vdisplay = mode->crtc_vdisplay;
+	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
+	u32 left_margin = 100, right_margin = 100;
+	u32 top_margin = 100, bottom_margin = 100;
+	u16 hsize = hdisplay * (left_margin + right_margin) / 200;
+	u16 vsize = vdisplay * (top_margin + bottom_margin) / 200;
+	u16 hact_end, vact_end;
+	u32 val;
+
+	vsize = rounddown(vsize, 2);
+	hsize = rounddown(hsize, 2);
+	hact_st += hdisplay * (100 - left_margin) / 200;
+	hact_end = hact_st + hsize;
+	val = hact_st << 16;
+	val |= hact_end;
+	vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val);
+	vact_st += vdisplay * (100 - top_margin) / 200;
+	vact_end = vact_st + vsize;
+	val = vact_st << 16;
+	val |= vact_end;
+	vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val);
+	val = scl_cal_scale2(vdisplay, vsize) << 16;
+	val |= scl_cal_scale2(hdisplay, hsize);
+	vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val);
+
+	val = 0;
+	if (hdisplay != hsize)
+		val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN;
+	if (vdisplay != vsize)
+		val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN;
+	vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val);
+
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		u16 vact_st_f1 = vtotal + vact_st + 1;
+		u16 vact_end_f1 = vact_st_f1 + vsize;
+
+		val = vact_st_f1 << 16 | vact_end_f1;
+		vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
+	}
+
+	vop2_vp_write(vp, RK3568_VP_DSP_BG, 0);
+}
+
+static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
+				u32 polflags)
+{
+	struct vop2 *vop2 = vp->vop2;
+	u32 die, dip;
+
+	die = vop2_readl(vop2, RK3568_DSP_IF_EN);
+	dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
+
+	switch (id) {
+	case RK3568_VOP2_EP_RGB:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_RGB |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
+		if (polflags & POLFLAG_DCLK_INV)
+			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
+		else
+			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16));
+		break;
+	case RK3568_VOP2_EP_HDMI:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_HDMI |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id);
+		break;
+	case RK3568_VOP2_EP_EDP:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_EDP |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id);
+		break;
+	case RK3568_VOP2_EP_MIPI0:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id);
+		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
+		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
+		break;
+	case RK3568_VOP2_EP_MIPI1:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
+		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
+		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
+		break;
+	case RK3568_VOP2_EP_LVDS0:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id);
+		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
+		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
+		break;
+	case RK3568_VOP2_EP_LVDS1:
+		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX;
+		die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 |
+			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id);
+		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
+		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
+		break;
+	default:
+		return;
+	};
+
+	dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
+
+	vop2_writel(vop2, RK3568_DSP_IF_EN, die);
+	vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
+}
+
+static int us_to_vertical_line(struct drm_display_mode *mode, int us)
+{
+	return us * mode->clock / mode->htotal / 1000;
+}
+
+static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
+				    struct drm_atomic_state *state)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct vop2 *vop2 = vp->vop2;
+	const struct vop2_data *vop2_data = vop2->data;
+	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
+	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+	unsigned long clock = mode->crtc_clock * 1000;
+	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
+	u16 hdisplay = mode->crtc_hdisplay;
+	u16 htotal = mode->crtc_htotal;
+	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
+	u16 hact_end = hact_st + hdisplay;
+	u16 vdisplay = mode->crtc_vdisplay;
+	u16 vtotal = mode->crtc_vtotal;
+	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
+	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
+	u16 vact_end = vact_st + vdisplay;
+	u8 out_mode;
+	u32 dsp_ctrl = 0;
+	int act_end;
+	u32 val, polflags;
+	int ret;
+	struct drm_encoder *encoder;
+
+	drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n",
+		hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
+		drm_mode_vrefresh(mode), vcstate->output_type, vp->id);
+
+	vop2_lock(vop2);
+
+	ret = clk_prepare_enable(vp->dclk);
+	if (ret < 0) {
+		drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
+			      vp->id, ret);
+		return;
+	}
+
+	if (!vop2->enable_count)
+		vop2_enable(vop2);
+
+	vop2->enable_count++;
+
+	vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY);
+
+	polflags = 0;
+	if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
+		polflags |= POLFLAG_DCLK_INV;
+	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+		polflags |= BIT(HSYNC_POSITIVE);
+	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+		polflags |= BIT(VSYNC_POSITIVE);
+
+	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
+		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+		struct device_node *node, *parent;
+
+		parent = of_get_parent(rkencoder->port);
+
+		for_each_endpoint_of_node(parent, node) {
+			struct device_node *crtc_port = of_graph_get_remote_port(node);
+			struct device_node *epn;
+			struct of_endpoint endpoint;
+
+			if (crtc->port != crtc_port) {
+				of_node_put(crtc_port);
+				continue;
+			}
+
+			of_node_put(crtc_port);
+
+			epn = of_graph_get_remote_endpoint(node);
+			of_graph_parse_endpoint(epn, &endpoint);
+			of_node_put(epn);
+
+			drm_dbg(vop2->drm, "vp%d is connected to %s, id %d\n",
+					   vp->id, encoder->name, endpoint.id);
+			rk3568_set_intf_mux(vp, endpoint.id, polflags);
+		}
+		of_node_put(parent);
+	}
+
+	if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
+	     !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT))
+		out_mode = ROCKCHIP_OUT_MODE_P888;
+	else
+		out_mode = vcstate->output_mode;
+
+	dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode);
+
+	if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP;
+
+	if (is_yuv_output(vcstate->bus_format))
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y;
+
+	vop2_dither_setup(crtc, &dsp_ctrl);
+
+	vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len);
+	val = hact_st << 16;
+	val |= hact_end;
+	vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val);
+
+	val = vact_st << 16;
+	val |= vact_end;
+	vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val);
+
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		u16 vact_st_f1 = vtotal + vact_st + 1;
+		u16 vact_end_f1 = vact_st_f1 + vdisplay;
+
+		val = vact_st_f1 << 16 | vact_end_f1;
+		vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val);
+
+		val = vtotal << 16 | (vtotal + vsync_len);
+		vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val);
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE;
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL;
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN;
+		vtotal += vtotal + 1;
+		act_end = vact_end_f1;
+	} else {
+		act_end = vact_end;
+	}
+
+	vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id),
+		    (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end);
+
+	vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len);
+
+	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
+		dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
+		clock *= 2;
+	}
+
+	vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
+
+	clk_set_rate(vp->dclk, clock);
+
+	vop2_post_config(crtc);
+
+	vop2_cfg_done(vp);
+
+	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
+
+	drm_crtc_vblank_on(crtc);
+
+	vop2_unlock(vop2);
+}
+
+static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
+				  struct drm_atomic_state *state)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct drm_plane *plane;
+	int nplanes = 0;
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+
+	drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
+		nplanes++;
+
+	if (nplanes > vp->nlayers)
+		return -EINVAL;
+
+	return 0;
+}
+
+static bool is_opaque(u16 alpha)
+{
+	return (alpha >> 8) == 0xff;
+}
+
+static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config,
+			     struct vop2_alpha *alpha)
+{
+	int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1;
+	int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1;
+	int src_color_mode = alpha_config->src_premulti_en ?
+				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
+	int dst_color_mode = alpha_config->dst_premulti_en ?
+				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
+
+	alpha->src_color_ctrl.val = 0;
+	alpha->dst_color_ctrl.val = 0;
+	alpha->src_alpha_ctrl.val = 0;
+	alpha->dst_alpha_ctrl.val = 0;
+
+	if (!alpha_config->src_pixel_alpha_en)
+		alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
+	else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en)
+		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX;
+	else
+		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
+
+	alpha->src_color_ctrl.bits.alpha_en = 1;
+
+	if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) {
+		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
+		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
+	} else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) {
+		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
+		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE;
+	} else {
+		alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL;
+		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
+	}
+	alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8;
+	alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
+	alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
+
+	alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
+	alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
+	alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
+	alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8;
+	alpha->dst_color_ctrl.bits.color_mode = dst_color_mode;
+	alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
+
+	alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
+	alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode;
+	alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
+	alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE;
+
+	alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
+	if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en)
+		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX;
+	else
+		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
+	alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION;
+	alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
+}
+
+static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id)
+{
+	struct vop2_video_port *vp;
+	int used_layer = 0;
+	int i;
+
+	for (i = 0; i < port_id; i++) {
+		vp = &vop2->vps[i];
+		used_layer += hweight32(vp->win_mask);
+	}
+
+	return used_layer;
+}
+
+static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win)
+{
+	u32 offset = (main_win->data->phys_id * 0x10);
+	struct vop2_alpha_config alpha_config;
+	struct vop2_alpha alpha;
+	struct drm_plane_state *bottom_win_pstate;
+	bool src_pixel_alpha_en = false;
+	u16 src_glb_alpha_val, dst_glb_alpha_val;
+	bool premulti_en = false;
+	bool swap = false;
+
+	/* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */
+	bottom_win_pstate = main_win->base.state;
+	src_glb_alpha_val = 0;
+	dst_glb_alpha_val = main_win->base.state->alpha;
+
+	if (!bottom_win_pstate->fb)
+		return;
+
+	alpha_config.src_premulti_en = premulti_en;
+	alpha_config.dst_premulti_en = false;
+	alpha_config.src_pixel_alpha_en = src_pixel_alpha_en;
+	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
+	alpha_config.src_glb_alpha_value = src_glb_alpha_val;
+	alpha_config.dst_glb_alpha_value = dst_glb_alpha_val;
+	vop2_parse_alpha(&alpha_config, &alpha);
+
+	alpha.src_color_ctrl.bits.src_dst_swap = swap;
+	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset,
+		    alpha.src_color_ctrl.val);
+	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset,
+		    alpha.dst_color_ctrl.val);
+	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset,
+		    alpha.src_alpha_ctrl.val);
+	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset,
+		    alpha.dst_alpha_ctrl.val);
+}
+
+static void vop2_setup_alpha(struct vop2_video_port *vp)
+{
+	struct vop2 *vop2 = vp->vop2;
+	struct drm_framebuffer *fb;
+	struct vop2_alpha_config alpha_config;
+	struct vop2_alpha alpha;
+	struct drm_plane *plane;
+	int pixel_alpha_en;
+	int premulti_en, gpremulti_en = 0;
+	int mixer_id;
+	u32 offset;
+	bool bottom_layer_alpha_en = false;
+	u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE;
+
+	mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id);
+	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
+
+	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
+		struct vop2_win *win = to_vop2_win(plane);
+
+		if (plane->state->normalized_zpos == 0 &&
+		    !is_opaque(plane->state->alpha) &&
+		    !vop2_cluster_window(win)) {
+			/*
+			 * If bottom layer have global alpha effect [except cluster layer,
+			 * because cluster have deal with bottom layer global alpha value
+			 * at cluster mix], bottom layer mix need deal with global alpha.
+			 */
+			bottom_layer_alpha_en = true;
+			dst_global_alpha = plane->state->alpha;
+		}
+	}
+
+	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
+		struct vop2_win *win = to_vop2_win(plane);
+		int zpos = plane->state->normalized_zpos;
+
+		if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
+			premulti_en = 1;
+		else
+			premulti_en = 0;
+
+		plane = &win->base;
+		fb = plane->state->fb;
+
+		pixel_alpha_en = fb->format->has_alpha;
+
+		alpha_config.src_premulti_en = premulti_en;
+
+		if (bottom_layer_alpha_en && zpos == 1) {
+			gpremulti_en = premulti_en;
+			/* Cd = Cs + (1 - As) * Cd * Agd */
+			alpha_config.dst_premulti_en = false;
+			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
+			alpha_config.src_glb_alpha_value = plane->state->alpha;
+			alpha_config.dst_glb_alpha_value = dst_global_alpha;
+		} else if (vop2_cluster_window(win)) {
+			/* Mix output data only have pixel alpha */
+			alpha_config.dst_premulti_en = true;
+			alpha_config.src_pixel_alpha_en = true;
+			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+		} else {
+			/* Cd = Cs + (1 - As) * Cd */
+			alpha_config.dst_premulti_en = true;
+			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
+			alpha_config.src_glb_alpha_value = plane->state->alpha;
+			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+		}
+
+		vop2_parse_alpha(&alpha_config, &alpha);
+
+		offset = (mixer_id + zpos - 1) * 0x10;
+		vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset,
+			    alpha.src_color_ctrl.val);
+		vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset,
+			    alpha.dst_color_ctrl.val);
+		vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset,
+			    alpha.src_alpha_ctrl.val);
+		vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset,
+			    alpha.dst_alpha_ctrl.val);
+	}
+
+	if (vp->id == 0) {
+		if (bottom_layer_alpha_en) {
+			/* Transfer pixel alpha to hdr mix */
+			alpha_config.src_premulti_en = gpremulti_en;
+			alpha_config.dst_premulti_en = true;
+			alpha_config.src_pixel_alpha_en = true;
+			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
+			vop2_parse_alpha(&alpha_config, &alpha);
+
+			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL,
+				alpha.src_color_ctrl.val);
+			vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL,
+				alpha.dst_color_ctrl.val);
+			vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL,
+				alpha.src_alpha_ctrl.val);
+			vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL,
+				alpha.dst_alpha_ctrl.val);
+		} else {
+			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0);
+		}
+	}
+}
+
+static void vop2_setup_layer_mixer(struct vop2_video_port *vp)
+{
+	struct vop2 *vop2 = vp->vop2;
+	struct drm_plane *plane;
+	u32 layer_sel = 0;
+	u32 port_sel;
+	unsigned int nlayer, ofs;
+	struct drm_display_mode *adjusted_mode;
+	u16 hsync_len;
+	u16 hdisplay;
+	u32 bg_dly;
+	u32 pre_scan_dly;
+	int i;
+	struct vop2_video_port *vp0 = &vop2->vps[0];
+	struct vop2_video_port *vp1 = &vop2->vps[1];
+	struct vop2_video_port *vp2 = &vop2->vps[2];
+
+	adjusted_mode = &vp->crtc.state->adjusted_mode;
+	hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
+	hdisplay = adjusted_mode->crtc_hdisplay;
+
+	bg_dly = vp->data->pre_scan_max_dly[3];
+	vop2_writel(vop2, RK3568_VP_BG_MIX_CTRL(vp->id),
+			    FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly));
+
+	pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len;
+	vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly);
+
+	vop2_writel(vop2, RK3568_OVL_CTRL, 0);
+	port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
+	port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT;
+
+	if (vp0->nlayers)
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX,
+				     vp0->nlayers - 1);
+	else
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8);
+
+	if (vp1->nlayers)
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX,
+				     (vp0->nlayers + vp1->nlayers - 1));
+	else
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
+
+	if (vp2->nlayers)
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX,
+			(vp2->nlayers + vp1->nlayers + vp0->nlayers - 1));
+	else
+		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
+
+	layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
+
+	ofs = 0;
+	for (i = 0; i < vp->id; i++)
+		ofs += vop2->vps[i].nlayers;
+
+	nlayer = 0;
+	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
+		struct vop2_win *win = to_vop2_win(plane);
+
+		switch (win->data->phys_id) {
+		case ROCKCHIP_VOP2_CLUSTER0:
+			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id);
+			break;
+		case ROCKCHIP_VOP2_CLUSTER1:
+			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id);
+			break;
+		case ROCKCHIP_VOP2_ESMART0:
+			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id);
+			break;
+		case ROCKCHIP_VOP2_ESMART1:
+			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id);
+			break;
+		case ROCKCHIP_VOP2_SMART0:
+			port_sel &= ~RK3568_OVL_PORT_SEL__SMART0;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id);
+			break;
+		case ROCKCHIP_VOP2_SMART1:
+			port_sel &= ~RK3568_OVL_PORT_SEL__SMART1;
+			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id);
+			break;
+		}
+
+		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
+		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, win->data->layer_sel_id);
+		nlayer++;
+	}
+
+	/* configure unused layers to 0x5 (reserved) */
+	for (; nlayer < 3; nlayer++) {
+		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
+		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5);
+	}
+
+	vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel);
+	vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
+	vop2_writel(vop2, RK3568_OVL_CTRL, RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD);
+}
+
+static void vop2_setup_dly_for_windows(struct vop2 *vop2)
+{
+	struct vop2_win *win;
+	int i = 0;
+	u32 cdly = 0, sdly = 0;
+
+	for (i = 0; i < vop2->data->win_size; i++) {
+		u32 dly;
+
+		win = &vop2->win[i];
+		dly = win->delay;
+
+		switch (win->data->phys_id) {
+		case ROCKCHIP_VOP2_CLUSTER0:
+			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly);
+			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly);
+			break;
+		case ROCKCHIP_VOP2_CLUSTER1:
+			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly);
+			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly);
+			break;
+		case ROCKCHIP_VOP2_ESMART0:
+			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly);
+			break;
+		case ROCKCHIP_VOP2_ESMART1:
+			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly);
+			break;
+		case ROCKCHIP_VOP2_SMART0:
+			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly);
+			break;
+		case ROCKCHIP_VOP2_SMART1:
+			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly);
+			break;
+		}
+	}
+
+	vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly);
+	vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly);
+}
+
+static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
+				   struct drm_atomic_state *state)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct vop2 *vop2 = vp->vop2;
+	struct drm_plane *plane;
+
+	vp->win_mask = 0;
+
+	drm_atomic_crtc_for_each_plane(plane, crtc) {
+		struct vop2_win *win = to_vop2_win(plane);
+
+		win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT];
+
+		vp->win_mask |= BIT(win->data->phys_id);
+
+		if (vop2_cluster_window(win))
+			vop2_setup_cluster_alpha(vop2, win);
+	}
+
+	if (!vp->win_mask)
+		return;
+
+	vop2_setup_layer_mixer(vp);
+	vop2_setup_alpha(vp);
+	vop2_setup_dly_for_windows(vop2);
+}
+
+static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
+				   struct drm_atomic_state *state)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+
+	vop2_post_config(crtc);
+
+	vop2_cfg_done(vp);
+
+	spin_lock_irq(&crtc->dev->event_lock);
+
+	if (crtc->state->event) {
+		WARN_ON(drm_crtc_vblank_get(crtc));
+		vp->event = crtc->state->event;
+		crtc->state->event = NULL;
+	}
+
+	spin_unlock_irq(&crtc->dev->event_lock);
+}
+
+static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
+	.mode_fixup = vop2_crtc_mode_fixup,
+	.atomic_check = vop2_crtc_atomic_check,
+	.atomic_begin = vop2_crtc_atomic_begin,
+	.atomic_flush = vop2_crtc_atomic_flush,
+	.atomic_enable = vop2_crtc_atomic_enable,
+	.atomic_disable = vop2_crtc_atomic_disable,
+};
+
+static void vop2_crtc_reset(struct drm_crtc *crtc)
+{
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
+
+	if (crtc->state) {
+		__drm_atomic_helper_crtc_destroy_state(crtc->state);
+		kfree(vcstate);
+	}
+
+	vcstate = kzalloc(sizeof(*vcstate), GFP_KERNEL);
+	if (!vcstate)
+		return;
+
+	crtc->state = &vcstate->base;
+	crtc->state->crtc = crtc;
+}
+
+static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc)
+{
+	struct rockchip_crtc_state *vcstate, *old_vcstate;
+
+	old_vcstate = to_rockchip_crtc_state(crtc->state);
+
+	vcstate = kmemdup(old_vcstate, sizeof(*old_vcstate), GFP_KERNEL);
+	if (!vcstate)
+		return NULL;
+
+	__drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
+
+	return &vcstate->base;
+}
+
+static void vop2_crtc_destroy_state(struct drm_crtc *crtc,
+				    struct drm_crtc_state *state)
+{
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state);
+
+	__drm_atomic_helper_crtc_destroy_state(&vcstate->base);
+	kfree(vcstate);
+}
+
+static const struct drm_crtc_funcs vop2_crtc_funcs = {
+	.set_config = drm_atomic_helper_set_config,
+	.page_flip = drm_atomic_helper_page_flip,
+	.destroy = drm_crtc_cleanup,
+	.reset = vop2_crtc_reset,
+	.atomic_duplicate_state = vop2_crtc_duplicate_state,
+	.atomic_destroy_state = vop2_crtc_destroy_state,
+	.enable_vblank = vop2_crtc_enable_vblank,
+	.disable_vblank = vop2_crtc_disable_vblank,
+};
+
+static irqreturn_t vop2_isr(int irq, void *data)
+{
+	struct vop2 *vop2 = data;
+	const struct vop2_data *vop2_data = vop2->data;
+	u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM];
+	int ret = IRQ_NONE;
+	int i;
+
+	/*
+	 * The irq is shared with the iommu. If the runtime-pm state of the
+	 * vop2-device is disabled the irq has to be targeted at the iommu.
+	 */
+	if (!pm_runtime_get_if_in_use(vop2->dev))
+		return IRQ_NONE;
+
+	for (i = 0; i < vop2_data->nr_vps; i++) {
+		struct vop2_video_port *vp = &vop2->vps[i];
+		struct drm_crtc *crtc = &vp->crtc;
+		u32 irqs;
+
+		irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
+		vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
+
+		if (irqs & VP_INT_DSP_HOLD_VALID) {
+			complete(&vp->dsp_hold_completion);
+			ret = IRQ_HANDLED;
+		}
+
+		if (irqs & VP_INT_FS_FIELD) {
+			drm_crtc_handle_vblank(crtc);
+			spin_lock(&crtc->dev->event_lock);
+			if (vp->event) {
+				u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
+
+				if (!(val & BIT(vp->id))) {
+					drm_crtc_send_vblank_event(crtc, vp->event);
+					vp->event = NULL;
+					drm_crtc_vblank_put(crtc);
+				}
+			}
+			spin_unlock(&crtc->dev->event_lock);
+
+			ret = IRQ_HANDLED;
+		}
+
+		if (irqs & VP_INT_POST_BUF_EMPTY) {
+			drm_err_ratelimited(vop2->drm,
+					    "POST_BUF_EMPTY irq err at vp%d\n",
+					    vp->id);
+			ret = IRQ_HANDLED;
+		}
+	}
+
+	axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS);
+	vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]);
+	axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS);
+	vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]);
+
+	for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) {
+		if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) {
+			drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n");
+			ret = IRQ_HANDLED;
+		}
+	}
+
+	pm_runtime_put(vop2->dev);
+
+	return ret;
+}
+
+static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win,
+			   unsigned long possible_crtcs)
+{
+	const struct vop2_win_data *win_data = win->data;
+	unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+				  BIT(DRM_MODE_BLEND_PREMULTI) |
+				  BIT(DRM_MODE_BLEND_COVERAGE);
+	int ret;
+
+	ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs,
+				       &vop2_plane_funcs, win_data->formats,
+				       win_data->nformats,
+				       win_data->format_modifiers,
+				       win->type, win_data->name);
+	if (ret) {
+		drm_err(vop2->drm, "failed to initialize plane %d\n", ret);
+		return ret;
+	}
+
+	drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs);
+
+	if (win->data->supported_rotations)
+		drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0,
+						   DRM_MODE_ROTATE_0 |
+						   win->data->supported_rotations);
+	drm_plane_create_alpha_property(&win->base);
+	drm_plane_create_blend_mode_property(&win->base, blend_caps);
+	drm_plane_create_zpos_property(&win->base, win->win_id, 0,
+				       vop2->registered_num_wins - 1);
+
+	return 0;
+}
+
+static struct vop2_video_port *get_activated_vp(struct vop2 *vop2, int n)
+{
+	int i, id = 0;
+
+	for (i = 0; i < vop2->data->nr_vps; i++) {
+		struct vop2_video_port *vp = &vop2->vps[i];
+
+		if (!vp->crtc.port)
+			continue;
+
+		if (n == id)
+			return vp;
+		id++;
+	}
+
+	return NULL;
+}
+
+#define NR_LAYERS 6
+
+static int vop2_create_crtc(struct vop2 *vop2)
+{
+	const struct vop2_data *vop2_data = vop2->data;
+	struct drm_device *drm = vop2->drm;
+	struct device *dev = vop2->dev;
+	struct drm_plane *plane;
+	struct device_node *port;
+	struct vop2_video_port *vp;
+	u32 possible_crtcs;
+	int i, nvp, nvps = 0;
+	int ret;
+
+	for (i = 0; i < vop2_data->nr_vps; i++) {
+		const struct vop2_video_port_data *vp_data;
+		struct device_node *np;
+		char dclk_name[9];
+
+		vp_data = &vop2_data->vp[i];
+		vp = &vop2->vps[i];
+		vp->vop2 = vop2;
+		vp->id = vp_data->id;
+		vp->regs = vp_data->regs;
+		vp->data = vp_data;
+
+		snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
+		vp->dclk = devm_clk_get(vop2->dev, dclk_name);
+		if (IS_ERR(vp->dclk)) {
+			drm_err(vop2->drm, "failed to get %s\n", dclk_name);
+			return PTR_ERR(vp->dclk);
+		}
+
+		np = of_graph_get_remote_node(dev->of_node, i, -1);
+		if (!np) {
+			drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
+			continue;
+		}
+		of_node_put(np);
+
+		port = of_graph_get_port_by_id(dev->of_node, i);
+		if (!port) {
+			drm_err(vop2->drm, "no port node found for video_port%d\n", i);
+			return -ENOENT;
+		}
+
+		vp->crtc.port = port;
+		nvps++;
+	}
+
+	nvp = 0;
+	for (i = 0; i < vop2->registered_num_wins; i++) {
+		struct vop2_win *win = &vop2->win[i];
+
+		if (win->type == DRM_PLANE_TYPE_PRIMARY) {
+			vp = get_activated_vp(vop2, nvp);
+
+			if (vp) {
+				possible_crtcs = BIT(nvp);
+				vp->primary_plane = win;
+
+				nvp++;
+			} else {
+				/* change the unused primary window to overlay window */
+				win->type = DRM_PLANE_TYPE_OVERLAY;
+			}
+		}
+
+		if (win->type == DRM_PLANE_TYPE_OVERLAY)
+			possible_crtcs = (1 << vop2_data->nr_vps) - 1;
+
+		ret = vop2_plane_init(vop2, win, possible_crtcs);
+
+		if (ret) {
+			drm_err(vop2->drm, "failed to init plane %s: %d\n",
+				win->data->name, ret);
+			return ret;
+		}
+	}
+
+	for (i = 0; i < vop2_data->nr_vps; i++) {
+		vp = &vop2->vps[i];
+
+		if (!vp->crtc.port)
+			continue;
+
+		plane = &vp->primary_plane->base;
+
+		ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL,
+						&vop2_crtc_funcs,
+						"video_port%d", vp->id);
+		if (ret) {
+			drm_err(vop2->drm, "crtc init for video_port%d failed\n", i);
+			return ret;
+		}
+
+		drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs);
+
+		init_completion(&vp->dsp_hold_completion);
+	}
+
+	for (i = 0; i < vop2->data->nr_vps; i++) {
+		struct vop2_video_port *vp = &vop2->vps[i];
+
+		if (vp->crtc.port)
+			vp->nlayers = NR_LAYERS / nvps;
+	}
+
+	return 0;
+}
+
+static void vop2_destroy_crtc(struct drm_crtc *crtc)
+{
+	of_node_put(crtc->port);
+
+	/*
+	 * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane()
+	 * references the CRTC.
+	 */
+	drm_crtc_cleanup(crtc);
+}
+
+static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = {
+	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0),
+	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5),
+	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14),
+	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18),
+	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31),
+	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31),
+	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31),
+	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31),
+	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31),
+	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19),
+	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15),
+	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31),
+	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8),
+	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9),
+	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11),
+
+	/* Scale */
+	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15),
+	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31),
+	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15),
+	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13),
+	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3),
+	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28),
+	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29),
+
+	/* cluster regs */
+	[VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1),
+	[VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0),
+	[VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7),
+
+	/* afbc regs */
+	[VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6),
+	[VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9),
+	[VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10),
+	[VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4),
+	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7),
+	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8),
+	[VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31),
+	[VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31),
+	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15),
+	[VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31),
+	[VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31),
+	[VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31),
+	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31),
+	[VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0),
+	[VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1),
+	[VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2),
+	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3),
+	[VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff },
+	[VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff },
+	[VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff },
+	[VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
+	[VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
+	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff },
+	[VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff },
+};
+
+static int vop2_cluster_init(struct vop2_win *win)
+{
+	struct vop2 *vop2 = win->vop2;
+	struct reg_field *cluster_regs;
+	int ret, i;
+
+	cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs),
+			       GFP_KERNEL);
+	if (!cluster_regs)
+		return -ENOMEM;
+
+	for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++)
+		if (cluster_regs[i].reg != 0xffffffff)
+			cluster_regs[i].reg += win->offset;
+
+	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
+					    cluster_regs,
+					    ARRAY_SIZE(vop2_cluster_regs));
+
+	kfree(cluster_regs);
+
+	return ret;
+};
+
+static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = {
+	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0),
+	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5),
+	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12),
+	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14),
+	[VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16),
+	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31),
+	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31),
+	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28),
+	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31),
+	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31),
+	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17),
+	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15),
+	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31),
+	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0),
+	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1),
+	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3),
+	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31),
+	[VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29),
+	[VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31),
+
+	/* Scale */
+	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15),
+	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31),
+	[VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15),
+	[VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31),
+	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1),
+	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3),
+	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5),
+	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7),
+	[VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9),
+	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11),
+	[VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13),
+	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15),
+	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17),
+	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8),
+	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9),
+	[VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10),
+	[VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11),
+	[VOP2_WIN_XMIRROR] = { .reg = 0xffffffff },
+	[VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff },
+	[VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff },
+	[VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff },
+};
+
+static int vop2_esmart_init(struct vop2_win *win)
+{
+	struct vop2 *vop2 = win->vop2;
+	struct reg_field *esmart_regs;
+	int ret, i;
+
+	esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs),
+			       GFP_KERNEL);
+	if (!esmart_regs)
+		return -ENOMEM;
+
+	for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++)
+		if (esmart_regs[i].reg != 0xffffffff)
+			esmart_regs[i].reg += win->offset;
+
+	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
+					    esmart_regs,
+					    ARRAY_SIZE(vop2_esmart_regs));
+
+	kfree(esmart_regs);
+
+	return ret;
+};
+
+static int vop2_win_init(struct vop2 *vop2)
+{
+	const struct vop2_data *vop2_data = vop2->data;
+	struct vop2_win *win;
+	int i, ret;
+
+	for (i = 0; i < vop2_data->win_size; i++) {
+		const struct vop2_win_data *win_data = &vop2_data->win[i];
+
+		win = &vop2->win[i];
+		win->data = win_data;
+		win->type = win_data->type;
+		win->offset = win_data->base;
+		win->win_id = i;
+		win->vop2 = vop2;
+		if (vop2_cluster_window(win))
+			ret = vop2_cluster_init(win);
+		else
+			ret = vop2_esmart_init(win);
+		if (ret)
+			return ret;
+	}
+
+	vop2->registered_num_wins = vop2_data->win_size;
+
+	return 0;
+}
+
+/*
+ * The window registers are only updated when config done is written.
+ * Until that they read back the old value. As we read-modify-write
+ * these registers mark them as non-volatile. This makes sure we read
+ * the new values from the regmap register cache.
+ */
+static const struct regmap_range vop2_nonvolatile_range[] = {
+	regmap_reg_range(0x1000, 0x23ff),
+};
+
+static const struct regmap_access_table vop2_volatile_table = {
+	.no_ranges = vop2_nonvolatile_range,
+	.n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range),
+};
+
+static const struct regmap_config vop2_regmap_config = {
+	.reg_bits	= 32,
+	.val_bits	= 32,
+	.reg_stride	= 4,
+	.max_register	= 0x3000,
+	.name		= "vop2",
+	.volatile_table	= &vop2_volatile_table,
+	.cache_type	= REGCACHE_RBTREE,
+};
+
+static int vop2_bind(struct device *dev, struct device *master, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	const struct vop2_data *vop2_data;
+	struct drm_device *drm = data;
+	struct vop2 *vop2;
+	struct resource *res;
+	size_t alloc_size;
+	int ret;
+
+	vop2_data = of_device_get_match_data(dev);
+	if (!vop2_data)
+		return -ENODEV;
+
+	/* Allocate vop2 struct and its vop2_win array */
+	alloc_size = sizeof(*vop2) + sizeof(*vop2->win) * vop2_data->win_size;
+	vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
+	if (!vop2)
+		return -ENOMEM;
+
+	vop2->dev = dev;
+	vop2->data = vop2_data;
+	vop2->drm = drm;
+
+	dev_set_drvdata(dev, vop2);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+	if (!res) {
+		drm_err(vop2->drm, "failed to get vop2 register byname\n");
+		return -EINVAL;
+	}
+
+	vop2->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(vop2->regs))
+		return PTR_ERR(vop2->regs);
+	vop2->len = resource_size(res);
+
+	vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config);
+
+	ret = vop2_win_init(vop2);
+	if (ret)
+		return ret;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma_lut");
+	if (res) {
+		vop2->lut_regs = devm_ioremap_resource(dev, res);
+		if (IS_ERR(vop2->lut_regs))
+			return PTR_ERR(vop2->lut_regs);
+	}
+
+	vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
+
+	vop2->hclk = devm_clk_get(vop2->dev, "hclk");
+	if (IS_ERR(vop2->hclk)) {
+		drm_err(vop2->drm, "failed to get hclk source\n");
+		return PTR_ERR(vop2->hclk);
+	}
+
+	vop2->aclk = devm_clk_get(vop2->dev, "aclk");
+	if (IS_ERR(vop2->aclk)) {
+		drm_err(vop2->drm, "failed to get aclk source\n");
+		return PTR_ERR(vop2->aclk);
+	}
+
+	vop2->irq = platform_get_irq(pdev, 0);
+	if (vop2->irq < 0) {
+		drm_err(vop2->drm, "cannot find irq for vop2\n");
+		return vop2->irq;
+	}
+
+	mutex_init(&vop2->vop2_lock);
+
+	ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
+	if (ret)
+		return ret;
+
+	ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev);
+	if (ret) {
+		drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret);
+		return ret;
+	}
+
+	ret = vop2_create_crtc(vop2);
+	if (ret)
+		return ret;
+
+	pm_runtime_enable(&pdev->dev);
+
+	return 0;
+}
+
+static void vop2_unbind(struct device *dev, struct device *master, void *data)
+{
+	struct vop2 *vop2 = dev_get_drvdata(dev);
+	struct drm_device *drm = vop2->drm;
+	struct list_head *plane_list = &drm->mode_config.plane_list;
+	struct list_head *crtc_list = &drm->mode_config.crtc_list;
+	struct drm_crtc *crtc, *tmpc;
+	struct drm_plane *plane, *tmpp;
+
+	rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
+
+	pm_runtime_disable(dev);
+
+	list_for_each_entry_safe(plane, tmpp, plane_list, head)
+		drm_plane_cleanup(plane);
+
+	list_for_each_entry_safe(crtc, tmpc, crtc_list, head)
+		vop2_destroy_crtc(crtc);
+}
+
+const struct component_ops vop2_component_ops = {
+	.bind = vop2_bind,
+	.unbind = vop2_unbind,
+};
+EXPORT_SYMBOL_GPL(vop2_component_ops);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
new file mode 100644
index 0000000000000..c727093a06d68
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
@@ -0,0 +1,477 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ */
+
+#ifndef _ROCKCHIP_DRM_VOP2_H
+#define _ROCKCHIP_DRM_VOP2_H
+
+#include "rockchip_drm_vop.h"
+
+#include <linux/regmap.h>
+#include <drm/drm_modes.h>
+
+#define VOP_FEATURE_OUTPUT_10BIT        BIT(0)
+
+#define WIN_FEATURE_AFBDC		BIT(0)
+#define WIN_FEATURE_CLUSTER		BIT(1)
+
+/*
+ *  the delay number of a window in different mode.
+ */
+enum win_dly_mode {
+	VOP2_DLY_MODE_DEFAULT,   /**< default mode */
+	VOP2_DLY_MODE_HISO_S,    /** HDR in SDR out mode, as a SDR window */
+	VOP2_DLY_MODE_HIHO_H,    /** HDR in HDR out mode, as a HDR window */
+	VOP2_DLY_MODE_MAX,
+};
+
+struct vop_rect {
+	int width;
+	int height;
+};
+
+enum vop2_scale_up_mode {
+	VOP2_SCALE_UP_NRST_NBOR,
+	VOP2_SCALE_UP_BIL,
+	VOP2_SCALE_UP_BIC,
+};
+
+enum vop2_scale_down_mode {
+	VOP2_SCALE_DOWN_NRST_NBOR,
+	VOP2_SCALE_DOWN_BIL,
+	VOP2_SCALE_DOWN_AVG,
+};
+
+enum vop2_win_regs {
+	VOP2_WIN_ENABLE,
+	VOP2_WIN_FORMAT,
+	VOP2_WIN_CSC_MODE,
+	VOP2_WIN_XMIRROR,
+	VOP2_WIN_YMIRROR,
+	VOP2_WIN_RB_SWAP,
+	VOP2_WIN_UV_SWAP,
+	VOP2_WIN_ACT_INFO,
+	VOP2_WIN_DSP_INFO,
+	VOP2_WIN_DSP_ST,
+	VOP2_WIN_YRGB_MST,
+	VOP2_WIN_UV_MST,
+	VOP2_WIN_YRGB_VIR,
+	VOP2_WIN_UV_VIR,
+	VOP2_WIN_YUV_CLIP,
+	VOP2_WIN_Y2R_EN,
+	VOP2_WIN_R2Y_EN,
+	VOP2_WIN_COLOR_KEY,
+	VOP2_WIN_COLOR_KEY_EN,
+	VOP2_WIN_DITHER_UP,
+
+	/* scale regs */
+	VOP2_WIN_SCALE_YRGB_X,
+	VOP2_WIN_SCALE_YRGB_Y,
+	VOP2_WIN_SCALE_CBCR_X,
+	VOP2_WIN_SCALE_CBCR_Y,
+	VOP2_WIN_YRGB_HOR_SCL_MODE,
+	VOP2_WIN_YRGB_HSCL_FILTER_MODE,
+	VOP2_WIN_YRGB_VER_SCL_MODE,
+	VOP2_WIN_YRGB_VSCL_FILTER_MODE,
+	VOP2_WIN_CBCR_VER_SCL_MODE,
+	VOP2_WIN_CBCR_HSCL_FILTER_MODE,
+	VOP2_WIN_CBCR_HOR_SCL_MODE,
+	VOP2_WIN_CBCR_VSCL_FILTER_MODE,
+	VOP2_WIN_VSD_CBCR_GT2,
+	VOP2_WIN_VSD_CBCR_GT4,
+	VOP2_WIN_VSD_YRGB_GT2,
+	VOP2_WIN_VSD_YRGB_GT4,
+	VOP2_WIN_BIC_COE_SEL,
+
+	/* cluster regs */
+	VOP2_WIN_CLUSTER_ENABLE,
+	VOP2_WIN_AFBC_ENABLE,
+	VOP2_WIN_CLUSTER_LB_MODE,
+
+	/* afbc regs */
+	VOP2_WIN_AFBC_FORMAT,
+	VOP2_WIN_AFBC_RB_SWAP,
+	VOP2_WIN_AFBC_UV_SWAP,
+	VOP2_WIN_AFBC_AUTO_GATING_EN,
+	VOP2_WIN_AFBC_BLOCK_SPLIT_EN,
+	VOP2_WIN_AFBC_PIC_VIR_WIDTH,
+	VOP2_WIN_AFBC_TILE_NUM,
+	VOP2_WIN_AFBC_PIC_OFFSET,
+	VOP2_WIN_AFBC_PIC_SIZE,
+	VOP2_WIN_AFBC_DSP_OFFSET,
+	VOP2_WIN_AFBC_TRANSFORM_OFFSET,
+	VOP2_WIN_AFBC_HDR_PTR,
+	VOP2_WIN_AFBC_HALF_BLOCK_EN,
+	VOP2_WIN_AFBC_ROTATE_270,
+	VOP2_WIN_AFBC_ROTATE_90,
+	VOP2_WIN_MAX_REG,
+};
+
+struct vop2_win_data {
+	const char *name;
+	unsigned int phys_id;
+
+	u32 base;
+	enum drm_plane_type type;
+
+	u32 nformats;
+	const u32 *formats;
+	const uint64_t *format_modifiers;
+	const unsigned int supported_rotations;
+
+	/**
+	 * @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2
+	 */
+	unsigned int layer_sel_id;
+	uint64_t feature;
+
+	unsigned int max_upscale_factor;
+	unsigned int max_downscale_factor;
+	const u8 dly[VOP2_DLY_MODE_MAX];
+};
+
+struct vop2_video_port_data {
+	unsigned int id;
+	u32 feature;
+	u16 gamma_lut_len;
+	u16 cubic_lut_len;
+	struct vop_rect max_output;
+	const u8 pre_scan_max_dly[4];
+	const struct vop2_video_port_regs *regs;
+	unsigned int offset;
+};
+
+struct vop2_data {
+	u8 nr_vps;
+	const struct vop2_ctrl *ctrl;
+	const struct vop2_win_data *win;
+	const struct vop2_video_port_data *vp;
+	const struct vop_csc_table *csc_table;
+	struct vop_rect max_input;
+	struct vop_rect max_output;
+
+	unsigned int win_size;
+	unsigned int soc_id;
+};
+
+/* interrupt define */
+#define FS_NEW_INTR			BIT(4)
+#define ADDR_SAME_INTR			BIT(5)
+#define LINE_FLAG1_INTR			BIT(6)
+#define WIN0_EMPTY_INTR			BIT(7)
+#define WIN1_EMPTY_INTR			BIT(8)
+#define WIN2_EMPTY_INTR			BIT(9)
+#define WIN3_EMPTY_INTR			BIT(10)
+#define HWC_EMPTY_INTR			BIT(11)
+#define POST_BUF_EMPTY_INTR		BIT(12)
+#define PWM_GEN_INTR			BIT(13)
+#define DMA_FINISH_INTR			BIT(14)
+#define FS_FIELD_INTR			BIT(15)
+#define FE_INTR				BIT(16)
+#define WB_UV_FIFO_FULL_INTR		BIT(17)
+#define WB_YRGB_FIFO_FULL_INTR		BIT(18)
+#define WB_COMPLETE_INTR		BIT(19)
+
+/*
+ * display output interface supported by rockchip lcdc
+ */
+#define ROCKCHIP_OUT_MODE_P888		0
+#define ROCKCHIP_OUT_MODE_BT1120	0
+#define ROCKCHIP_OUT_MODE_P666		1
+#define ROCKCHIP_OUT_MODE_P565		2
+#define ROCKCHIP_OUT_MODE_BT656		5
+#define ROCKCHIP_OUT_MODE_S888		8
+#define ROCKCHIP_OUT_MODE_S888_DUMMY	12
+#define ROCKCHIP_OUT_MODE_YUV420	14
+/* for use special outface */
+#define ROCKCHIP_OUT_MODE_AAAA		15
+
+enum vop_csc_format {
+	CSC_BT601L,
+	CSC_BT709L,
+	CSC_BT601F,
+	CSC_BT2020,
+};
+
+enum src_factor_mode {
+	SRC_FAC_ALPHA_ZERO,
+	SRC_FAC_ALPHA_ONE,
+	SRC_FAC_ALPHA_DST,
+	SRC_FAC_ALPHA_DST_INVERSE,
+	SRC_FAC_ALPHA_SRC,
+	SRC_FAC_ALPHA_SRC_GLOBAL,
+};
+
+enum dst_factor_mode {
+	DST_FAC_ALPHA_ZERO,
+	DST_FAC_ALPHA_ONE,
+	DST_FAC_ALPHA_SRC,
+	DST_FAC_ALPHA_SRC_INVERSE,
+	DST_FAC_ALPHA_DST,
+	DST_FAC_ALPHA_DST_GLOBAL,
+};
+
+#define RK3568_GRF_VO_CON1			0x0364
+/* System registers definition */
+#define RK3568_REG_CFG_DONE			0x000
+#define RK3568_VERSION_INFO			0x004
+#define RK3568_SYS_AUTO_GATING_CTRL		0x008
+#define RK3568_SYS_AXI_LUT_CTRL			0x024
+#define RK3568_DSP_IF_EN			0x028
+#define RK3568_DSP_IF_CTRL			0x02c
+#define RK3568_DSP_IF_POL			0x030
+#define RK3568_WB_CTRL				0x40
+#define RK3568_WB_XSCAL_FACTOR			0x44
+#define RK3568_WB_YRGB_MST			0x48
+#define RK3568_WB_CBR_MST			0x4C
+#define RK3568_OTP_WIN_EN			0x050
+#define RK3568_LUT_PORT_SEL			0x058
+#define RK3568_SYS_STATUS0			0x060
+#define RK3568_VP_LINE_FLAG(vp)			(0x70 + (vp) * 0x4)
+#define RK3568_SYS0_INT_EN			0x80
+#define RK3568_SYS0_INT_CLR			0x84
+#define RK3568_SYS0_INT_STATUS			0x88
+#define RK3568_SYS1_INT_EN			0x90
+#define RK3568_SYS1_INT_CLR			0x94
+#define RK3568_SYS1_INT_STATUS			0x98
+#define RK3568_VP_INT_EN(vp)			(0xA0 + (vp) * 0x10)
+#define RK3568_VP_INT_CLR(vp)			(0xA4 + (vp) * 0x10)
+#define RK3568_VP_INT_STATUS(vp)		(0xA8 + (vp) * 0x10)
+#define RK3568_VP_INT_RAW_STATUS(vp)		(0xAC + (vp) * 0x10)
+
+/* Video Port registers definition */
+#define RK3568_VP_DSP_CTRL			0x00
+#define RK3568_VP_MIPI_CTRL			0x04
+#define RK3568_VP_COLOR_BAR_CTRL		0x08
+#define RK3568_VP_3D_LUT_CTRL			0x10
+#define RK3568_VP_3D_LUT_MST			0x20
+#define RK3568_VP_DSP_BG			0x2C
+#define RK3568_VP_PRE_SCAN_HTIMING		0x30
+#define RK3568_VP_POST_DSP_HACT_INFO		0x34
+#define RK3568_VP_POST_DSP_VACT_INFO		0x38
+#define RK3568_VP_POST_SCL_FACTOR_YRGB		0x3C
+#define RK3568_VP_POST_SCL_CTRL			0x40
+#define RK3568_VP_POST_DSP_VACT_INFO_F1		0x44
+#define RK3568_VP_DSP_HTOTAL_HS_END		0x48
+#define RK3568_VP_DSP_HACT_ST_END		0x4C
+#define RK3568_VP_DSP_VTOTAL_VS_END		0x50
+#define RK3568_VP_DSP_VACT_ST_END		0x54
+#define RK3568_VP_DSP_VS_ST_END_F1		0x58
+#define RK3568_VP_DSP_VACT_ST_END_F1		0x5C
+#define RK3568_VP_BCSH_CTRL			0x60
+#define RK3568_VP_BCSH_BCS			0x64
+#define RK3568_VP_BCSH_H			0x68
+#define RK3568_VP_BCSH_COLOR_BAR		0x6C
+
+/* Overlay registers definition    */
+#define RK3568_OVL_CTRL				0x600
+#define RK3568_OVL_LAYER_SEL			0x604
+#define RK3568_OVL_PORT_SEL			0x608
+#define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL	0x610
+#define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL	0x614
+#define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL	0x618
+#define RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL	0x61C
+#define RK3568_MIX0_SRC_COLOR_CTRL		0x650
+#define RK3568_MIX0_DST_COLOR_CTRL		0x654
+#define RK3568_MIX0_SRC_ALPHA_CTRL		0x658
+#define RK3568_MIX0_DST_ALPHA_CTRL		0x65C
+#define RK3568_HDR0_SRC_COLOR_CTRL		0x6C0
+#define RK3568_HDR0_DST_COLOR_CTRL		0x6C4
+#define RK3568_HDR0_SRC_ALPHA_CTRL		0x6C8
+#define RK3568_HDR0_DST_ALPHA_CTRL		0x6CC
+#define RK3568_VP_BG_MIX_CTRL(vp)		(0x6E0 + (vp) * 4)
+#define RK3568_CLUSTER_DLY_NUM			0x6F0
+#define RK3568_SMART_DLY_NUM			0x6F8
+
+/* Cluster register definition, offset relative to window base */
+#define RK3568_CLUSTER_WIN_CTRL0		0x00
+#define RK3568_CLUSTER_WIN_CTRL1		0x04
+#define RK3568_CLUSTER_WIN_YRGB_MST		0x10
+#define RK3568_CLUSTER_WIN_CBR_MST		0x14
+#define RK3568_CLUSTER_WIN_VIR			0x18
+#define RK3568_CLUSTER_WIN_ACT_INFO		0x20
+#define RK3568_CLUSTER_WIN_DSP_INFO		0x24
+#define RK3568_CLUSTER_WIN_DSP_ST		0x28
+#define RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB	0x30
+#define RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET	0x3C
+#define RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL	0x50
+#define RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE	0x54
+#define RK3568_CLUSTER_WIN_AFBCD_HDR_PTR	0x58
+#define RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH	0x5C
+#define RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE	0x60
+#define RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET	0x64
+#define RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET	0x68
+#define RK3568_CLUSTER_WIN_AFBCD_CTRL		0x6C
+
+#define RK3568_CLUSTER_CTRL			0x100
+
+/* (E)smart register definition, offset relative to window base */
+#define RK3568_SMART_CTRL0			0x00
+#define RK3568_SMART_CTRL1			0x04
+#define RK3568_SMART_REGION0_CTRL		0x10
+#define RK3568_SMART_REGION0_YRGB_MST		0x14
+#define RK3568_SMART_REGION0_CBR_MST		0x18
+#define RK3568_SMART_REGION0_VIR		0x1C
+#define RK3568_SMART_REGION0_ACT_INFO		0x20
+#define RK3568_SMART_REGION0_DSP_INFO		0x24
+#define RK3568_SMART_REGION0_DSP_ST		0x28
+#define RK3568_SMART_REGION0_SCL_CTRL		0x30
+#define RK3568_SMART_REGION0_SCL_FACTOR_YRGB	0x34
+#define RK3568_SMART_REGION0_SCL_FACTOR_CBR	0x38
+#define RK3568_SMART_REGION0_SCL_OFFSET		0x3C
+#define RK3568_SMART_REGION1_CTRL		0x40
+#define RK3568_SMART_REGION1_YRGB_MST		0x44
+#define RK3568_SMART_REGION1_CBR_MST		0x48
+#define RK3568_SMART_REGION1_VIR		0x4C
+#define RK3568_SMART_REGION1_ACT_INFO		0x50
+#define RK3568_SMART_REGION1_DSP_INFO		0x54
+#define RK3568_SMART_REGION1_DSP_ST		0x58
+#define RK3568_SMART_REGION1_SCL_CTRL		0x60
+#define RK3568_SMART_REGION1_SCL_FACTOR_YRGB	0x64
+#define RK3568_SMART_REGION1_SCL_FACTOR_CBR	0x68
+#define RK3568_SMART_REGION1_SCL_OFFSET		0x6C
+#define RK3568_SMART_REGION2_CTRL		0x70
+#define RK3568_SMART_REGION2_YRGB_MST		0x74
+#define RK3568_SMART_REGION2_CBR_MST		0x78
+#define RK3568_SMART_REGION2_VIR		0x7C
+#define RK3568_SMART_REGION2_ACT_INFO		0x80
+#define RK3568_SMART_REGION2_DSP_INFO		0x84
+#define RK3568_SMART_REGION2_DSP_ST		0x88
+#define RK3568_SMART_REGION2_SCL_CTRL		0x90
+#define RK3568_SMART_REGION2_SCL_FACTOR_YRGB	0x94
+#define RK3568_SMART_REGION2_SCL_FACTOR_CBR	0x98
+#define RK3568_SMART_REGION2_SCL_OFFSET		0x9C
+#define RK3568_SMART_REGION3_CTRL		0xA0
+#define RK3568_SMART_REGION3_YRGB_MST		0xA4
+#define RK3568_SMART_REGION3_CBR_MST		0xA8
+#define RK3568_SMART_REGION3_VIR		0xAC
+#define RK3568_SMART_REGION3_ACT_INFO		0xB0
+#define RK3568_SMART_REGION3_DSP_INFO		0xB4
+#define RK3568_SMART_REGION3_DSP_ST		0xB8
+#define RK3568_SMART_REGION3_SCL_CTRL		0xC0
+#define RK3568_SMART_REGION3_SCL_FACTOR_YRGB	0xC4
+#define RK3568_SMART_REGION3_SCL_FACTOR_CBR	0xC8
+#define RK3568_SMART_REGION3_SCL_OFFSET		0xCC
+#define RK3568_SMART_COLOR_KEY_CTRL		0xD0
+
+/* HDR register definition */
+#define RK3568_HDR_LUT_CTRL			0x2000
+#define RK3568_HDR_LUT_MST			0x2004
+#define RK3568_SDR2HDR_CTRL			0x2010
+#define RK3568_HDR2SDR_CTRL			0x2020
+#define RK3568_HDR2SDR_SRC_RANGE		0x2024
+#define RK3568_HDR2SDR_NORMFACEETF		0x2028
+#define RK3568_HDR2SDR_DST_RANGE		0x202C
+#define RK3568_HDR2SDR_NORMFACCGAMMA		0x2030
+#define RK3568_HDR_EETF_OETF_Y0			0x203C
+#define RK3568_HDR_SAT_Y0			0x20C0
+#define RK3568_HDR_EOTF_OETF_Y0			0x20F0
+#define RK3568_HDR_OETF_DX_POW1			0x2200
+#define RK3568_HDR_OETF_XN1			0x2300
+
+#define RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN		BIT(15)
+
+#define RK3568_VP_DSP_CTRL__STANDBY			BIT(31)
+#define RK3568_VP_DSP_CTRL__DITHER_DOWN_MODE		BIT(20)
+#define RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL		GENMASK(19, 18)
+#define RK3568_VP_DSP_CTRL__DITHER_DOWN_EN		BIT(17)
+#define RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN		BIT(16)
+#define RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y		BIT(15)
+#define RK3568_VP_DSP_CTRL__DSP_RB_SWAP			BIT(9)
+#define RK3568_VP_DSP_CTRL__DSP_INTERLACE		BIT(7)
+#define RK3568_VP_DSP_CTRL__DSP_FILED_POL		BIT(6)
+#define RK3568_VP_DSP_CTRL__P2I_EN			BIT(5)
+#define RK3568_VP_DSP_CTRL__CORE_DCLK_DIV		BIT(4)
+#define RK3568_VP_DSP_CTRL__OUT_MODE			GENMASK(3, 0)
+
+#define RK3568_VP_POST_SCL_CTRL__VSCALEDOWN		BIT(1)
+#define RK3568_VP_POST_SCL_CTRL__HSCALEDOWN		BIT(0)
+
+#define RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX		GENMASK(26, 25)
+#define RK3568_SYS_DSP_INFACE_EN_LVDS1			BIT(24)
+#define RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX		GENMASK(22, 21)
+#define RK3568_SYS_DSP_INFACE_EN_MIPI1			BIT(20)
+#define RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX		GENMASK(19, 18)
+#define RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX		GENMASK(17, 16)
+#define RK3568_SYS_DSP_INFACE_EN_EDP_MUX		GENMASK(15, 14)
+#define RK3568_SYS_DSP_INFACE_EN_HDMI_MUX		GENMASK(11, 10)
+#define RK3568_SYS_DSP_INFACE_EN_RGB_MUX		GENMASK(9, 8)
+#define RK3568_SYS_DSP_INFACE_EN_LVDS0			BIT(5)
+#define RK3568_SYS_DSP_INFACE_EN_MIPI0			BIT(4)
+#define RK3568_SYS_DSP_INFACE_EN_EDP			BIT(3)
+#define RK3568_SYS_DSP_INFACE_EN_HDMI			BIT(1)
+#define RK3568_SYS_DSP_INFACE_EN_RGB			BIT(0)
+
+#define RK3568_DSP_IF_POL__MIPI_PIN_POL			GENMASK(19, 16)
+#define RK3568_DSP_IF_POL__EDP_PIN_POL			GENMASK(15, 12)
+#define RK3568_DSP_IF_POL__HDMI_PIN_POL			GENMASK(7, 4)
+#define RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL		GENMASK(3, 0)
+
+#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2_PHASE_LOCK	BIT(5)
+#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2			BIT(4)
+
+#define RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN	BIT(31)
+
+#define RK3568_DSP_IF_POL__CFG_DONE_IMD			BIT(28)
+
+#define VOP2_SYS_AXI_BUS_NUM				2
+
+#define VOP2_CLUSTER_YUV444_10				0x12
+
+#define VOP2_COLOR_KEY_MASK				BIT(31)
+
+#define RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD		BIT(28)
+
+#define RK3568_VP_BG_MIX_CTRL__BG_DLY			GENMASK(31, 24)
+
+#define RK3568_OVL_PORT_SEL__SEL_PORT			GENMASK(31, 16)
+#define RK3568_OVL_PORT_SEL__SMART1			GENMASK(31, 30)
+#define RK3568_OVL_PORT_SEL__SMART0			GENMASK(29, 28)
+#define RK3568_OVL_PORT_SEL__ESMART1			GENMASK(27, 26)
+#define RK3568_OVL_PORT_SEL__ESMART0			GENMASK(25, 24)
+#define RK3568_OVL_PORT_SEL__CLUSTER1			GENMASK(19, 18)
+#define RK3568_OVL_PORT_SEL__CLUSTER0			GENMASK(17, 16)
+#define RK3568_OVL_PORT_SET__PORT2_MUX			GENMASK(11, 8)
+#define RK3568_OVL_PORT_SET__PORT1_MUX			GENMASK(7, 4)
+#define RK3568_OVL_PORT_SET__PORT0_MUX			GENMASK(3, 0)
+#define RK3568_OVL_LAYER_SEL__LAYER(layer, x)		((x) << ((layer) * 4))
+
+#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_1		GENMASK(31, 24)
+#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_0		GENMASK(23, 16)
+#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_1		GENMASK(15, 8)
+#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_0		GENMASK(7, 0)
+
+#define RK3568_SMART_DLY_NUM__SMART1			GENMASK(31, 24)
+#define RK3568_SMART_DLY_NUM__SMART0			GENMASK(23, 16)
+#define RK3568_SMART_DLY_NUM__ESMART1			GENMASK(15, 8)
+#define RK3568_SMART_DLY_NUM__ESMART0			GENMASK(7, 0)
+
+#define VP_INT_DSP_HOLD_VALID	BIT(6)
+#define VP_INT_FS_FIELD		BIT(5)
+#define VP_INT_POST_BUF_EMPTY	BIT(4)
+#define VP_INT_LINE_FLAG1	BIT(3)
+#define VP_INT_LINE_FLAG0	BIT(2)
+#define VOP2_INT_BUS_ERRPR	BIT(1)
+#define VP_INT_FS		BIT(0)
+
+#define POLFLAG_DCLK_INV	BIT(3)
+
+enum vop2_layer_phy_id {
+	ROCKCHIP_VOP2_CLUSTER0 = 0,
+	ROCKCHIP_VOP2_CLUSTER1,
+	ROCKCHIP_VOP2_ESMART0,
+	ROCKCHIP_VOP2_ESMART1,
+	ROCKCHIP_VOP2_SMART0,
+	ROCKCHIP_VOP2_SMART1,
+	ROCKCHIP_VOP2_CLUSTER2,
+	ROCKCHIP_VOP2_CLUSTER3,
+	ROCKCHIP_VOP2_ESMART2,
+	ROCKCHIP_VOP2_ESMART3,
+	ROCKCHIP_VOP2_PHY_ID_INVALID = -1,
+};
+
+extern const struct component_ops vop2_component_ops;
+
+#endif /* _ROCKCHIP_DRM_VOP2_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
new file mode 100644
index 0000000000000..9bf0637bf8e26
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) Rockchip Electronics Co.Ltd
+ * Author: Andy Yan <andy.yan@rock-chips.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/component.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_print.h>
+
+#include "rockchip_drm_vop2.h"
+
+static const uint32_t formats_win_full_10bit[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV16,
+	DRM_FORMAT_NV24,
+};
+
+static const uint32_t formats_win_full_10bit_yuyv[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV16,
+	DRM_FORMAT_NV24,
+	DRM_FORMAT_YVYU,
+	DRM_FORMAT_VYUY,
+};
+
+static const uint32_t formats_win_lite[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+};
+
+static const uint64_t format_modifiers[] = {
+	DRM_FORMAT_MOD_LINEAR,
+	DRM_FORMAT_MOD_INVALID,
+};
+
+static const uint64_t format_modifiers_afbc[] = {
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_SPARSE),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_CBR),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR |
+				AFBC_FORMAT_MOD_SPARSE),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_CBR |
+				AFBC_FORMAT_MOD_SPARSE),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR |
+				AFBC_FORMAT_MOD_CBR),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR |
+				AFBC_FORMAT_MOD_CBR |
+				AFBC_FORMAT_MOD_SPARSE),
+
+	/* SPLIT mandates SPARSE, RGB modes mandates YTR */
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR |
+				AFBC_FORMAT_MOD_SPARSE |
+				AFBC_FORMAT_MOD_SPLIT),
+	DRM_FORMAT_MOD_INVALID,
+};
+
+static const struct vop2_video_port_data rk3568_vop_video_ports[] = {
+	{
+		.id = 0,
+		.feature = VOP_FEATURE_OUTPUT_10BIT,
+		.gamma_lut_len = 1024,
+		.cubic_lut_len = 9 * 9 * 9,
+		.max_output = { 4096, 2304 },
+		.pre_scan_max_dly = { 69, 53, 53, 42 },
+		.offset = 0xc00,
+	}, {
+		.id = 1,
+		.gamma_lut_len = 1024,
+		.max_output = { 2048, 1536 },
+		.pre_scan_max_dly = { 40, 40, 40, 40 },
+		.offset = 0xd00,
+	}, {
+		.id = 2,
+		.gamma_lut_len = 1024,
+		.max_output = { 1920, 1080 },
+		.pre_scan_max_dly = { 40, 40, 40, 40 },
+		.offset = 0xe00,
+	},
+};
+
+/*
+ * rk3568 vop with 2 cluster, 2 esmart win, 2 smart win.
+ * Every cluster can work as 4K win or split into two win.
+ * All win in cluster support AFBCD.
+ *
+ * Every esmart win and smart win support 4 Multi-region.
+ *
+ * Scale filter mode:
+ *
+ * * Cluster:  bicubic for horizontal scale up, others use bilinear
+ * * ESmart:
+ *    * nearest-neighbor/bilinear/bicubic for scale up
+ *    * nearest-neighbor/bilinear/average for scale down
+ *
+ *
+ * @TODO describe the wind like cpu-map dt nodes;
+ */
+static const struct vop2_win_data rk3568_vop_win_data[] = {
+	{
+		.name = "Smart0-win0",
+		.phys_id = ROCKCHIP_VOP2_SMART0,
+		.base = 0x1c00,
+		.formats = formats_win_lite,
+		.nformats = ARRAY_SIZE(formats_win_lite),
+		.format_modifiers = format_modifiers,
+		.layer_sel_id = 3,
+		.supported_rotations = DRM_MODE_REFLECT_Y,
+		.type = DRM_PLANE_TYPE_PRIMARY,
+		.max_upscale_factor = 8,
+		.max_downscale_factor = 8,
+		.dly = { 20, 47, 41 },
+	}, {
+		.name = "Smart1-win0",
+		.phys_id = ROCKCHIP_VOP2_SMART1,
+		.formats = formats_win_lite,
+		.nformats = ARRAY_SIZE(formats_win_lite),
+		.format_modifiers = format_modifiers,
+		.base = 0x1e00,
+		.layer_sel_id = 7,
+		.supported_rotations = DRM_MODE_REFLECT_Y,
+		.type = DRM_PLANE_TYPE_PRIMARY,
+		.max_upscale_factor = 8,
+		.max_downscale_factor = 8,
+		.dly = { 20, 47, 41 },
+	}, {
+		.name = "Esmart1-win0",
+		.phys_id = ROCKCHIP_VOP2_ESMART1,
+		.formats = formats_win_full_10bit_yuyv,
+		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
+		.format_modifiers = format_modifiers,
+		.base = 0x1a00,
+		.layer_sel_id = 6,
+		.supported_rotations = DRM_MODE_REFLECT_Y,
+		.type = DRM_PLANE_TYPE_PRIMARY,
+		.max_upscale_factor = 8,
+		.max_downscale_factor = 8,
+		.dly = { 20, 47, 41 },
+	}, {
+		.name = "Esmart0-win0",
+		.phys_id = ROCKCHIP_VOP2_ESMART0,
+		.formats = formats_win_full_10bit_yuyv,
+		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
+		.format_modifiers = format_modifiers,
+		.base = 0x1800,
+		.layer_sel_id = 2,
+		.supported_rotations = DRM_MODE_REFLECT_Y,
+		.type = DRM_PLANE_TYPE_OVERLAY,
+		.max_upscale_factor = 8,
+		.max_downscale_factor = 8,
+		.dly = { 20, 47, 41 },
+	}, {
+		.name = "Cluster0-win0",
+		.phys_id = ROCKCHIP_VOP2_CLUSTER0,
+		.base = 0x1000,
+		.formats = formats_win_full_10bit,
+		.nformats = ARRAY_SIZE(formats_win_full_10bit),
+		.format_modifiers = format_modifiers_afbc,
+		.layer_sel_id = 0,
+		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
+					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
+		.max_upscale_factor = 4,
+		.max_downscale_factor = 4,
+		.dly = { 0, 27, 21 },
+		.type = DRM_PLANE_TYPE_OVERLAY,
+		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
+	}, {
+		.name = "Cluster1-win0",
+		.phys_id = ROCKCHIP_VOP2_CLUSTER1,
+		.base = 0x1200,
+		.formats = formats_win_full_10bit,
+		.nformats = ARRAY_SIZE(formats_win_full_10bit),
+		.format_modifiers = format_modifiers_afbc,
+		.layer_sel_id = 1,
+		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
+					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
+		.type = DRM_PLANE_TYPE_OVERLAY,
+		.max_upscale_factor = 4,
+		.max_downscale_factor = 4,
+		.dly = { 0, 27, 21 },
+		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
+	},
+};
+
+static const struct vop2_data rk3566_vop = {
+	.nr_vps = 3,
+	.max_input = { 4096, 2304 },
+	.max_output = { 4096, 2304 },
+	.vp = rk3568_vop_video_ports,
+	.win = rk3568_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
+	.soc_id = 3566,
+};
+
+static const struct vop2_data rk3568_vop = {
+	.nr_vps = 3,
+	.max_input = { 4096, 2304 },
+	.max_output = { 4096, 2304 },
+	.vp = rk3568_vop_video_ports,
+	.win = rk3568_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
+	.soc_id = 3568,
+};
+
+static const struct of_device_id vop2_dt_match[] = {
+	{
+		.compatible = "rockchip,rk3566-vop",
+		.data = &rk3566_vop,
+	}, {
+		.compatible = "rockchip,rk3568-vop",
+		.data = &rk3568_vop,
+	}, {
+	},
+};
+MODULE_DEVICE_TABLE(of, vop2_dt_match);
+
+static int vop2_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	return component_add(dev, &vop2_component_ops);
+}
+
+static int vop2_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &vop2_component_ops);
+
+	return 0;
+}
+
+struct platform_driver vop2_platform_driver = {
+	.probe = vop2_probe,
+	.remove = vop2_remove,
+	.driver = {
+		.name = "rockchip-vop2",
+		.of_match_table = of_match_ptr(vop2_dt_match),
+	},
+};
-- 
2.30.2


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

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

* [PATCH v6 22/23] dt-bindings: display: rockchip: Add binding for VOP2
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

The VOP2 is found on newer Rockchip SoCs like the rk3568 or the rk3566.
The binding differs slightly from the existing VOP binding, so add a new
binding file for it.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Reviewed-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v5:
    - Add Robs Reviewed-by:
    
    Changes since v4:
    - Fix clk names in example
    - Drop unnecessary assigned-clocks, assigned-clock-rates and assigned-clock-parents
    
    Changes since v3:
    - drop redundant _vop suffix from clock names
    
    Changes since v3:
    - new patch

 .../display/rockchip/rockchip-vop2.yaml       | 140 ++++++++++++++++++
 1 file changed, 140 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
new file mode 100644
index 0000000000000..655d9b327f7d3
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
@@ -0,0 +1,140 @@
+# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/rockchip/rockchip-vop2.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip SoC display controller (VOP2)
+
+description:
+  VOP2 (Video Output Processor v2) is the display controller for the Rockchip
+  series of SoCs which transfers the image data from a video memory
+  buffer to an external LCD interface.
+
+maintainers:
+  - Sandy Huang <hjc@rock-chips.com>
+  - Heiko Stuebner <heiko@sntech.de>
+
+properties:
+  compatible:
+    enum:
+      - rockchip,rk3566-vop
+      - rockchip,rk3568-vop
+
+  reg:
+    minItems: 1
+    items:
+      - description:
+          Must contain one entry corresponding to the base address and length
+          of the register space.
+      - description:
+          Can optionally contain a second entry corresponding to
+          the CRTC gamma LUT address.
+
+  interrupts:
+    maxItems: 1
+    description:
+      The VOP interrupt is shared by several interrupt sources, such as
+      frame start (VSYNC), line flag and other status interrupts.
+
+  clocks:
+    items:
+      - description: Clock for ddr buffer transfer.
+      - description: Clock for the ahb bus to R/W the phy regs.
+      - description: Pixel clock for video port 0.
+      - description: Pixel clock for video port 1.
+      - description: Pixel clock for video port 2.
+
+  clock-names:
+    items:
+      - const: aclk
+      - const: hclk
+      - const: dclk_vp0
+      - const: dclk_vp1
+      - const: dclk_vp2
+
+  rockchip,grf:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Phandle to GRF regs used for misc control
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          Output endpoint of VP0
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          Output endpoint of VP1
+
+      port@2:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          Output endpoint of VP2
+
+  iommus:
+    maxItems: 1
+
+  power-domains:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+        #include <dt-bindings/clock/rk3568-cru.h>
+        #include <dt-bindings/interrupt-controller/arm-gic.h>
+        #include <dt-bindings/power/rk3568-power.h>
+        bus {
+            #address-cells = <2>;
+            #size-cells = <2>;
+            vop: vop@fe040000 {
+                compatible = "rockchip,rk3568-vop";
+                reg = <0x0 0xfe040000 0x0 0x3000>, <0x0 0xfe044000 0x0 0x1000>;
+                interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+                clocks = <&cru ACLK_VOP>,
+                         <&cru HCLK_VOP>,
+                         <&cru DCLK_VOP0>,
+                         <&cru DCLK_VOP1>,
+                         <&cru DCLK_VOP2>;
+                clock-names = "aclk",
+                              "hclk",
+                              "dclk_vp0",
+                              "dclk_vp1",
+                              "dclk_vp2";
+                power-domains = <&power RK3568_PD_VO>;
+                iommus = <&vop_mmu>;
+                vop_out: ports {
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+                    vp0: port@0 {
+                        reg = <0>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+                    };
+                    vp1: port@1 {
+                        reg = <1>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+                    };
+                    vp2: port@2 {
+                        reg = <2>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+                    };
+                };
+            };
+        };
-- 
2.30.2


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

* [PATCH v6 22/23] dt-bindings: display: rockchip: Add binding for VOP2
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

The VOP2 is found on newer Rockchip SoCs like the rk3568 or the rk3566.
The binding differs slightly from the existing VOP binding, so add a new
binding file for it.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Reviewed-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v5:
    - Add Robs Reviewed-by:
    
    Changes since v4:
    - Fix clk names in example
    - Drop unnecessary assigned-clocks, assigned-clock-rates and assigned-clock-parents
    
    Changes since v3:
    - drop redundant _vop suffix from clock names
    
    Changes since v3:
    - new patch

 .../display/rockchip/rockchip-vop2.yaml       | 140 ++++++++++++++++++
 1 file changed, 140 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
new file mode 100644
index 0000000000000..655d9b327f7d3
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
@@ -0,0 +1,140 @@
+# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/rockchip/rockchip-vop2.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip SoC display controller (VOP2)
+
+description:
+  VOP2 (Video Output Processor v2) is the display controller for the Rockchip
+  series of SoCs which transfers the image data from a video memory
+  buffer to an external LCD interface.
+
+maintainers:
+  - Sandy Huang <hjc@rock-chips.com>
+  - Heiko Stuebner <heiko@sntech.de>
+
+properties:
+  compatible:
+    enum:
+      - rockchip,rk3566-vop
+      - rockchip,rk3568-vop
+
+  reg:
+    minItems: 1
+    items:
+      - description:
+          Must contain one entry corresponding to the base address and length
+          of the register space.
+      - description:
+          Can optionally contain a second entry corresponding to
+          the CRTC gamma LUT address.
+
+  interrupts:
+    maxItems: 1
+    description:
+      The VOP interrupt is shared by several interrupt sources, such as
+      frame start (VSYNC), line flag and other status interrupts.
+
+  clocks:
+    items:
+      - description: Clock for ddr buffer transfer.
+      - description: Clock for the ahb bus to R/W the phy regs.
+      - description: Pixel clock for video port 0.
+      - description: Pixel clock for video port 1.
+      - description: Pixel clock for video port 2.
+
+  clock-names:
+    items:
+      - const: aclk
+      - const: hclk
+      - const: dclk_vp0
+      - const: dclk_vp1
+      - const: dclk_vp2
+
+  rockchip,grf:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Phandle to GRF regs used for misc control
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          Output endpoint of VP0
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          Output endpoint of VP1
+
+      port@2:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          Output endpoint of VP2
+
+  iommus:
+    maxItems: 1
+
+  power-domains:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+        #include <dt-bindings/clock/rk3568-cru.h>
+        #include <dt-bindings/interrupt-controller/arm-gic.h>
+        #include <dt-bindings/power/rk3568-power.h>
+        bus {
+            #address-cells = <2>;
+            #size-cells = <2>;
+            vop: vop@fe040000 {
+                compatible = "rockchip,rk3568-vop";
+                reg = <0x0 0xfe040000 0x0 0x3000>, <0x0 0xfe044000 0x0 0x1000>;
+                interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+                clocks = <&cru ACLK_VOP>,
+                         <&cru HCLK_VOP>,
+                         <&cru DCLK_VOP0>,
+                         <&cru DCLK_VOP1>,
+                         <&cru DCLK_VOP2>;
+                clock-names = "aclk",
+                              "hclk",
+                              "dclk_vp0",
+                              "dclk_vp1",
+                              "dclk_vp2";
+                power-domains = <&power RK3568_PD_VO>;
+                iommus = <&vop_mmu>;
+                vop_out: ports {
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+                    vp0: port@0 {
+                        reg = <0>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+                    };
+                    vp1: port@1 {
+                        reg = <1>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+                    };
+                    vp2: port@2 {
+                        reg = <2>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+                    };
+                };
+            };
+        };
-- 
2.30.2


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

* [PATCH v6 22/23] dt-bindings: display: rockchip: Add binding for VOP2
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

The VOP2 is found on newer Rockchip SoCs like the rk3568 or the rk3566.
The binding differs slightly from the existing VOP binding, so add a new
binding file for it.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Reviewed-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v5:
    - Add Robs Reviewed-by:
    
    Changes since v4:
    - Fix clk names in example
    - Drop unnecessary assigned-clocks, assigned-clock-rates and assigned-clock-parents
    
    Changes since v3:
    - drop redundant _vop suffix from clock names
    
    Changes since v3:
    - new patch

 .../display/rockchip/rockchip-vop2.yaml       | 140 ++++++++++++++++++
 1 file changed, 140 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
new file mode 100644
index 0000000000000..655d9b327f7d3
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
@@ -0,0 +1,140 @@
+# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/rockchip/rockchip-vop2.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip SoC display controller (VOP2)
+
+description:
+  VOP2 (Video Output Processor v2) is the display controller for the Rockchip
+  series of SoCs which transfers the image data from a video memory
+  buffer to an external LCD interface.
+
+maintainers:
+  - Sandy Huang <hjc@rock-chips.com>
+  - Heiko Stuebner <heiko@sntech.de>
+
+properties:
+  compatible:
+    enum:
+      - rockchip,rk3566-vop
+      - rockchip,rk3568-vop
+
+  reg:
+    minItems: 1
+    items:
+      - description:
+          Must contain one entry corresponding to the base address and length
+          of the register space.
+      - description:
+          Can optionally contain a second entry corresponding to
+          the CRTC gamma LUT address.
+
+  interrupts:
+    maxItems: 1
+    description:
+      The VOP interrupt is shared by several interrupt sources, such as
+      frame start (VSYNC), line flag and other status interrupts.
+
+  clocks:
+    items:
+      - description: Clock for ddr buffer transfer.
+      - description: Clock for the ahb bus to R/W the phy regs.
+      - description: Pixel clock for video port 0.
+      - description: Pixel clock for video port 1.
+      - description: Pixel clock for video port 2.
+
+  clock-names:
+    items:
+      - const: aclk
+      - const: hclk
+      - const: dclk_vp0
+      - const: dclk_vp1
+      - const: dclk_vp2
+
+  rockchip,grf:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Phandle to GRF regs used for misc control
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          Output endpoint of VP0
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          Output endpoint of VP1
+
+      port@2:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          Output endpoint of VP2
+
+  iommus:
+    maxItems: 1
+
+  power-domains:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+        #include <dt-bindings/clock/rk3568-cru.h>
+        #include <dt-bindings/interrupt-controller/arm-gic.h>
+        #include <dt-bindings/power/rk3568-power.h>
+        bus {
+            #address-cells = <2>;
+            #size-cells = <2>;
+            vop: vop@fe040000 {
+                compatible = "rockchip,rk3568-vop";
+                reg = <0x0 0xfe040000 0x0 0x3000>, <0x0 0xfe044000 0x0 0x1000>;
+                interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+                clocks = <&cru ACLK_VOP>,
+                         <&cru HCLK_VOP>,
+                         <&cru DCLK_VOP0>,
+                         <&cru DCLK_VOP1>,
+                         <&cru DCLK_VOP2>;
+                clock-names = "aclk",
+                              "hclk",
+                              "dclk_vp0",
+                              "dclk_vp1",
+                              "dclk_vp2";
+                power-domains = <&power RK3568_PD_VO>;
+                iommus = <&vop_mmu>;
+                vop_out: ports {
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+                    vp0: port@0 {
+                        reg = <0>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+                    };
+                    vp1: port@1 {
+                        reg = <1>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+                    };
+                    vp2: port@2 {
+                        reg = <2>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+                    };
+                };
+            };
+        };
-- 
2.30.2


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

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

* [PATCH v6 22/23] dt-bindings: display: rockchip: Add binding for VOP2
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer, Rob Herring

The VOP2 is found on newer Rockchip SoCs like the rk3568 or the rk3566.
The binding differs slightly from the existing VOP binding, so add a new
binding file for it.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Reviewed-by: Rob Herring <robh@kernel.org>
---

Notes:
    Changes since v5:
    - Add Robs Reviewed-by:
    
    Changes since v4:
    - Fix clk names in example
    - Drop unnecessary assigned-clocks, assigned-clock-rates and assigned-clock-parents
    
    Changes since v3:
    - drop redundant _vop suffix from clock names
    
    Changes since v3:
    - new patch

 .../display/rockchip/rockchip-vop2.yaml       | 140 ++++++++++++++++++
 1 file changed, 140 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
new file mode 100644
index 0000000000000..655d9b327f7d3
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
@@ -0,0 +1,140 @@
+# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/rockchip/rockchip-vop2.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip SoC display controller (VOP2)
+
+description:
+  VOP2 (Video Output Processor v2) is the display controller for the Rockchip
+  series of SoCs which transfers the image data from a video memory
+  buffer to an external LCD interface.
+
+maintainers:
+  - Sandy Huang <hjc@rock-chips.com>
+  - Heiko Stuebner <heiko@sntech.de>
+
+properties:
+  compatible:
+    enum:
+      - rockchip,rk3566-vop
+      - rockchip,rk3568-vop
+
+  reg:
+    minItems: 1
+    items:
+      - description:
+          Must contain one entry corresponding to the base address and length
+          of the register space.
+      - description:
+          Can optionally contain a second entry corresponding to
+          the CRTC gamma LUT address.
+
+  interrupts:
+    maxItems: 1
+    description:
+      The VOP interrupt is shared by several interrupt sources, such as
+      frame start (VSYNC), line flag and other status interrupts.
+
+  clocks:
+    items:
+      - description: Clock for ddr buffer transfer.
+      - description: Clock for the ahb bus to R/W the phy regs.
+      - description: Pixel clock for video port 0.
+      - description: Pixel clock for video port 1.
+      - description: Pixel clock for video port 2.
+
+  clock-names:
+    items:
+      - const: aclk
+      - const: hclk
+      - const: dclk_vp0
+      - const: dclk_vp1
+      - const: dclk_vp2
+
+  rockchip,grf:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Phandle to GRF regs used for misc control
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          Output endpoint of VP0
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          Output endpoint of VP1
+
+      port@2:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          Output endpoint of VP2
+
+  iommus:
+    maxItems: 1
+
+  power-domains:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+        #include <dt-bindings/clock/rk3568-cru.h>
+        #include <dt-bindings/interrupt-controller/arm-gic.h>
+        #include <dt-bindings/power/rk3568-power.h>
+        bus {
+            #address-cells = <2>;
+            #size-cells = <2>;
+            vop: vop@fe040000 {
+                compatible = "rockchip,rk3568-vop";
+                reg = <0x0 0xfe040000 0x0 0x3000>, <0x0 0xfe044000 0x0 0x1000>;
+                interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+                clocks = <&cru ACLK_VOP>,
+                         <&cru HCLK_VOP>,
+                         <&cru DCLK_VOP0>,
+                         <&cru DCLK_VOP1>,
+                         <&cru DCLK_VOP2>;
+                clock-names = "aclk",
+                              "hclk",
+                              "dclk_vp0",
+                              "dclk_vp1",
+                              "dclk_vp2";
+                power-domains = <&power RK3568_PD_VO>;
+                iommus = <&vop_mmu>;
+                vop_out: ports {
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+                    vp0: port@0 {
+                        reg = <0>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+                    };
+                    vp1: port@1 {
+                        reg = <1>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+                    };
+                    vp2: port@2 {
+                        reg = <2>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+                    };
+                };
+            };
+        };
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* [PATCH v6 23/23] dt-bindings: display: rockchip: dw-hdmi: fix ports description
  2022-02-17  8:29 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17  8:29   ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

Current port description doesn't cover all possible cases. It currently
expects one single port with two endpoints.

When the HDMI connector is described in the device tree there can be two
ports, first one going to the VOP and the second one going to the connector.

Also on SoCs which only have a single VOP there will be only one
endpoint instead of two.

This patch addresses both issues. With this there can either be a single
port ("port") , or two of them ("port@0", "port@1") when the connector
is also in the device tree. Also the first or only port can either have
one endpoint ("endpoint") for single VOP SoCs or two ("endpoint@0",
"endpoint@1") for dual VOP SoCs.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - new patch

 .../display/rockchip/rockchip,dw-hdmi.yaml    | 24 +++++++------------
 1 file changed, 9 insertions(+), 15 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index 7dd753630b46a..fc26f1d4d001c 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -102,27 +102,21 @@ properties:
   ports:
     $ref: /schemas/graph.yaml#/properties/ports
 
-    properties:
-      port:
-        $ref: /schemas/graph.yaml#/$defs/port-base
-        unevaluatedProperties: false
+    patternProperties:
+      "^port(@0)?$":
+        $ref: /schemas/graph.yaml#/properties/port
         description: Input of the DWC HDMI TX
-
         properties:
+          endpoint:
+            description: Connection to the VOP
           endpoint@0:
-            $ref: /schemas/graph.yaml#/properties/endpoint
             description: Connection to the VOPB
-
           endpoint@1:
-            $ref: /schemas/graph.yaml#/properties/endpoint
             description: Connection to the VOPL
-
-        required:
-          - endpoint@0
-          - endpoint@1
-
-    required:
-      - port
+    properties:
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Output of the DWC HDMI TX
 
   rockchip,grf:
     $ref: /schemas/types.yaml#/definitions/phandle
-- 
2.30.2


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

* [PATCH v6 23/23] dt-bindings: display: rockchip: dw-hdmi: fix ports description
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

Current port description doesn't cover all possible cases. It currently
expects one single port with two endpoints.

When the HDMI connector is described in the device tree there can be two
ports, first one going to the VOP and the second one going to the connector.

Also on SoCs which only have a single VOP there will be only one
endpoint instead of two.

This patch addresses both issues. With this there can either be a single
port ("port") , or two of them ("port@0", "port@1") when the connector
is also in the device tree. Also the first or only port can either have
one endpoint ("endpoint") for single VOP SoCs or two ("endpoint@0",
"endpoint@1") for dual VOP SoCs.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - new patch

 .../display/rockchip/rockchip,dw-hdmi.yaml    | 24 +++++++------------
 1 file changed, 9 insertions(+), 15 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index 7dd753630b46a..fc26f1d4d001c 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -102,27 +102,21 @@ properties:
   ports:
     $ref: /schemas/graph.yaml#/properties/ports
 
-    properties:
-      port:
-        $ref: /schemas/graph.yaml#/$defs/port-base
-        unevaluatedProperties: false
+    patternProperties:
+      "^port(@0)?$":
+        $ref: /schemas/graph.yaml#/properties/port
         description: Input of the DWC HDMI TX
-
         properties:
+          endpoint:
+            description: Connection to the VOP
           endpoint@0:
-            $ref: /schemas/graph.yaml#/properties/endpoint
             description: Connection to the VOPB
-
           endpoint@1:
-            $ref: /schemas/graph.yaml#/properties/endpoint
             description: Connection to the VOPL
-
-        required:
-          - endpoint@0
-          - endpoint@1
-
-    required:
-      - port
+    properties:
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Output of the DWC HDMI TX
 
   rockchip,grf:
     $ref: /schemas/types.yaml#/definitions/phandle
-- 
2.30.2


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

* [PATCH v6 23/23] dt-bindings: display: rockchip: dw-hdmi: fix ports description
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

Current port description doesn't cover all possible cases. It currently
expects one single port with two endpoints.

When the HDMI connector is described in the device tree there can be two
ports, first one going to the VOP and the second one going to the connector.

Also on SoCs which only have a single VOP there will be only one
endpoint instead of two.

This patch addresses both issues. With this there can either be a single
port ("port") , or two of them ("port@0", "port@1") when the connector
is also in the device tree. Also the first or only port can either have
one endpoint ("endpoint") for single VOP SoCs or two ("endpoint@0",
"endpoint@1") for dual VOP SoCs.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - new patch

 .../display/rockchip/rockchip,dw-hdmi.yaml    | 24 +++++++------------
 1 file changed, 9 insertions(+), 15 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index 7dd753630b46a..fc26f1d4d001c 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -102,27 +102,21 @@ properties:
   ports:
     $ref: /schemas/graph.yaml#/properties/ports
 
-    properties:
-      port:
-        $ref: /schemas/graph.yaml#/$defs/port-base
-        unevaluatedProperties: false
+    patternProperties:
+      "^port(@0)?$":
+        $ref: /schemas/graph.yaml#/properties/port
         description: Input of the DWC HDMI TX
-
         properties:
+          endpoint:
+            description: Connection to the VOP
           endpoint@0:
-            $ref: /schemas/graph.yaml#/properties/endpoint
             description: Connection to the VOPB
-
           endpoint@1:
-            $ref: /schemas/graph.yaml#/properties/endpoint
             description: Connection to the VOPL
-
-        required:
-          - endpoint@0
-          - endpoint@1
-
-    required:
-      - port
+    properties:
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Output of the DWC HDMI TX
 
   rockchip,grf:
     $ref: /schemas/types.yaml#/definitions/phandle
-- 
2.30.2


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

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

* [PATCH v6 23/23] dt-bindings: display: rockchip: dw-hdmi: fix ports description
@ 2022-02-17  8:29   ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17  8:29 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel, Andy Yan,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

Current port description doesn't cover all possible cases. It currently
expects one single port with two endpoints.

When the HDMI connector is described in the device tree there can be two
ports, first one going to the VOP and the second one going to the connector.

Also on SoCs which only have a single VOP there will be only one
endpoint instead of two.

This patch addresses both issues. With this there can either be a single
port ("port") , or two of them ("port@0", "port@1") when the connector
is also in the device tree. Also the first or only port can either have
one endpoint ("endpoint") for single VOP SoCs or two ("endpoint@0",
"endpoint@1") for dual VOP SoCs.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v5:
    - new patch

 .../display/rockchip/rockchip,dw-hdmi.yaml    | 24 +++++++------------
 1 file changed, 9 insertions(+), 15 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
index 7dd753630b46a..fc26f1d4d001c 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
@@ -102,27 +102,21 @@ properties:
   ports:
     $ref: /schemas/graph.yaml#/properties/ports
 
-    properties:
-      port:
-        $ref: /schemas/graph.yaml#/$defs/port-base
-        unevaluatedProperties: false
+    patternProperties:
+      "^port(@0)?$":
+        $ref: /schemas/graph.yaml#/properties/port
         description: Input of the DWC HDMI TX
-
         properties:
+          endpoint:
+            description: Connection to the VOP
           endpoint@0:
-            $ref: /schemas/graph.yaml#/properties/endpoint
             description: Connection to the VOPB
-
           endpoint@1:
-            $ref: /schemas/graph.yaml#/properties/endpoint
             description: Connection to the VOPL
-
-        required:
-          - endpoint@0
-          - endpoint@1
-
-    required:
-      - port
+    properties:
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Output of the DWC HDMI TX
 
   rockchip,grf:
     $ref: /schemas/types.yaml#/definitions/phandle
-- 
2.30.2


_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 00/23] drm/rockchip: RK356x VOP2 support
  2022-02-17  8:29 ` Sascha Hauer
                   ` (25 preceding siblings ...)
  (?)
@ 2022-02-17 10:07 ` Frank Wunderlich
  -1 siblings, 0 replies; 201+ messages in thread
From: Frank Wunderlich @ 2022-02-17 10:07 UTC (permalink / raw)
  To: linux-rockchip

Am 17. Februar 2022 09:29:31 MEZ schrieb Sascha Hauer <s.hauer@pengutronix.de>:
>This is v6 of adding RK356x VOP2 support. Biggest change this time is
>that I no longer modify struct drm_encoder, instead the rockchip
>drivers
>now embed struct drm_encoder in a rockchip specific struct.
>
>Sascha
>
>Changes since v5:
>- Add new patch to fix dw-hdmi of_graph binding
>- Drop "drm/encoder: Add of_graph port to struct drm_encoder" and solve
>  issue internally in the driver
>- make checkpatch cleaner
>
>Changes since v4:
>- Reorder patches in a way that binding/dts/driver patches are closer
>together
>- Drop clk patches already applied by Heiko
>
>Changes since v3:
>- added changelog to each patch
>- Add 4k support to hdmi driver
>- rebase on v5.17-rc1
>
>Changes since v2:
>- Add pin names to HDMI supply pin description
>- Add hclk support to HDMI driver
>- Dual license rockchip-vop2 binding, update binding
>- Add HDMI connector to board dts files
>- drop unnecessary gamma_lut registers from vop2
>- Update dclk_vop[012] clock handling, no longer hacks needed
>- Complete regmap conversion
>
>Changes since v1:
>- drop all unnecessary waiting for frames within atomic modeset and
>plane update
>- Cluster subwin support removed
>- gamma support removed
>- unnecessary irq_lock removed
>- interrupt handling simplified
>- simplified zpos handling
>- drop is_alpha_support(), use fb->format->has_alpha instead
>- use devm_regulator_get() rather than devm_regulator_get_optional()
>for hdmi regulators
>- Use fixed number of planes per video port
>- Drop homegrown regmap code from vop2 driver (not complete yet)
>- Add separate include file for vop2 driver to not pollute the vop
>include
>
>Andy Yan (1):
>  drm: rockchip: Add VOP2 driver
>
>Benjamin Gaignard (1):
>  dt-bindings: display: rockchip: dw-hdmi: Add compatible for rk3568
>    HDMI
>
>Douglas Anderson (2):
>  drm/rockchip: dw_hdmi: Use auto-generated tables
>  drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
>
>Michael Riesch (1):
>  arm64: dts: rockchip: enable vop2 and hdmi tx on quartz64a
>
>Nickey Yang (1):
>  drm/rockchip: dw_hdmi: add default 594Mhz clk for 4K@60hz
>
>Sascha Hauer (17):
>  drm/rockchip: Embed drm_encoder into rockchip_decoder
>  drm/rockchip: dw_hdmi: rename vpll clock to reference clock
>  dt-bindings: display: rockchip: dw-hdmi: use "ref" as clock name
>  arm64: dts: rockchip: rk3399: rename HDMI ref clock to 'ref'
>  drm/rockchip: dw_hdmi: add rk3568 support
>  drm/rockchip: dw_hdmi: add regulator support
>  dt-bindings: display: rockchip: dw-hdmi: Add regulator support
>  drm/rockchip: dw_hdmi: Add support for hclk
>  dt-bindings: display: rockchip: dw-hdmi: Add additional clock
>  drm/rockchip: dw_hdmi: drop mode_valid hook
> dt-bindings: display: rockchip: dw-hdmi: Make unwedge pinctrl optional
>  arm64: dts: rockchip: rk356x: Add VOP2 nodes
>  arm64: dts: rockchip: rk356x: Add HDMI nodes
>  arm64: dts: rockchip: rk3568-evb: Enable VOP2 and hdmi
>  drm/rockchip: Make VOP driver optional
>  dt-bindings: display: rockchip: Add binding for VOP2
>  dt-bindings: display: rockchip: dw-hdmi: fix ports description
>
> .../display/rockchip/rockchip,dw-hdmi.yaml    |   53 +-
> .../display/rockchip/rockchip-vop2.yaml       |  140 +
> arch/arm64/boot/dts/rockchip/rk3399.dtsi      |    2 +-
> .../boot/dts/rockchip/rk3566-quartz64-a.dts   |   47 +
> arch/arm64/boot/dts/rockchip/rk3566.dtsi      |    4 +
> .../boot/dts/rockchip/rk3568-evb1-v10.dts     |   47 +
> arch/arm64/boot/dts/rockchip/rk3568.dtsi      |    4 +
> arch/arm64/boot/dts/rockchip/rk356x.dtsi      |   83 +
> drivers/gpu/drm/rockchip/Kconfig              |   14 +
> drivers/gpu/drm/rockchip/Makefile             |    4 +-
> .../gpu/drm/rockchip/analogix_dp-rockchip.c   |   32 +-
> drivers/gpu/drm/rockchip/cdn-dp-core.c        |   18 +-
> drivers/gpu/drm/rockchip/cdn-dp-core.h        |    2 +-
> .../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c   |   17 +-
> drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c   |  294 +-
> drivers/gpu/drm/rockchip/inno_hdmi.c          |   32 +-
> drivers/gpu/drm/rockchip/rk3066_hdmi.c        |   34 +-
> drivers/gpu/drm/rockchip/rockchip_drm_drv.c   |    3 +-
> drivers/gpu/drm/rockchip/rockchip_drm_drv.h   |   17 +-
> drivers/gpu/drm/rockchip/rockchip_drm_fb.c    |    2 +
> drivers/gpu/drm/rockchip/rockchip_drm_vop.h   |   15 +
> drivers/gpu/drm/rockchip/rockchip_drm_vop2.c  | 2708 +++++++++++++++++
> drivers/gpu/drm/rockchip/rockchip_drm_vop2.h  |  477 +++
> drivers/gpu/drm/rockchip/rockchip_lvds.c      |   26 +-
> drivers/gpu/drm/rockchip/rockchip_vop2_reg.c  |  281 ++
> include/dt-bindings/soc/rockchip,vop2.h       |   14 +
> 26 files changed, 4175 insertions(+), 195 deletions(-)
>create mode 100644
>Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> create mode 100644 include/dt-bindings/soc/rockchip,vop2.h

Hi,

Hast du in der v6 die Fehlermeldungen da schon gefixt,die ich dir gemeldet habe?

Interessanterweise hatte ich die nur mit Graphischer Oberfläche (wayland). Mit fbcon hatte ich die nicht.

Muss dazu sagen,dass ich auch schon mit gpu rumexperimentiere (ubuntu 21.10 userspace mit weston)...hab die ber noch nicht komplett zum laufen bekommen.

Hdmi selbst funktioniert,bis auf die Meldungen...sollten aber nicht vom GPU-part kommen.

Gruß Frank

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

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
  2022-02-17  8:29   ` Sascha Hauer
  (?)
@ 2022-02-17 12:00     ` Andy Yan
  -1 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-17 12:00 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis

Hi Sascha:

On 2/17/22 16:29, Sascha Hauer wrote:
> From: Andy Yan <andy.yan@rock-chips.com>
>
> The VOP2 unit is found on Rockchip SoCs beginning with rk3566/rk3568.
> It replaces the VOP unit found in the older Rockchip SoCs.
>
> This driver has been derived from the downstream Rockchip Kernel and
> heavily modified:
>
> - All nonstandard DRM properties have been removed
> - dropped struct vop2_plane_state and pass around less data between
>    functions
> - Dropped all DRM_FORMAT_* not known on upstream
> - rework register access to get rid of excessively used macros
> - Drop all waiting for framesyncs
>
> The driver is tested with HDMI and MIPI-DSI display on a RK3568-EVB
> board. Overlay support is tested with the modetest utility. AFBC support
> on the cluster windows is tested with weston-simple-dmabuf-egl on
> weston using the (yet to be upstreamed) panfrost driver support.
>
> Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>
> Notes:
>      Changes since v5:
>      - consistently use u8/u16/u32 rather than uint8_t/uint16_t/uint32_t
>      - Use spin_lock rather than spin_lock_irqsave
>      - replace printk with drm_dbg
>      - break some overlong lines
>      
>      Changes since v4:
>      - Avoid stack frame overflow by not allocating big array on the stack
>      
>      Changes since v3:
>      - Sort includes
>      - fix typos
>      - Drop spinlock
>      - Use regmap_set_bits()/regmap_clear_bits()
>      - simplify vop2_scale_factor()
>      - simplify vop2_afbc_transform_offset()
>      
>      Changes since v4:
>      - Sort nodes alphabetically
>      
>      Changes since v3:
>      - Fix HDMI connector type
>
>   drivers/gpu/drm/rockchip/Kconfig             |    6 +
>   drivers/gpu/drm/rockchip/Makefile            |    1 +
>   drivers/gpu/drm/rockchip/rockchip_drm_drv.c  |    1 +
>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h  |    6 +-
>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c   |    2 +
>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h  |   15 +
>   drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2708 ++++++++++++++++++
>   drivers/gpu/drm/rockchip/rockchip_drm_vop2.h |  477 +++
>   drivers/gpu/drm/rockchip/rockchip_vop2_reg.c |  281 ++
>   9 files changed, 3496 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
>
> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> index b9b156308460a..4ff0043f0ee70 100644
> --- a/drivers/gpu/drm/rockchip/Kconfig
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
>   	  This selects support for the VOP driver. You should enable it
>   	  on all older SoCs up to RK3399.
>   
> +config ROCKCHIP_VOP2
> +	bool "Rockchip VOP2 driver"
> +	help
> +	  This selects support for the VOP2 driver. You should enable it
> +	  on all newer SoCs beginning form RK3568.
> +
>   config ROCKCHIP_ANALOGIX_DP
>   	bool "Rockchip specific extensions for Analogix DP driver"
>   	depends on ROCKCHIP_VOP
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index dfc5512fdb9f1..3ff7b21c04149 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -6,6 +6,7 @@
>   rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>   		rockchip_drm_gem.o
>   
> +rockchipdrm-$(CONFIG_ROCKCHIP_VOP2) += rockchip_drm_vop2.o rockchip_vop2_reg.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> index 82c8faf1fb6b8..95f6c5985fdd7 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -459,6 +459,7 @@ static int __init rockchip_drm_init(void)
>   
>   	num_rockchip_sub_drivers = 0;
>   	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
> +	ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2);
>   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
>   				CONFIG_ROCKCHIP_LVDS);
>   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> index d3e42410ae5da..5cb207aaaadd0 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -18,7 +18,7 @@
>   
>   #define ROCKCHIP_MAX_FB_BUFFER	3
>   #define ROCKCHIP_MAX_CONNECTOR	2
> -#define ROCKCHIP_MAX_CRTC	2
> +#define ROCKCHIP_MAX_CRTC	4
>   
>   struct drm_device;
>   struct drm_connector;
> @@ -31,6 +31,9 @@ struct rockchip_crtc_state {
>   	int output_bpc;
>   	int output_flags;
>   	bool enable_afbc;
> +	u32 bus_format;
> +	u32 bus_flags;
> +	int color_space;
>   };
>   #define to_rockchip_crtc_state(s) \
>   		container_of(s, struct rockchip_crtc_state, base)
> @@ -63,6 +66,7 @@ extern struct platform_driver rockchip_dp_driver;
>   extern struct platform_driver rockchip_lvds_driver;
>   extern struct platform_driver vop_platform_driver;
>   extern struct platform_driver rk3066_hdmi_driver;
> +extern struct platform_driver vop2_platform_driver;
>   
>   struct rockchip_encoder {
>   	struct device_node *port;
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> index 3aa37e177667e..0d2cb4f3922b8 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> @@ -134,4 +134,6 @@ void rockchip_drm_mode_config_init(struct drm_device *dev)
>   
>   	dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
>   	dev->mode_config.helper_private = &rockchip_mode_config_helpers;
> +
> +	dev->mode_config.normalize_zpos = true;
>   }
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> index 857d97cdc67c6..1e364d7b50e69 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -54,9 +54,23 @@ struct vop_afbc {
>   	struct vop_reg enable;
>   	struct vop_reg win_sel;
>   	struct vop_reg format;
> +	struct vop_reg rb_swap;
> +	struct vop_reg uv_swap;
> +	struct vop_reg auto_gating_en;
> +	struct vop_reg block_split_en;
> +	struct vop_reg pic_vir_width;
> +	struct vop_reg tile_num;
>   	struct vop_reg hreg_block_split;
> +	struct vop_reg pic_offset;
>   	struct vop_reg pic_size;
> +	struct vop_reg dsp_offset;
> +	struct vop_reg transform_offset;
>   	struct vop_reg hdr_ptr;
> +	struct vop_reg half_block_en;
> +	struct vop_reg xmirror;
> +	struct vop_reg ymirror;
> +	struct vop_reg rotate_270;
> +	struct vop_reg rotate_90;
>   	struct vop_reg rstn;
>   };
>   
> @@ -410,4 +424,5 @@ static inline int scl_vop_cal_lb_mode(int width, bool is_yuv)
>   }
>   
>   extern const struct component_ops vop_component_ops;
> +
>   #endif /* _ROCKCHIP_DRM_VOP_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> new file mode 100644
> index 0000000000000..394dd6c583682
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> @@ -0,0 +1,2708 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
> + * Author: Andy Yan <andy.yan@rock-chips.com>
> + */
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/delay.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/swab.h>
> +
> +#include <drm/drm.h>
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_uapi.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_debugfs.h>
> +#include <drm/drm_flip_work.h>
> +#include <drm/drm_plane_helper.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_vblank.h>
> +
> +#include <uapi/linux/videodev2.h>
> +#include <dt-bindings/soc/rockchip,vop2.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_gem.h"
> +#include "rockchip_drm_fb.h"
> +#include "rockchip_drm_vop2.h"
> +
> +/*
> + * VOP2 architecture
> + *
> + +----------+   +-------------+                                                        +-----------+
> + |  Cluster |   | Sel 1 from 6|                                                        | 1 from 3  |
> + |  window0 |   |    Layer0   |                                                        |    RGB    |
> + +----------+   +-------------+              +---------------+    +-------------+      +-----------+
> + +----------+   +-------------+              |N from 6 layers|    |             |
> + |  Cluster |   | Sel 1 from 6|              |   Overlay0    +--->| Video Port0 |      +-----------+
> + |  window1 |   |    Layer1   |              |               |    |             |      | 1 from 3  |
> + +----------+   +-------------+              +---------------+    +-------------+      |   LVDS    |
> + +----------+   +-------------+                                                        +-----------+
> + |  Esmart  |   | Sel 1 from 6|
> + |  window0 |   |   Layer2    |              +---------------+    +-------------+      +-----------+
> + +----------+   +-------------+              |N from 6 Layers|    |             | +--> | 1 from 3  |
> + +----------+   +-------------+   -------->  |   Overlay1    +--->| Video Port1 |      |   MIPI    |
> + |  Esmart  |   | Sel 1 from 6|   -------->  |               |    |             |      +-----------+
> + |  Window1 |   |   Layer3    |              +---------------+    +-------------+
> + +----------+   +-------------+                                                        +-----------+
> + +----------+   +-------------+                                                        | 1 from 3  |
> + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |   HDMI    |
> + |  Window0 |   |    Layer4   |              |N from 6 Layers|    |             |      +-----------+
> + +----------+   +-------------+              |   Overlay2    +--->| Video Port2 |
> + +----------+   +-------------+              |               |    |             |      +-----------+
> + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |  1 from 3 |
> + |  Window1 |   |    Layer5   |                                                        |    eDP    |
> + +----------+   +-------------+                                                        +-----------+
> + *
> + */
> +
> +enum vop2_data_format {
> +	VOP2_FMT_ARGB8888 = 0,
> +	VOP2_FMT_RGB888,
> +	VOP2_FMT_RGB565,
> +	VOP2_FMT_XRGB101010,
> +	VOP2_FMT_YUV420SP,
> +	VOP2_FMT_YUV422SP,
> +	VOP2_FMT_YUV444SP,
> +	VOP2_FMT_YUYV422 = 8,
> +	VOP2_FMT_YUYV420,
> +	VOP2_FMT_VYUY422,
> +	VOP2_FMT_VYUY420,
> +	VOP2_FMT_YUV420SP_TILE_8x4 = 0x10,
> +	VOP2_FMT_YUV420SP_TILE_16x2,
> +	VOP2_FMT_YUV422SP_TILE_8x4,
> +	VOP2_FMT_YUV422SP_TILE_16x2,
> +	VOP2_FMT_YUV420SP_10,
> +	VOP2_FMT_YUV422SP_10,
> +	VOP2_FMT_YUV444SP_10,
> +};
> +
> +enum vop2_afbc_format {
> +	VOP2_AFBC_FMT_RGB565,
> +	VOP2_AFBC_FMT_ARGB2101010 = 2,
> +	VOP2_AFBC_FMT_YUV420_10BIT,
> +	VOP2_AFBC_FMT_RGB888,
> +	VOP2_AFBC_FMT_ARGB8888,
> +	VOP2_AFBC_FMT_YUV420 = 9,
> +	VOP2_AFBC_FMT_YUV422 = 0xb,
> +	VOP2_AFBC_FMT_YUV422_10BIT = 0xe,
> +	VOP2_AFBC_FMT_INVALID = -1,
> +};
> +
> +union vop2_alpha_ctrl {
> +	u32 val;
> +	struct {
> +		/* [0:1] */
> +		u32 color_mode:1;
> +		u32 alpha_mode:1;
> +		/* [2:3] */
> +		u32 blend_mode:2;
> +		u32 alpha_cal_mode:1;
> +		/* [5:7] */
> +		u32 factor_mode:3;
> +		/* [8:9] */
> +		u32 alpha_en:1;
> +		u32 src_dst_swap:1;
> +		u32 reserved:6;
> +		/* [16:23] */
> +		u32 glb_alpha:8;
> +	} bits;
> +};
> +
> +struct vop2_alpha {
> +	union vop2_alpha_ctrl src_color_ctrl;
> +	union vop2_alpha_ctrl dst_color_ctrl;
> +	union vop2_alpha_ctrl src_alpha_ctrl;
> +	union vop2_alpha_ctrl dst_alpha_ctrl;
> +};
> +
> +struct vop2_alpha_config {
> +	bool src_premulti_en;
> +	bool dst_premulti_en;
> +	bool src_pixel_alpha_en;
> +	bool dst_pixel_alpha_en;
> +	u16 src_glb_alpha_value;
> +	u16 dst_glb_alpha_value;
> +};
> +
> +struct vop2_win {
> +	struct vop2 *vop2;
> +	struct drm_plane base;
> +	const struct vop2_win_data *data;
> +	struct regmap_field *reg[VOP2_WIN_MAX_REG];
> +
> +	/**
> +	 * @win_id: graphic window id, a cluster may be split into two
> +	 * graphics windows.
> +	 */
> +	u8 win_id;
> +	u8 delay;
> +	u32 offset;
> +
> +	enum drm_plane_type type;
> +};
> +
> +struct vop2_video_port {
> +	struct drm_crtc crtc;
> +	struct vop2 *vop2;
> +	struct clk *dclk;
> +	unsigned int id;
> +	const struct vop2_video_port_regs *regs;
> +	const struct vop2_video_port_data *data;
> +
> +	struct completion dsp_hold_completion;
> +
> +	/**
> +	 * @win_mask: Bitmask of windows attached to the video port;
> +	 */
> +	u32 win_mask;
> +
> +	struct vop2_win *primary_plane;
> +	struct drm_pending_vblank_event *event;
> +
> +	unsigned int nlayers;
> +};
> +
> +struct vop2 {
> +	struct device *dev;
> +	struct drm_device *drm;
> +	struct vop2_video_port vps[ROCKCHIP_MAX_CRTC];
> +
> +	const struct vop2_data *data;
> +	/*
> +	 * Number of windows that are registered as plane, may be less than the
> +	 * total number of hardware windows.
> +	 */
> +	u32 registered_num_wins;
> +
> +	void __iomem *regs;
> +	struct regmap *map;
> +
> +	struct regmap *grf;
> +
> +	/* physical map length of vop2 register */
> +	u32 len;
> +
> +	void __iomem *lut_regs;
> +
> +	/* protects crtc enable/disable */
> +	struct mutex vop2_lock;
> +
> +	int irq;
> +
> +	/*
> +	 * Some global resources are shared between all video ports(crtcs), so
> +	 * we need a ref counter here.
> +	 */
> +	unsigned int enable_count;
> +	struct clk *hclk;
> +	struct clk *aclk;
> +
> +	/* must be put at the end of the struct */
> +	struct vop2_win win[];
> +};
> +
> +static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
> +{
> +	return container_of(crtc, struct vop2_video_port, crtc);
> +}
> +
> +static struct vop2_win *to_vop2_win(struct drm_plane *p)
> +{
> +	return container_of(p, struct vop2_win, base);
> +}
> +
> +static void vop2_lock(struct vop2 *vop2)
> +{
> +	mutex_lock(&vop2->vop2_lock);
> +}
> +
> +static void vop2_unlock(struct vop2 *vop2)
> +{
> +	mutex_unlock(&vop2->vop2_lock);
> +}
> +
> +static void vop2_writel(struct vop2 *vop2, u32 offset, u32 v)
> +{
> +	regmap_write(vop2->map, offset, v);
> +}
> +
> +static void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
> +{
> +	regmap_write(vp->vop2->map, vp->data->offset + offset, v);
> +}
> +
> +static u32 vop2_readl(struct vop2 *vop2, u32 offset)
> +{
> +	u32 val;
> +
> +	regmap_read(vop2->map, offset, &val);
> +
> +	return val;
> +}
> +
> +static void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v)
> +{
> +	regmap_field_write(win->reg[reg], v);
> +}
> +
> +static bool vop2_cluster_window(const struct vop2_win *win)
> +{
> +	return win->data->feature & WIN_FEATURE_CLUSTER;
> +}
> +
> +static void vop2_cfg_done(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE,
> +			BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> +}
> +
> +static void vop2_win_disable(struct vop2_win *win)
> +{
> +	vop2_win_write(win, VOP2_WIN_ENABLE, 0);
> +
> +	if (vop2_cluster_window(win))
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0);
> +}
> +
> +static enum vop2_data_format vop2_convert_format(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +		return VOP2_FMT_ARGB8888;
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		return VOP2_FMT_RGB888;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		return VOP2_FMT_RGB565;
> +	case DRM_FORMAT_NV12:
> +		return VOP2_FMT_YUV420SP;
> +	case DRM_FORMAT_NV16:
> +		return VOP2_FMT_YUV422SP;
> +	case DRM_FORMAT_NV24:
> +		return VOP2_FMT_YUV444SP;
> +	case DRM_FORMAT_YUYV:
> +	case DRM_FORMAT_YVYU:
> +		return VOP2_FMT_VYUY422;
> +	case DRM_FORMAT_VYUY:
> +	case DRM_FORMAT_UYVY:
> +		return VOP2_FMT_YUYV422;
> +	default:
> +		DRM_ERROR("unsupported format[%08x]\n", format);
> +		return -EINVAL;
> +	}
> +}
> +
> +static enum vop2_afbc_format vop2_convert_afbc_format(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +		return VOP2_AFBC_FMT_ARGB8888;
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		return VOP2_AFBC_FMT_RGB888;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		return VOP2_AFBC_FMT_RGB565;
> +	case DRM_FORMAT_NV12:
> +		return VOP2_AFBC_FMT_YUV420;
> +	case DRM_FORMAT_NV16:
> +		return VOP2_AFBC_FMT_YUV422;
> +	default:
> +		return VOP2_AFBC_FMT_INVALID;
> +	}
> +
> +	return VOP2_AFBC_FMT_INVALID;
> +}
> +
> +static bool vop2_win_rb_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_BGR888:
> +	case DRM_FORMAT_BGR565:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_afbc_rb_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV24:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_afbc_uv_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV12:
> +	case DRM_FORMAT_NV16:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_win_uv_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV12:
> +	case DRM_FORMAT_NV16:
> +	case DRM_FORMAT_NV24:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_win_dither_up(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_BGR565:
> +	case DRM_FORMAT_RGB565:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
> +{
> +	/*
> +	 * FIXME:
> +	 *
> +	 * There is no media type for YUV444 output,
> +	 * so when out_mode is AAAA or P888, assume output is YUV444 on
> +	 * yuv format.
> +	 *
> +	 * From H/W testing, YUV444 mode need a rb swap.
> +	 */
> +	if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 ||
> +	    bus_format == MEDIA_BUS_FMT_VYUY8_1X16 ||
> +	    bus_format == MEDIA_BUS_FMT_YVYU8_2X8 ||
> +	    bus_format == MEDIA_BUS_FMT_VYUY8_2X8 ||
> +	    ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
> +	      bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
> +	     (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
> +	      output_mode == ROCKCHIP_OUT_MODE_P888)))
> +		return true;
> +	else
> +		return false;
> +}
> +
> +static bool is_yuv_output(u32 bus_format)
> +{
> +	switch (bus_format) {
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +	case MEDIA_BUS_FMT_YUV10_1X30:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> +	case MEDIA_BUS_FMT_YUYV8_2X8:
> +	case MEDIA_BUS_FMT_YVYU8_2X8:
> +	case MEDIA_BUS_FMT_UYVY8_2X8:
> +	case MEDIA_BUS_FMT_VYUY8_2X8:
> +	case MEDIA_BUS_FMT_YUYV8_1X16:
> +	case MEDIA_BUS_FMT_YVYU8_1X16:
> +	case MEDIA_BUS_FMT_UYVY8_1X16:
> +	case MEDIA_BUS_FMT_VYUY8_1X16:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool rockchip_afbc(struct drm_plane *plane, u64 modifier)
> +{
> +	int i;
> +
> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> +		return false;
> +
> +	for (i = 0 ; i < plane->modifier_count; i++)
> +		if (plane->modifiers[i] == modifier)
> +			return true;
> +
> +	return false;
> +
> +}
> +
> +static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
> +					u64 modifier)
> +{
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2 *vop2 = win->vop2;
> +
> +	if (modifier == DRM_FORMAT_MOD_INVALID)
> +		return false;
> +
> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> +		return true;
> +
> +	if (!rockchip_afbc(plane, modifier)) {
> +		drm_err(vop2->drm, "Unsupported format modifier 0x%llx\n",
> +			modifier);
> +
> +		return false;
> +	}
> +
> +	return vop2_convert_afbc_format(format) >= 0;
> +}
> +
> +static u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate,
> +				      bool afbc_half_block_en)
> +{
> +	struct drm_rect *src = &pstate->src;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	u32 bpp = fb->format->cpp[0] * 8;
> +	u32 vir_width = (fb->pitches[0] << 3) / bpp;
> +	u32 width = drm_rect_width(src) >> 16;
> +	u32 height = drm_rect_height(src) >> 16;
> +	u32 act_xoffset = src->x1 >> 16;
> +	u32 act_yoffset = src->y1 >> 16;
> +	u32 align16_crop = 0;
> +	u32 align64_crop = 0;
> +	u32 height_tmp;
> +	u8 tx, ty;
> +	u8 bottom_crop_line_num = 0;
> +
> +	/* 16 pixel align */
> +	if (height & 0xf)
> +		align16_crop = 16 - (height & 0xf);
> +
> +	height_tmp = height + align16_crop;
> +
> +	/* 64 pixel align */
> +	if (height_tmp & 0x3f)
> +		align64_crop = 64 - (height_tmp & 0x3f);
> +
> +	bottom_crop_line_num = align16_crop + align64_crop;
> +
> +	switch (pstate->rotation &
> +		(DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y |
> +		 DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) {
> +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> +		tx = 16 - ((act_xoffset + width) & 0xf);
> +		ty = bottom_crop_line_num - act_yoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90:
> +		tx = bottom_crop_line_num - act_yoffset;
> +		ty = vir_width - width - act_xoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270:
> +		tx = act_yoffset;
> +		ty = act_xoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X:
> +		tx = 16 - ((act_xoffset + width) & 0xf);
> +		ty = act_yoffset;
> +		break;
> +	case DRM_MODE_REFLECT_Y:
> +		tx = act_xoffset;
> +		ty = bottom_crop_line_num - act_yoffset;
> +		break;
> +	case DRM_MODE_ROTATE_90:
> +		tx = bottom_crop_line_num - act_yoffset;
> +		ty = act_xoffset;
> +		break;
> +	case DRM_MODE_ROTATE_270:
> +		tx = act_yoffset;
> +		ty = vir_width - width - act_xoffset;
> +		break;
> +	case 0:
> +		tx = act_xoffset;
> +		ty = act_yoffset;
> +		break;
> +	}
> +
> +	if (afbc_half_block_en)
> +		ty &= 0x7f;
> +
> +#define TRANSFORM_XOFFSET GENMASK(7, 0)
> +#define TRANSFORM_YOFFSET GENMASK(23, 16)
> +	return FIELD_PREP(TRANSFORM_XOFFSET, tx) |
> +		FIELD_PREP(TRANSFORM_YOFFSET, ty);
> +}
> +
> +/*
> + * A Cluster window has 2048 x 16 line buffer, which can
> + * works at 2048 x 16(Full) or 4096 x 8 (Half) mode.
> + * for Cluster_lb_mode register:
> + * 0: half mode, for plane input width range 2048 ~ 4096
> + * 1: half mode, for cluster work at 2 * 2048 plane mode
> + * 2: half mode, for rotate_90/270 mode
> + *
> + */
> +static int vop2_get_cluster_lb_mode(struct vop2_win *win,
> +				    struct drm_plane_state *pstate)
> +{
> +	if ((pstate->rotation & DRM_MODE_ROTATE_270) ||
> +	    (pstate->rotation & DRM_MODE_ROTATE_90))
> +		return 2;
> +	else
> +		return 0;
> +}
> +
> +static u16 vop2_scale_factor(u32 src, u32 dst)
> +{
> +	u32 fac;
> +	int shift;
> +
> +	if (src == dst)
> +		return 0;
> +
> +	if (dst < 2)
> +		return U16_MAX;
> +
> +	if (src < 2)
> +		return 0;
> +
> +	if (src > dst)
> +		shift = 12;
> +	else
> +		shift = 16;
> +
> +	src--;
> +	dst--;
> +
> +	fac = DIV_ROUND_UP(src << shift, dst) - 1;
> +
> +	if (fac > U16_MAX)
> +		return U16_MAX;
> +
> +	return fac;
> +}
> +
> +static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
> +			     u32 src_w, u32 src_h, u32 dst_w,
> +			     u32 dst_h, u32 pixel_format)
> +{
> +	const struct drm_format_info *info;
> +	u16 hor_scl_mode, ver_scl_mode;
> +	u16 hscl_filter_mode, vscl_filter_mode;
> +	u8 gt2 = 0;
> +	u8 gt4 = 0;
> +	u32 val;
> +
> +	info = drm_format_info(pixel_format);
> +
> +	if (src_h >= (4 * dst_h)) {
> +		gt4 = 1;
> +		src_h >>= 2;
> +	} else if (src_h >= (2 * dst_h)) {
> +		gt2 = 1;
> +		src_h >>= 1;
> +	}
> +
> +	hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> +	ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> +
> +	if (hor_scl_mode == SCALE_UP)
> +		hscl_filter_mode = VOP2_SCALE_UP_BIC;
> +	else
> +		hscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> +
> +	if (ver_scl_mode == SCALE_UP)
> +		vscl_filter_mode = VOP2_SCALE_UP_BIL;
> +	else
> +		vscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> +
> +	/*
> +	 * RK3568 VOP Esmart/Smart dsp_w should be even pixel
> +	 * at scale down mode
> +	 */
> +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> +		if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) {
> +			drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n",
> +				win->data->name, dst_w);
> +			dst_w++;
> +		}
> +	}
> +
> +	val = vop2_scale_factor(src_w, dst_w);
> +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val);
> +	val = vop2_scale_factor(src_h, dst_h);
> +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val);
> +
> +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4);
> +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2);
> +
> +	vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode);
> +	vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode);
> +
> +	if (vop2_cluster_window(win))
> +		return;
> +
> +	vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode);
> +	vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode);
> +
> +	if (info->is_yuv) {
> +		src_w /= info->hsub;
> +		src_h /= info->vsub;
> +
> +		gt4 = gt2 = 0;
> +
> +		if (src_h >= (4 * dst_h)) {
> +			gt4 = 1;
> +			src_h >>= 2;
> +		} else if (src_h >= (2 * dst_h)) {
> +			gt2 = 1;
> +			src_h >>= 1;
> +		}
> +
> +		hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> +		ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> +
> +		val = vop2_scale_factor(src_w, dst_w);
> +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val);
> +
> +		val = vop2_scale_factor(src_h, dst_h);
> +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val);
> +
> +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4);
> +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2);
> +		vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode);
> +	}
> +}
> +
> +static int vop2_convert_csc_mode(int csc_mode)
> +{
> +	switch (csc_mode) {
> +	case V4L2_COLORSPACE_SMPTE170M:
> +	case V4L2_COLORSPACE_470_SYSTEM_M:
> +	case V4L2_COLORSPACE_470_SYSTEM_BG:
> +		return CSC_BT601L;
> +	case V4L2_COLORSPACE_REC709:
> +	case V4L2_COLORSPACE_SMPTE240M:
> +	case V4L2_COLORSPACE_DEFAULT:
> +		return CSC_BT709L;
> +	case V4L2_COLORSPACE_JPEG:
> +		return CSC_BT601F;
> +	case V4L2_COLORSPACE_BT2020:
> +		return CSC_BT2020;
> +	default:
> +		return CSC_BT709L;
> +	}
> +}
> +
> +/*
> + * colorspace path:
> + *      Input        Win csc                     Output
> + * 1. YUV(2020)  --> Y2R->2020To709->R2Y   --> YUV_OUTPUT(601/709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 2. YUV(2020)  --> bypasss               --> YUV_OUTPUT(2020)
> + *    RGB        --> 709To2020->R2Y       __/
> + *
> + * 3. YUV(2020)  --> Y2R->2020To709        --> RGB_OUTPUT(709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 4. YUV(601/709)-> Y2R->709To2020->R2Y   --> YUV_OUTPUT(2020)
> + *    RGB        --> 709To2020->R2Y       __/
> + *
> + * 5. YUV(601/709)-> bypass                --> YUV_OUTPUT(709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 6. YUV(601/709)-> bypass                --> YUV_OUTPUT(601)
> + *    RGB        --> R2Y(601)             __/
> + *
> + * 7. YUV        --> Y2R(709)              --> RGB_OUTPUT(709)
> + *    RGB        --> bypass               __/
> + *
> + * 8. RGB        --> 709To2020->R2Y        --> YUV_OUTPUT(2020)
> + *
> + * 9. RGB        --> R2Y(709)              --> YUV_OUTPUT(709)
> + *
> + * 10. RGB       --> R2Y(601)              --> YUV_OUTPUT(601)
> + *
> + * 11. RGB       --> bypass                --> RGB_OUTPUT(709)
> + */
> +
> +static void vop2_setup_csc_mode(struct vop2_video_port *vp,
> +				struct vop2_win *win,
> +				struct drm_plane_state *pstate)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
> +	int is_input_yuv = pstate->fb->format->is_yuv;
> +	int is_output_yuv = is_yuv_output(vcstate->bus_format);
> +	int input_csc = V4L2_COLORSPACE_DEFAULT;
> +	int output_csc = vcstate->color_space;
> +	bool r2y_en, y2r_en;
> +	int csc_mode;
> +
> +	if (is_input_yuv && !is_output_yuv) {
> +		y2r_en = true;
> +		r2y_en = false;
> +		csc_mode = vop2_convert_csc_mode(input_csc);
> +	} else if (!is_input_yuv && is_output_yuv) {
> +		y2r_en = false;
> +		r2y_en = true;
> +		csc_mode = vop2_convert_csc_mode(output_csc);
> +	} else {
> +		y2r_en = false;
> +		r2y_en = false;
> +		csc_mode = false;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en);
> +	vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en);
> +	vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
> +}
> +
> +static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq);
> +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq);
> +}
> +
> +static void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16);
> +}
> +
> +static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
> +{
> +	int ret;
> +
> +	ret = clk_prepare_enable(vop2->hclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable hclk - %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(vop2->aclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable aclk - %d\n", ret);
> +		goto err;
> +	}
> +
> +	return 0;
> +err:
> +	clk_disable_unprepare(vop2->hclk);
> +
> +	return ret;
> +}
> +
> +static void vop2_enable(struct vop2 *vop2)
> +{
> +	int ret;
> +
> +	ret = pm_runtime_get_sync(vop2->dev);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret);
> +		return;
> +	}
> +
> +	ret = vop2_core_clks_prepare_enable(vop2);
> +	if (ret) {
> +		pm_runtime_put_sync(vop2->dev);
> +		return;
> +	}
> +
> +	if (vop2->data->soc_id == 3566)
> +		vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
> +
> +	vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> +
> +	/*
> +	 * Disable auto gating, this is a workaround to
> +	 * avoid display image shift when a window enabled.
> +	 */
> +	regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL,
> +			  RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN);
> +
> +	vop2_writel(vop2, RK3568_SYS0_INT_CLR,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS0_INT_EN,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS1_INT_CLR,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS1_INT_EN,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +}
> +
> +static void vop2_disable(struct vop2 *vop2)
> +{
> +	pm_runtime_put_sync(vop2->dev);
> +
> +	clk_disable_unprepare(vop2->aclk);
> +	clk_disable_unprepare(vop2->hclk);
> +}
> +
> +static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
> +				     struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	int ret;
> +
> +	vop2_lock(vop2);
> +
> +	drm_crtc_vblank_off(crtc);
> +
> +	/*
> +	 * Vop standby will take effect at end of current frame,
> +	 * if dsp hold valid irq happen, it means standby complete.
> +	 *
> +	 * we must wait standby complete when we want to disable aclk,
> +	 * if not, memory bus maybe dead.
> +	 */
> +	reinit_completion(&vp->dsp_hold_completion);
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
> +
> +	ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
> +					  msecs_to_jiffies(50));
> +	if (!ret)
> +		drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
> +
> +	vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
> +
> +	clk_disable_unprepare(vp->dclk);
> +
> +	vop2->enable_count--;
> +
> +	if (!vop2->enable_count)
> +		vop2_disable(vop2);
> +
> +	vop2_unlock(vop2);
> +
> +	if (crtc->state->event && !crtc->state->active) {
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +
> +		crtc->state->event = NULL;
> +	}
> +}
> +
> +static int vop2_plane_atomic_check(struct drm_plane *plane,
> +				   struct drm_atomic_state *astate)
> +{
> +	struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane);
> +	struct drm_framebuffer *fb = pstate->fb;
> +	struct drm_crtc *crtc = pstate->crtc;
> +	struct drm_crtc_state *cstate;
> +	struct vop2_video_port *vp;
> +	struct vop2 *vop2;
> +	const struct vop2_data *vop2_data;
> +	struct drm_rect *dest = &pstate->dst;
> +	struct drm_rect *src = &pstate->src;
> +	int min_scale = FRAC_16_16(1, 8);
> +	int max_scale = FRAC_16_16(8, 1);
> +	int format;
> +	int ret;
> +
> +	if (!crtc)
> +		return 0;
> +
> +	vp = to_vop2_video_port(crtc);
> +	vop2 = vp->vop2;
> +	vop2_data = vop2->data;
> +
> +	cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc);
> +	if (WARN_ON(!cstate))
> +		return -EINVAL;
> +
> +	ret = drm_atomic_helper_check_plane_state(pstate, cstate,
> +						  min_scale, max_scale,
> +						  true, true);
> +	if (ret)
> +		return ret;
> +
> +	if (!pstate->visible)
> +		return 0;
> +
> +	format = vop2_convert_format(fb->format->format);
> +	if (format < 0)
> +		return format;
> +
> +	if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 ||
> +	    drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) {
> +		drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
> +			  drm_rect_width(src) >> 16, drm_rect_height(src) >> 16,
> +			  drm_rect_width(dest), drm_rect_height(dest));
> +		pstate->visible = false;
> +		return 0;
> +	}
> +
> +	if (drm_rect_width(src) >> 16 > vop2_data->max_input.width ||
> +	    drm_rect_height(src) >> 16 > vop2_data->max_input.height) {
> +		drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
> +			  drm_rect_width(src) >> 16,
> +			  drm_rect_height(src) >> 16,
> +			  vop2_data->max_input.width,
> +			  vop2_data->max_input.height);
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Src.x1 can be odd when do clip, but yuv plane start point
> +	 * need align with 2 pixel.
> +	 */
> +	if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
> +		drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vop2_plane_atomic_disable(struct drm_plane *plane,
> +				      struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state, plane);
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2 *vop2 = win->vop2;
> +
> +	drm_dbg(vop2->drm, "%s disable\n", win->data->name);
> +
> +	if (!old_pstate->crtc)
> +		return;
> +
> +	vop2_win_disable(win);
> +	vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0);
> +}
> +
> +/*
> + * The color key is 10 bit, so all format should
> + * convert to 10 bit here.
> + */
> +static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
> +{
> +	struct drm_plane_state *pstate = plane->state;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	struct vop2_win *win = to_vop2_win(plane);
> +	u32 color_key_en = 0;
> +	u32 r = 0;
> +	u32 g = 0;
> +	u32 b = 0;
> +
> +	if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) {
> +		vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0);
> +		return;
> +	}
> +
> +	switch (fb->format->format) {
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		r = (color_key & 0xf800) >> 11;
> +		g = (color_key & 0x7e0) >> 5;
> +		b = (color_key & 0x1f);
> +		r <<= 5;
> +		g <<= 4;
> +		b <<= 5;
> +		color_key_en = 1;
> +		break;
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		r = (color_key & 0xff0000) >> 16;
> +		g = (color_key & 0xff00) >> 8;
> +		b = (color_key & 0xff);
> +		r <<= 2;
> +		g <<= 2;
> +		b <<= 2;
> +		color_key_en = 1;
> +		break;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en);
> +	vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b);
> +}
> +
> +static void vop2_plane_atomic_update(struct drm_plane *plane,
> +				     struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *pstate = plane->state;
> +	struct drm_crtc *crtc = pstate->crtc;
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
> +	struct vop2 *vop2 = win->vop2;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	u32 bpp = fb->format->cpp[0] * 8;
> +	u32 actual_w, actual_h, dsp_w, dsp_h;
> +	u32 act_info, dsp_info;
> +	u32 format;
> +	u32 afbc_format;
> +	u32 rb_swap;
> +	u32 uv_swap;
> +	struct drm_rect *src = &pstate->src;
> +	struct drm_rect *dest = &pstate->dst;
> +	u32 afbc_tile_num;
> +	u32 transform_offset;
> +	bool dither_up;
> +	bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
> +	bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
> +	bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
> +	bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
> +	struct rockchip_gem_object *rk_obj;
> +	unsigned long offset;
> +	bool afbc_en;
> +	dma_addr_t yrgb_mst;
> +	dma_addr_t uv_mst;
> +
> +	/*
> +	 * can't update plane when vop2 is disabled.
> +	 */
> +	if (WARN_ON(!crtc))
> +		return;
> +
> +	if (!pstate->visible) {
> +		vop2_plane_atomic_disable(plane, state);
> +		return;
> +	}
> +
> +	afbc_en = rockchip_afbc(plane, fb->modifier);
> +
> +	offset = (src->x1 >> 16) * fb->format->cpp[0];
> +
> +	/*
> +	 * AFBC HDR_PTR must set to the zero offset of the framebuffer.
> +	 */
> +	if (afbc_en)
> +		offset = 0;
> +	else if (pstate->rotation & DRM_MODE_REFLECT_Y)
> +		offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
> +	else
> +		offset += (src->y1 >> 16) * fb->pitches[0];
> +
> +	rk_obj = to_rockchip_obj(fb->obj[0]);
> +
> +	yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
> +	if (fb->format->is_yuv) {
> +		int hsub = fb->format->hsub;
> +		int vsub = fb->format->vsub;
> +
> +		offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub;
> +		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
> +
> +		if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en)
> +			offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub;
> +
> +		rk_obj = to_rockchip_obj(fb->obj[0]);
> +		uv_mst = rk_obj->dma_addr + offset + fb->offsets[1];
> +	}
> +
> +	actual_w = drm_rect_width(src) >> 16;
> +	actual_h = drm_rect_height(src) >> 16;
> +	dsp_w = drm_rect_width(dest);
> +
> +	if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
> +		drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
> +			  vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
> +		dsp_w = adjusted_mode->hdisplay - dest->x1;
> +		if (dsp_w < 4)
> +			dsp_w = 4;
> +		actual_w = dsp_w * actual_w / drm_rect_width(dest);
> +	}
> +
> +	dsp_h = drm_rect_height(dest);
> +
> +	if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
> +		drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
> +			  vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
> +		dsp_h = adjusted_mode->vdisplay - dest->y1;
> +		if (dsp_h < 4)
> +			dsp_h = 4;
> +		actual_h = dsp_h * actual_h / drm_rect_height(dest);
> +	}
> +
> +	/*
> +	 * This is workaround solution for IC design:
> +	 * esmart can't support scale down when actual_w % 16 == 1.
> +	 */
> +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> +		if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
> +			drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
> +				vp->id, win->data->name, actual_w);
> +			actual_w -= 1;
> +		}
> +	}
> +
> +	if (afbc_en && actual_w % 4) {
> +		drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
> +			  vp->id, win->data->name, actual_w);
> +		actual_w = ALIGN_DOWN(actual_w, 4);
> +	}
> +
> +	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
> +	dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff);
> +
> +	format = vop2_convert_format(fb->format->format);
> +
> +	drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n",
> +		      vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h,
> +		      dest->x1, dest->y1,
> +		      &fb->format->format,
> +		      afbc_en ? "AFBC" : "", &yrgb_mst);
> +
> +	if (afbc_en) {
> +		u32 stride;
> +
> +		/* the afbc superblock is 16 x 16 */
> +		afbc_format = vop2_convert_afbc_format(fb->format->format);
> +
> +		/* Enable color transform for YTR */
> +		if (fb->modifier & AFBC_FORMAT_MOD_YTR)
> +			afbc_format |= (1 << 4);
> +
> +		afbc_tile_num = ALIGN(actual_w, 16) >> 4;
> +
> +		/*
> +		 * AFBC pic_vir_width is count by pixel, this is different
> +		 * with WIN_VIR_STRIDE.
> +		 */
> +		stride = (fb->pitches[0] << 3) / bpp;
> +		if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
> +			drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligened\n",
> +				  vp->id, win->data->name, stride);
> +
> +		rb_swap = vop2_afbc_rb_swap(fb->format->format);
> +		uv_swap = vop2_afbc_uv_swap(fb->format->format);
> +		/*
> +		 * This is a workaround for crazy IC design, Cluster
> +		 * and Esmart/Smart use different format configuration map:
> +		 * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart.
> +		 *
> +		 * This is one thing we can make the convert simple:
> +		 * AFBCD decode all the YUV data to YUV444. So we just
> +		 * set all the yuv 10 bit to YUV444_10.
> +		 */
> +		if (fb->format->is_yuv && (bpp == 10))
> +			format = VOP2_CLUSTER_YUV444_10;
> +
> +		if (vop2_cluster_window(win))
> +			vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
> +		vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
> +		vop2_win_write(win, VOP2_WIN_AFBC_RB_SWAP, rb_swap);
> +		vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap);
> +		vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
> +		vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
> +		if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) {
> +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0);
> +			transform_offset = vop2_afbc_transform_offset(pstate, false);
> +		} else {
> +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 1);
> +			transform_offset = vop2_afbc_transform_offset(pstate, true);
> +		}
> +		vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
> +		vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset);
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1));
> +		vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16)));
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride);
> +		vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num);
> +		vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror);
> +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270);
> +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90);
> +	} else {
> +		vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4));
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror);
> +
> +	if (rotate_90 || rotate_270) {
> +		act_info = swahw32(act_info);
> +		actual_w = drm_rect_height(src) >> 16;
> +		actual_h = drm_rect_width(src) >> 16;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_FORMAT, format);
> +	vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst);
> +
> +	rb_swap = vop2_win_rb_swap(fb->format->format);
> +	vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap);
> +	if (!vop2_cluster_window(win)) {
> +		uv_swap = vop2_win_uv_swap(fb->format->format);
> +		vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
> +	}
> +
> +	if (fb->format->is_yuv) {
> +		vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4));
> +		vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst);
> +	}
> +
> +	vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format);
> +	if (!vop2_cluster_window(win))
> +		vop2_plane_setup_color_key(plane, 0);
> +	vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info);
> +	vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info);
> +	vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff));
> +
> +	vop2_setup_csc_mode(vp, win, pstate);
> +
> +	dither_up = vop2_win_dither_up(fb->format->format);
> +	vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up);
> +
> +	vop2_win_write(win, VOP2_WIN_ENABLE, 1);
> +
> +	if (vop2_cluster_window(win)) {
> +		int lb_mode = vop2_get_cluster_lb_mode(win, pstate);
> +
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode);
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1);
> +	}
> +}
> +
> +static const struct drm_plane_helper_funcs vop2_plane_helper_funcs = {
> +	.atomic_check = vop2_plane_atomic_check,
> +	.atomic_update = vop2_plane_atomic_update,
> +	.atomic_disable = vop2_plane_atomic_disable,
> +};
> +
> +static const struct drm_plane_funcs vop2_plane_funcs = {
> +	.update_plane	= drm_atomic_helper_update_plane,
> +	.disable_plane	= drm_atomic_helper_disable_plane,
> +	.destroy = drm_plane_cleanup,
> +	.reset = drm_atomic_helper_plane_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> +	.format_mod_supported = rockchip_vop2_mod_supported,
> +};
> +
> +static int vop2_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD);
> +
> +	return 0;
> +}
> +
> +static void vop2_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD);
> +}
> +
> +static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
> +				 const struct drm_display_mode *mode,
> +				 struct drm_display_mode *adj_mode)
> +{
> +	drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V |
> +					CRTC_STEREO_DOUBLE);
> +
> +	return true;
> +}
> +
> +static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	switch (vcstate->bus_format) {
> +	case MEDIA_BUS_FMT_RGB565_1X16:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> +		break;
> +	case MEDIA_BUS_FMT_RGB666_1X18:
> +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> +	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> +		*dsp_ctrl |= RGB888_TO_RGB666;
> +		break;
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA)
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> +
> +	*dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL,
> +				DITHER_DOWN_ALLEGRO);
> +}
> +
> +static void vop2_post_config(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	u16 vtotal = mode->crtc_vtotal;
> +	u16 hdisplay = mode->crtc_hdisplay;
> +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> +	u16 vdisplay = mode->crtc_vdisplay;
> +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> +	u32 left_margin = 100, right_margin = 100;
> +	u32 top_margin = 100, bottom_margin = 100;
> +	u16 hsize = hdisplay * (left_margin + right_margin) / 200;
> +	u16 vsize = vdisplay * (top_margin + bottom_margin) / 200;
> +	u16 hact_end, vact_end;
> +	u32 val;
> +
> +	vsize = rounddown(vsize, 2);
> +	hsize = rounddown(hsize, 2);
> +	hact_st += hdisplay * (100 - left_margin) / 200;
> +	hact_end = hact_st + hsize;
> +	val = hact_st << 16;
> +	val |= hact_end;
> +	vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val);
> +	vact_st += vdisplay * (100 - top_margin) / 200;
> +	vact_end = vact_st + vsize;
> +	val = vact_st << 16;
> +	val |= vact_end;
> +	vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val);
> +	val = scl_cal_scale2(vdisplay, vsize) << 16;
> +	val |= scl_cal_scale2(hdisplay, hsize);
> +	vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val);
> +
> +	val = 0;
> +	if (hdisplay != hsize)
> +		val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN;
> +	if (vdisplay != vsize)
> +		val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN;
> +	vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val);
> +
> +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> +		u16 vact_st_f1 = vtotal + vact_st + 1;
> +		u16 vact_end_f1 = vact_st_f1 + vsize;
> +
> +		val = vact_st_f1 << 16 | vact_end_f1;
> +		vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
> +	}
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_BG, 0);
> +}
> +
> +static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
> +				u32 polflags)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	u32 die, dip;
> +
> +	die = vop2_readl(vop2, RK3568_DSP_IF_EN);
> +	dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
> +
> +	switch (id) {
> +	case RK3568_VOP2_EP_RGB:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_RGB |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
> +		if (polflags & POLFLAG_DCLK_INV)
> +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
> +		else
> +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16));
> +		break;
> +	case RK3568_VOP2_EP_HDMI:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_HDMI |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id);
> +		break;
> +	case RK3568_VOP2_EP_EDP:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_EDP |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id);
> +		break;
> +	case RK3568_VOP2_EP_MIPI0:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_MIPI1:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_LVDS0:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_LVDS1:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> +		break;
> +	default:
> +		return;
> +	};
> +
> +	dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
> +
> +	vop2_writel(vop2, RK3568_DSP_IF_EN, die);
> +	vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
> +}
> +
> +static int us_to_vertical_line(struct drm_display_mode *mode, int us)
> +{
> +	return us * mode->clock / mode->htotal / 1000;
> +}
> +
> +static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
> +				    struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	const struct vop2_data *vop2_data = vop2->data;
> +	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	unsigned long clock = mode->crtc_clock * 1000;
> +	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
> +	u16 hdisplay = mode->crtc_hdisplay;
> +	u16 htotal = mode->crtc_htotal;
> +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> +	u16 hact_end = hact_st + hdisplay;
> +	u16 vdisplay = mode->crtc_vdisplay;
> +	u16 vtotal = mode->crtc_vtotal;
> +	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
> +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> +	u16 vact_end = vact_st + vdisplay;
> +	u8 out_mode;
> +	u32 dsp_ctrl = 0;
> +	int act_end;
> +	u32 val, polflags;
> +	int ret;
> +	struct drm_encoder *encoder;
> +
> +	drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n",
> +		hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
> +		drm_mode_vrefresh(mode), vcstate->output_type, vp->id);
> +
> +	vop2_lock(vop2);
> +
> +	ret = clk_prepare_enable(vp->dclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
> +			      vp->id, ret);
> +		return;
> +	}
> +
> +	if (!vop2->enable_count)
> +		vop2_enable(vop2);
> +
> +	vop2->enable_count++;
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY);
> +
> +	polflags = 0;
> +	if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
> +		polflags |= POLFLAG_DCLK_INV;
> +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> +		polflags |= BIT(HSYNC_POSITIVE);
> +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> +		polflags |= BIT(VSYNC_POSITIVE);
> +
> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> +		struct device_node *node, *parent;
> +
> +		parent = of_get_parent(rkencoder->port);
> +
> +		for_each_endpoint_of_node(parent, node) {

Is there any hurt directly use our downstream vendor kernel method here: 
use vcstate->output_if set by encoder driver to get which interface we 
should enable here?

You method is ok with device tree,  but it tied up this driver to device 
tree, we are now tring to extend vop2 driver work with ACPI, so we hope 
this driver can be much more flexible.

> +			struct device_node *crtc_port = of_graph_get_remote_port(node);
> +			struct device_node *epn;
> +			struct of_endpoint endpoint;
> +
> +			if (crtc->port != crtc_port) {
> +				of_node_put(crtc_port);
> +				continue;
> +			}
> +
> +			of_node_put(crtc_port);
> +
> +			epn = of_graph_get_remote_endpoint(node);
> +			of_graph_parse_endpoint(epn, &endpoint);
> +			of_node_put(epn);
> +
> +			drm_dbg(vop2->drm, "vp%d is connected to %s, id %d\n",
> +					   vp->id, encoder->name, endpoint.id);
> +			rk3568_set_intf_mux(vp, endpoint.id, polflags);
> +		}
> +		of_node_put(parent);
> +	}
> +
> +	if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
> +	     !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT))
> +		out_mode = ROCKCHIP_OUT_MODE_P888;
> +	else
> +		out_mode = vcstate->output_mode;
> +
> +	dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode);
> +
> +	if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP;
> +
> +	if (is_yuv_output(vcstate->bus_format))
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y;
> +
> +	vop2_dither_setup(crtc, &dsp_ctrl);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len);
> +	val = hact_st << 16;
> +	val |= hact_end;
> +	vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val);
> +
> +	val = vact_st << 16;
> +	val |= vact_end;
> +	vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val);
> +
> +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> +		u16 vact_st_f1 = vtotal + vact_st + 1;
> +		u16 vact_end_f1 = vact_st_f1 + vdisplay;
> +
> +		val = vact_st_f1 << 16 | vact_end_f1;
> +		vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val);
> +
> +		val = vtotal << 16 | (vtotal + vsync_len);
> +		vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val);
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE;
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL;
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN;
> +		vtotal += vtotal + 1;
> +		act_end = vact_end_f1;
> +	} else {
> +		act_end = vact_end;
> +	}
> +
> +	vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id),
> +		    (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len);
> +
> +	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
> +		clock *= 2;
> +	}
> +
> +	vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
> +
> +	clk_set_rate(vp->dclk, clock);
> +
> +	vop2_post_config(crtc);
> +
> +	vop2_cfg_done(vp);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
> +
> +	drm_crtc_vblank_on(crtc);
> +
> +	vop2_unlock(vop2);
> +}
> +
> +static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
> +				  struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_plane *plane;
> +	int nplanes = 0;
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +
> +	drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
> +		nplanes++;
> +
> +	if (nplanes > vp->nlayers)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static bool is_opaque(u16 alpha)
> +{
> +	return (alpha >> 8) == 0xff;
> +}
> +
> +static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config,
> +			     struct vop2_alpha *alpha)
> +{
> +	int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1;
> +	int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1;
> +	int src_color_mode = alpha_config->src_premulti_en ?
> +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> +	int dst_color_mode = alpha_config->dst_premulti_en ?
> +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> +
> +	alpha->src_color_ctrl.val = 0;
> +	alpha->dst_color_ctrl.val = 0;
> +	alpha->src_alpha_ctrl.val = 0;
> +	alpha->dst_alpha_ctrl.val = 0;
> +
> +	if (!alpha_config->src_pixel_alpha_en)
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> +	else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en)
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> +	else
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> +
> +	alpha->src_color_ctrl.bits.alpha_en = 1;
> +
> +	if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) {
> +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> +	} else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) {
> +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE;
> +	} else {
> +		alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> +	}
> +	alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8;
> +	alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +
> +	alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +	alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> +	alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8;
> +	alpha->dst_color_ctrl.bits.color_mode = dst_color_mode;
> +	alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> +
> +	alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode;
> +	alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +	alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE;
> +
> +	alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en)
> +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> +	else
> +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> +	alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION;
> +	alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> +}
> +
> +static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id)
> +{
> +	struct vop2_video_port *vp;
> +	int used_layer = 0;
> +	int i;
> +
> +	for (i = 0; i < port_id; i++) {
> +		vp = &vop2->vps[i];
> +		used_layer += hweight32(vp->win_mask);
> +	}
> +
> +	return used_layer;
> +}
> +
> +static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win)
> +{
> +	u32 offset = (main_win->data->phys_id * 0x10);
> +	struct vop2_alpha_config alpha_config;
> +	struct vop2_alpha alpha;
> +	struct drm_plane_state *bottom_win_pstate;
> +	bool src_pixel_alpha_en = false;
> +	u16 src_glb_alpha_val, dst_glb_alpha_val;
> +	bool premulti_en = false;
> +	bool swap = false;
> +
> +	/* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */
> +	bottom_win_pstate = main_win->base.state;
> +	src_glb_alpha_val = 0;
> +	dst_glb_alpha_val = main_win->base.state->alpha;
> +
> +	if (!bottom_win_pstate->fb)
> +		return;
> +
> +	alpha_config.src_premulti_en = premulti_en;
> +	alpha_config.dst_premulti_en = false;
> +	alpha_config.src_pixel_alpha_en = src_pixel_alpha_en;
> +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> +	alpha_config.src_glb_alpha_value = src_glb_alpha_val;
> +	alpha_config.dst_glb_alpha_value = dst_glb_alpha_val;
> +	vop2_parse_alpha(&alpha_config, &alpha);
> +
> +	alpha.src_color_ctrl.bits.src_dst_swap = swap;
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset,
> +		    alpha.src_color_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset,
> +		    alpha.dst_color_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset,
> +		    alpha.src_alpha_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset,
> +		    alpha.dst_alpha_ctrl.val);
> +}
> +
> +static void vop2_setup_alpha(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_framebuffer *fb;
> +	struct vop2_alpha_config alpha_config;
> +	struct vop2_alpha alpha;
> +	struct drm_plane *plane;
> +	int pixel_alpha_en;
> +	int premulti_en, gpremulti_en = 0;
> +	int mixer_id;
> +	u32 offset;
> +	bool bottom_layer_alpha_en = false;
> +	u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE;
> +
> +	mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id);
> +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> +
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		if (plane->state->normalized_zpos == 0 &&
> +		    !is_opaque(plane->state->alpha) &&
> +		    !vop2_cluster_window(win)) {
> +			/*
> +			 * If bottom layer have global alpha effect [except cluster layer,
> +			 * because cluster have deal with bottom layer global alpha value
> +			 * at cluster mix], bottom layer mix need deal with global alpha.
> +			 */
> +			bottom_layer_alpha_en = true;
> +			dst_global_alpha = plane->state->alpha;
> +		}
> +	}
> +
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +		int zpos = plane->state->normalized_zpos;
> +
> +		if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
> +			premulti_en = 1;
> +		else
> +			premulti_en = 0;
> +
> +		plane = &win->base;
> +		fb = plane->state->fb;
> +
> +		pixel_alpha_en = fb->format->has_alpha;
> +
> +		alpha_config.src_premulti_en = premulti_en;
> +
> +		if (bottom_layer_alpha_en && zpos == 1) {
> +			gpremulti_en = premulti_en;
> +			/* Cd = Cs + (1 - As) * Cd * Agd */
> +			alpha_config.dst_premulti_en = false;
> +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> +			alpha_config.dst_glb_alpha_value = dst_global_alpha;
> +		} else if (vop2_cluster_window(win)) {
> +			/* Mix output data only have pixel alpha */
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = true;
> +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +		} else {
> +			/* Cd = Cs + (1 - As) * Cd */
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +		}
> +
> +		vop2_parse_alpha(&alpha_config, &alpha);
> +
> +		offset = (mixer_id + zpos - 1) * 0x10;
> +		vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset,
> +			    alpha.src_color_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset,
> +			    alpha.dst_color_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset,
> +			    alpha.src_alpha_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset,
> +			    alpha.dst_alpha_ctrl.val);
> +	}
> +
> +	if (vp->id == 0) {
> +		if (bottom_layer_alpha_en) {
> +			/* Transfer pixel alpha to hdr mix */
> +			alpha_config.src_premulti_en = gpremulti_en;
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = true;
> +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			vop2_parse_alpha(&alpha_config, &alpha);
> +
> +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL,
> +				alpha.src_color_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL,
> +				alpha.dst_color_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL,
> +				alpha.src_alpha_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL,
> +				alpha.dst_alpha_ctrl.val);
> +		} else {
> +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0);
> +		}
> +	}
> +}
> +
> +static void vop2_setup_layer_mixer(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_plane *plane;
> +	u32 layer_sel = 0;
> +	u32 port_sel;
> +	unsigned int nlayer, ofs;
> +	struct drm_display_mode *adjusted_mode;
> +	u16 hsync_len;
> +	u16 hdisplay;
> +	u32 bg_dly;
> +	u32 pre_scan_dly;
> +	int i;
> +	struct vop2_video_port *vp0 = &vop2->vps[0];
> +	struct vop2_video_port *vp1 = &vop2->vps[1];
> +	struct vop2_video_port *vp2 = &vop2->vps[2];
> +
> +	adjusted_mode = &vp->crtc.state->adjusted_mode;
> +	hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
> +	hdisplay = adjusted_mode->crtc_hdisplay;
> +
> +	bg_dly = vp->data->pre_scan_max_dly[3];
> +	vop2_writel(vop2, RK3568_VP_BG_MIX_CTRL(vp->id),
> +			    FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly));
> +
> +	pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len;
> +	vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly);
> +
> +	vop2_writel(vop2, RK3568_OVL_CTRL, 0);
> +	port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
> +	port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT;
> +
> +	if (vp0->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX,
> +				     vp0->nlayers - 1);
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8);
> +
> +	if (vp1->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX,
> +				     (vp0->nlayers + vp1->nlayers - 1));
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> +
> +	if (vp2->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX,
> +			(vp2->nlayers + vp1->nlayers + vp0->nlayers - 1));
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> +
> +	layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
> +
> +	ofs = 0;
> +	for (i = 0; i < vp->id; i++)
> +		ofs += vop2->vps[i].nlayers;
> +
> +	nlayer = 0;
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		switch (win->data->phys_id) {
> +		case ROCKCHIP_VOP2_CLUSTER0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_CLUSTER1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_SMART0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_SMART1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id);
> +			break;
> +		}
> +
> +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, win->data->layer_sel_id);
> +		nlayer++;
> +	}
> +
> +	/* configure unused layers to 0x5 (reserved) */
> +	for (; nlayer < 3; nlayer++) {
> +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5);
> +	}
> +
> +	vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel);
> +	vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
> +	vop2_writel(vop2, RK3568_OVL_CTRL, RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD);
> +}
> +
> +static void vop2_setup_dly_for_windows(struct vop2 *vop2)
> +{
> +	struct vop2_win *win;
> +	int i = 0;
> +	u32 cdly = 0, sdly = 0;
> +
> +	for (i = 0; i < vop2->data->win_size; i++) {
> +		u32 dly;
> +
> +		win = &vop2->win[i];
> +		dly = win->delay;
> +
> +		switch (win->data->phys_id) {
> +		case ROCKCHIP_VOP2_CLUSTER0:
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly);
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_CLUSTER1:
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly);
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART0:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART1:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_SMART0:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_SMART1:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly);
> +			break;
> +		}
> +	}
> +
> +	vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly);
> +	vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly);
> +}
> +
> +static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_plane *plane;
> +
> +	vp->win_mask = 0;
> +
> +	drm_atomic_crtc_for_each_plane(plane, crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT];
> +
> +		vp->win_mask |= BIT(win->data->phys_id);
> +
> +		if (vop2_cluster_window(win))
> +			vop2_setup_cluster_alpha(vop2, win);
> +	}
> +
> +	if (!vp->win_mask)
> +		return;
> +
> +	vop2_setup_layer_mixer(vp);
> +	vop2_setup_alpha(vp);
> +	vop2_setup_dly_for_windows(vop2);
> +}
> +
> +static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_post_config(crtc);
> +
> +	vop2_cfg_done(vp);
> +
> +	spin_lock_irq(&crtc->dev->event_lock);
> +
> +	if (crtc->state->event) {
> +		WARN_ON(drm_crtc_vblank_get(crtc));
> +		vp->event = crtc->state->event;
> +		crtc->state->event = NULL;
> +	}
> +
> +	spin_unlock_irq(&crtc->dev->event_lock);
> +}
> +
> +static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
> +	.mode_fixup = vop2_crtc_mode_fixup,
> +	.atomic_check = vop2_crtc_atomic_check,
> +	.atomic_begin = vop2_crtc_atomic_begin,
> +	.atomic_flush = vop2_crtc_atomic_flush,
> +	.atomic_enable = vop2_crtc_atomic_enable,
> +	.atomic_disable = vop2_crtc_atomic_disable,
> +};
> +
> +static void vop2_crtc_reset(struct drm_crtc *crtc)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	if (crtc->state) {
> +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
> +		kfree(vcstate);
> +	}
> +
> +	vcstate = kzalloc(sizeof(*vcstate), GFP_KERNEL);
> +	if (!vcstate)
> +		return;
> +
> +	crtc->state = &vcstate->base;
> +	crtc->state->crtc = crtc;
> +}
> +
> +static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc)
> +{
> +	struct rockchip_crtc_state *vcstate, *old_vcstate;
> +
> +	old_vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	vcstate = kmemdup(old_vcstate, sizeof(*old_vcstate), GFP_KERNEL);
> +	if (!vcstate)
> +		return NULL;
> +
> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
> +
> +	return &vcstate->base;
> +}
> +
> +static void vop2_crtc_destroy_state(struct drm_crtc *crtc,
> +				    struct drm_crtc_state *state)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state);
> +
> +	__drm_atomic_helper_crtc_destroy_state(&vcstate->base);
> +	kfree(vcstate);
> +}
> +
> +static const struct drm_crtc_funcs vop2_crtc_funcs = {
> +	.set_config = drm_atomic_helper_set_config,
> +	.page_flip = drm_atomic_helper_page_flip,
> +	.destroy = drm_crtc_cleanup,
> +	.reset = vop2_crtc_reset,
> +	.atomic_duplicate_state = vop2_crtc_duplicate_state,
> +	.atomic_destroy_state = vop2_crtc_destroy_state,
> +	.enable_vblank = vop2_crtc_enable_vblank,
> +	.disable_vblank = vop2_crtc_disable_vblank,
> +};
> +
> +static irqreturn_t vop2_isr(int irq, void *data)
> +{
> +	struct vop2 *vop2 = data;
> +	const struct vop2_data *vop2_data = vop2->data;
> +	u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM];
> +	int ret = IRQ_NONE;
> +	int i;
> +
> +	/*
> +	 * The irq is shared with the iommu. If the runtime-pm state of the
> +	 * vop2-device is disabled the irq has to be targeted at the iommu.
> +	 */
> +	if (!pm_runtime_get_if_in_use(vop2->dev))
> +		return IRQ_NONE;
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +		struct drm_crtc *crtc = &vp->crtc;
> +		u32 irqs;
> +
> +		irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
> +		vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
> +
> +		if (irqs & VP_INT_DSP_HOLD_VALID) {
> +			complete(&vp->dsp_hold_completion);
> +			ret = IRQ_HANDLED;
> +		}
> +
> +		if (irqs & VP_INT_FS_FIELD) {
> +			drm_crtc_handle_vblank(crtc);
> +			spin_lock(&crtc->dev->event_lock);
> +			if (vp->event) {
> +				u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
> +
> +				if (!(val & BIT(vp->id))) {
> +					drm_crtc_send_vblank_event(crtc, vp->event);
> +					vp->event = NULL;
> +					drm_crtc_vblank_put(crtc);
> +				}
> +			}
> +			spin_unlock(&crtc->dev->event_lock);
> +
> +			ret = IRQ_HANDLED;
> +		}
> +
> +		if (irqs & VP_INT_POST_BUF_EMPTY) {
> +			drm_err_ratelimited(vop2->drm,
> +					    "POST_BUF_EMPTY irq err at vp%d\n",
> +					    vp->id);
> +			ret = IRQ_HANDLED;
> +		}
> +	}
> +
> +	axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS);
> +	vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]);
> +	axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS);
> +	vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]);
> +
> +	for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) {
> +		if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) {
> +			drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n");
> +			ret = IRQ_HANDLED;
> +		}
> +	}
> +
> +	pm_runtime_put(vop2->dev);
> +
> +	return ret;
> +}
> +
> +static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win,
> +			   unsigned long possible_crtcs)
> +{
> +	const struct vop2_win_data *win_data = win->data;
> +	unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +				  BIT(DRM_MODE_BLEND_PREMULTI) |
> +				  BIT(DRM_MODE_BLEND_COVERAGE);
> +	int ret;
> +
> +	ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs,
> +				       &vop2_plane_funcs, win_data->formats,
> +				       win_data->nformats,
> +				       win_data->format_modifiers,
> +				       win->type, win_data->name);
> +	if (ret) {
> +		drm_err(vop2->drm, "failed to initialize plane %d\n", ret);
> +		return ret;
> +	}
> +
> +	drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs);
> +
> +	if (win->data->supported_rotations)
> +		drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0,
> +						   DRM_MODE_ROTATE_0 |
> +						   win->data->supported_rotations);
> +	drm_plane_create_alpha_property(&win->base);
> +	drm_plane_create_blend_mode_property(&win->base, blend_caps);
> +	drm_plane_create_zpos_property(&win->base, win->win_id, 0,
> +				       vop2->registered_num_wins - 1);
> +
> +	return 0;
> +}
> +
> +static struct vop2_video_port *get_activated_vp(struct vop2 *vop2, int n)
> +{
> +	int i, id = 0;
> +
> +	for (i = 0; i < vop2->data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +
> +		if (!vp->crtc.port)
> +			continue;
> +
> +		if (n == id)
> +			return vp;
> +		id++;
> +	}
> +
> +	return NULL;
> +}
> +
> +#define NR_LAYERS 6
> +
> +static int vop2_create_crtc(struct vop2 *vop2)
> +{
> +	const struct vop2_data *vop2_data = vop2->data;
> +	struct drm_device *drm = vop2->drm;
> +	struct device *dev = vop2->dev;
> +	struct drm_plane *plane;
> +	struct device_node *port;
> +	struct vop2_video_port *vp;
> +	u32 possible_crtcs;
> +	int i, nvp, nvps = 0;
> +	int ret;
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		const struct vop2_video_port_data *vp_data;
> +		struct device_node *np;
> +		char dclk_name[9];
> +
> +		vp_data = &vop2_data->vp[i];
> +		vp = &vop2->vps[i];
> +		vp->vop2 = vop2;
> +		vp->id = vp_data->id;
> +		vp->regs = vp_data->regs;
> +		vp->data = vp_data;
> +
> +		snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
> +		vp->dclk = devm_clk_get(vop2->dev, dclk_name);
> +		if (IS_ERR(vp->dclk)) {
> +			drm_err(vop2->drm, "failed to get %s\n", dclk_name);
> +			return PTR_ERR(vp->dclk);
> +		}
> +
> +		np = of_graph_get_remote_node(dev->of_node, i, -1);
> +		if (!np) {
> +			drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
> +			continue;
> +		}
> +		of_node_put(np);
> +
> +		port = of_graph_get_port_by_id(dev->of_node, i);
> +		if (!port) {
> +			drm_err(vop2->drm, "no port node found for video_port%d\n", i);
> +			return -ENOENT;
> +		}
> +
> +		vp->crtc.port = port;
> +		nvps++;
> +	}
> +
> +	nvp = 0;
> +	for (i = 0; i < vop2->registered_num_wins; i++) {
> +		struct vop2_win *win = &vop2->win[i];
> +
> +		if (win->type == DRM_PLANE_TYPE_PRIMARY) {
> +			vp = get_activated_vp(vop2, nvp);
> +
> +			if (vp) {
> +				possible_crtcs = BIT(nvp);
> +				vp->primary_plane = win;
> +
> +				nvp++;
> +			} else {
> +				/* change the unused primary window to overlay window */
> +				win->type = DRM_PLANE_TYPE_OVERLAY;
> +			}
> +		}
> +
> +		if (win->type == DRM_PLANE_TYPE_OVERLAY)
> +			possible_crtcs = (1 << vop2_data->nr_vps) - 1;
> +
> +		ret = vop2_plane_init(vop2, win, possible_crtcs);
> +
> +		if (ret) {
> +			drm_err(vop2->drm, "failed to init plane %s: %d\n",
> +				win->data->name, ret);
> +			return ret;
> +		}
> +	}
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		vp = &vop2->vps[i];
> +
> +		if (!vp->crtc.port)
> +			continue;
> +
> +		plane = &vp->primary_plane->base;
> +
> +		ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL,
> +						&vop2_crtc_funcs,
> +						"video_port%d", vp->id);
> +		if (ret) {
> +			drm_err(vop2->drm, "crtc init for video_port%d failed\n", i);
> +			return ret;
> +		}
> +
> +		drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs);
> +
> +		init_completion(&vp->dsp_hold_completion);
> +	}
> +
> +	for (i = 0; i < vop2->data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +
> +		if (vp->crtc.port)
> +			vp->nlayers = NR_LAYERS / nvps;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vop2_destroy_crtc(struct drm_crtc *crtc)
> +{
> +	of_node_put(crtc->port);
> +
> +	/*
> +	 * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane()
> +	 * references the CRTC.
> +	 */
> +	drm_crtc_cleanup(crtc);
> +}
> +
> +static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = {
> +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0),
> +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5),
> +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14),
> +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18),
> +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31),
> +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31),
> +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31),
> +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31),
> +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31),
> +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19),
> +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15),
> +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31),
> +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8),
> +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9),
> +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11),
> +
> +	/* Scale */
> +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15),
> +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31),
> +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15),
> +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13),
> +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3),
> +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28),
> +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29),
> +
> +	/* cluster regs */
> +	[VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1),
> +	[VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0),
> +	[VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7),
> +
> +	/* afbc regs */
> +	[VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6),
> +	[VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9),
> +	[VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10),
> +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4),
> +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7),
> +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8),
> +	[VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31),
> +	[VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31),
> +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15),
> +	[VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31),
> +	[VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0),
> +	[VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1),
> +	[VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2),
> +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3),
> +	[VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff },
> +	[VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
> +	[VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
> +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff },
> +	[VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff },
> +};
> +
> +static int vop2_cluster_init(struct vop2_win *win)
> +{
> +	struct vop2 *vop2 = win->vop2;
> +	struct reg_field *cluster_regs;
> +	int ret, i;
> +
> +	cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs),
> +			       GFP_KERNEL);
> +	if (!cluster_regs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++)
> +		if (cluster_regs[i].reg != 0xffffffff)
> +			cluster_regs[i].reg += win->offset;
> +
> +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> +					    cluster_regs,
> +					    ARRAY_SIZE(vop2_cluster_regs));
> +
> +	kfree(cluster_regs);
> +
> +	return ret;
> +};
> +
> +static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = {
> +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0),
> +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5),
> +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12),
> +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14),
> +	[VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16),
> +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31),
> +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31),
> +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28),
> +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31),
> +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31),
> +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17),
> +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15),
> +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31),
> +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0),
> +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1),
> +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3),
> +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31),
> +	[VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29),
> +	[VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31),
> +
> +	/* Scale */
> +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15),
> +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31),
> +	[VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15),
> +	[VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31),
> +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1),
> +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3),
> +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5),
> +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7),
> +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9),
> +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11),
> +	[VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13),
> +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15),
> +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17),
> +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8),
> +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9),
> +	[VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10),
> +	[VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11),
> +	[VOP2_WIN_XMIRROR] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff },
> +};
> +
> +static int vop2_esmart_init(struct vop2_win *win)
> +{
> +	struct vop2 *vop2 = win->vop2;
> +	struct reg_field *esmart_regs;
> +	int ret, i;
> +
> +	esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs),
> +			       GFP_KERNEL);
> +	if (!esmart_regs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++)
> +		if (esmart_regs[i].reg != 0xffffffff)
> +			esmart_regs[i].reg += win->offset;
> +
> +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> +					    esmart_regs,
> +					    ARRAY_SIZE(vop2_esmart_regs));
> +
> +	kfree(esmart_regs);
> +
> +	return ret;
> +};
> +
> +static int vop2_win_init(struct vop2 *vop2)
> +{
> +	const struct vop2_data *vop2_data = vop2->data;
> +	struct vop2_win *win;
> +	int i, ret;
> +
> +	for (i = 0; i < vop2_data->win_size; i++) {
> +		const struct vop2_win_data *win_data = &vop2_data->win[i];
> +
> +		win = &vop2->win[i];
> +		win->data = win_data;
> +		win->type = win_data->type;
> +		win->offset = win_data->base;
> +		win->win_id = i;
> +		win->vop2 = vop2;
> +		if (vop2_cluster_window(win))
> +			ret = vop2_cluster_init(win);
> +		else
> +			ret = vop2_esmart_init(win);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	vop2->registered_num_wins = vop2_data->win_size;
> +
> +	return 0;
> +}
> +
> +/*
> + * The window registers are only updated when config done is written.
> + * Until that they read back the old value. As we read-modify-write
> + * these registers mark them as non-volatile. This makes sure we read
> + * the new values from the regmap register cache.
> + */
> +static const struct regmap_range vop2_nonvolatile_range[] = {
> +	regmap_reg_range(0x1000, 0x23ff),
> +};
> +
> +static const struct regmap_access_table vop2_volatile_table = {
> +	.no_ranges = vop2_nonvolatile_range,
> +	.n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range),
> +};
> +
> +static const struct regmap_config vop2_regmap_config = {
> +	.reg_bits	= 32,
> +	.val_bits	= 32,
> +	.reg_stride	= 4,
> +	.max_register	= 0x3000,
> +	.name		= "vop2",
> +	.volatile_table	= &vop2_volatile_table,
> +	.cache_type	= REGCACHE_RBTREE,
> +};
> +
> +static int vop2_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	const struct vop2_data *vop2_data;
> +	struct drm_device *drm = data;
> +	struct vop2 *vop2;
> +	struct resource *res;
> +	size_t alloc_size;
> +	int ret;
> +
> +	vop2_data = of_device_get_match_data(dev);
> +	if (!vop2_data)
> +		return -ENODEV;
> +
> +	/* Allocate vop2 struct and its vop2_win array */
> +	alloc_size = sizeof(*vop2) + sizeof(*vop2->win) * vop2_data->win_size;
> +	vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
> +	if (!vop2)
> +		return -ENOMEM;
> +
> +	vop2->dev = dev;
> +	vop2->data = vop2_data;
> +	vop2->drm = drm;
> +
> +	dev_set_drvdata(dev, vop2);
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
> +	if (!res) {
> +		drm_err(vop2->drm, "failed to get vop2 register byname\n");
> +		return -EINVAL;
> +	}
> +
> +	vop2->regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(vop2->regs))
> +		return PTR_ERR(vop2->regs);
> +	vop2->len = resource_size(res);
> +
> +	vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config);
> +
> +	ret = vop2_win_init(vop2);
> +	if (ret)
> +		return ret;
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma_lut");
> +	if (res) {
> +		vop2->lut_regs = devm_ioremap_resource(dev, res);
> +		if (IS_ERR(vop2->lut_regs))
> +			return PTR_ERR(vop2->lut_regs);
> +	}
> +
> +	vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
> +
> +	vop2->hclk = devm_clk_get(vop2->dev, "hclk");
> +	if (IS_ERR(vop2->hclk)) {
> +		drm_err(vop2->drm, "failed to get hclk source\n");
> +		return PTR_ERR(vop2->hclk);
> +	}
> +
> +	vop2->aclk = devm_clk_get(vop2->dev, "aclk");
> +	if (IS_ERR(vop2->aclk)) {
> +		drm_err(vop2->drm, "failed to get aclk source\n");
> +		return PTR_ERR(vop2->aclk);
> +	}
> +
> +	vop2->irq = platform_get_irq(pdev, 0);
> +	if (vop2->irq < 0) {
> +		drm_err(vop2->drm, "cannot find irq for vop2\n");
> +		return vop2->irq;
> +	}
> +
> +	mutex_init(&vop2->vop2_lock);
> +
> +	ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
> +	if (ret)
> +		return ret;
> +
> +	ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev);
> +	if (ret) {
> +		drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = vop2_create_crtc(vop2);
> +	if (ret)
> +		return ret;
> +
> +	pm_runtime_enable(&pdev->dev);
> +
> +	return 0;
> +}
> +
> +static void vop2_unbind(struct device *dev, struct device *master, void *data)
> +{
> +	struct vop2 *vop2 = dev_get_drvdata(dev);
> +	struct drm_device *drm = vop2->drm;
> +	struct list_head *plane_list = &drm->mode_config.plane_list;
> +	struct list_head *crtc_list = &drm->mode_config.crtc_list;
> +	struct drm_crtc *crtc, *tmpc;
> +	struct drm_plane *plane, *tmpp;
> +
> +	rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
> +
> +	pm_runtime_disable(dev);
> +
> +	list_for_each_entry_safe(plane, tmpp, plane_list, head)
> +		drm_plane_cleanup(plane);
> +
> +	list_for_each_entry_safe(crtc, tmpc, crtc_list, head)
> +		vop2_destroy_crtc(crtc);
> +}
> +
> +const struct component_ops vop2_component_ops = {
> +	.bind = vop2_bind,
> +	.unbind = vop2_unbind,
> +};
> +EXPORT_SYMBOL_GPL(vop2_component_ops);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> new file mode 100644
> index 0000000000000..c727093a06d68
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> @@ -0,0 +1,477 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao@rock-chips.com>
> + */
> +
> +#ifndef _ROCKCHIP_DRM_VOP2_H
> +#define _ROCKCHIP_DRM_VOP2_H
> +
> +#include "rockchip_drm_vop.h"
> +
> +#include <linux/regmap.h>
> +#include <drm/drm_modes.h>
> +
> +#define VOP_FEATURE_OUTPUT_10BIT        BIT(0)
> +
> +#define WIN_FEATURE_AFBDC		BIT(0)
> +#define WIN_FEATURE_CLUSTER		BIT(1)
> +
> +/*
> + *  the delay number of a window in different mode.
> + */
> +enum win_dly_mode {
> +	VOP2_DLY_MODE_DEFAULT,   /**< default mode */
> +	VOP2_DLY_MODE_HISO_S,    /** HDR in SDR out mode, as a SDR window */
> +	VOP2_DLY_MODE_HIHO_H,    /** HDR in HDR out mode, as a HDR window */
> +	VOP2_DLY_MODE_MAX,
> +};
> +
> +struct vop_rect {
> +	int width;
> +	int height;
> +};
> +
> +enum vop2_scale_up_mode {
> +	VOP2_SCALE_UP_NRST_NBOR,
> +	VOP2_SCALE_UP_BIL,
> +	VOP2_SCALE_UP_BIC,
> +};
> +
> +enum vop2_scale_down_mode {
> +	VOP2_SCALE_DOWN_NRST_NBOR,
> +	VOP2_SCALE_DOWN_BIL,
> +	VOP2_SCALE_DOWN_AVG,
> +};
> +
> +enum vop2_win_regs {
> +	VOP2_WIN_ENABLE,
> +	VOP2_WIN_FORMAT,
> +	VOP2_WIN_CSC_MODE,
> +	VOP2_WIN_XMIRROR,
> +	VOP2_WIN_YMIRROR,
> +	VOP2_WIN_RB_SWAP,
> +	VOP2_WIN_UV_SWAP,
> +	VOP2_WIN_ACT_INFO,
> +	VOP2_WIN_DSP_INFO,
> +	VOP2_WIN_DSP_ST,
> +	VOP2_WIN_YRGB_MST,
> +	VOP2_WIN_UV_MST,
> +	VOP2_WIN_YRGB_VIR,
> +	VOP2_WIN_UV_VIR,
> +	VOP2_WIN_YUV_CLIP,
> +	VOP2_WIN_Y2R_EN,
> +	VOP2_WIN_R2Y_EN,
> +	VOP2_WIN_COLOR_KEY,
> +	VOP2_WIN_COLOR_KEY_EN,
> +	VOP2_WIN_DITHER_UP,
> +
> +	/* scale regs */
> +	VOP2_WIN_SCALE_YRGB_X,
> +	VOP2_WIN_SCALE_YRGB_Y,
> +	VOP2_WIN_SCALE_CBCR_X,
> +	VOP2_WIN_SCALE_CBCR_Y,
> +	VOP2_WIN_YRGB_HOR_SCL_MODE,
> +	VOP2_WIN_YRGB_HSCL_FILTER_MODE,
> +	VOP2_WIN_YRGB_VER_SCL_MODE,
> +	VOP2_WIN_YRGB_VSCL_FILTER_MODE,
> +	VOP2_WIN_CBCR_VER_SCL_MODE,
> +	VOP2_WIN_CBCR_HSCL_FILTER_MODE,
> +	VOP2_WIN_CBCR_HOR_SCL_MODE,
> +	VOP2_WIN_CBCR_VSCL_FILTER_MODE,
> +	VOP2_WIN_VSD_CBCR_GT2,
> +	VOP2_WIN_VSD_CBCR_GT4,
> +	VOP2_WIN_VSD_YRGB_GT2,
> +	VOP2_WIN_VSD_YRGB_GT4,
> +	VOP2_WIN_BIC_COE_SEL,
> +
> +	/* cluster regs */
> +	VOP2_WIN_CLUSTER_ENABLE,
> +	VOP2_WIN_AFBC_ENABLE,
> +	VOP2_WIN_CLUSTER_LB_MODE,
> +
> +	/* afbc regs */
> +	VOP2_WIN_AFBC_FORMAT,
> +	VOP2_WIN_AFBC_RB_SWAP,
> +	VOP2_WIN_AFBC_UV_SWAP,
> +	VOP2_WIN_AFBC_AUTO_GATING_EN,
> +	VOP2_WIN_AFBC_BLOCK_SPLIT_EN,
> +	VOP2_WIN_AFBC_PIC_VIR_WIDTH,
> +	VOP2_WIN_AFBC_TILE_NUM,
> +	VOP2_WIN_AFBC_PIC_OFFSET,
> +	VOP2_WIN_AFBC_PIC_SIZE,
> +	VOP2_WIN_AFBC_DSP_OFFSET,
> +	VOP2_WIN_AFBC_TRANSFORM_OFFSET,
> +	VOP2_WIN_AFBC_HDR_PTR,
> +	VOP2_WIN_AFBC_HALF_BLOCK_EN,
> +	VOP2_WIN_AFBC_ROTATE_270,
> +	VOP2_WIN_AFBC_ROTATE_90,
> +	VOP2_WIN_MAX_REG,
> +};
> +
> +struct vop2_win_data {
> +	const char *name;
> +	unsigned int phys_id;
> +
> +	u32 base;
> +	enum drm_plane_type type;
> +
> +	u32 nformats;
> +	const u32 *formats;
> +	const uint64_t *format_modifiers;
> +	const unsigned int supported_rotations;
> +
> +	/**
> +	 * @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2
> +	 */
> +	unsigned int layer_sel_id;
> +	uint64_t feature;
> +
> +	unsigned int max_upscale_factor;
> +	unsigned int max_downscale_factor;
> +	const u8 dly[VOP2_DLY_MODE_MAX];
> +};
> +
> +struct vop2_video_port_data {
> +	unsigned int id;
> +	u32 feature;
> +	u16 gamma_lut_len;
> +	u16 cubic_lut_len;
> +	struct vop_rect max_output;
> +	const u8 pre_scan_max_dly[4];
> +	const struct vop2_video_port_regs *regs;
> +	unsigned int offset;
> +};
> +
> +struct vop2_data {
> +	u8 nr_vps;
> +	const struct vop2_ctrl *ctrl;
> +	const struct vop2_win_data *win;
> +	const struct vop2_video_port_data *vp;
> +	const struct vop_csc_table *csc_table;
> +	struct vop_rect max_input;
> +	struct vop_rect max_output;
> +
> +	unsigned int win_size;
> +	unsigned int soc_id;
> +};
> +
> +/* interrupt define */
> +#define FS_NEW_INTR			BIT(4)
> +#define ADDR_SAME_INTR			BIT(5)
> +#define LINE_FLAG1_INTR			BIT(6)
> +#define WIN0_EMPTY_INTR			BIT(7)
> +#define WIN1_EMPTY_INTR			BIT(8)
> +#define WIN2_EMPTY_INTR			BIT(9)
> +#define WIN3_EMPTY_INTR			BIT(10)
> +#define HWC_EMPTY_INTR			BIT(11)
> +#define POST_BUF_EMPTY_INTR		BIT(12)
> +#define PWM_GEN_INTR			BIT(13)
> +#define DMA_FINISH_INTR			BIT(14)
> +#define FS_FIELD_INTR			BIT(15)
> +#define FE_INTR				BIT(16)
> +#define WB_UV_FIFO_FULL_INTR		BIT(17)
> +#define WB_YRGB_FIFO_FULL_INTR		BIT(18)
> +#define WB_COMPLETE_INTR		BIT(19)
> +
> +/*
> + * display output interface supported by rockchip lcdc
> + */
> +#define ROCKCHIP_OUT_MODE_P888		0
> +#define ROCKCHIP_OUT_MODE_BT1120	0
> +#define ROCKCHIP_OUT_MODE_P666		1
> +#define ROCKCHIP_OUT_MODE_P565		2
> +#define ROCKCHIP_OUT_MODE_BT656		5
> +#define ROCKCHIP_OUT_MODE_S888		8
> +#define ROCKCHIP_OUT_MODE_S888_DUMMY	12
> +#define ROCKCHIP_OUT_MODE_YUV420	14
> +/* for use special outface */
> +#define ROCKCHIP_OUT_MODE_AAAA		15
> +
> +enum vop_csc_format {
> +	CSC_BT601L,
> +	CSC_BT709L,
> +	CSC_BT601F,
> +	CSC_BT2020,
> +};
> +
> +enum src_factor_mode {
> +	SRC_FAC_ALPHA_ZERO,
> +	SRC_FAC_ALPHA_ONE,
> +	SRC_FAC_ALPHA_DST,
> +	SRC_FAC_ALPHA_DST_INVERSE,
> +	SRC_FAC_ALPHA_SRC,
> +	SRC_FAC_ALPHA_SRC_GLOBAL,
> +};
> +
> +enum dst_factor_mode {
> +	DST_FAC_ALPHA_ZERO,
> +	DST_FAC_ALPHA_ONE,
> +	DST_FAC_ALPHA_SRC,
> +	DST_FAC_ALPHA_SRC_INVERSE,
> +	DST_FAC_ALPHA_DST,
> +	DST_FAC_ALPHA_DST_GLOBAL,
> +};
> +
> +#define RK3568_GRF_VO_CON1			0x0364
> +/* System registers definition */
> +#define RK3568_REG_CFG_DONE			0x000
> +#define RK3568_VERSION_INFO			0x004
> +#define RK3568_SYS_AUTO_GATING_CTRL		0x008
> +#define RK3568_SYS_AXI_LUT_CTRL			0x024
> +#define RK3568_DSP_IF_EN			0x028
> +#define RK3568_DSP_IF_CTRL			0x02c
> +#define RK3568_DSP_IF_POL			0x030
> +#define RK3568_WB_CTRL				0x40
> +#define RK3568_WB_XSCAL_FACTOR			0x44
> +#define RK3568_WB_YRGB_MST			0x48
> +#define RK3568_WB_CBR_MST			0x4C
> +#define RK3568_OTP_WIN_EN			0x050
> +#define RK3568_LUT_PORT_SEL			0x058
> +#define RK3568_SYS_STATUS0			0x060
> +#define RK3568_VP_LINE_FLAG(vp)			(0x70 + (vp) * 0x4)
> +#define RK3568_SYS0_INT_EN			0x80
> +#define RK3568_SYS0_INT_CLR			0x84
> +#define RK3568_SYS0_INT_STATUS			0x88
> +#define RK3568_SYS1_INT_EN			0x90
> +#define RK3568_SYS1_INT_CLR			0x94
> +#define RK3568_SYS1_INT_STATUS			0x98
> +#define RK3568_VP_INT_EN(vp)			(0xA0 + (vp) * 0x10)
> +#define RK3568_VP_INT_CLR(vp)			(0xA4 + (vp) * 0x10)
> +#define RK3568_VP_INT_STATUS(vp)		(0xA8 + (vp) * 0x10)
> +#define RK3568_VP_INT_RAW_STATUS(vp)		(0xAC + (vp) * 0x10)
> +
> +/* Video Port registers definition */
> +#define RK3568_VP_DSP_CTRL			0x00
> +#define RK3568_VP_MIPI_CTRL			0x04
> +#define RK3568_VP_COLOR_BAR_CTRL		0x08
> +#define RK3568_VP_3D_LUT_CTRL			0x10
> +#define RK3568_VP_3D_LUT_MST			0x20
> +#define RK3568_VP_DSP_BG			0x2C
> +#define RK3568_VP_PRE_SCAN_HTIMING		0x30
> +#define RK3568_VP_POST_DSP_HACT_INFO		0x34
> +#define RK3568_VP_POST_DSP_VACT_INFO		0x38
> +#define RK3568_VP_POST_SCL_FACTOR_YRGB		0x3C
> +#define RK3568_VP_POST_SCL_CTRL			0x40
> +#define RK3568_VP_POST_DSP_VACT_INFO_F1		0x44
> +#define RK3568_VP_DSP_HTOTAL_HS_END		0x48
> +#define RK3568_VP_DSP_HACT_ST_END		0x4C
> +#define RK3568_VP_DSP_VTOTAL_VS_END		0x50
> +#define RK3568_VP_DSP_VACT_ST_END		0x54
> +#define RK3568_VP_DSP_VS_ST_END_F1		0x58
> +#define RK3568_VP_DSP_VACT_ST_END_F1		0x5C
> +#define RK3568_VP_BCSH_CTRL			0x60
> +#define RK3568_VP_BCSH_BCS			0x64
> +#define RK3568_VP_BCSH_H			0x68
> +#define RK3568_VP_BCSH_COLOR_BAR		0x6C
> +
> +/* Overlay registers definition    */
> +#define RK3568_OVL_CTRL				0x600
> +#define RK3568_OVL_LAYER_SEL			0x604
> +#define RK3568_OVL_PORT_SEL			0x608
> +#define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL	0x610
> +#define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL	0x614
> +#define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL	0x618
> +#define RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL	0x61C
> +#define RK3568_MIX0_SRC_COLOR_CTRL		0x650
> +#define RK3568_MIX0_DST_COLOR_CTRL		0x654
> +#define RK3568_MIX0_SRC_ALPHA_CTRL		0x658
> +#define RK3568_MIX0_DST_ALPHA_CTRL		0x65C
> +#define RK3568_HDR0_SRC_COLOR_CTRL		0x6C0
> +#define RK3568_HDR0_DST_COLOR_CTRL		0x6C4
> +#define RK3568_HDR0_SRC_ALPHA_CTRL		0x6C8
> +#define RK3568_HDR0_DST_ALPHA_CTRL		0x6CC
> +#define RK3568_VP_BG_MIX_CTRL(vp)		(0x6E0 + (vp) * 4)
> +#define RK3568_CLUSTER_DLY_NUM			0x6F0
> +#define RK3568_SMART_DLY_NUM			0x6F8
> +
> +/* Cluster register definition, offset relative to window base */
> +#define RK3568_CLUSTER_WIN_CTRL0		0x00
> +#define RK3568_CLUSTER_WIN_CTRL1		0x04
> +#define RK3568_CLUSTER_WIN_YRGB_MST		0x10
> +#define RK3568_CLUSTER_WIN_CBR_MST		0x14
> +#define RK3568_CLUSTER_WIN_VIR			0x18
> +#define RK3568_CLUSTER_WIN_ACT_INFO		0x20
> +#define RK3568_CLUSTER_WIN_DSP_INFO		0x24
> +#define RK3568_CLUSTER_WIN_DSP_ST		0x28
> +#define RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB	0x30
> +#define RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET	0x3C
> +#define RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL	0x50
> +#define RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE	0x54
> +#define RK3568_CLUSTER_WIN_AFBCD_HDR_PTR	0x58
> +#define RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH	0x5C
> +#define RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE	0x60
> +#define RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET	0x64
> +#define RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET	0x68
> +#define RK3568_CLUSTER_WIN_AFBCD_CTRL		0x6C
> +
> +#define RK3568_CLUSTER_CTRL			0x100
> +
> +/* (E)smart register definition, offset relative to window base */
> +#define RK3568_SMART_CTRL0			0x00
> +#define RK3568_SMART_CTRL1			0x04
> +#define RK3568_SMART_REGION0_CTRL		0x10
> +#define RK3568_SMART_REGION0_YRGB_MST		0x14
> +#define RK3568_SMART_REGION0_CBR_MST		0x18
> +#define RK3568_SMART_REGION0_VIR		0x1C
> +#define RK3568_SMART_REGION0_ACT_INFO		0x20
> +#define RK3568_SMART_REGION0_DSP_INFO		0x24
> +#define RK3568_SMART_REGION0_DSP_ST		0x28
> +#define RK3568_SMART_REGION0_SCL_CTRL		0x30
> +#define RK3568_SMART_REGION0_SCL_FACTOR_YRGB	0x34
> +#define RK3568_SMART_REGION0_SCL_FACTOR_CBR	0x38
> +#define RK3568_SMART_REGION0_SCL_OFFSET		0x3C
> +#define RK3568_SMART_REGION1_CTRL		0x40
> +#define RK3568_SMART_REGION1_YRGB_MST		0x44
> +#define RK3568_SMART_REGION1_CBR_MST		0x48
> +#define RK3568_SMART_REGION1_VIR		0x4C
> +#define RK3568_SMART_REGION1_ACT_INFO		0x50
> +#define RK3568_SMART_REGION1_DSP_INFO		0x54
> +#define RK3568_SMART_REGION1_DSP_ST		0x58
> +#define RK3568_SMART_REGION1_SCL_CTRL		0x60
> +#define RK3568_SMART_REGION1_SCL_FACTOR_YRGB	0x64
> +#define RK3568_SMART_REGION1_SCL_FACTOR_CBR	0x68
> +#define RK3568_SMART_REGION1_SCL_OFFSET		0x6C
> +#define RK3568_SMART_REGION2_CTRL		0x70
> +#define RK3568_SMART_REGION2_YRGB_MST		0x74
> +#define RK3568_SMART_REGION2_CBR_MST		0x78
> +#define RK3568_SMART_REGION2_VIR		0x7C
> +#define RK3568_SMART_REGION2_ACT_INFO		0x80
> +#define RK3568_SMART_REGION2_DSP_INFO		0x84
> +#define RK3568_SMART_REGION2_DSP_ST		0x88
> +#define RK3568_SMART_REGION2_SCL_CTRL		0x90
> +#define RK3568_SMART_REGION2_SCL_FACTOR_YRGB	0x94
> +#define RK3568_SMART_REGION2_SCL_FACTOR_CBR	0x98
> +#define RK3568_SMART_REGION2_SCL_OFFSET		0x9C
> +#define RK3568_SMART_REGION3_CTRL		0xA0
> +#define RK3568_SMART_REGION3_YRGB_MST		0xA4
> +#define RK3568_SMART_REGION3_CBR_MST		0xA8
> +#define RK3568_SMART_REGION3_VIR		0xAC
> +#define RK3568_SMART_REGION3_ACT_INFO		0xB0
> +#define RK3568_SMART_REGION3_DSP_INFO		0xB4
> +#define RK3568_SMART_REGION3_DSP_ST		0xB8
> +#define RK3568_SMART_REGION3_SCL_CTRL		0xC0
> +#define RK3568_SMART_REGION3_SCL_FACTOR_YRGB	0xC4
> +#define RK3568_SMART_REGION3_SCL_FACTOR_CBR	0xC8
> +#define RK3568_SMART_REGION3_SCL_OFFSET		0xCC
> +#define RK3568_SMART_COLOR_KEY_CTRL		0xD0
> +
> +/* HDR register definition */
> +#define RK3568_HDR_LUT_CTRL			0x2000
> +#define RK3568_HDR_LUT_MST			0x2004
> +#define RK3568_SDR2HDR_CTRL			0x2010
> +#define RK3568_HDR2SDR_CTRL			0x2020
> +#define RK3568_HDR2SDR_SRC_RANGE		0x2024
> +#define RK3568_HDR2SDR_NORMFACEETF		0x2028
> +#define RK3568_HDR2SDR_DST_RANGE		0x202C
> +#define RK3568_HDR2SDR_NORMFACCGAMMA		0x2030
> +#define RK3568_HDR_EETF_OETF_Y0			0x203C
> +#define RK3568_HDR_SAT_Y0			0x20C0
> +#define RK3568_HDR_EOTF_OETF_Y0			0x20F0
> +#define RK3568_HDR_OETF_DX_POW1			0x2200
> +#define RK3568_HDR_OETF_XN1			0x2300
> +
> +#define RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN		BIT(15)
> +
> +#define RK3568_VP_DSP_CTRL__STANDBY			BIT(31)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_MODE		BIT(20)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL		GENMASK(19, 18)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_EN		BIT(17)
> +#define RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN		BIT(16)
> +#define RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y		BIT(15)
> +#define RK3568_VP_DSP_CTRL__DSP_RB_SWAP			BIT(9)
> +#define RK3568_VP_DSP_CTRL__DSP_INTERLACE		BIT(7)
> +#define RK3568_VP_DSP_CTRL__DSP_FILED_POL		BIT(6)
> +#define RK3568_VP_DSP_CTRL__P2I_EN			BIT(5)
> +#define RK3568_VP_DSP_CTRL__CORE_DCLK_DIV		BIT(4)
> +#define RK3568_VP_DSP_CTRL__OUT_MODE			GENMASK(3, 0)
> +
> +#define RK3568_VP_POST_SCL_CTRL__VSCALEDOWN		BIT(1)
> +#define RK3568_VP_POST_SCL_CTRL__HSCALEDOWN		BIT(0)
> +
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX		GENMASK(26, 25)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS1			BIT(24)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX		GENMASK(22, 21)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI1			BIT(20)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX		GENMASK(19, 18)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX		GENMASK(17, 16)
> +#define RK3568_SYS_DSP_INFACE_EN_EDP_MUX		GENMASK(15, 14)
> +#define RK3568_SYS_DSP_INFACE_EN_HDMI_MUX		GENMASK(11, 10)
> +#define RK3568_SYS_DSP_INFACE_EN_RGB_MUX		GENMASK(9, 8)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS0			BIT(5)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI0			BIT(4)
> +#define RK3568_SYS_DSP_INFACE_EN_EDP			BIT(3)
> +#define RK3568_SYS_DSP_INFACE_EN_HDMI			BIT(1)
> +#define RK3568_SYS_DSP_INFACE_EN_RGB			BIT(0)
> +
> +#define RK3568_DSP_IF_POL__MIPI_PIN_POL			GENMASK(19, 16)
> +#define RK3568_DSP_IF_POL__EDP_PIN_POL			GENMASK(15, 12)
> +#define RK3568_DSP_IF_POL__HDMI_PIN_POL			GENMASK(7, 4)
> +#define RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL		GENMASK(3, 0)
> +
> +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2_PHASE_LOCK	BIT(5)
> +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2			BIT(4)
> +
> +#define RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN	BIT(31)
> +
> +#define RK3568_DSP_IF_POL__CFG_DONE_IMD			BIT(28)
> +
> +#define VOP2_SYS_AXI_BUS_NUM				2
> +
> +#define VOP2_CLUSTER_YUV444_10				0x12
> +
> +#define VOP2_COLOR_KEY_MASK				BIT(31)
> +
> +#define RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD		BIT(28)
> +
> +#define RK3568_VP_BG_MIX_CTRL__BG_DLY			GENMASK(31, 24)
> +
> +#define RK3568_OVL_PORT_SEL__SEL_PORT			GENMASK(31, 16)
> +#define RK3568_OVL_PORT_SEL__SMART1			GENMASK(31, 30)
> +#define RK3568_OVL_PORT_SEL__SMART0			GENMASK(29, 28)
> +#define RK3568_OVL_PORT_SEL__ESMART1			GENMASK(27, 26)
> +#define RK3568_OVL_PORT_SEL__ESMART0			GENMASK(25, 24)
> +#define RK3568_OVL_PORT_SEL__CLUSTER1			GENMASK(19, 18)
> +#define RK3568_OVL_PORT_SEL__CLUSTER0			GENMASK(17, 16)
> +#define RK3568_OVL_PORT_SET__PORT2_MUX			GENMASK(11, 8)
> +#define RK3568_OVL_PORT_SET__PORT1_MUX			GENMASK(7, 4)
> +#define RK3568_OVL_PORT_SET__PORT0_MUX			GENMASK(3, 0)
> +#define RK3568_OVL_LAYER_SEL__LAYER(layer, x)		((x) << ((layer) * 4))
> +
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_1		GENMASK(31, 24)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_0		GENMASK(23, 16)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_1		GENMASK(15, 8)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_0		GENMASK(7, 0)
> +
> +#define RK3568_SMART_DLY_NUM__SMART1			GENMASK(31, 24)
> +#define RK3568_SMART_DLY_NUM__SMART0			GENMASK(23, 16)
> +#define RK3568_SMART_DLY_NUM__ESMART1			GENMASK(15, 8)
> +#define RK3568_SMART_DLY_NUM__ESMART0			GENMASK(7, 0)
> +
> +#define VP_INT_DSP_HOLD_VALID	BIT(6)
> +#define VP_INT_FS_FIELD		BIT(5)
> +#define VP_INT_POST_BUF_EMPTY	BIT(4)
> +#define VP_INT_LINE_FLAG1	BIT(3)
> +#define VP_INT_LINE_FLAG0	BIT(2)
> +#define VOP2_INT_BUS_ERRPR	BIT(1)
> +#define VP_INT_FS		BIT(0)
> +
> +#define POLFLAG_DCLK_INV	BIT(3)
> +
> +enum vop2_layer_phy_id {
> +	ROCKCHIP_VOP2_CLUSTER0 = 0,
> +	ROCKCHIP_VOP2_CLUSTER1,
> +	ROCKCHIP_VOP2_ESMART0,
> +	ROCKCHIP_VOP2_ESMART1,
> +	ROCKCHIP_VOP2_SMART0,
> +	ROCKCHIP_VOP2_SMART1,
> +	ROCKCHIP_VOP2_CLUSTER2,
> +	ROCKCHIP_VOP2_CLUSTER3,
> +	ROCKCHIP_VOP2_ESMART2,
> +	ROCKCHIP_VOP2_ESMART3,
> +	ROCKCHIP_VOP2_PHY_ID_INVALID = -1,
> +};
> +
> +extern const struct component_ops vop2_component_ops;
> +
> +#endif /* _ROCKCHIP_DRM_VOP2_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> new file mode 100644
> index 0000000000000..9bf0637bf8e26
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> @@ -0,0 +1,281 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) Rockchip Electronics Co.Ltd
> + * Author: Andy Yan <andy.yan@rock-chips.com>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/component.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_plane.h>
> +#include <drm/drm_print.h>
> +
> +#include "rockchip_drm_vop2.h"
> +
> +static const uint32_t formats_win_full_10bit[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV24,
> +};
> +
> +static const uint32_t formats_win_full_10bit_yuyv[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV24,
> +	DRM_FORMAT_YVYU,
> +	DRM_FORMAT_VYUY,
> +};
> +
> +static const uint32_t formats_win_lite[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +};
> +
> +static const uint64_t format_modifiers[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	DRM_FORMAT_MOD_INVALID,
> +};
> +
> +static const uint64_t format_modifiers_afbc[] = {
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_CBR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_CBR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_CBR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_CBR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	/* SPLIT mandates SPARSE, RGB modes mandates YTR */
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_SPARSE |
> +				AFBC_FORMAT_MOD_SPLIT),
> +	DRM_FORMAT_MOD_INVALID,
> +};
> +
> +static const struct vop2_video_port_data rk3568_vop_video_ports[] = {
> +	{
> +		.id = 0,
> +		.feature = VOP_FEATURE_OUTPUT_10BIT,
> +		.gamma_lut_len = 1024,
> +		.cubic_lut_len = 9 * 9 * 9,
> +		.max_output = { 4096, 2304 },
> +		.pre_scan_max_dly = { 69, 53, 53, 42 },
> +		.offset = 0xc00,
> +	}, {
> +		.id = 1,
> +		.gamma_lut_len = 1024,
> +		.max_output = { 2048, 1536 },
> +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> +		.offset = 0xd00,
> +	}, {
> +		.id = 2,
> +		.gamma_lut_len = 1024,
> +		.max_output = { 1920, 1080 },
> +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> +		.offset = 0xe00,
> +	},
> +};
> +
> +/*
> + * rk3568 vop with 2 cluster, 2 esmart win, 2 smart win.
> + * Every cluster can work as 4K win or split into two win.
> + * All win in cluster support AFBCD.
> + *
> + * Every esmart win and smart win support 4 Multi-region.
> + *
> + * Scale filter mode:
> + *
> + * * Cluster:  bicubic for horizontal scale up, others use bilinear
> + * * ESmart:
> + *    * nearest-neighbor/bilinear/bicubic for scale up
> + *    * nearest-neighbor/bilinear/average for scale down
> + *
> + *
> + * @TODO describe the wind like cpu-map dt nodes;
> + */
> +static const struct vop2_win_data rk3568_vop_win_data[] = {
> +	{
> +		.name = "Smart0-win0",
> +		.phys_id = ROCKCHIP_VOP2_SMART0,
> +		.base = 0x1c00,
> +		.formats = formats_win_lite,
> +		.nformats = ARRAY_SIZE(formats_win_lite),
> +		.format_modifiers = format_modifiers,
> +		.layer_sel_id = 3,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Smart1-win0",
> +		.phys_id = ROCKCHIP_VOP2_SMART1,
> +		.formats = formats_win_lite,
> +		.nformats = ARRAY_SIZE(formats_win_lite),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1e00,
> +		.layer_sel_id = 7,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Esmart1-win0",
> +		.phys_id = ROCKCHIP_VOP2_ESMART1,
> +		.formats = formats_win_full_10bit_yuyv,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1a00,
> +		.layer_sel_id = 6,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Esmart0-win0",
> +		.phys_id = ROCKCHIP_VOP2_ESMART0,
> +		.formats = formats_win_full_10bit_yuyv,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1800,
> +		.layer_sel_id = 2,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Cluster0-win0",
> +		.phys_id = ROCKCHIP_VOP2_CLUSTER0,
> +		.base = 0x1000,
> +		.formats = formats_win_full_10bit,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> +		.format_modifiers = format_modifiers_afbc,
> +		.layer_sel_id = 0,
> +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> +		.max_upscale_factor = 4,
> +		.max_downscale_factor = 4,
> +		.dly = { 0, 27, 21 },
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> +	}, {
> +		.name = "Cluster1-win0",
> +		.phys_id = ROCKCHIP_VOP2_CLUSTER1,
> +		.base = 0x1200,
> +		.formats = formats_win_full_10bit,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> +		.format_modifiers = format_modifiers_afbc,
> +		.layer_sel_id = 1,
> +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.max_upscale_factor = 4,
> +		.max_downscale_factor = 4,
> +		.dly = { 0, 27, 21 },
> +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> +	},
> +};
> +
> +static const struct vop2_data rk3566_vop = {
> +	.nr_vps = 3,
> +	.max_input = { 4096, 2304 },
> +	.max_output = { 4096, 2304 },
> +	.vp = rk3568_vop_video_ports,
> +	.win = rk3568_vop_win_data,
> +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> +	.soc_id = 3566,
> +};
> +
> +static const struct vop2_data rk3568_vop = {
> +	.nr_vps = 3,
> +	.max_input = { 4096, 2304 },
> +	.max_output = { 4096, 2304 },
> +	.vp = rk3568_vop_video_ports,
> +	.win = rk3568_vop_win_data,
> +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> +	.soc_id = 3568,
> +};
> +
> +static const struct of_device_id vop2_dt_match[] = {
> +	{
> +		.compatible = "rockchip,rk3566-vop",
> +		.data = &rk3566_vop,
> +	}, {
> +		.compatible = "rockchip,rk3568-vop",
> +		.data = &rk3568_vop,
> +	}, {
> +	},
> +};
> +MODULE_DEVICE_TABLE(of, vop2_dt_match);
> +
> +static int vop2_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +
> +	return component_add(dev, &vop2_component_ops);
> +}
> +
> +static int vop2_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &vop2_component_ops);
> +
> +	return 0;
> +}
> +
> +struct platform_driver vop2_platform_driver = {
> +	.probe = vop2_probe,
> +	.remove = vop2_remove,
> +	.driver = {
> +		.name = "rockchip-vop2",
> +		.of_match_table = of_match_ptr(vop2_dt_match),
> +	},
> +};

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-17 12:00     ` Andy Yan
  0 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-17 12:00 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Sandy Huang, linux-rockchip,
	Michael Riesch, kernel, Peter Geis, linux-arm-kernel

Hi Sascha:

On 2/17/22 16:29, Sascha Hauer wrote:
> From: Andy Yan <andy.yan@rock-chips.com>
>
> The VOP2 unit is found on Rockchip SoCs beginning with rk3566/rk3568.
> It replaces the VOP unit found in the older Rockchip SoCs.
>
> This driver has been derived from the downstream Rockchip Kernel and
> heavily modified:
>
> - All nonstandard DRM properties have been removed
> - dropped struct vop2_plane_state and pass around less data between
>    functions
> - Dropped all DRM_FORMAT_* not known on upstream
> - rework register access to get rid of excessively used macros
> - Drop all waiting for framesyncs
>
> The driver is tested with HDMI and MIPI-DSI display on a RK3568-EVB
> board. Overlay support is tested with the modetest utility. AFBC support
> on the cluster windows is tested with weston-simple-dmabuf-egl on
> weston using the (yet to be upstreamed) panfrost driver support.
>
> Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>
> Notes:
>      Changes since v5:
>      - consistently use u8/u16/u32 rather than uint8_t/uint16_t/uint32_t
>      - Use spin_lock rather than spin_lock_irqsave
>      - replace printk with drm_dbg
>      - break some overlong lines
>      
>      Changes since v4:
>      - Avoid stack frame overflow by not allocating big array on the stack
>      
>      Changes since v3:
>      - Sort includes
>      - fix typos
>      - Drop spinlock
>      - Use regmap_set_bits()/regmap_clear_bits()
>      - simplify vop2_scale_factor()
>      - simplify vop2_afbc_transform_offset()
>      
>      Changes since v4:
>      - Sort nodes alphabetically
>      
>      Changes since v3:
>      - Fix HDMI connector type
>
>   drivers/gpu/drm/rockchip/Kconfig             |    6 +
>   drivers/gpu/drm/rockchip/Makefile            |    1 +
>   drivers/gpu/drm/rockchip/rockchip_drm_drv.c  |    1 +
>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h  |    6 +-
>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c   |    2 +
>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h  |   15 +
>   drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2708 ++++++++++++++++++
>   drivers/gpu/drm/rockchip/rockchip_drm_vop2.h |  477 +++
>   drivers/gpu/drm/rockchip/rockchip_vop2_reg.c |  281 ++
>   9 files changed, 3496 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
>
> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> index b9b156308460a..4ff0043f0ee70 100644
> --- a/drivers/gpu/drm/rockchip/Kconfig
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
>   	  This selects support for the VOP driver. You should enable it
>   	  on all older SoCs up to RK3399.
>   
> +config ROCKCHIP_VOP2
> +	bool "Rockchip VOP2 driver"
> +	help
> +	  This selects support for the VOP2 driver. You should enable it
> +	  on all newer SoCs beginning form RK3568.
> +
>   config ROCKCHIP_ANALOGIX_DP
>   	bool "Rockchip specific extensions for Analogix DP driver"
>   	depends on ROCKCHIP_VOP
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index dfc5512fdb9f1..3ff7b21c04149 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -6,6 +6,7 @@
>   rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>   		rockchip_drm_gem.o
>   
> +rockchipdrm-$(CONFIG_ROCKCHIP_VOP2) += rockchip_drm_vop2.o rockchip_vop2_reg.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> index 82c8faf1fb6b8..95f6c5985fdd7 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -459,6 +459,7 @@ static int __init rockchip_drm_init(void)
>   
>   	num_rockchip_sub_drivers = 0;
>   	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
> +	ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2);
>   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
>   				CONFIG_ROCKCHIP_LVDS);
>   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> index d3e42410ae5da..5cb207aaaadd0 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -18,7 +18,7 @@
>   
>   #define ROCKCHIP_MAX_FB_BUFFER	3
>   #define ROCKCHIP_MAX_CONNECTOR	2
> -#define ROCKCHIP_MAX_CRTC	2
> +#define ROCKCHIP_MAX_CRTC	4
>   
>   struct drm_device;
>   struct drm_connector;
> @@ -31,6 +31,9 @@ struct rockchip_crtc_state {
>   	int output_bpc;
>   	int output_flags;
>   	bool enable_afbc;
> +	u32 bus_format;
> +	u32 bus_flags;
> +	int color_space;
>   };
>   #define to_rockchip_crtc_state(s) \
>   		container_of(s, struct rockchip_crtc_state, base)
> @@ -63,6 +66,7 @@ extern struct platform_driver rockchip_dp_driver;
>   extern struct platform_driver rockchip_lvds_driver;
>   extern struct platform_driver vop_platform_driver;
>   extern struct platform_driver rk3066_hdmi_driver;
> +extern struct platform_driver vop2_platform_driver;
>   
>   struct rockchip_encoder {
>   	struct device_node *port;
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> index 3aa37e177667e..0d2cb4f3922b8 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> @@ -134,4 +134,6 @@ void rockchip_drm_mode_config_init(struct drm_device *dev)
>   
>   	dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
>   	dev->mode_config.helper_private = &rockchip_mode_config_helpers;
> +
> +	dev->mode_config.normalize_zpos = true;
>   }
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> index 857d97cdc67c6..1e364d7b50e69 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -54,9 +54,23 @@ struct vop_afbc {
>   	struct vop_reg enable;
>   	struct vop_reg win_sel;
>   	struct vop_reg format;
> +	struct vop_reg rb_swap;
> +	struct vop_reg uv_swap;
> +	struct vop_reg auto_gating_en;
> +	struct vop_reg block_split_en;
> +	struct vop_reg pic_vir_width;
> +	struct vop_reg tile_num;
>   	struct vop_reg hreg_block_split;
> +	struct vop_reg pic_offset;
>   	struct vop_reg pic_size;
> +	struct vop_reg dsp_offset;
> +	struct vop_reg transform_offset;
>   	struct vop_reg hdr_ptr;
> +	struct vop_reg half_block_en;
> +	struct vop_reg xmirror;
> +	struct vop_reg ymirror;
> +	struct vop_reg rotate_270;
> +	struct vop_reg rotate_90;
>   	struct vop_reg rstn;
>   };
>   
> @@ -410,4 +424,5 @@ static inline int scl_vop_cal_lb_mode(int width, bool is_yuv)
>   }
>   
>   extern const struct component_ops vop_component_ops;
> +
>   #endif /* _ROCKCHIP_DRM_VOP_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> new file mode 100644
> index 0000000000000..394dd6c583682
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> @@ -0,0 +1,2708 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
> + * Author: Andy Yan <andy.yan@rock-chips.com>
> + */
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/delay.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/swab.h>
> +
> +#include <drm/drm.h>
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_uapi.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_debugfs.h>
> +#include <drm/drm_flip_work.h>
> +#include <drm/drm_plane_helper.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_vblank.h>
> +
> +#include <uapi/linux/videodev2.h>
> +#include <dt-bindings/soc/rockchip,vop2.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_gem.h"
> +#include "rockchip_drm_fb.h"
> +#include "rockchip_drm_vop2.h"
> +
> +/*
> + * VOP2 architecture
> + *
> + +----------+   +-------------+                                                        +-----------+
> + |  Cluster |   | Sel 1 from 6|                                                        | 1 from 3  |
> + |  window0 |   |    Layer0   |                                                        |    RGB    |
> + +----------+   +-------------+              +---------------+    +-------------+      +-----------+
> + +----------+   +-------------+              |N from 6 layers|    |             |
> + |  Cluster |   | Sel 1 from 6|              |   Overlay0    +--->| Video Port0 |      +-----------+
> + |  window1 |   |    Layer1   |              |               |    |             |      | 1 from 3  |
> + +----------+   +-------------+              +---------------+    +-------------+      |   LVDS    |
> + +----------+   +-------------+                                                        +-----------+
> + |  Esmart  |   | Sel 1 from 6|
> + |  window0 |   |   Layer2    |              +---------------+    +-------------+      +-----------+
> + +----------+   +-------------+              |N from 6 Layers|    |             | +--> | 1 from 3  |
> + +----------+   +-------------+   -------->  |   Overlay1    +--->| Video Port1 |      |   MIPI    |
> + |  Esmart  |   | Sel 1 from 6|   -------->  |               |    |             |      +-----------+
> + |  Window1 |   |   Layer3    |              +---------------+    +-------------+
> + +----------+   +-------------+                                                        +-----------+
> + +----------+   +-------------+                                                        | 1 from 3  |
> + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |   HDMI    |
> + |  Window0 |   |    Layer4   |              |N from 6 Layers|    |             |      +-----------+
> + +----------+   +-------------+              |   Overlay2    +--->| Video Port2 |
> + +----------+   +-------------+              |               |    |             |      +-----------+
> + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |  1 from 3 |
> + |  Window1 |   |    Layer5   |                                                        |    eDP    |
> + +----------+   +-------------+                                                        +-----------+
> + *
> + */
> +
> +enum vop2_data_format {
> +	VOP2_FMT_ARGB8888 = 0,
> +	VOP2_FMT_RGB888,
> +	VOP2_FMT_RGB565,
> +	VOP2_FMT_XRGB101010,
> +	VOP2_FMT_YUV420SP,
> +	VOP2_FMT_YUV422SP,
> +	VOP2_FMT_YUV444SP,
> +	VOP2_FMT_YUYV422 = 8,
> +	VOP2_FMT_YUYV420,
> +	VOP2_FMT_VYUY422,
> +	VOP2_FMT_VYUY420,
> +	VOP2_FMT_YUV420SP_TILE_8x4 = 0x10,
> +	VOP2_FMT_YUV420SP_TILE_16x2,
> +	VOP2_FMT_YUV422SP_TILE_8x4,
> +	VOP2_FMT_YUV422SP_TILE_16x2,
> +	VOP2_FMT_YUV420SP_10,
> +	VOP2_FMT_YUV422SP_10,
> +	VOP2_FMT_YUV444SP_10,
> +};
> +
> +enum vop2_afbc_format {
> +	VOP2_AFBC_FMT_RGB565,
> +	VOP2_AFBC_FMT_ARGB2101010 = 2,
> +	VOP2_AFBC_FMT_YUV420_10BIT,
> +	VOP2_AFBC_FMT_RGB888,
> +	VOP2_AFBC_FMT_ARGB8888,
> +	VOP2_AFBC_FMT_YUV420 = 9,
> +	VOP2_AFBC_FMT_YUV422 = 0xb,
> +	VOP2_AFBC_FMT_YUV422_10BIT = 0xe,
> +	VOP2_AFBC_FMT_INVALID = -1,
> +};
> +
> +union vop2_alpha_ctrl {
> +	u32 val;
> +	struct {
> +		/* [0:1] */
> +		u32 color_mode:1;
> +		u32 alpha_mode:1;
> +		/* [2:3] */
> +		u32 blend_mode:2;
> +		u32 alpha_cal_mode:1;
> +		/* [5:7] */
> +		u32 factor_mode:3;
> +		/* [8:9] */
> +		u32 alpha_en:1;
> +		u32 src_dst_swap:1;
> +		u32 reserved:6;
> +		/* [16:23] */
> +		u32 glb_alpha:8;
> +	} bits;
> +};
> +
> +struct vop2_alpha {
> +	union vop2_alpha_ctrl src_color_ctrl;
> +	union vop2_alpha_ctrl dst_color_ctrl;
> +	union vop2_alpha_ctrl src_alpha_ctrl;
> +	union vop2_alpha_ctrl dst_alpha_ctrl;
> +};
> +
> +struct vop2_alpha_config {
> +	bool src_premulti_en;
> +	bool dst_premulti_en;
> +	bool src_pixel_alpha_en;
> +	bool dst_pixel_alpha_en;
> +	u16 src_glb_alpha_value;
> +	u16 dst_glb_alpha_value;
> +};
> +
> +struct vop2_win {
> +	struct vop2 *vop2;
> +	struct drm_plane base;
> +	const struct vop2_win_data *data;
> +	struct regmap_field *reg[VOP2_WIN_MAX_REG];
> +
> +	/**
> +	 * @win_id: graphic window id, a cluster may be split into two
> +	 * graphics windows.
> +	 */
> +	u8 win_id;
> +	u8 delay;
> +	u32 offset;
> +
> +	enum drm_plane_type type;
> +};
> +
> +struct vop2_video_port {
> +	struct drm_crtc crtc;
> +	struct vop2 *vop2;
> +	struct clk *dclk;
> +	unsigned int id;
> +	const struct vop2_video_port_regs *regs;
> +	const struct vop2_video_port_data *data;
> +
> +	struct completion dsp_hold_completion;
> +
> +	/**
> +	 * @win_mask: Bitmask of windows attached to the video port;
> +	 */
> +	u32 win_mask;
> +
> +	struct vop2_win *primary_plane;
> +	struct drm_pending_vblank_event *event;
> +
> +	unsigned int nlayers;
> +};
> +
> +struct vop2 {
> +	struct device *dev;
> +	struct drm_device *drm;
> +	struct vop2_video_port vps[ROCKCHIP_MAX_CRTC];
> +
> +	const struct vop2_data *data;
> +	/*
> +	 * Number of windows that are registered as plane, may be less than the
> +	 * total number of hardware windows.
> +	 */
> +	u32 registered_num_wins;
> +
> +	void __iomem *regs;
> +	struct regmap *map;
> +
> +	struct regmap *grf;
> +
> +	/* physical map length of vop2 register */
> +	u32 len;
> +
> +	void __iomem *lut_regs;
> +
> +	/* protects crtc enable/disable */
> +	struct mutex vop2_lock;
> +
> +	int irq;
> +
> +	/*
> +	 * Some global resources are shared between all video ports(crtcs), so
> +	 * we need a ref counter here.
> +	 */
> +	unsigned int enable_count;
> +	struct clk *hclk;
> +	struct clk *aclk;
> +
> +	/* must be put at the end of the struct */
> +	struct vop2_win win[];
> +};
> +
> +static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
> +{
> +	return container_of(crtc, struct vop2_video_port, crtc);
> +}
> +
> +static struct vop2_win *to_vop2_win(struct drm_plane *p)
> +{
> +	return container_of(p, struct vop2_win, base);
> +}
> +
> +static void vop2_lock(struct vop2 *vop2)
> +{
> +	mutex_lock(&vop2->vop2_lock);
> +}
> +
> +static void vop2_unlock(struct vop2 *vop2)
> +{
> +	mutex_unlock(&vop2->vop2_lock);
> +}
> +
> +static void vop2_writel(struct vop2 *vop2, u32 offset, u32 v)
> +{
> +	regmap_write(vop2->map, offset, v);
> +}
> +
> +static void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
> +{
> +	regmap_write(vp->vop2->map, vp->data->offset + offset, v);
> +}
> +
> +static u32 vop2_readl(struct vop2 *vop2, u32 offset)
> +{
> +	u32 val;
> +
> +	regmap_read(vop2->map, offset, &val);
> +
> +	return val;
> +}
> +
> +static void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v)
> +{
> +	regmap_field_write(win->reg[reg], v);
> +}
> +
> +static bool vop2_cluster_window(const struct vop2_win *win)
> +{
> +	return win->data->feature & WIN_FEATURE_CLUSTER;
> +}
> +
> +static void vop2_cfg_done(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE,
> +			BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> +}
> +
> +static void vop2_win_disable(struct vop2_win *win)
> +{
> +	vop2_win_write(win, VOP2_WIN_ENABLE, 0);
> +
> +	if (vop2_cluster_window(win))
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0);
> +}
> +
> +static enum vop2_data_format vop2_convert_format(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +		return VOP2_FMT_ARGB8888;
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		return VOP2_FMT_RGB888;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		return VOP2_FMT_RGB565;
> +	case DRM_FORMAT_NV12:
> +		return VOP2_FMT_YUV420SP;
> +	case DRM_FORMAT_NV16:
> +		return VOP2_FMT_YUV422SP;
> +	case DRM_FORMAT_NV24:
> +		return VOP2_FMT_YUV444SP;
> +	case DRM_FORMAT_YUYV:
> +	case DRM_FORMAT_YVYU:
> +		return VOP2_FMT_VYUY422;
> +	case DRM_FORMAT_VYUY:
> +	case DRM_FORMAT_UYVY:
> +		return VOP2_FMT_YUYV422;
> +	default:
> +		DRM_ERROR("unsupported format[%08x]\n", format);
> +		return -EINVAL;
> +	}
> +}
> +
> +static enum vop2_afbc_format vop2_convert_afbc_format(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +		return VOP2_AFBC_FMT_ARGB8888;
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		return VOP2_AFBC_FMT_RGB888;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		return VOP2_AFBC_FMT_RGB565;
> +	case DRM_FORMAT_NV12:
> +		return VOP2_AFBC_FMT_YUV420;
> +	case DRM_FORMAT_NV16:
> +		return VOP2_AFBC_FMT_YUV422;
> +	default:
> +		return VOP2_AFBC_FMT_INVALID;
> +	}
> +
> +	return VOP2_AFBC_FMT_INVALID;
> +}
> +
> +static bool vop2_win_rb_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_BGR888:
> +	case DRM_FORMAT_BGR565:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_afbc_rb_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV24:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_afbc_uv_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV12:
> +	case DRM_FORMAT_NV16:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_win_uv_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV12:
> +	case DRM_FORMAT_NV16:
> +	case DRM_FORMAT_NV24:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_win_dither_up(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_BGR565:
> +	case DRM_FORMAT_RGB565:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
> +{
> +	/*
> +	 * FIXME:
> +	 *
> +	 * There is no media type for YUV444 output,
> +	 * so when out_mode is AAAA or P888, assume output is YUV444 on
> +	 * yuv format.
> +	 *
> +	 * From H/W testing, YUV444 mode need a rb swap.
> +	 */
> +	if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 ||
> +	    bus_format == MEDIA_BUS_FMT_VYUY8_1X16 ||
> +	    bus_format == MEDIA_BUS_FMT_YVYU8_2X8 ||
> +	    bus_format == MEDIA_BUS_FMT_VYUY8_2X8 ||
> +	    ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
> +	      bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
> +	     (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
> +	      output_mode == ROCKCHIP_OUT_MODE_P888)))
> +		return true;
> +	else
> +		return false;
> +}
> +
> +static bool is_yuv_output(u32 bus_format)
> +{
> +	switch (bus_format) {
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +	case MEDIA_BUS_FMT_YUV10_1X30:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> +	case MEDIA_BUS_FMT_YUYV8_2X8:
> +	case MEDIA_BUS_FMT_YVYU8_2X8:
> +	case MEDIA_BUS_FMT_UYVY8_2X8:
> +	case MEDIA_BUS_FMT_VYUY8_2X8:
> +	case MEDIA_BUS_FMT_YUYV8_1X16:
> +	case MEDIA_BUS_FMT_YVYU8_1X16:
> +	case MEDIA_BUS_FMT_UYVY8_1X16:
> +	case MEDIA_BUS_FMT_VYUY8_1X16:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool rockchip_afbc(struct drm_plane *plane, u64 modifier)
> +{
> +	int i;
> +
> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> +		return false;
> +
> +	for (i = 0 ; i < plane->modifier_count; i++)
> +		if (plane->modifiers[i] == modifier)
> +			return true;
> +
> +	return false;
> +
> +}
> +
> +static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
> +					u64 modifier)
> +{
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2 *vop2 = win->vop2;
> +
> +	if (modifier == DRM_FORMAT_MOD_INVALID)
> +		return false;
> +
> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> +		return true;
> +
> +	if (!rockchip_afbc(plane, modifier)) {
> +		drm_err(vop2->drm, "Unsupported format modifier 0x%llx\n",
> +			modifier);
> +
> +		return false;
> +	}
> +
> +	return vop2_convert_afbc_format(format) >= 0;
> +}
> +
> +static u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate,
> +				      bool afbc_half_block_en)
> +{
> +	struct drm_rect *src = &pstate->src;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	u32 bpp = fb->format->cpp[0] * 8;
> +	u32 vir_width = (fb->pitches[0] << 3) / bpp;
> +	u32 width = drm_rect_width(src) >> 16;
> +	u32 height = drm_rect_height(src) >> 16;
> +	u32 act_xoffset = src->x1 >> 16;
> +	u32 act_yoffset = src->y1 >> 16;
> +	u32 align16_crop = 0;
> +	u32 align64_crop = 0;
> +	u32 height_tmp;
> +	u8 tx, ty;
> +	u8 bottom_crop_line_num = 0;
> +
> +	/* 16 pixel align */
> +	if (height & 0xf)
> +		align16_crop = 16 - (height & 0xf);
> +
> +	height_tmp = height + align16_crop;
> +
> +	/* 64 pixel align */
> +	if (height_tmp & 0x3f)
> +		align64_crop = 64 - (height_tmp & 0x3f);
> +
> +	bottom_crop_line_num = align16_crop + align64_crop;
> +
> +	switch (pstate->rotation &
> +		(DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y |
> +		 DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) {
> +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> +		tx = 16 - ((act_xoffset + width) & 0xf);
> +		ty = bottom_crop_line_num - act_yoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90:
> +		tx = bottom_crop_line_num - act_yoffset;
> +		ty = vir_width - width - act_xoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270:
> +		tx = act_yoffset;
> +		ty = act_xoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X:
> +		tx = 16 - ((act_xoffset + width) & 0xf);
> +		ty = act_yoffset;
> +		break;
> +	case DRM_MODE_REFLECT_Y:
> +		tx = act_xoffset;
> +		ty = bottom_crop_line_num - act_yoffset;
> +		break;
> +	case DRM_MODE_ROTATE_90:
> +		tx = bottom_crop_line_num - act_yoffset;
> +		ty = act_xoffset;
> +		break;
> +	case DRM_MODE_ROTATE_270:
> +		tx = act_yoffset;
> +		ty = vir_width - width - act_xoffset;
> +		break;
> +	case 0:
> +		tx = act_xoffset;
> +		ty = act_yoffset;
> +		break;
> +	}
> +
> +	if (afbc_half_block_en)
> +		ty &= 0x7f;
> +
> +#define TRANSFORM_XOFFSET GENMASK(7, 0)
> +#define TRANSFORM_YOFFSET GENMASK(23, 16)
> +	return FIELD_PREP(TRANSFORM_XOFFSET, tx) |
> +		FIELD_PREP(TRANSFORM_YOFFSET, ty);
> +}
> +
> +/*
> + * A Cluster window has 2048 x 16 line buffer, which can
> + * works at 2048 x 16(Full) or 4096 x 8 (Half) mode.
> + * for Cluster_lb_mode register:
> + * 0: half mode, for plane input width range 2048 ~ 4096
> + * 1: half mode, for cluster work at 2 * 2048 plane mode
> + * 2: half mode, for rotate_90/270 mode
> + *
> + */
> +static int vop2_get_cluster_lb_mode(struct vop2_win *win,
> +				    struct drm_plane_state *pstate)
> +{
> +	if ((pstate->rotation & DRM_MODE_ROTATE_270) ||
> +	    (pstate->rotation & DRM_MODE_ROTATE_90))
> +		return 2;
> +	else
> +		return 0;
> +}
> +
> +static u16 vop2_scale_factor(u32 src, u32 dst)
> +{
> +	u32 fac;
> +	int shift;
> +
> +	if (src == dst)
> +		return 0;
> +
> +	if (dst < 2)
> +		return U16_MAX;
> +
> +	if (src < 2)
> +		return 0;
> +
> +	if (src > dst)
> +		shift = 12;
> +	else
> +		shift = 16;
> +
> +	src--;
> +	dst--;
> +
> +	fac = DIV_ROUND_UP(src << shift, dst) - 1;
> +
> +	if (fac > U16_MAX)
> +		return U16_MAX;
> +
> +	return fac;
> +}
> +
> +static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
> +			     u32 src_w, u32 src_h, u32 dst_w,
> +			     u32 dst_h, u32 pixel_format)
> +{
> +	const struct drm_format_info *info;
> +	u16 hor_scl_mode, ver_scl_mode;
> +	u16 hscl_filter_mode, vscl_filter_mode;
> +	u8 gt2 = 0;
> +	u8 gt4 = 0;
> +	u32 val;
> +
> +	info = drm_format_info(pixel_format);
> +
> +	if (src_h >= (4 * dst_h)) {
> +		gt4 = 1;
> +		src_h >>= 2;
> +	} else if (src_h >= (2 * dst_h)) {
> +		gt2 = 1;
> +		src_h >>= 1;
> +	}
> +
> +	hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> +	ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> +
> +	if (hor_scl_mode == SCALE_UP)
> +		hscl_filter_mode = VOP2_SCALE_UP_BIC;
> +	else
> +		hscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> +
> +	if (ver_scl_mode == SCALE_UP)
> +		vscl_filter_mode = VOP2_SCALE_UP_BIL;
> +	else
> +		vscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> +
> +	/*
> +	 * RK3568 VOP Esmart/Smart dsp_w should be even pixel
> +	 * at scale down mode
> +	 */
> +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> +		if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) {
> +			drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n",
> +				win->data->name, dst_w);
> +			dst_w++;
> +		}
> +	}
> +
> +	val = vop2_scale_factor(src_w, dst_w);
> +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val);
> +	val = vop2_scale_factor(src_h, dst_h);
> +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val);
> +
> +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4);
> +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2);
> +
> +	vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode);
> +	vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode);
> +
> +	if (vop2_cluster_window(win))
> +		return;
> +
> +	vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode);
> +	vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode);
> +
> +	if (info->is_yuv) {
> +		src_w /= info->hsub;
> +		src_h /= info->vsub;
> +
> +		gt4 = gt2 = 0;
> +
> +		if (src_h >= (4 * dst_h)) {
> +			gt4 = 1;
> +			src_h >>= 2;
> +		} else if (src_h >= (2 * dst_h)) {
> +			gt2 = 1;
> +			src_h >>= 1;
> +		}
> +
> +		hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> +		ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> +
> +		val = vop2_scale_factor(src_w, dst_w);
> +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val);
> +
> +		val = vop2_scale_factor(src_h, dst_h);
> +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val);
> +
> +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4);
> +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2);
> +		vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode);
> +	}
> +}
> +
> +static int vop2_convert_csc_mode(int csc_mode)
> +{
> +	switch (csc_mode) {
> +	case V4L2_COLORSPACE_SMPTE170M:
> +	case V4L2_COLORSPACE_470_SYSTEM_M:
> +	case V4L2_COLORSPACE_470_SYSTEM_BG:
> +		return CSC_BT601L;
> +	case V4L2_COLORSPACE_REC709:
> +	case V4L2_COLORSPACE_SMPTE240M:
> +	case V4L2_COLORSPACE_DEFAULT:
> +		return CSC_BT709L;
> +	case V4L2_COLORSPACE_JPEG:
> +		return CSC_BT601F;
> +	case V4L2_COLORSPACE_BT2020:
> +		return CSC_BT2020;
> +	default:
> +		return CSC_BT709L;
> +	}
> +}
> +
> +/*
> + * colorspace path:
> + *      Input        Win csc                     Output
> + * 1. YUV(2020)  --> Y2R->2020To709->R2Y   --> YUV_OUTPUT(601/709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 2. YUV(2020)  --> bypasss               --> YUV_OUTPUT(2020)
> + *    RGB        --> 709To2020->R2Y       __/
> + *
> + * 3. YUV(2020)  --> Y2R->2020To709        --> RGB_OUTPUT(709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 4. YUV(601/709)-> Y2R->709To2020->R2Y   --> YUV_OUTPUT(2020)
> + *    RGB        --> 709To2020->R2Y       __/
> + *
> + * 5. YUV(601/709)-> bypass                --> YUV_OUTPUT(709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 6. YUV(601/709)-> bypass                --> YUV_OUTPUT(601)
> + *    RGB        --> R2Y(601)             __/
> + *
> + * 7. YUV        --> Y2R(709)              --> RGB_OUTPUT(709)
> + *    RGB        --> bypass               __/
> + *
> + * 8. RGB        --> 709To2020->R2Y        --> YUV_OUTPUT(2020)
> + *
> + * 9. RGB        --> R2Y(709)              --> YUV_OUTPUT(709)
> + *
> + * 10. RGB       --> R2Y(601)              --> YUV_OUTPUT(601)
> + *
> + * 11. RGB       --> bypass                --> RGB_OUTPUT(709)
> + */
> +
> +static void vop2_setup_csc_mode(struct vop2_video_port *vp,
> +				struct vop2_win *win,
> +				struct drm_plane_state *pstate)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
> +	int is_input_yuv = pstate->fb->format->is_yuv;
> +	int is_output_yuv = is_yuv_output(vcstate->bus_format);
> +	int input_csc = V4L2_COLORSPACE_DEFAULT;
> +	int output_csc = vcstate->color_space;
> +	bool r2y_en, y2r_en;
> +	int csc_mode;
> +
> +	if (is_input_yuv && !is_output_yuv) {
> +		y2r_en = true;
> +		r2y_en = false;
> +		csc_mode = vop2_convert_csc_mode(input_csc);
> +	} else if (!is_input_yuv && is_output_yuv) {
> +		y2r_en = false;
> +		r2y_en = true;
> +		csc_mode = vop2_convert_csc_mode(output_csc);
> +	} else {
> +		y2r_en = false;
> +		r2y_en = false;
> +		csc_mode = false;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en);
> +	vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en);
> +	vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
> +}
> +
> +static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq);
> +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq);
> +}
> +
> +static void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16);
> +}
> +
> +static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
> +{
> +	int ret;
> +
> +	ret = clk_prepare_enable(vop2->hclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable hclk - %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(vop2->aclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable aclk - %d\n", ret);
> +		goto err;
> +	}
> +
> +	return 0;
> +err:
> +	clk_disable_unprepare(vop2->hclk);
> +
> +	return ret;
> +}
> +
> +static void vop2_enable(struct vop2 *vop2)
> +{
> +	int ret;
> +
> +	ret = pm_runtime_get_sync(vop2->dev);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret);
> +		return;
> +	}
> +
> +	ret = vop2_core_clks_prepare_enable(vop2);
> +	if (ret) {
> +		pm_runtime_put_sync(vop2->dev);
> +		return;
> +	}
> +
> +	if (vop2->data->soc_id == 3566)
> +		vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
> +
> +	vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> +
> +	/*
> +	 * Disable auto gating, this is a workaround to
> +	 * avoid display image shift when a window enabled.
> +	 */
> +	regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL,
> +			  RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN);
> +
> +	vop2_writel(vop2, RK3568_SYS0_INT_CLR,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS0_INT_EN,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS1_INT_CLR,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS1_INT_EN,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +}
> +
> +static void vop2_disable(struct vop2 *vop2)
> +{
> +	pm_runtime_put_sync(vop2->dev);
> +
> +	clk_disable_unprepare(vop2->aclk);
> +	clk_disable_unprepare(vop2->hclk);
> +}
> +
> +static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
> +				     struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	int ret;
> +
> +	vop2_lock(vop2);
> +
> +	drm_crtc_vblank_off(crtc);
> +
> +	/*
> +	 * Vop standby will take effect at end of current frame,
> +	 * if dsp hold valid irq happen, it means standby complete.
> +	 *
> +	 * we must wait standby complete when we want to disable aclk,
> +	 * if not, memory bus maybe dead.
> +	 */
> +	reinit_completion(&vp->dsp_hold_completion);
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
> +
> +	ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
> +					  msecs_to_jiffies(50));
> +	if (!ret)
> +		drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
> +
> +	vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
> +
> +	clk_disable_unprepare(vp->dclk);
> +
> +	vop2->enable_count--;
> +
> +	if (!vop2->enable_count)
> +		vop2_disable(vop2);
> +
> +	vop2_unlock(vop2);
> +
> +	if (crtc->state->event && !crtc->state->active) {
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +
> +		crtc->state->event = NULL;
> +	}
> +}
> +
> +static int vop2_plane_atomic_check(struct drm_plane *plane,
> +				   struct drm_atomic_state *astate)
> +{
> +	struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane);
> +	struct drm_framebuffer *fb = pstate->fb;
> +	struct drm_crtc *crtc = pstate->crtc;
> +	struct drm_crtc_state *cstate;
> +	struct vop2_video_port *vp;
> +	struct vop2 *vop2;
> +	const struct vop2_data *vop2_data;
> +	struct drm_rect *dest = &pstate->dst;
> +	struct drm_rect *src = &pstate->src;
> +	int min_scale = FRAC_16_16(1, 8);
> +	int max_scale = FRAC_16_16(8, 1);
> +	int format;
> +	int ret;
> +
> +	if (!crtc)
> +		return 0;
> +
> +	vp = to_vop2_video_port(crtc);
> +	vop2 = vp->vop2;
> +	vop2_data = vop2->data;
> +
> +	cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc);
> +	if (WARN_ON(!cstate))
> +		return -EINVAL;
> +
> +	ret = drm_atomic_helper_check_plane_state(pstate, cstate,
> +						  min_scale, max_scale,
> +						  true, true);
> +	if (ret)
> +		return ret;
> +
> +	if (!pstate->visible)
> +		return 0;
> +
> +	format = vop2_convert_format(fb->format->format);
> +	if (format < 0)
> +		return format;
> +
> +	if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 ||
> +	    drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) {
> +		drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
> +			  drm_rect_width(src) >> 16, drm_rect_height(src) >> 16,
> +			  drm_rect_width(dest), drm_rect_height(dest));
> +		pstate->visible = false;
> +		return 0;
> +	}
> +
> +	if (drm_rect_width(src) >> 16 > vop2_data->max_input.width ||
> +	    drm_rect_height(src) >> 16 > vop2_data->max_input.height) {
> +		drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
> +			  drm_rect_width(src) >> 16,
> +			  drm_rect_height(src) >> 16,
> +			  vop2_data->max_input.width,
> +			  vop2_data->max_input.height);
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Src.x1 can be odd when do clip, but yuv plane start point
> +	 * need align with 2 pixel.
> +	 */
> +	if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
> +		drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vop2_plane_atomic_disable(struct drm_plane *plane,
> +				      struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state, plane);
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2 *vop2 = win->vop2;
> +
> +	drm_dbg(vop2->drm, "%s disable\n", win->data->name);
> +
> +	if (!old_pstate->crtc)
> +		return;
> +
> +	vop2_win_disable(win);
> +	vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0);
> +}
> +
> +/*
> + * The color key is 10 bit, so all format should
> + * convert to 10 bit here.
> + */
> +static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
> +{
> +	struct drm_plane_state *pstate = plane->state;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	struct vop2_win *win = to_vop2_win(plane);
> +	u32 color_key_en = 0;
> +	u32 r = 0;
> +	u32 g = 0;
> +	u32 b = 0;
> +
> +	if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) {
> +		vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0);
> +		return;
> +	}
> +
> +	switch (fb->format->format) {
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		r = (color_key & 0xf800) >> 11;
> +		g = (color_key & 0x7e0) >> 5;
> +		b = (color_key & 0x1f);
> +		r <<= 5;
> +		g <<= 4;
> +		b <<= 5;
> +		color_key_en = 1;
> +		break;
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		r = (color_key & 0xff0000) >> 16;
> +		g = (color_key & 0xff00) >> 8;
> +		b = (color_key & 0xff);
> +		r <<= 2;
> +		g <<= 2;
> +		b <<= 2;
> +		color_key_en = 1;
> +		break;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en);
> +	vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b);
> +}
> +
> +static void vop2_plane_atomic_update(struct drm_plane *plane,
> +				     struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *pstate = plane->state;
> +	struct drm_crtc *crtc = pstate->crtc;
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
> +	struct vop2 *vop2 = win->vop2;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	u32 bpp = fb->format->cpp[0] * 8;
> +	u32 actual_w, actual_h, dsp_w, dsp_h;
> +	u32 act_info, dsp_info;
> +	u32 format;
> +	u32 afbc_format;
> +	u32 rb_swap;
> +	u32 uv_swap;
> +	struct drm_rect *src = &pstate->src;
> +	struct drm_rect *dest = &pstate->dst;
> +	u32 afbc_tile_num;
> +	u32 transform_offset;
> +	bool dither_up;
> +	bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
> +	bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
> +	bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
> +	bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
> +	struct rockchip_gem_object *rk_obj;
> +	unsigned long offset;
> +	bool afbc_en;
> +	dma_addr_t yrgb_mst;
> +	dma_addr_t uv_mst;
> +
> +	/*
> +	 * can't update plane when vop2 is disabled.
> +	 */
> +	if (WARN_ON(!crtc))
> +		return;
> +
> +	if (!pstate->visible) {
> +		vop2_plane_atomic_disable(plane, state);
> +		return;
> +	}
> +
> +	afbc_en = rockchip_afbc(plane, fb->modifier);
> +
> +	offset = (src->x1 >> 16) * fb->format->cpp[0];
> +
> +	/*
> +	 * AFBC HDR_PTR must set to the zero offset of the framebuffer.
> +	 */
> +	if (afbc_en)
> +		offset = 0;
> +	else if (pstate->rotation & DRM_MODE_REFLECT_Y)
> +		offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
> +	else
> +		offset += (src->y1 >> 16) * fb->pitches[0];
> +
> +	rk_obj = to_rockchip_obj(fb->obj[0]);
> +
> +	yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
> +	if (fb->format->is_yuv) {
> +		int hsub = fb->format->hsub;
> +		int vsub = fb->format->vsub;
> +
> +		offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub;
> +		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
> +
> +		if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en)
> +			offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub;
> +
> +		rk_obj = to_rockchip_obj(fb->obj[0]);
> +		uv_mst = rk_obj->dma_addr + offset + fb->offsets[1];
> +	}
> +
> +	actual_w = drm_rect_width(src) >> 16;
> +	actual_h = drm_rect_height(src) >> 16;
> +	dsp_w = drm_rect_width(dest);
> +
> +	if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
> +		drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
> +			  vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
> +		dsp_w = adjusted_mode->hdisplay - dest->x1;
> +		if (dsp_w < 4)
> +			dsp_w = 4;
> +		actual_w = dsp_w * actual_w / drm_rect_width(dest);
> +	}
> +
> +	dsp_h = drm_rect_height(dest);
> +
> +	if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
> +		drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
> +			  vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
> +		dsp_h = adjusted_mode->vdisplay - dest->y1;
> +		if (dsp_h < 4)
> +			dsp_h = 4;
> +		actual_h = dsp_h * actual_h / drm_rect_height(dest);
> +	}
> +
> +	/*
> +	 * This is workaround solution for IC design:
> +	 * esmart can't support scale down when actual_w % 16 == 1.
> +	 */
> +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> +		if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
> +			drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
> +				vp->id, win->data->name, actual_w);
> +			actual_w -= 1;
> +		}
> +	}
> +
> +	if (afbc_en && actual_w % 4) {
> +		drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
> +			  vp->id, win->data->name, actual_w);
> +		actual_w = ALIGN_DOWN(actual_w, 4);
> +	}
> +
> +	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
> +	dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff);
> +
> +	format = vop2_convert_format(fb->format->format);
> +
> +	drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n",
> +		      vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h,
> +		      dest->x1, dest->y1,
> +		      &fb->format->format,
> +		      afbc_en ? "AFBC" : "", &yrgb_mst);
> +
> +	if (afbc_en) {
> +		u32 stride;
> +
> +		/* the afbc superblock is 16 x 16 */
> +		afbc_format = vop2_convert_afbc_format(fb->format->format);
> +
> +		/* Enable color transform for YTR */
> +		if (fb->modifier & AFBC_FORMAT_MOD_YTR)
> +			afbc_format |= (1 << 4);
> +
> +		afbc_tile_num = ALIGN(actual_w, 16) >> 4;
> +
> +		/*
> +		 * AFBC pic_vir_width is count by pixel, this is different
> +		 * with WIN_VIR_STRIDE.
> +		 */
> +		stride = (fb->pitches[0] << 3) / bpp;
> +		if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
> +			drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligened\n",
> +				  vp->id, win->data->name, stride);
> +
> +		rb_swap = vop2_afbc_rb_swap(fb->format->format);
> +		uv_swap = vop2_afbc_uv_swap(fb->format->format);
> +		/*
> +		 * This is a workaround for crazy IC design, Cluster
> +		 * and Esmart/Smart use different format configuration map:
> +		 * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart.
> +		 *
> +		 * This is one thing we can make the convert simple:
> +		 * AFBCD decode all the YUV data to YUV444. So we just
> +		 * set all the yuv 10 bit to YUV444_10.
> +		 */
> +		if (fb->format->is_yuv && (bpp == 10))
> +			format = VOP2_CLUSTER_YUV444_10;
> +
> +		if (vop2_cluster_window(win))
> +			vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
> +		vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
> +		vop2_win_write(win, VOP2_WIN_AFBC_RB_SWAP, rb_swap);
> +		vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap);
> +		vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
> +		vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
> +		if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) {
> +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0);
> +			transform_offset = vop2_afbc_transform_offset(pstate, false);
> +		} else {
> +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 1);
> +			transform_offset = vop2_afbc_transform_offset(pstate, true);
> +		}
> +		vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
> +		vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset);
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1));
> +		vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16)));
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride);
> +		vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num);
> +		vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror);
> +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270);
> +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90);
> +	} else {
> +		vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4));
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror);
> +
> +	if (rotate_90 || rotate_270) {
> +		act_info = swahw32(act_info);
> +		actual_w = drm_rect_height(src) >> 16;
> +		actual_h = drm_rect_width(src) >> 16;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_FORMAT, format);
> +	vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst);
> +
> +	rb_swap = vop2_win_rb_swap(fb->format->format);
> +	vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap);
> +	if (!vop2_cluster_window(win)) {
> +		uv_swap = vop2_win_uv_swap(fb->format->format);
> +		vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
> +	}
> +
> +	if (fb->format->is_yuv) {
> +		vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4));
> +		vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst);
> +	}
> +
> +	vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format);
> +	if (!vop2_cluster_window(win))
> +		vop2_plane_setup_color_key(plane, 0);
> +	vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info);
> +	vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info);
> +	vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff));
> +
> +	vop2_setup_csc_mode(vp, win, pstate);
> +
> +	dither_up = vop2_win_dither_up(fb->format->format);
> +	vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up);
> +
> +	vop2_win_write(win, VOP2_WIN_ENABLE, 1);
> +
> +	if (vop2_cluster_window(win)) {
> +		int lb_mode = vop2_get_cluster_lb_mode(win, pstate);
> +
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode);
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1);
> +	}
> +}
> +
> +static const struct drm_plane_helper_funcs vop2_plane_helper_funcs = {
> +	.atomic_check = vop2_plane_atomic_check,
> +	.atomic_update = vop2_plane_atomic_update,
> +	.atomic_disable = vop2_plane_atomic_disable,
> +};
> +
> +static const struct drm_plane_funcs vop2_plane_funcs = {
> +	.update_plane	= drm_atomic_helper_update_plane,
> +	.disable_plane	= drm_atomic_helper_disable_plane,
> +	.destroy = drm_plane_cleanup,
> +	.reset = drm_atomic_helper_plane_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> +	.format_mod_supported = rockchip_vop2_mod_supported,
> +};
> +
> +static int vop2_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD);
> +
> +	return 0;
> +}
> +
> +static void vop2_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD);
> +}
> +
> +static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
> +				 const struct drm_display_mode *mode,
> +				 struct drm_display_mode *adj_mode)
> +{
> +	drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V |
> +					CRTC_STEREO_DOUBLE);
> +
> +	return true;
> +}
> +
> +static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	switch (vcstate->bus_format) {
> +	case MEDIA_BUS_FMT_RGB565_1X16:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> +		break;
> +	case MEDIA_BUS_FMT_RGB666_1X18:
> +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> +	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> +		*dsp_ctrl |= RGB888_TO_RGB666;
> +		break;
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA)
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> +
> +	*dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL,
> +				DITHER_DOWN_ALLEGRO);
> +}
> +
> +static void vop2_post_config(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	u16 vtotal = mode->crtc_vtotal;
> +	u16 hdisplay = mode->crtc_hdisplay;
> +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> +	u16 vdisplay = mode->crtc_vdisplay;
> +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> +	u32 left_margin = 100, right_margin = 100;
> +	u32 top_margin = 100, bottom_margin = 100;
> +	u16 hsize = hdisplay * (left_margin + right_margin) / 200;
> +	u16 vsize = vdisplay * (top_margin + bottom_margin) / 200;
> +	u16 hact_end, vact_end;
> +	u32 val;
> +
> +	vsize = rounddown(vsize, 2);
> +	hsize = rounddown(hsize, 2);
> +	hact_st += hdisplay * (100 - left_margin) / 200;
> +	hact_end = hact_st + hsize;
> +	val = hact_st << 16;
> +	val |= hact_end;
> +	vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val);
> +	vact_st += vdisplay * (100 - top_margin) / 200;
> +	vact_end = vact_st + vsize;
> +	val = vact_st << 16;
> +	val |= vact_end;
> +	vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val);
> +	val = scl_cal_scale2(vdisplay, vsize) << 16;
> +	val |= scl_cal_scale2(hdisplay, hsize);
> +	vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val);
> +
> +	val = 0;
> +	if (hdisplay != hsize)
> +		val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN;
> +	if (vdisplay != vsize)
> +		val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN;
> +	vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val);
> +
> +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> +		u16 vact_st_f1 = vtotal + vact_st + 1;
> +		u16 vact_end_f1 = vact_st_f1 + vsize;
> +
> +		val = vact_st_f1 << 16 | vact_end_f1;
> +		vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
> +	}
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_BG, 0);
> +}
> +
> +static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
> +				u32 polflags)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	u32 die, dip;
> +
> +	die = vop2_readl(vop2, RK3568_DSP_IF_EN);
> +	dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
> +
> +	switch (id) {
> +	case RK3568_VOP2_EP_RGB:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_RGB |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
> +		if (polflags & POLFLAG_DCLK_INV)
> +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
> +		else
> +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16));
> +		break;
> +	case RK3568_VOP2_EP_HDMI:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_HDMI |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id);
> +		break;
> +	case RK3568_VOP2_EP_EDP:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_EDP |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id);
> +		break;
> +	case RK3568_VOP2_EP_MIPI0:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_MIPI1:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_LVDS0:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_LVDS1:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> +		break;
> +	default:
> +		return;
> +	};
> +
> +	dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
> +
> +	vop2_writel(vop2, RK3568_DSP_IF_EN, die);
> +	vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
> +}
> +
> +static int us_to_vertical_line(struct drm_display_mode *mode, int us)
> +{
> +	return us * mode->clock / mode->htotal / 1000;
> +}
> +
> +static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
> +				    struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	const struct vop2_data *vop2_data = vop2->data;
> +	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	unsigned long clock = mode->crtc_clock * 1000;
> +	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
> +	u16 hdisplay = mode->crtc_hdisplay;
> +	u16 htotal = mode->crtc_htotal;
> +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> +	u16 hact_end = hact_st + hdisplay;
> +	u16 vdisplay = mode->crtc_vdisplay;
> +	u16 vtotal = mode->crtc_vtotal;
> +	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
> +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> +	u16 vact_end = vact_st + vdisplay;
> +	u8 out_mode;
> +	u32 dsp_ctrl = 0;
> +	int act_end;
> +	u32 val, polflags;
> +	int ret;
> +	struct drm_encoder *encoder;
> +
> +	drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n",
> +		hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
> +		drm_mode_vrefresh(mode), vcstate->output_type, vp->id);
> +
> +	vop2_lock(vop2);
> +
> +	ret = clk_prepare_enable(vp->dclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
> +			      vp->id, ret);
> +		return;
> +	}
> +
> +	if (!vop2->enable_count)
> +		vop2_enable(vop2);
> +
> +	vop2->enable_count++;
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY);
> +
> +	polflags = 0;
> +	if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
> +		polflags |= POLFLAG_DCLK_INV;
> +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> +		polflags |= BIT(HSYNC_POSITIVE);
> +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> +		polflags |= BIT(VSYNC_POSITIVE);
> +
> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> +		struct device_node *node, *parent;
> +
> +		parent = of_get_parent(rkencoder->port);
> +
> +		for_each_endpoint_of_node(parent, node) {

Is there any hurt directly use our downstream vendor kernel method here: 
use vcstate->output_if set by encoder driver to get which interface we 
should enable here?

You method is ok with device tree,  but it tied up this driver to device 
tree, we are now tring to extend vop2 driver work with ACPI, so we hope 
this driver can be much more flexible.

> +			struct device_node *crtc_port = of_graph_get_remote_port(node);
> +			struct device_node *epn;
> +			struct of_endpoint endpoint;
> +
> +			if (crtc->port != crtc_port) {
> +				of_node_put(crtc_port);
> +				continue;
> +			}
> +
> +			of_node_put(crtc_port);
> +
> +			epn = of_graph_get_remote_endpoint(node);
> +			of_graph_parse_endpoint(epn, &endpoint);
> +			of_node_put(epn);
> +
> +			drm_dbg(vop2->drm, "vp%d is connected to %s, id %d\n",
> +					   vp->id, encoder->name, endpoint.id);
> +			rk3568_set_intf_mux(vp, endpoint.id, polflags);
> +		}
> +		of_node_put(parent);
> +	}
> +
> +	if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
> +	     !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT))
> +		out_mode = ROCKCHIP_OUT_MODE_P888;
> +	else
> +		out_mode = vcstate->output_mode;
> +
> +	dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode);
> +
> +	if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP;
> +
> +	if (is_yuv_output(vcstate->bus_format))
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y;
> +
> +	vop2_dither_setup(crtc, &dsp_ctrl);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len);
> +	val = hact_st << 16;
> +	val |= hact_end;
> +	vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val);
> +
> +	val = vact_st << 16;
> +	val |= vact_end;
> +	vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val);
> +
> +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> +		u16 vact_st_f1 = vtotal + vact_st + 1;
> +		u16 vact_end_f1 = vact_st_f1 + vdisplay;
> +
> +		val = vact_st_f1 << 16 | vact_end_f1;
> +		vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val);
> +
> +		val = vtotal << 16 | (vtotal + vsync_len);
> +		vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val);
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE;
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL;
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN;
> +		vtotal += vtotal + 1;
> +		act_end = vact_end_f1;
> +	} else {
> +		act_end = vact_end;
> +	}
> +
> +	vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id),
> +		    (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len);
> +
> +	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
> +		clock *= 2;
> +	}
> +
> +	vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
> +
> +	clk_set_rate(vp->dclk, clock);
> +
> +	vop2_post_config(crtc);
> +
> +	vop2_cfg_done(vp);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
> +
> +	drm_crtc_vblank_on(crtc);
> +
> +	vop2_unlock(vop2);
> +}
> +
> +static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
> +				  struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_plane *plane;
> +	int nplanes = 0;
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +
> +	drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
> +		nplanes++;
> +
> +	if (nplanes > vp->nlayers)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static bool is_opaque(u16 alpha)
> +{
> +	return (alpha >> 8) == 0xff;
> +}
> +
> +static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config,
> +			     struct vop2_alpha *alpha)
> +{
> +	int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1;
> +	int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1;
> +	int src_color_mode = alpha_config->src_premulti_en ?
> +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> +	int dst_color_mode = alpha_config->dst_premulti_en ?
> +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> +
> +	alpha->src_color_ctrl.val = 0;
> +	alpha->dst_color_ctrl.val = 0;
> +	alpha->src_alpha_ctrl.val = 0;
> +	alpha->dst_alpha_ctrl.val = 0;
> +
> +	if (!alpha_config->src_pixel_alpha_en)
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> +	else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en)
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> +	else
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> +
> +	alpha->src_color_ctrl.bits.alpha_en = 1;
> +
> +	if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) {
> +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> +	} else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) {
> +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE;
> +	} else {
> +		alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> +	}
> +	alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8;
> +	alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +
> +	alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +	alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> +	alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8;
> +	alpha->dst_color_ctrl.bits.color_mode = dst_color_mode;
> +	alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> +
> +	alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode;
> +	alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +	alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE;
> +
> +	alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en)
> +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> +	else
> +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> +	alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION;
> +	alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> +}
> +
> +static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id)
> +{
> +	struct vop2_video_port *vp;
> +	int used_layer = 0;
> +	int i;
> +
> +	for (i = 0; i < port_id; i++) {
> +		vp = &vop2->vps[i];
> +		used_layer += hweight32(vp->win_mask);
> +	}
> +
> +	return used_layer;
> +}
> +
> +static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win)
> +{
> +	u32 offset = (main_win->data->phys_id * 0x10);
> +	struct vop2_alpha_config alpha_config;
> +	struct vop2_alpha alpha;
> +	struct drm_plane_state *bottom_win_pstate;
> +	bool src_pixel_alpha_en = false;
> +	u16 src_glb_alpha_val, dst_glb_alpha_val;
> +	bool premulti_en = false;
> +	bool swap = false;
> +
> +	/* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */
> +	bottom_win_pstate = main_win->base.state;
> +	src_glb_alpha_val = 0;
> +	dst_glb_alpha_val = main_win->base.state->alpha;
> +
> +	if (!bottom_win_pstate->fb)
> +		return;
> +
> +	alpha_config.src_premulti_en = premulti_en;
> +	alpha_config.dst_premulti_en = false;
> +	alpha_config.src_pixel_alpha_en = src_pixel_alpha_en;
> +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> +	alpha_config.src_glb_alpha_value = src_glb_alpha_val;
> +	alpha_config.dst_glb_alpha_value = dst_glb_alpha_val;
> +	vop2_parse_alpha(&alpha_config, &alpha);
> +
> +	alpha.src_color_ctrl.bits.src_dst_swap = swap;
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset,
> +		    alpha.src_color_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset,
> +		    alpha.dst_color_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset,
> +		    alpha.src_alpha_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset,
> +		    alpha.dst_alpha_ctrl.val);
> +}
> +
> +static void vop2_setup_alpha(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_framebuffer *fb;
> +	struct vop2_alpha_config alpha_config;
> +	struct vop2_alpha alpha;
> +	struct drm_plane *plane;
> +	int pixel_alpha_en;
> +	int premulti_en, gpremulti_en = 0;
> +	int mixer_id;
> +	u32 offset;
> +	bool bottom_layer_alpha_en = false;
> +	u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE;
> +
> +	mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id);
> +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> +
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		if (plane->state->normalized_zpos == 0 &&
> +		    !is_opaque(plane->state->alpha) &&
> +		    !vop2_cluster_window(win)) {
> +			/*
> +			 * If bottom layer have global alpha effect [except cluster layer,
> +			 * because cluster have deal with bottom layer global alpha value
> +			 * at cluster mix], bottom layer mix need deal with global alpha.
> +			 */
> +			bottom_layer_alpha_en = true;
> +			dst_global_alpha = plane->state->alpha;
> +		}
> +	}
> +
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +		int zpos = plane->state->normalized_zpos;
> +
> +		if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
> +			premulti_en = 1;
> +		else
> +			premulti_en = 0;
> +
> +		plane = &win->base;
> +		fb = plane->state->fb;
> +
> +		pixel_alpha_en = fb->format->has_alpha;
> +
> +		alpha_config.src_premulti_en = premulti_en;
> +
> +		if (bottom_layer_alpha_en && zpos == 1) {
> +			gpremulti_en = premulti_en;
> +			/* Cd = Cs + (1 - As) * Cd * Agd */
> +			alpha_config.dst_premulti_en = false;
> +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> +			alpha_config.dst_glb_alpha_value = dst_global_alpha;
> +		} else if (vop2_cluster_window(win)) {
> +			/* Mix output data only have pixel alpha */
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = true;
> +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +		} else {
> +			/* Cd = Cs + (1 - As) * Cd */
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +		}
> +
> +		vop2_parse_alpha(&alpha_config, &alpha);
> +
> +		offset = (mixer_id + zpos - 1) * 0x10;
> +		vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset,
> +			    alpha.src_color_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset,
> +			    alpha.dst_color_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset,
> +			    alpha.src_alpha_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset,
> +			    alpha.dst_alpha_ctrl.val);
> +	}
> +
> +	if (vp->id == 0) {
> +		if (bottom_layer_alpha_en) {
> +			/* Transfer pixel alpha to hdr mix */
> +			alpha_config.src_premulti_en = gpremulti_en;
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = true;
> +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			vop2_parse_alpha(&alpha_config, &alpha);
> +
> +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL,
> +				alpha.src_color_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL,
> +				alpha.dst_color_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL,
> +				alpha.src_alpha_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL,
> +				alpha.dst_alpha_ctrl.val);
> +		} else {
> +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0);
> +		}
> +	}
> +}
> +
> +static void vop2_setup_layer_mixer(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_plane *plane;
> +	u32 layer_sel = 0;
> +	u32 port_sel;
> +	unsigned int nlayer, ofs;
> +	struct drm_display_mode *adjusted_mode;
> +	u16 hsync_len;
> +	u16 hdisplay;
> +	u32 bg_dly;
> +	u32 pre_scan_dly;
> +	int i;
> +	struct vop2_video_port *vp0 = &vop2->vps[0];
> +	struct vop2_video_port *vp1 = &vop2->vps[1];
> +	struct vop2_video_port *vp2 = &vop2->vps[2];
> +
> +	adjusted_mode = &vp->crtc.state->adjusted_mode;
> +	hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
> +	hdisplay = adjusted_mode->crtc_hdisplay;
> +
> +	bg_dly = vp->data->pre_scan_max_dly[3];
> +	vop2_writel(vop2, RK3568_VP_BG_MIX_CTRL(vp->id),
> +			    FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly));
> +
> +	pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len;
> +	vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly);
> +
> +	vop2_writel(vop2, RK3568_OVL_CTRL, 0);
> +	port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
> +	port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT;
> +
> +	if (vp0->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX,
> +				     vp0->nlayers - 1);
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8);
> +
> +	if (vp1->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX,
> +				     (vp0->nlayers + vp1->nlayers - 1));
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> +
> +	if (vp2->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX,
> +			(vp2->nlayers + vp1->nlayers + vp0->nlayers - 1));
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> +
> +	layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
> +
> +	ofs = 0;
> +	for (i = 0; i < vp->id; i++)
> +		ofs += vop2->vps[i].nlayers;
> +
> +	nlayer = 0;
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		switch (win->data->phys_id) {
> +		case ROCKCHIP_VOP2_CLUSTER0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_CLUSTER1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_SMART0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_SMART1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id);
> +			break;
> +		}
> +
> +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, win->data->layer_sel_id);
> +		nlayer++;
> +	}
> +
> +	/* configure unused layers to 0x5 (reserved) */
> +	for (; nlayer < 3; nlayer++) {
> +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5);
> +	}
> +
> +	vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel);
> +	vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
> +	vop2_writel(vop2, RK3568_OVL_CTRL, RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD);
> +}
> +
> +static void vop2_setup_dly_for_windows(struct vop2 *vop2)
> +{
> +	struct vop2_win *win;
> +	int i = 0;
> +	u32 cdly = 0, sdly = 0;
> +
> +	for (i = 0; i < vop2->data->win_size; i++) {
> +		u32 dly;
> +
> +		win = &vop2->win[i];
> +		dly = win->delay;
> +
> +		switch (win->data->phys_id) {
> +		case ROCKCHIP_VOP2_CLUSTER0:
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly);
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_CLUSTER1:
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly);
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART0:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART1:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_SMART0:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_SMART1:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly);
> +			break;
> +		}
> +	}
> +
> +	vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly);
> +	vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly);
> +}
> +
> +static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_plane *plane;
> +
> +	vp->win_mask = 0;
> +
> +	drm_atomic_crtc_for_each_plane(plane, crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT];
> +
> +		vp->win_mask |= BIT(win->data->phys_id);
> +
> +		if (vop2_cluster_window(win))
> +			vop2_setup_cluster_alpha(vop2, win);
> +	}
> +
> +	if (!vp->win_mask)
> +		return;
> +
> +	vop2_setup_layer_mixer(vp);
> +	vop2_setup_alpha(vp);
> +	vop2_setup_dly_for_windows(vop2);
> +}
> +
> +static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_post_config(crtc);
> +
> +	vop2_cfg_done(vp);
> +
> +	spin_lock_irq(&crtc->dev->event_lock);
> +
> +	if (crtc->state->event) {
> +		WARN_ON(drm_crtc_vblank_get(crtc));
> +		vp->event = crtc->state->event;
> +		crtc->state->event = NULL;
> +	}
> +
> +	spin_unlock_irq(&crtc->dev->event_lock);
> +}
> +
> +static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
> +	.mode_fixup = vop2_crtc_mode_fixup,
> +	.atomic_check = vop2_crtc_atomic_check,
> +	.atomic_begin = vop2_crtc_atomic_begin,
> +	.atomic_flush = vop2_crtc_atomic_flush,
> +	.atomic_enable = vop2_crtc_atomic_enable,
> +	.atomic_disable = vop2_crtc_atomic_disable,
> +};
> +
> +static void vop2_crtc_reset(struct drm_crtc *crtc)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	if (crtc->state) {
> +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
> +		kfree(vcstate);
> +	}
> +
> +	vcstate = kzalloc(sizeof(*vcstate), GFP_KERNEL);
> +	if (!vcstate)
> +		return;
> +
> +	crtc->state = &vcstate->base;
> +	crtc->state->crtc = crtc;
> +}
> +
> +static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc)
> +{
> +	struct rockchip_crtc_state *vcstate, *old_vcstate;
> +
> +	old_vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	vcstate = kmemdup(old_vcstate, sizeof(*old_vcstate), GFP_KERNEL);
> +	if (!vcstate)
> +		return NULL;
> +
> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
> +
> +	return &vcstate->base;
> +}
> +
> +static void vop2_crtc_destroy_state(struct drm_crtc *crtc,
> +				    struct drm_crtc_state *state)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state);
> +
> +	__drm_atomic_helper_crtc_destroy_state(&vcstate->base);
> +	kfree(vcstate);
> +}
> +
> +static const struct drm_crtc_funcs vop2_crtc_funcs = {
> +	.set_config = drm_atomic_helper_set_config,
> +	.page_flip = drm_atomic_helper_page_flip,
> +	.destroy = drm_crtc_cleanup,
> +	.reset = vop2_crtc_reset,
> +	.atomic_duplicate_state = vop2_crtc_duplicate_state,
> +	.atomic_destroy_state = vop2_crtc_destroy_state,
> +	.enable_vblank = vop2_crtc_enable_vblank,
> +	.disable_vblank = vop2_crtc_disable_vblank,
> +};
> +
> +static irqreturn_t vop2_isr(int irq, void *data)
> +{
> +	struct vop2 *vop2 = data;
> +	const struct vop2_data *vop2_data = vop2->data;
> +	u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM];
> +	int ret = IRQ_NONE;
> +	int i;
> +
> +	/*
> +	 * The irq is shared with the iommu. If the runtime-pm state of the
> +	 * vop2-device is disabled the irq has to be targeted at the iommu.
> +	 */
> +	if (!pm_runtime_get_if_in_use(vop2->dev))
> +		return IRQ_NONE;
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +		struct drm_crtc *crtc = &vp->crtc;
> +		u32 irqs;
> +
> +		irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
> +		vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
> +
> +		if (irqs & VP_INT_DSP_HOLD_VALID) {
> +			complete(&vp->dsp_hold_completion);
> +			ret = IRQ_HANDLED;
> +		}
> +
> +		if (irqs & VP_INT_FS_FIELD) {
> +			drm_crtc_handle_vblank(crtc);
> +			spin_lock(&crtc->dev->event_lock);
> +			if (vp->event) {
> +				u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
> +
> +				if (!(val & BIT(vp->id))) {
> +					drm_crtc_send_vblank_event(crtc, vp->event);
> +					vp->event = NULL;
> +					drm_crtc_vblank_put(crtc);
> +				}
> +			}
> +			spin_unlock(&crtc->dev->event_lock);
> +
> +			ret = IRQ_HANDLED;
> +		}
> +
> +		if (irqs & VP_INT_POST_BUF_EMPTY) {
> +			drm_err_ratelimited(vop2->drm,
> +					    "POST_BUF_EMPTY irq err at vp%d\n",
> +					    vp->id);
> +			ret = IRQ_HANDLED;
> +		}
> +	}
> +
> +	axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS);
> +	vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]);
> +	axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS);
> +	vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]);
> +
> +	for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) {
> +		if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) {
> +			drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n");
> +			ret = IRQ_HANDLED;
> +		}
> +	}
> +
> +	pm_runtime_put(vop2->dev);
> +
> +	return ret;
> +}
> +
> +static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win,
> +			   unsigned long possible_crtcs)
> +{
> +	const struct vop2_win_data *win_data = win->data;
> +	unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +				  BIT(DRM_MODE_BLEND_PREMULTI) |
> +				  BIT(DRM_MODE_BLEND_COVERAGE);
> +	int ret;
> +
> +	ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs,
> +				       &vop2_plane_funcs, win_data->formats,
> +				       win_data->nformats,
> +				       win_data->format_modifiers,
> +				       win->type, win_data->name);
> +	if (ret) {
> +		drm_err(vop2->drm, "failed to initialize plane %d\n", ret);
> +		return ret;
> +	}
> +
> +	drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs);
> +
> +	if (win->data->supported_rotations)
> +		drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0,
> +						   DRM_MODE_ROTATE_0 |
> +						   win->data->supported_rotations);
> +	drm_plane_create_alpha_property(&win->base);
> +	drm_plane_create_blend_mode_property(&win->base, blend_caps);
> +	drm_plane_create_zpos_property(&win->base, win->win_id, 0,
> +				       vop2->registered_num_wins - 1);
> +
> +	return 0;
> +}
> +
> +static struct vop2_video_port *get_activated_vp(struct vop2 *vop2, int n)
> +{
> +	int i, id = 0;
> +
> +	for (i = 0; i < vop2->data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +
> +		if (!vp->crtc.port)
> +			continue;
> +
> +		if (n == id)
> +			return vp;
> +		id++;
> +	}
> +
> +	return NULL;
> +}
> +
> +#define NR_LAYERS 6
> +
> +static int vop2_create_crtc(struct vop2 *vop2)
> +{
> +	const struct vop2_data *vop2_data = vop2->data;
> +	struct drm_device *drm = vop2->drm;
> +	struct device *dev = vop2->dev;
> +	struct drm_plane *plane;
> +	struct device_node *port;
> +	struct vop2_video_port *vp;
> +	u32 possible_crtcs;
> +	int i, nvp, nvps = 0;
> +	int ret;
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		const struct vop2_video_port_data *vp_data;
> +		struct device_node *np;
> +		char dclk_name[9];
> +
> +		vp_data = &vop2_data->vp[i];
> +		vp = &vop2->vps[i];
> +		vp->vop2 = vop2;
> +		vp->id = vp_data->id;
> +		vp->regs = vp_data->regs;
> +		vp->data = vp_data;
> +
> +		snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
> +		vp->dclk = devm_clk_get(vop2->dev, dclk_name);
> +		if (IS_ERR(vp->dclk)) {
> +			drm_err(vop2->drm, "failed to get %s\n", dclk_name);
> +			return PTR_ERR(vp->dclk);
> +		}
> +
> +		np = of_graph_get_remote_node(dev->of_node, i, -1);
> +		if (!np) {
> +			drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
> +			continue;
> +		}
> +		of_node_put(np);
> +
> +		port = of_graph_get_port_by_id(dev->of_node, i);
> +		if (!port) {
> +			drm_err(vop2->drm, "no port node found for video_port%d\n", i);
> +			return -ENOENT;
> +		}
> +
> +		vp->crtc.port = port;
> +		nvps++;
> +	}
> +
> +	nvp = 0;
> +	for (i = 0; i < vop2->registered_num_wins; i++) {
> +		struct vop2_win *win = &vop2->win[i];
> +
> +		if (win->type == DRM_PLANE_TYPE_PRIMARY) {
> +			vp = get_activated_vp(vop2, nvp);
> +
> +			if (vp) {
> +				possible_crtcs = BIT(nvp);
> +				vp->primary_plane = win;
> +
> +				nvp++;
> +			} else {
> +				/* change the unused primary window to overlay window */
> +				win->type = DRM_PLANE_TYPE_OVERLAY;
> +			}
> +		}
> +
> +		if (win->type == DRM_PLANE_TYPE_OVERLAY)
> +			possible_crtcs = (1 << vop2_data->nr_vps) - 1;
> +
> +		ret = vop2_plane_init(vop2, win, possible_crtcs);
> +
> +		if (ret) {
> +			drm_err(vop2->drm, "failed to init plane %s: %d\n",
> +				win->data->name, ret);
> +			return ret;
> +		}
> +	}
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		vp = &vop2->vps[i];
> +
> +		if (!vp->crtc.port)
> +			continue;
> +
> +		plane = &vp->primary_plane->base;
> +
> +		ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL,
> +						&vop2_crtc_funcs,
> +						"video_port%d", vp->id);
> +		if (ret) {
> +			drm_err(vop2->drm, "crtc init for video_port%d failed\n", i);
> +			return ret;
> +		}
> +
> +		drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs);
> +
> +		init_completion(&vp->dsp_hold_completion);
> +	}
> +
> +	for (i = 0; i < vop2->data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +
> +		if (vp->crtc.port)
> +			vp->nlayers = NR_LAYERS / nvps;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vop2_destroy_crtc(struct drm_crtc *crtc)
> +{
> +	of_node_put(crtc->port);
> +
> +	/*
> +	 * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane()
> +	 * references the CRTC.
> +	 */
> +	drm_crtc_cleanup(crtc);
> +}
> +
> +static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = {
> +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0),
> +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5),
> +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14),
> +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18),
> +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31),
> +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31),
> +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31),
> +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31),
> +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31),
> +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19),
> +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15),
> +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31),
> +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8),
> +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9),
> +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11),
> +
> +	/* Scale */
> +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15),
> +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31),
> +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15),
> +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13),
> +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3),
> +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28),
> +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29),
> +
> +	/* cluster regs */
> +	[VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1),
> +	[VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0),
> +	[VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7),
> +
> +	/* afbc regs */
> +	[VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6),
> +	[VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9),
> +	[VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10),
> +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4),
> +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7),
> +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8),
> +	[VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31),
> +	[VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31),
> +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15),
> +	[VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31),
> +	[VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0),
> +	[VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1),
> +	[VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2),
> +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3),
> +	[VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff },
> +	[VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
> +	[VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
> +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff },
> +	[VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff },
> +};
> +
> +static int vop2_cluster_init(struct vop2_win *win)
> +{
> +	struct vop2 *vop2 = win->vop2;
> +	struct reg_field *cluster_regs;
> +	int ret, i;
> +
> +	cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs),
> +			       GFP_KERNEL);
> +	if (!cluster_regs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++)
> +		if (cluster_regs[i].reg != 0xffffffff)
> +			cluster_regs[i].reg += win->offset;
> +
> +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> +					    cluster_regs,
> +					    ARRAY_SIZE(vop2_cluster_regs));
> +
> +	kfree(cluster_regs);
> +
> +	return ret;
> +};
> +
> +static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = {
> +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0),
> +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5),
> +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12),
> +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14),
> +	[VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16),
> +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31),
> +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31),
> +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28),
> +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31),
> +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31),
> +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17),
> +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15),
> +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31),
> +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0),
> +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1),
> +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3),
> +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31),
> +	[VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29),
> +	[VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31),
> +
> +	/* Scale */
> +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15),
> +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31),
> +	[VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15),
> +	[VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31),
> +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1),
> +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3),
> +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5),
> +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7),
> +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9),
> +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11),
> +	[VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13),
> +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15),
> +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17),
> +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8),
> +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9),
> +	[VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10),
> +	[VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11),
> +	[VOP2_WIN_XMIRROR] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff },
> +};
> +
> +static int vop2_esmart_init(struct vop2_win *win)
> +{
> +	struct vop2 *vop2 = win->vop2;
> +	struct reg_field *esmart_regs;
> +	int ret, i;
> +
> +	esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs),
> +			       GFP_KERNEL);
> +	if (!esmart_regs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++)
> +		if (esmart_regs[i].reg != 0xffffffff)
> +			esmart_regs[i].reg += win->offset;
> +
> +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> +					    esmart_regs,
> +					    ARRAY_SIZE(vop2_esmart_regs));
> +
> +	kfree(esmart_regs);
> +
> +	return ret;
> +};
> +
> +static int vop2_win_init(struct vop2 *vop2)
> +{
> +	const struct vop2_data *vop2_data = vop2->data;
> +	struct vop2_win *win;
> +	int i, ret;
> +
> +	for (i = 0; i < vop2_data->win_size; i++) {
> +		const struct vop2_win_data *win_data = &vop2_data->win[i];
> +
> +		win = &vop2->win[i];
> +		win->data = win_data;
> +		win->type = win_data->type;
> +		win->offset = win_data->base;
> +		win->win_id = i;
> +		win->vop2 = vop2;
> +		if (vop2_cluster_window(win))
> +			ret = vop2_cluster_init(win);
> +		else
> +			ret = vop2_esmart_init(win);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	vop2->registered_num_wins = vop2_data->win_size;
> +
> +	return 0;
> +}
> +
> +/*
> + * The window registers are only updated when config done is written.
> + * Until that they read back the old value. As we read-modify-write
> + * these registers mark them as non-volatile. This makes sure we read
> + * the new values from the regmap register cache.
> + */
> +static const struct regmap_range vop2_nonvolatile_range[] = {
> +	regmap_reg_range(0x1000, 0x23ff),
> +};
> +
> +static const struct regmap_access_table vop2_volatile_table = {
> +	.no_ranges = vop2_nonvolatile_range,
> +	.n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range),
> +};
> +
> +static const struct regmap_config vop2_regmap_config = {
> +	.reg_bits	= 32,
> +	.val_bits	= 32,
> +	.reg_stride	= 4,
> +	.max_register	= 0x3000,
> +	.name		= "vop2",
> +	.volatile_table	= &vop2_volatile_table,
> +	.cache_type	= REGCACHE_RBTREE,
> +};
> +
> +static int vop2_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	const struct vop2_data *vop2_data;
> +	struct drm_device *drm = data;
> +	struct vop2 *vop2;
> +	struct resource *res;
> +	size_t alloc_size;
> +	int ret;
> +
> +	vop2_data = of_device_get_match_data(dev);
> +	if (!vop2_data)
> +		return -ENODEV;
> +
> +	/* Allocate vop2 struct and its vop2_win array */
> +	alloc_size = sizeof(*vop2) + sizeof(*vop2->win) * vop2_data->win_size;
> +	vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
> +	if (!vop2)
> +		return -ENOMEM;
> +
> +	vop2->dev = dev;
> +	vop2->data = vop2_data;
> +	vop2->drm = drm;
> +
> +	dev_set_drvdata(dev, vop2);
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
> +	if (!res) {
> +		drm_err(vop2->drm, "failed to get vop2 register byname\n");
> +		return -EINVAL;
> +	}
> +
> +	vop2->regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(vop2->regs))
> +		return PTR_ERR(vop2->regs);
> +	vop2->len = resource_size(res);
> +
> +	vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config);
> +
> +	ret = vop2_win_init(vop2);
> +	if (ret)
> +		return ret;
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma_lut");
> +	if (res) {
> +		vop2->lut_regs = devm_ioremap_resource(dev, res);
> +		if (IS_ERR(vop2->lut_regs))
> +			return PTR_ERR(vop2->lut_regs);
> +	}
> +
> +	vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
> +
> +	vop2->hclk = devm_clk_get(vop2->dev, "hclk");
> +	if (IS_ERR(vop2->hclk)) {
> +		drm_err(vop2->drm, "failed to get hclk source\n");
> +		return PTR_ERR(vop2->hclk);
> +	}
> +
> +	vop2->aclk = devm_clk_get(vop2->dev, "aclk");
> +	if (IS_ERR(vop2->aclk)) {
> +		drm_err(vop2->drm, "failed to get aclk source\n");
> +		return PTR_ERR(vop2->aclk);
> +	}
> +
> +	vop2->irq = platform_get_irq(pdev, 0);
> +	if (vop2->irq < 0) {
> +		drm_err(vop2->drm, "cannot find irq for vop2\n");
> +		return vop2->irq;
> +	}
> +
> +	mutex_init(&vop2->vop2_lock);
> +
> +	ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
> +	if (ret)
> +		return ret;
> +
> +	ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev);
> +	if (ret) {
> +		drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = vop2_create_crtc(vop2);
> +	if (ret)
> +		return ret;
> +
> +	pm_runtime_enable(&pdev->dev);
> +
> +	return 0;
> +}
> +
> +static void vop2_unbind(struct device *dev, struct device *master, void *data)
> +{
> +	struct vop2 *vop2 = dev_get_drvdata(dev);
> +	struct drm_device *drm = vop2->drm;
> +	struct list_head *plane_list = &drm->mode_config.plane_list;
> +	struct list_head *crtc_list = &drm->mode_config.crtc_list;
> +	struct drm_crtc *crtc, *tmpc;
> +	struct drm_plane *plane, *tmpp;
> +
> +	rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
> +
> +	pm_runtime_disable(dev);
> +
> +	list_for_each_entry_safe(plane, tmpp, plane_list, head)
> +		drm_plane_cleanup(plane);
> +
> +	list_for_each_entry_safe(crtc, tmpc, crtc_list, head)
> +		vop2_destroy_crtc(crtc);
> +}
> +
> +const struct component_ops vop2_component_ops = {
> +	.bind = vop2_bind,
> +	.unbind = vop2_unbind,
> +};
> +EXPORT_SYMBOL_GPL(vop2_component_ops);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> new file mode 100644
> index 0000000000000..c727093a06d68
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> @@ -0,0 +1,477 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao@rock-chips.com>
> + */
> +
> +#ifndef _ROCKCHIP_DRM_VOP2_H
> +#define _ROCKCHIP_DRM_VOP2_H
> +
> +#include "rockchip_drm_vop.h"
> +
> +#include <linux/regmap.h>
> +#include <drm/drm_modes.h>
> +
> +#define VOP_FEATURE_OUTPUT_10BIT        BIT(0)
> +
> +#define WIN_FEATURE_AFBDC		BIT(0)
> +#define WIN_FEATURE_CLUSTER		BIT(1)
> +
> +/*
> + *  the delay number of a window in different mode.
> + */
> +enum win_dly_mode {
> +	VOP2_DLY_MODE_DEFAULT,   /**< default mode */
> +	VOP2_DLY_MODE_HISO_S,    /** HDR in SDR out mode, as a SDR window */
> +	VOP2_DLY_MODE_HIHO_H,    /** HDR in HDR out mode, as a HDR window */
> +	VOP2_DLY_MODE_MAX,
> +};
> +
> +struct vop_rect {
> +	int width;
> +	int height;
> +};
> +
> +enum vop2_scale_up_mode {
> +	VOP2_SCALE_UP_NRST_NBOR,
> +	VOP2_SCALE_UP_BIL,
> +	VOP2_SCALE_UP_BIC,
> +};
> +
> +enum vop2_scale_down_mode {
> +	VOP2_SCALE_DOWN_NRST_NBOR,
> +	VOP2_SCALE_DOWN_BIL,
> +	VOP2_SCALE_DOWN_AVG,
> +};
> +
> +enum vop2_win_regs {
> +	VOP2_WIN_ENABLE,
> +	VOP2_WIN_FORMAT,
> +	VOP2_WIN_CSC_MODE,
> +	VOP2_WIN_XMIRROR,
> +	VOP2_WIN_YMIRROR,
> +	VOP2_WIN_RB_SWAP,
> +	VOP2_WIN_UV_SWAP,
> +	VOP2_WIN_ACT_INFO,
> +	VOP2_WIN_DSP_INFO,
> +	VOP2_WIN_DSP_ST,
> +	VOP2_WIN_YRGB_MST,
> +	VOP2_WIN_UV_MST,
> +	VOP2_WIN_YRGB_VIR,
> +	VOP2_WIN_UV_VIR,
> +	VOP2_WIN_YUV_CLIP,
> +	VOP2_WIN_Y2R_EN,
> +	VOP2_WIN_R2Y_EN,
> +	VOP2_WIN_COLOR_KEY,
> +	VOP2_WIN_COLOR_KEY_EN,
> +	VOP2_WIN_DITHER_UP,
> +
> +	/* scale regs */
> +	VOP2_WIN_SCALE_YRGB_X,
> +	VOP2_WIN_SCALE_YRGB_Y,
> +	VOP2_WIN_SCALE_CBCR_X,
> +	VOP2_WIN_SCALE_CBCR_Y,
> +	VOP2_WIN_YRGB_HOR_SCL_MODE,
> +	VOP2_WIN_YRGB_HSCL_FILTER_MODE,
> +	VOP2_WIN_YRGB_VER_SCL_MODE,
> +	VOP2_WIN_YRGB_VSCL_FILTER_MODE,
> +	VOP2_WIN_CBCR_VER_SCL_MODE,
> +	VOP2_WIN_CBCR_HSCL_FILTER_MODE,
> +	VOP2_WIN_CBCR_HOR_SCL_MODE,
> +	VOP2_WIN_CBCR_VSCL_FILTER_MODE,
> +	VOP2_WIN_VSD_CBCR_GT2,
> +	VOP2_WIN_VSD_CBCR_GT4,
> +	VOP2_WIN_VSD_YRGB_GT2,
> +	VOP2_WIN_VSD_YRGB_GT4,
> +	VOP2_WIN_BIC_COE_SEL,
> +
> +	/* cluster regs */
> +	VOP2_WIN_CLUSTER_ENABLE,
> +	VOP2_WIN_AFBC_ENABLE,
> +	VOP2_WIN_CLUSTER_LB_MODE,
> +
> +	/* afbc regs */
> +	VOP2_WIN_AFBC_FORMAT,
> +	VOP2_WIN_AFBC_RB_SWAP,
> +	VOP2_WIN_AFBC_UV_SWAP,
> +	VOP2_WIN_AFBC_AUTO_GATING_EN,
> +	VOP2_WIN_AFBC_BLOCK_SPLIT_EN,
> +	VOP2_WIN_AFBC_PIC_VIR_WIDTH,
> +	VOP2_WIN_AFBC_TILE_NUM,
> +	VOP2_WIN_AFBC_PIC_OFFSET,
> +	VOP2_WIN_AFBC_PIC_SIZE,
> +	VOP2_WIN_AFBC_DSP_OFFSET,
> +	VOP2_WIN_AFBC_TRANSFORM_OFFSET,
> +	VOP2_WIN_AFBC_HDR_PTR,
> +	VOP2_WIN_AFBC_HALF_BLOCK_EN,
> +	VOP2_WIN_AFBC_ROTATE_270,
> +	VOP2_WIN_AFBC_ROTATE_90,
> +	VOP2_WIN_MAX_REG,
> +};
> +
> +struct vop2_win_data {
> +	const char *name;
> +	unsigned int phys_id;
> +
> +	u32 base;
> +	enum drm_plane_type type;
> +
> +	u32 nformats;
> +	const u32 *formats;
> +	const uint64_t *format_modifiers;
> +	const unsigned int supported_rotations;
> +
> +	/**
> +	 * @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2
> +	 */
> +	unsigned int layer_sel_id;
> +	uint64_t feature;
> +
> +	unsigned int max_upscale_factor;
> +	unsigned int max_downscale_factor;
> +	const u8 dly[VOP2_DLY_MODE_MAX];
> +};
> +
> +struct vop2_video_port_data {
> +	unsigned int id;
> +	u32 feature;
> +	u16 gamma_lut_len;
> +	u16 cubic_lut_len;
> +	struct vop_rect max_output;
> +	const u8 pre_scan_max_dly[4];
> +	const struct vop2_video_port_regs *regs;
> +	unsigned int offset;
> +};
> +
> +struct vop2_data {
> +	u8 nr_vps;
> +	const struct vop2_ctrl *ctrl;
> +	const struct vop2_win_data *win;
> +	const struct vop2_video_port_data *vp;
> +	const struct vop_csc_table *csc_table;
> +	struct vop_rect max_input;
> +	struct vop_rect max_output;
> +
> +	unsigned int win_size;
> +	unsigned int soc_id;
> +};
> +
> +/* interrupt define */
> +#define FS_NEW_INTR			BIT(4)
> +#define ADDR_SAME_INTR			BIT(5)
> +#define LINE_FLAG1_INTR			BIT(6)
> +#define WIN0_EMPTY_INTR			BIT(7)
> +#define WIN1_EMPTY_INTR			BIT(8)
> +#define WIN2_EMPTY_INTR			BIT(9)
> +#define WIN3_EMPTY_INTR			BIT(10)
> +#define HWC_EMPTY_INTR			BIT(11)
> +#define POST_BUF_EMPTY_INTR		BIT(12)
> +#define PWM_GEN_INTR			BIT(13)
> +#define DMA_FINISH_INTR			BIT(14)
> +#define FS_FIELD_INTR			BIT(15)
> +#define FE_INTR				BIT(16)
> +#define WB_UV_FIFO_FULL_INTR		BIT(17)
> +#define WB_YRGB_FIFO_FULL_INTR		BIT(18)
> +#define WB_COMPLETE_INTR		BIT(19)
> +
> +/*
> + * display output interface supported by rockchip lcdc
> + */
> +#define ROCKCHIP_OUT_MODE_P888		0
> +#define ROCKCHIP_OUT_MODE_BT1120	0
> +#define ROCKCHIP_OUT_MODE_P666		1
> +#define ROCKCHIP_OUT_MODE_P565		2
> +#define ROCKCHIP_OUT_MODE_BT656		5
> +#define ROCKCHIP_OUT_MODE_S888		8
> +#define ROCKCHIP_OUT_MODE_S888_DUMMY	12
> +#define ROCKCHIP_OUT_MODE_YUV420	14
> +/* for use special outface */
> +#define ROCKCHIP_OUT_MODE_AAAA		15
> +
> +enum vop_csc_format {
> +	CSC_BT601L,
> +	CSC_BT709L,
> +	CSC_BT601F,
> +	CSC_BT2020,
> +};
> +
> +enum src_factor_mode {
> +	SRC_FAC_ALPHA_ZERO,
> +	SRC_FAC_ALPHA_ONE,
> +	SRC_FAC_ALPHA_DST,
> +	SRC_FAC_ALPHA_DST_INVERSE,
> +	SRC_FAC_ALPHA_SRC,
> +	SRC_FAC_ALPHA_SRC_GLOBAL,
> +};
> +
> +enum dst_factor_mode {
> +	DST_FAC_ALPHA_ZERO,
> +	DST_FAC_ALPHA_ONE,
> +	DST_FAC_ALPHA_SRC,
> +	DST_FAC_ALPHA_SRC_INVERSE,
> +	DST_FAC_ALPHA_DST,
> +	DST_FAC_ALPHA_DST_GLOBAL,
> +};
> +
> +#define RK3568_GRF_VO_CON1			0x0364
> +/* System registers definition */
> +#define RK3568_REG_CFG_DONE			0x000
> +#define RK3568_VERSION_INFO			0x004
> +#define RK3568_SYS_AUTO_GATING_CTRL		0x008
> +#define RK3568_SYS_AXI_LUT_CTRL			0x024
> +#define RK3568_DSP_IF_EN			0x028
> +#define RK3568_DSP_IF_CTRL			0x02c
> +#define RK3568_DSP_IF_POL			0x030
> +#define RK3568_WB_CTRL				0x40
> +#define RK3568_WB_XSCAL_FACTOR			0x44
> +#define RK3568_WB_YRGB_MST			0x48
> +#define RK3568_WB_CBR_MST			0x4C
> +#define RK3568_OTP_WIN_EN			0x050
> +#define RK3568_LUT_PORT_SEL			0x058
> +#define RK3568_SYS_STATUS0			0x060
> +#define RK3568_VP_LINE_FLAG(vp)			(0x70 + (vp) * 0x4)
> +#define RK3568_SYS0_INT_EN			0x80
> +#define RK3568_SYS0_INT_CLR			0x84
> +#define RK3568_SYS0_INT_STATUS			0x88
> +#define RK3568_SYS1_INT_EN			0x90
> +#define RK3568_SYS1_INT_CLR			0x94
> +#define RK3568_SYS1_INT_STATUS			0x98
> +#define RK3568_VP_INT_EN(vp)			(0xA0 + (vp) * 0x10)
> +#define RK3568_VP_INT_CLR(vp)			(0xA4 + (vp) * 0x10)
> +#define RK3568_VP_INT_STATUS(vp)		(0xA8 + (vp) * 0x10)
> +#define RK3568_VP_INT_RAW_STATUS(vp)		(0xAC + (vp) * 0x10)
> +
> +/* Video Port registers definition */
> +#define RK3568_VP_DSP_CTRL			0x00
> +#define RK3568_VP_MIPI_CTRL			0x04
> +#define RK3568_VP_COLOR_BAR_CTRL		0x08
> +#define RK3568_VP_3D_LUT_CTRL			0x10
> +#define RK3568_VP_3D_LUT_MST			0x20
> +#define RK3568_VP_DSP_BG			0x2C
> +#define RK3568_VP_PRE_SCAN_HTIMING		0x30
> +#define RK3568_VP_POST_DSP_HACT_INFO		0x34
> +#define RK3568_VP_POST_DSP_VACT_INFO		0x38
> +#define RK3568_VP_POST_SCL_FACTOR_YRGB		0x3C
> +#define RK3568_VP_POST_SCL_CTRL			0x40
> +#define RK3568_VP_POST_DSP_VACT_INFO_F1		0x44
> +#define RK3568_VP_DSP_HTOTAL_HS_END		0x48
> +#define RK3568_VP_DSP_HACT_ST_END		0x4C
> +#define RK3568_VP_DSP_VTOTAL_VS_END		0x50
> +#define RK3568_VP_DSP_VACT_ST_END		0x54
> +#define RK3568_VP_DSP_VS_ST_END_F1		0x58
> +#define RK3568_VP_DSP_VACT_ST_END_F1		0x5C
> +#define RK3568_VP_BCSH_CTRL			0x60
> +#define RK3568_VP_BCSH_BCS			0x64
> +#define RK3568_VP_BCSH_H			0x68
> +#define RK3568_VP_BCSH_COLOR_BAR		0x6C
> +
> +/* Overlay registers definition    */
> +#define RK3568_OVL_CTRL				0x600
> +#define RK3568_OVL_LAYER_SEL			0x604
> +#define RK3568_OVL_PORT_SEL			0x608
> +#define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL	0x610
> +#define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL	0x614
> +#define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL	0x618
> +#define RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL	0x61C
> +#define RK3568_MIX0_SRC_COLOR_CTRL		0x650
> +#define RK3568_MIX0_DST_COLOR_CTRL		0x654
> +#define RK3568_MIX0_SRC_ALPHA_CTRL		0x658
> +#define RK3568_MIX0_DST_ALPHA_CTRL		0x65C
> +#define RK3568_HDR0_SRC_COLOR_CTRL		0x6C0
> +#define RK3568_HDR0_DST_COLOR_CTRL		0x6C4
> +#define RK3568_HDR0_SRC_ALPHA_CTRL		0x6C8
> +#define RK3568_HDR0_DST_ALPHA_CTRL		0x6CC
> +#define RK3568_VP_BG_MIX_CTRL(vp)		(0x6E0 + (vp) * 4)
> +#define RK3568_CLUSTER_DLY_NUM			0x6F0
> +#define RK3568_SMART_DLY_NUM			0x6F8
> +
> +/* Cluster register definition, offset relative to window base */
> +#define RK3568_CLUSTER_WIN_CTRL0		0x00
> +#define RK3568_CLUSTER_WIN_CTRL1		0x04
> +#define RK3568_CLUSTER_WIN_YRGB_MST		0x10
> +#define RK3568_CLUSTER_WIN_CBR_MST		0x14
> +#define RK3568_CLUSTER_WIN_VIR			0x18
> +#define RK3568_CLUSTER_WIN_ACT_INFO		0x20
> +#define RK3568_CLUSTER_WIN_DSP_INFO		0x24
> +#define RK3568_CLUSTER_WIN_DSP_ST		0x28
> +#define RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB	0x30
> +#define RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET	0x3C
> +#define RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL	0x50
> +#define RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE	0x54
> +#define RK3568_CLUSTER_WIN_AFBCD_HDR_PTR	0x58
> +#define RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH	0x5C
> +#define RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE	0x60
> +#define RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET	0x64
> +#define RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET	0x68
> +#define RK3568_CLUSTER_WIN_AFBCD_CTRL		0x6C
> +
> +#define RK3568_CLUSTER_CTRL			0x100
> +
> +/* (E)smart register definition, offset relative to window base */
> +#define RK3568_SMART_CTRL0			0x00
> +#define RK3568_SMART_CTRL1			0x04
> +#define RK3568_SMART_REGION0_CTRL		0x10
> +#define RK3568_SMART_REGION0_YRGB_MST		0x14
> +#define RK3568_SMART_REGION0_CBR_MST		0x18
> +#define RK3568_SMART_REGION0_VIR		0x1C
> +#define RK3568_SMART_REGION0_ACT_INFO		0x20
> +#define RK3568_SMART_REGION0_DSP_INFO		0x24
> +#define RK3568_SMART_REGION0_DSP_ST		0x28
> +#define RK3568_SMART_REGION0_SCL_CTRL		0x30
> +#define RK3568_SMART_REGION0_SCL_FACTOR_YRGB	0x34
> +#define RK3568_SMART_REGION0_SCL_FACTOR_CBR	0x38
> +#define RK3568_SMART_REGION0_SCL_OFFSET		0x3C
> +#define RK3568_SMART_REGION1_CTRL		0x40
> +#define RK3568_SMART_REGION1_YRGB_MST		0x44
> +#define RK3568_SMART_REGION1_CBR_MST		0x48
> +#define RK3568_SMART_REGION1_VIR		0x4C
> +#define RK3568_SMART_REGION1_ACT_INFO		0x50
> +#define RK3568_SMART_REGION1_DSP_INFO		0x54
> +#define RK3568_SMART_REGION1_DSP_ST		0x58
> +#define RK3568_SMART_REGION1_SCL_CTRL		0x60
> +#define RK3568_SMART_REGION1_SCL_FACTOR_YRGB	0x64
> +#define RK3568_SMART_REGION1_SCL_FACTOR_CBR	0x68
> +#define RK3568_SMART_REGION1_SCL_OFFSET		0x6C
> +#define RK3568_SMART_REGION2_CTRL		0x70
> +#define RK3568_SMART_REGION2_YRGB_MST		0x74
> +#define RK3568_SMART_REGION2_CBR_MST		0x78
> +#define RK3568_SMART_REGION2_VIR		0x7C
> +#define RK3568_SMART_REGION2_ACT_INFO		0x80
> +#define RK3568_SMART_REGION2_DSP_INFO		0x84
> +#define RK3568_SMART_REGION2_DSP_ST		0x88
> +#define RK3568_SMART_REGION2_SCL_CTRL		0x90
> +#define RK3568_SMART_REGION2_SCL_FACTOR_YRGB	0x94
> +#define RK3568_SMART_REGION2_SCL_FACTOR_CBR	0x98
> +#define RK3568_SMART_REGION2_SCL_OFFSET		0x9C
> +#define RK3568_SMART_REGION3_CTRL		0xA0
> +#define RK3568_SMART_REGION3_YRGB_MST		0xA4
> +#define RK3568_SMART_REGION3_CBR_MST		0xA8
> +#define RK3568_SMART_REGION3_VIR		0xAC
> +#define RK3568_SMART_REGION3_ACT_INFO		0xB0
> +#define RK3568_SMART_REGION3_DSP_INFO		0xB4
> +#define RK3568_SMART_REGION3_DSP_ST		0xB8
> +#define RK3568_SMART_REGION3_SCL_CTRL		0xC0
> +#define RK3568_SMART_REGION3_SCL_FACTOR_YRGB	0xC4
> +#define RK3568_SMART_REGION3_SCL_FACTOR_CBR	0xC8
> +#define RK3568_SMART_REGION3_SCL_OFFSET		0xCC
> +#define RK3568_SMART_COLOR_KEY_CTRL		0xD0
> +
> +/* HDR register definition */
> +#define RK3568_HDR_LUT_CTRL			0x2000
> +#define RK3568_HDR_LUT_MST			0x2004
> +#define RK3568_SDR2HDR_CTRL			0x2010
> +#define RK3568_HDR2SDR_CTRL			0x2020
> +#define RK3568_HDR2SDR_SRC_RANGE		0x2024
> +#define RK3568_HDR2SDR_NORMFACEETF		0x2028
> +#define RK3568_HDR2SDR_DST_RANGE		0x202C
> +#define RK3568_HDR2SDR_NORMFACCGAMMA		0x2030
> +#define RK3568_HDR_EETF_OETF_Y0			0x203C
> +#define RK3568_HDR_SAT_Y0			0x20C0
> +#define RK3568_HDR_EOTF_OETF_Y0			0x20F0
> +#define RK3568_HDR_OETF_DX_POW1			0x2200
> +#define RK3568_HDR_OETF_XN1			0x2300
> +
> +#define RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN		BIT(15)
> +
> +#define RK3568_VP_DSP_CTRL__STANDBY			BIT(31)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_MODE		BIT(20)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL		GENMASK(19, 18)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_EN		BIT(17)
> +#define RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN		BIT(16)
> +#define RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y		BIT(15)
> +#define RK3568_VP_DSP_CTRL__DSP_RB_SWAP			BIT(9)
> +#define RK3568_VP_DSP_CTRL__DSP_INTERLACE		BIT(7)
> +#define RK3568_VP_DSP_CTRL__DSP_FILED_POL		BIT(6)
> +#define RK3568_VP_DSP_CTRL__P2I_EN			BIT(5)
> +#define RK3568_VP_DSP_CTRL__CORE_DCLK_DIV		BIT(4)
> +#define RK3568_VP_DSP_CTRL__OUT_MODE			GENMASK(3, 0)
> +
> +#define RK3568_VP_POST_SCL_CTRL__VSCALEDOWN		BIT(1)
> +#define RK3568_VP_POST_SCL_CTRL__HSCALEDOWN		BIT(0)
> +
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX		GENMASK(26, 25)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS1			BIT(24)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX		GENMASK(22, 21)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI1			BIT(20)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX		GENMASK(19, 18)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX		GENMASK(17, 16)
> +#define RK3568_SYS_DSP_INFACE_EN_EDP_MUX		GENMASK(15, 14)
> +#define RK3568_SYS_DSP_INFACE_EN_HDMI_MUX		GENMASK(11, 10)
> +#define RK3568_SYS_DSP_INFACE_EN_RGB_MUX		GENMASK(9, 8)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS0			BIT(5)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI0			BIT(4)
> +#define RK3568_SYS_DSP_INFACE_EN_EDP			BIT(3)
> +#define RK3568_SYS_DSP_INFACE_EN_HDMI			BIT(1)
> +#define RK3568_SYS_DSP_INFACE_EN_RGB			BIT(0)
> +
> +#define RK3568_DSP_IF_POL__MIPI_PIN_POL			GENMASK(19, 16)
> +#define RK3568_DSP_IF_POL__EDP_PIN_POL			GENMASK(15, 12)
> +#define RK3568_DSP_IF_POL__HDMI_PIN_POL			GENMASK(7, 4)
> +#define RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL		GENMASK(3, 0)
> +
> +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2_PHASE_LOCK	BIT(5)
> +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2			BIT(4)
> +
> +#define RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN	BIT(31)
> +
> +#define RK3568_DSP_IF_POL__CFG_DONE_IMD			BIT(28)
> +
> +#define VOP2_SYS_AXI_BUS_NUM				2
> +
> +#define VOP2_CLUSTER_YUV444_10				0x12
> +
> +#define VOP2_COLOR_KEY_MASK				BIT(31)
> +
> +#define RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD		BIT(28)
> +
> +#define RK3568_VP_BG_MIX_CTRL__BG_DLY			GENMASK(31, 24)
> +
> +#define RK3568_OVL_PORT_SEL__SEL_PORT			GENMASK(31, 16)
> +#define RK3568_OVL_PORT_SEL__SMART1			GENMASK(31, 30)
> +#define RK3568_OVL_PORT_SEL__SMART0			GENMASK(29, 28)
> +#define RK3568_OVL_PORT_SEL__ESMART1			GENMASK(27, 26)
> +#define RK3568_OVL_PORT_SEL__ESMART0			GENMASK(25, 24)
> +#define RK3568_OVL_PORT_SEL__CLUSTER1			GENMASK(19, 18)
> +#define RK3568_OVL_PORT_SEL__CLUSTER0			GENMASK(17, 16)
> +#define RK3568_OVL_PORT_SET__PORT2_MUX			GENMASK(11, 8)
> +#define RK3568_OVL_PORT_SET__PORT1_MUX			GENMASK(7, 4)
> +#define RK3568_OVL_PORT_SET__PORT0_MUX			GENMASK(3, 0)
> +#define RK3568_OVL_LAYER_SEL__LAYER(layer, x)		((x) << ((layer) * 4))
> +
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_1		GENMASK(31, 24)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_0		GENMASK(23, 16)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_1		GENMASK(15, 8)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_0		GENMASK(7, 0)
> +
> +#define RK3568_SMART_DLY_NUM__SMART1			GENMASK(31, 24)
> +#define RK3568_SMART_DLY_NUM__SMART0			GENMASK(23, 16)
> +#define RK3568_SMART_DLY_NUM__ESMART1			GENMASK(15, 8)
> +#define RK3568_SMART_DLY_NUM__ESMART0			GENMASK(7, 0)
> +
> +#define VP_INT_DSP_HOLD_VALID	BIT(6)
> +#define VP_INT_FS_FIELD		BIT(5)
> +#define VP_INT_POST_BUF_EMPTY	BIT(4)
> +#define VP_INT_LINE_FLAG1	BIT(3)
> +#define VP_INT_LINE_FLAG0	BIT(2)
> +#define VOP2_INT_BUS_ERRPR	BIT(1)
> +#define VP_INT_FS		BIT(0)
> +
> +#define POLFLAG_DCLK_INV	BIT(3)
> +
> +enum vop2_layer_phy_id {
> +	ROCKCHIP_VOP2_CLUSTER0 = 0,
> +	ROCKCHIP_VOP2_CLUSTER1,
> +	ROCKCHIP_VOP2_ESMART0,
> +	ROCKCHIP_VOP2_ESMART1,
> +	ROCKCHIP_VOP2_SMART0,
> +	ROCKCHIP_VOP2_SMART1,
> +	ROCKCHIP_VOP2_CLUSTER2,
> +	ROCKCHIP_VOP2_CLUSTER3,
> +	ROCKCHIP_VOP2_ESMART2,
> +	ROCKCHIP_VOP2_ESMART3,
> +	ROCKCHIP_VOP2_PHY_ID_INVALID = -1,
> +};
> +
> +extern const struct component_ops vop2_component_ops;
> +
> +#endif /* _ROCKCHIP_DRM_VOP2_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> new file mode 100644
> index 0000000000000..9bf0637bf8e26
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> @@ -0,0 +1,281 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) Rockchip Electronics Co.Ltd
> + * Author: Andy Yan <andy.yan@rock-chips.com>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/component.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_plane.h>
> +#include <drm/drm_print.h>
> +
> +#include "rockchip_drm_vop2.h"
> +
> +static const uint32_t formats_win_full_10bit[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV24,
> +};
> +
> +static const uint32_t formats_win_full_10bit_yuyv[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV24,
> +	DRM_FORMAT_YVYU,
> +	DRM_FORMAT_VYUY,
> +};
> +
> +static const uint32_t formats_win_lite[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +};
> +
> +static const uint64_t format_modifiers[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	DRM_FORMAT_MOD_INVALID,
> +};
> +
> +static const uint64_t format_modifiers_afbc[] = {
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_CBR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_CBR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_CBR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_CBR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	/* SPLIT mandates SPARSE, RGB modes mandates YTR */
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_SPARSE |
> +				AFBC_FORMAT_MOD_SPLIT),
> +	DRM_FORMAT_MOD_INVALID,
> +};
> +
> +static const struct vop2_video_port_data rk3568_vop_video_ports[] = {
> +	{
> +		.id = 0,
> +		.feature = VOP_FEATURE_OUTPUT_10BIT,
> +		.gamma_lut_len = 1024,
> +		.cubic_lut_len = 9 * 9 * 9,
> +		.max_output = { 4096, 2304 },
> +		.pre_scan_max_dly = { 69, 53, 53, 42 },
> +		.offset = 0xc00,
> +	}, {
> +		.id = 1,
> +		.gamma_lut_len = 1024,
> +		.max_output = { 2048, 1536 },
> +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> +		.offset = 0xd00,
> +	}, {
> +		.id = 2,
> +		.gamma_lut_len = 1024,
> +		.max_output = { 1920, 1080 },
> +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> +		.offset = 0xe00,
> +	},
> +};
> +
> +/*
> + * rk3568 vop with 2 cluster, 2 esmart win, 2 smart win.
> + * Every cluster can work as 4K win or split into two win.
> + * All win in cluster support AFBCD.
> + *
> + * Every esmart win and smart win support 4 Multi-region.
> + *
> + * Scale filter mode:
> + *
> + * * Cluster:  bicubic for horizontal scale up, others use bilinear
> + * * ESmart:
> + *    * nearest-neighbor/bilinear/bicubic for scale up
> + *    * nearest-neighbor/bilinear/average for scale down
> + *
> + *
> + * @TODO describe the wind like cpu-map dt nodes;
> + */
> +static const struct vop2_win_data rk3568_vop_win_data[] = {
> +	{
> +		.name = "Smart0-win0",
> +		.phys_id = ROCKCHIP_VOP2_SMART0,
> +		.base = 0x1c00,
> +		.formats = formats_win_lite,
> +		.nformats = ARRAY_SIZE(formats_win_lite),
> +		.format_modifiers = format_modifiers,
> +		.layer_sel_id = 3,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Smart1-win0",
> +		.phys_id = ROCKCHIP_VOP2_SMART1,
> +		.formats = formats_win_lite,
> +		.nformats = ARRAY_SIZE(formats_win_lite),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1e00,
> +		.layer_sel_id = 7,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Esmart1-win0",
> +		.phys_id = ROCKCHIP_VOP2_ESMART1,
> +		.formats = formats_win_full_10bit_yuyv,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1a00,
> +		.layer_sel_id = 6,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Esmart0-win0",
> +		.phys_id = ROCKCHIP_VOP2_ESMART0,
> +		.formats = formats_win_full_10bit_yuyv,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1800,
> +		.layer_sel_id = 2,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Cluster0-win0",
> +		.phys_id = ROCKCHIP_VOP2_CLUSTER0,
> +		.base = 0x1000,
> +		.formats = formats_win_full_10bit,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> +		.format_modifiers = format_modifiers_afbc,
> +		.layer_sel_id = 0,
> +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> +		.max_upscale_factor = 4,
> +		.max_downscale_factor = 4,
> +		.dly = { 0, 27, 21 },
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> +	}, {
> +		.name = "Cluster1-win0",
> +		.phys_id = ROCKCHIP_VOP2_CLUSTER1,
> +		.base = 0x1200,
> +		.formats = formats_win_full_10bit,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> +		.format_modifiers = format_modifiers_afbc,
> +		.layer_sel_id = 1,
> +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.max_upscale_factor = 4,
> +		.max_downscale_factor = 4,
> +		.dly = { 0, 27, 21 },
> +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> +	},
> +};
> +
> +static const struct vop2_data rk3566_vop = {
> +	.nr_vps = 3,
> +	.max_input = { 4096, 2304 },
> +	.max_output = { 4096, 2304 },
> +	.vp = rk3568_vop_video_ports,
> +	.win = rk3568_vop_win_data,
> +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> +	.soc_id = 3566,
> +};
> +
> +static const struct vop2_data rk3568_vop = {
> +	.nr_vps = 3,
> +	.max_input = { 4096, 2304 },
> +	.max_output = { 4096, 2304 },
> +	.vp = rk3568_vop_video_ports,
> +	.win = rk3568_vop_win_data,
> +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> +	.soc_id = 3568,
> +};
> +
> +static const struct of_device_id vop2_dt_match[] = {
> +	{
> +		.compatible = "rockchip,rk3566-vop",
> +		.data = &rk3566_vop,
> +	}, {
> +		.compatible = "rockchip,rk3568-vop",
> +		.data = &rk3568_vop,
> +	}, {
> +	},
> +};
> +MODULE_DEVICE_TABLE(of, vop2_dt_match);
> +
> +static int vop2_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +
> +	return component_add(dev, &vop2_component_ops);
> +}
> +
> +static int vop2_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &vop2_component_ops);
> +
> +	return 0;
> +}
> +
> +struct platform_driver vop2_platform_driver = {
> +	.probe = vop2_probe,
> +	.remove = vop2_remove,
> +	.driver = {
> +		.name = "rockchip-vop2",
> +		.of_match_table = of_match_ptr(vop2_dt_match),
> +	},
> +};

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-17 12:00     ` Andy Yan
  0 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-17 12:00 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis

Hi Sascha:

On 2/17/22 16:29, Sascha Hauer wrote:
> From: Andy Yan <andy.yan@rock-chips.com>
>
> The VOP2 unit is found on Rockchip SoCs beginning with rk3566/rk3568.
> It replaces the VOP unit found in the older Rockchip SoCs.
>
> This driver has been derived from the downstream Rockchip Kernel and
> heavily modified:
>
> - All nonstandard DRM properties have been removed
> - dropped struct vop2_plane_state and pass around less data between
>    functions
> - Dropped all DRM_FORMAT_* not known on upstream
> - rework register access to get rid of excessively used macros
> - Drop all waiting for framesyncs
>
> The driver is tested with HDMI and MIPI-DSI display on a RK3568-EVB
> board. Overlay support is tested with the modetest utility. AFBC support
> on the cluster windows is tested with weston-simple-dmabuf-egl on
> weston using the (yet to be upstreamed) panfrost driver support.
>
> Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>
> Notes:
>      Changes since v5:
>      - consistently use u8/u16/u32 rather than uint8_t/uint16_t/uint32_t
>      - Use spin_lock rather than spin_lock_irqsave
>      - replace printk with drm_dbg
>      - break some overlong lines
>      
>      Changes since v4:
>      - Avoid stack frame overflow by not allocating big array on the stack
>      
>      Changes since v3:
>      - Sort includes
>      - fix typos
>      - Drop spinlock
>      - Use regmap_set_bits()/regmap_clear_bits()
>      - simplify vop2_scale_factor()
>      - simplify vop2_afbc_transform_offset()
>      
>      Changes since v4:
>      - Sort nodes alphabetically
>      
>      Changes since v3:
>      - Fix HDMI connector type
>
>   drivers/gpu/drm/rockchip/Kconfig             |    6 +
>   drivers/gpu/drm/rockchip/Makefile            |    1 +
>   drivers/gpu/drm/rockchip/rockchip_drm_drv.c  |    1 +
>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h  |    6 +-
>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c   |    2 +
>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h  |   15 +
>   drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2708 ++++++++++++++++++
>   drivers/gpu/drm/rockchip/rockchip_drm_vop2.h |  477 +++
>   drivers/gpu/drm/rockchip/rockchip_vop2_reg.c |  281 ++
>   9 files changed, 3496 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
>
> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> index b9b156308460a..4ff0043f0ee70 100644
> --- a/drivers/gpu/drm/rockchip/Kconfig
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
>   	  This selects support for the VOP driver. You should enable it
>   	  on all older SoCs up to RK3399.
>   
> +config ROCKCHIP_VOP2
> +	bool "Rockchip VOP2 driver"
> +	help
> +	  This selects support for the VOP2 driver. You should enable it
> +	  on all newer SoCs beginning form RK3568.
> +
>   config ROCKCHIP_ANALOGIX_DP
>   	bool "Rockchip specific extensions for Analogix DP driver"
>   	depends on ROCKCHIP_VOP
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index dfc5512fdb9f1..3ff7b21c04149 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -6,6 +6,7 @@
>   rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>   		rockchip_drm_gem.o
>   
> +rockchipdrm-$(CONFIG_ROCKCHIP_VOP2) += rockchip_drm_vop2.o rockchip_vop2_reg.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> index 82c8faf1fb6b8..95f6c5985fdd7 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -459,6 +459,7 @@ static int __init rockchip_drm_init(void)
>   
>   	num_rockchip_sub_drivers = 0;
>   	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
> +	ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2);
>   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
>   				CONFIG_ROCKCHIP_LVDS);
>   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> index d3e42410ae5da..5cb207aaaadd0 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -18,7 +18,7 @@
>   
>   #define ROCKCHIP_MAX_FB_BUFFER	3
>   #define ROCKCHIP_MAX_CONNECTOR	2
> -#define ROCKCHIP_MAX_CRTC	2
> +#define ROCKCHIP_MAX_CRTC	4
>   
>   struct drm_device;
>   struct drm_connector;
> @@ -31,6 +31,9 @@ struct rockchip_crtc_state {
>   	int output_bpc;
>   	int output_flags;
>   	bool enable_afbc;
> +	u32 bus_format;
> +	u32 bus_flags;
> +	int color_space;
>   };
>   #define to_rockchip_crtc_state(s) \
>   		container_of(s, struct rockchip_crtc_state, base)
> @@ -63,6 +66,7 @@ extern struct platform_driver rockchip_dp_driver;
>   extern struct platform_driver rockchip_lvds_driver;
>   extern struct platform_driver vop_platform_driver;
>   extern struct platform_driver rk3066_hdmi_driver;
> +extern struct platform_driver vop2_platform_driver;
>   
>   struct rockchip_encoder {
>   	struct device_node *port;
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> index 3aa37e177667e..0d2cb4f3922b8 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> @@ -134,4 +134,6 @@ void rockchip_drm_mode_config_init(struct drm_device *dev)
>   
>   	dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
>   	dev->mode_config.helper_private = &rockchip_mode_config_helpers;
> +
> +	dev->mode_config.normalize_zpos = true;
>   }
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> index 857d97cdc67c6..1e364d7b50e69 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -54,9 +54,23 @@ struct vop_afbc {
>   	struct vop_reg enable;
>   	struct vop_reg win_sel;
>   	struct vop_reg format;
> +	struct vop_reg rb_swap;
> +	struct vop_reg uv_swap;
> +	struct vop_reg auto_gating_en;
> +	struct vop_reg block_split_en;
> +	struct vop_reg pic_vir_width;
> +	struct vop_reg tile_num;
>   	struct vop_reg hreg_block_split;
> +	struct vop_reg pic_offset;
>   	struct vop_reg pic_size;
> +	struct vop_reg dsp_offset;
> +	struct vop_reg transform_offset;
>   	struct vop_reg hdr_ptr;
> +	struct vop_reg half_block_en;
> +	struct vop_reg xmirror;
> +	struct vop_reg ymirror;
> +	struct vop_reg rotate_270;
> +	struct vop_reg rotate_90;
>   	struct vop_reg rstn;
>   };
>   
> @@ -410,4 +424,5 @@ static inline int scl_vop_cal_lb_mode(int width, bool is_yuv)
>   }
>   
>   extern const struct component_ops vop_component_ops;
> +
>   #endif /* _ROCKCHIP_DRM_VOP_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> new file mode 100644
> index 0000000000000..394dd6c583682
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> @@ -0,0 +1,2708 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
> + * Author: Andy Yan <andy.yan@rock-chips.com>
> + */
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/delay.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/swab.h>
> +
> +#include <drm/drm.h>
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_uapi.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_debugfs.h>
> +#include <drm/drm_flip_work.h>
> +#include <drm/drm_plane_helper.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_vblank.h>
> +
> +#include <uapi/linux/videodev2.h>
> +#include <dt-bindings/soc/rockchip,vop2.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_gem.h"
> +#include "rockchip_drm_fb.h"
> +#include "rockchip_drm_vop2.h"
> +
> +/*
> + * VOP2 architecture
> + *
> + +----------+   +-------------+                                                        +-----------+
> + |  Cluster |   | Sel 1 from 6|                                                        | 1 from 3  |
> + |  window0 |   |    Layer0   |                                                        |    RGB    |
> + +----------+   +-------------+              +---------------+    +-------------+      +-----------+
> + +----------+   +-------------+              |N from 6 layers|    |             |
> + |  Cluster |   | Sel 1 from 6|              |   Overlay0    +--->| Video Port0 |      +-----------+
> + |  window1 |   |    Layer1   |              |               |    |             |      | 1 from 3  |
> + +----------+   +-------------+              +---------------+    +-------------+      |   LVDS    |
> + +----------+   +-------------+                                                        +-----------+
> + |  Esmart  |   | Sel 1 from 6|
> + |  window0 |   |   Layer2    |              +---------------+    +-------------+      +-----------+
> + +----------+   +-------------+              |N from 6 Layers|    |             | +--> | 1 from 3  |
> + +----------+   +-------------+   -------->  |   Overlay1    +--->| Video Port1 |      |   MIPI    |
> + |  Esmart  |   | Sel 1 from 6|   -------->  |               |    |             |      +-----------+
> + |  Window1 |   |   Layer3    |              +---------------+    +-------------+
> + +----------+   +-------------+                                                        +-----------+
> + +----------+   +-------------+                                                        | 1 from 3  |
> + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |   HDMI    |
> + |  Window0 |   |    Layer4   |              |N from 6 Layers|    |             |      +-----------+
> + +----------+   +-------------+              |   Overlay2    +--->| Video Port2 |
> + +----------+   +-------------+              |               |    |             |      +-----------+
> + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |  1 from 3 |
> + |  Window1 |   |    Layer5   |                                                        |    eDP    |
> + +----------+   +-------------+                                                        +-----------+
> + *
> + */
> +
> +enum vop2_data_format {
> +	VOP2_FMT_ARGB8888 = 0,
> +	VOP2_FMT_RGB888,
> +	VOP2_FMT_RGB565,
> +	VOP2_FMT_XRGB101010,
> +	VOP2_FMT_YUV420SP,
> +	VOP2_FMT_YUV422SP,
> +	VOP2_FMT_YUV444SP,
> +	VOP2_FMT_YUYV422 = 8,
> +	VOP2_FMT_YUYV420,
> +	VOP2_FMT_VYUY422,
> +	VOP2_FMT_VYUY420,
> +	VOP2_FMT_YUV420SP_TILE_8x4 = 0x10,
> +	VOP2_FMT_YUV420SP_TILE_16x2,
> +	VOP2_FMT_YUV422SP_TILE_8x4,
> +	VOP2_FMT_YUV422SP_TILE_16x2,
> +	VOP2_FMT_YUV420SP_10,
> +	VOP2_FMT_YUV422SP_10,
> +	VOP2_FMT_YUV444SP_10,
> +};
> +
> +enum vop2_afbc_format {
> +	VOP2_AFBC_FMT_RGB565,
> +	VOP2_AFBC_FMT_ARGB2101010 = 2,
> +	VOP2_AFBC_FMT_YUV420_10BIT,
> +	VOP2_AFBC_FMT_RGB888,
> +	VOP2_AFBC_FMT_ARGB8888,
> +	VOP2_AFBC_FMT_YUV420 = 9,
> +	VOP2_AFBC_FMT_YUV422 = 0xb,
> +	VOP2_AFBC_FMT_YUV422_10BIT = 0xe,
> +	VOP2_AFBC_FMT_INVALID = -1,
> +};
> +
> +union vop2_alpha_ctrl {
> +	u32 val;
> +	struct {
> +		/* [0:1] */
> +		u32 color_mode:1;
> +		u32 alpha_mode:1;
> +		/* [2:3] */
> +		u32 blend_mode:2;
> +		u32 alpha_cal_mode:1;
> +		/* [5:7] */
> +		u32 factor_mode:3;
> +		/* [8:9] */
> +		u32 alpha_en:1;
> +		u32 src_dst_swap:1;
> +		u32 reserved:6;
> +		/* [16:23] */
> +		u32 glb_alpha:8;
> +	} bits;
> +};
> +
> +struct vop2_alpha {
> +	union vop2_alpha_ctrl src_color_ctrl;
> +	union vop2_alpha_ctrl dst_color_ctrl;
> +	union vop2_alpha_ctrl src_alpha_ctrl;
> +	union vop2_alpha_ctrl dst_alpha_ctrl;
> +};
> +
> +struct vop2_alpha_config {
> +	bool src_premulti_en;
> +	bool dst_premulti_en;
> +	bool src_pixel_alpha_en;
> +	bool dst_pixel_alpha_en;
> +	u16 src_glb_alpha_value;
> +	u16 dst_glb_alpha_value;
> +};
> +
> +struct vop2_win {
> +	struct vop2 *vop2;
> +	struct drm_plane base;
> +	const struct vop2_win_data *data;
> +	struct regmap_field *reg[VOP2_WIN_MAX_REG];
> +
> +	/**
> +	 * @win_id: graphic window id, a cluster may be split into two
> +	 * graphics windows.
> +	 */
> +	u8 win_id;
> +	u8 delay;
> +	u32 offset;
> +
> +	enum drm_plane_type type;
> +};
> +
> +struct vop2_video_port {
> +	struct drm_crtc crtc;
> +	struct vop2 *vop2;
> +	struct clk *dclk;
> +	unsigned int id;
> +	const struct vop2_video_port_regs *regs;
> +	const struct vop2_video_port_data *data;
> +
> +	struct completion dsp_hold_completion;
> +
> +	/**
> +	 * @win_mask: Bitmask of windows attached to the video port;
> +	 */
> +	u32 win_mask;
> +
> +	struct vop2_win *primary_plane;
> +	struct drm_pending_vblank_event *event;
> +
> +	unsigned int nlayers;
> +};
> +
> +struct vop2 {
> +	struct device *dev;
> +	struct drm_device *drm;
> +	struct vop2_video_port vps[ROCKCHIP_MAX_CRTC];
> +
> +	const struct vop2_data *data;
> +	/*
> +	 * Number of windows that are registered as plane, may be less than the
> +	 * total number of hardware windows.
> +	 */
> +	u32 registered_num_wins;
> +
> +	void __iomem *regs;
> +	struct regmap *map;
> +
> +	struct regmap *grf;
> +
> +	/* physical map length of vop2 register */
> +	u32 len;
> +
> +	void __iomem *lut_regs;
> +
> +	/* protects crtc enable/disable */
> +	struct mutex vop2_lock;
> +
> +	int irq;
> +
> +	/*
> +	 * Some global resources are shared between all video ports(crtcs), so
> +	 * we need a ref counter here.
> +	 */
> +	unsigned int enable_count;
> +	struct clk *hclk;
> +	struct clk *aclk;
> +
> +	/* must be put at the end of the struct */
> +	struct vop2_win win[];
> +};
> +
> +static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
> +{
> +	return container_of(crtc, struct vop2_video_port, crtc);
> +}
> +
> +static struct vop2_win *to_vop2_win(struct drm_plane *p)
> +{
> +	return container_of(p, struct vop2_win, base);
> +}
> +
> +static void vop2_lock(struct vop2 *vop2)
> +{
> +	mutex_lock(&vop2->vop2_lock);
> +}
> +
> +static void vop2_unlock(struct vop2 *vop2)
> +{
> +	mutex_unlock(&vop2->vop2_lock);
> +}
> +
> +static void vop2_writel(struct vop2 *vop2, u32 offset, u32 v)
> +{
> +	regmap_write(vop2->map, offset, v);
> +}
> +
> +static void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
> +{
> +	regmap_write(vp->vop2->map, vp->data->offset + offset, v);
> +}
> +
> +static u32 vop2_readl(struct vop2 *vop2, u32 offset)
> +{
> +	u32 val;
> +
> +	regmap_read(vop2->map, offset, &val);
> +
> +	return val;
> +}
> +
> +static void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v)
> +{
> +	regmap_field_write(win->reg[reg], v);
> +}
> +
> +static bool vop2_cluster_window(const struct vop2_win *win)
> +{
> +	return win->data->feature & WIN_FEATURE_CLUSTER;
> +}
> +
> +static void vop2_cfg_done(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE,
> +			BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> +}
> +
> +static void vop2_win_disable(struct vop2_win *win)
> +{
> +	vop2_win_write(win, VOP2_WIN_ENABLE, 0);
> +
> +	if (vop2_cluster_window(win))
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0);
> +}
> +
> +static enum vop2_data_format vop2_convert_format(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +		return VOP2_FMT_ARGB8888;
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		return VOP2_FMT_RGB888;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		return VOP2_FMT_RGB565;
> +	case DRM_FORMAT_NV12:
> +		return VOP2_FMT_YUV420SP;
> +	case DRM_FORMAT_NV16:
> +		return VOP2_FMT_YUV422SP;
> +	case DRM_FORMAT_NV24:
> +		return VOP2_FMT_YUV444SP;
> +	case DRM_FORMAT_YUYV:
> +	case DRM_FORMAT_YVYU:
> +		return VOP2_FMT_VYUY422;
> +	case DRM_FORMAT_VYUY:
> +	case DRM_FORMAT_UYVY:
> +		return VOP2_FMT_YUYV422;
> +	default:
> +		DRM_ERROR("unsupported format[%08x]\n", format);
> +		return -EINVAL;
> +	}
> +}
> +
> +static enum vop2_afbc_format vop2_convert_afbc_format(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +		return VOP2_AFBC_FMT_ARGB8888;
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		return VOP2_AFBC_FMT_RGB888;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		return VOP2_AFBC_FMT_RGB565;
> +	case DRM_FORMAT_NV12:
> +		return VOP2_AFBC_FMT_YUV420;
> +	case DRM_FORMAT_NV16:
> +		return VOP2_AFBC_FMT_YUV422;
> +	default:
> +		return VOP2_AFBC_FMT_INVALID;
> +	}
> +
> +	return VOP2_AFBC_FMT_INVALID;
> +}
> +
> +static bool vop2_win_rb_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_BGR888:
> +	case DRM_FORMAT_BGR565:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_afbc_rb_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV24:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_afbc_uv_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV12:
> +	case DRM_FORMAT_NV16:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_win_uv_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV12:
> +	case DRM_FORMAT_NV16:
> +	case DRM_FORMAT_NV24:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_win_dither_up(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_BGR565:
> +	case DRM_FORMAT_RGB565:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
> +{
> +	/*
> +	 * FIXME:
> +	 *
> +	 * There is no media type for YUV444 output,
> +	 * so when out_mode is AAAA or P888, assume output is YUV444 on
> +	 * yuv format.
> +	 *
> +	 * From H/W testing, YUV444 mode need a rb swap.
> +	 */
> +	if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 ||
> +	    bus_format == MEDIA_BUS_FMT_VYUY8_1X16 ||
> +	    bus_format == MEDIA_BUS_FMT_YVYU8_2X8 ||
> +	    bus_format == MEDIA_BUS_FMT_VYUY8_2X8 ||
> +	    ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
> +	      bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
> +	     (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
> +	      output_mode == ROCKCHIP_OUT_MODE_P888)))
> +		return true;
> +	else
> +		return false;
> +}
> +
> +static bool is_yuv_output(u32 bus_format)
> +{
> +	switch (bus_format) {
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +	case MEDIA_BUS_FMT_YUV10_1X30:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> +	case MEDIA_BUS_FMT_YUYV8_2X8:
> +	case MEDIA_BUS_FMT_YVYU8_2X8:
> +	case MEDIA_BUS_FMT_UYVY8_2X8:
> +	case MEDIA_BUS_FMT_VYUY8_2X8:
> +	case MEDIA_BUS_FMT_YUYV8_1X16:
> +	case MEDIA_BUS_FMT_YVYU8_1X16:
> +	case MEDIA_BUS_FMT_UYVY8_1X16:
> +	case MEDIA_BUS_FMT_VYUY8_1X16:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool rockchip_afbc(struct drm_plane *plane, u64 modifier)
> +{
> +	int i;
> +
> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> +		return false;
> +
> +	for (i = 0 ; i < plane->modifier_count; i++)
> +		if (plane->modifiers[i] == modifier)
> +			return true;
> +
> +	return false;
> +
> +}
> +
> +static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
> +					u64 modifier)
> +{
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2 *vop2 = win->vop2;
> +
> +	if (modifier == DRM_FORMAT_MOD_INVALID)
> +		return false;
> +
> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> +		return true;
> +
> +	if (!rockchip_afbc(plane, modifier)) {
> +		drm_err(vop2->drm, "Unsupported format modifier 0x%llx\n",
> +			modifier);
> +
> +		return false;
> +	}
> +
> +	return vop2_convert_afbc_format(format) >= 0;
> +}
> +
> +static u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate,
> +				      bool afbc_half_block_en)
> +{
> +	struct drm_rect *src = &pstate->src;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	u32 bpp = fb->format->cpp[0] * 8;
> +	u32 vir_width = (fb->pitches[0] << 3) / bpp;
> +	u32 width = drm_rect_width(src) >> 16;
> +	u32 height = drm_rect_height(src) >> 16;
> +	u32 act_xoffset = src->x1 >> 16;
> +	u32 act_yoffset = src->y1 >> 16;
> +	u32 align16_crop = 0;
> +	u32 align64_crop = 0;
> +	u32 height_tmp;
> +	u8 tx, ty;
> +	u8 bottom_crop_line_num = 0;
> +
> +	/* 16 pixel align */
> +	if (height & 0xf)
> +		align16_crop = 16 - (height & 0xf);
> +
> +	height_tmp = height + align16_crop;
> +
> +	/* 64 pixel align */
> +	if (height_tmp & 0x3f)
> +		align64_crop = 64 - (height_tmp & 0x3f);
> +
> +	bottom_crop_line_num = align16_crop + align64_crop;
> +
> +	switch (pstate->rotation &
> +		(DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y |
> +		 DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) {
> +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> +		tx = 16 - ((act_xoffset + width) & 0xf);
> +		ty = bottom_crop_line_num - act_yoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90:
> +		tx = bottom_crop_line_num - act_yoffset;
> +		ty = vir_width - width - act_xoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270:
> +		tx = act_yoffset;
> +		ty = act_xoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X:
> +		tx = 16 - ((act_xoffset + width) & 0xf);
> +		ty = act_yoffset;
> +		break;
> +	case DRM_MODE_REFLECT_Y:
> +		tx = act_xoffset;
> +		ty = bottom_crop_line_num - act_yoffset;
> +		break;
> +	case DRM_MODE_ROTATE_90:
> +		tx = bottom_crop_line_num - act_yoffset;
> +		ty = act_xoffset;
> +		break;
> +	case DRM_MODE_ROTATE_270:
> +		tx = act_yoffset;
> +		ty = vir_width - width - act_xoffset;
> +		break;
> +	case 0:
> +		tx = act_xoffset;
> +		ty = act_yoffset;
> +		break;
> +	}
> +
> +	if (afbc_half_block_en)
> +		ty &= 0x7f;
> +
> +#define TRANSFORM_XOFFSET GENMASK(7, 0)
> +#define TRANSFORM_YOFFSET GENMASK(23, 16)
> +	return FIELD_PREP(TRANSFORM_XOFFSET, tx) |
> +		FIELD_PREP(TRANSFORM_YOFFSET, ty);
> +}
> +
> +/*
> + * A Cluster window has 2048 x 16 line buffer, which can
> + * works at 2048 x 16(Full) or 4096 x 8 (Half) mode.
> + * for Cluster_lb_mode register:
> + * 0: half mode, for plane input width range 2048 ~ 4096
> + * 1: half mode, for cluster work at 2 * 2048 plane mode
> + * 2: half mode, for rotate_90/270 mode
> + *
> + */
> +static int vop2_get_cluster_lb_mode(struct vop2_win *win,
> +				    struct drm_plane_state *pstate)
> +{
> +	if ((pstate->rotation & DRM_MODE_ROTATE_270) ||
> +	    (pstate->rotation & DRM_MODE_ROTATE_90))
> +		return 2;
> +	else
> +		return 0;
> +}
> +
> +static u16 vop2_scale_factor(u32 src, u32 dst)
> +{
> +	u32 fac;
> +	int shift;
> +
> +	if (src == dst)
> +		return 0;
> +
> +	if (dst < 2)
> +		return U16_MAX;
> +
> +	if (src < 2)
> +		return 0;
> +
> +	if (src > dst)
> +		shift = 12;
> +	else
> +		shift = 16;
> +
> +	src--;
> +	dst--;
> +
> +	fac = DIV_ROUND_UP(src << shift, dst) - 1;
> +
> +	if (fac > U16_MAX)
> +		return U16_MAX;
> +
> +	return fac;
> +}
> +
> +static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
> +			     u32 src_w, u32 src_h, u32 dst_w,
> +			     u32 dst_h, u32 pixel_format)
> +{
> +	const struct drm_format_info *info;
> +	u16 hor_scl_mode, ver_scl_mode;
> +	u16 hscl_filter_mode, vscl_filter_mode;
> +	u8 gt2 = 0;
> +	u8 gt4 = 0;
> +	u32 val;
> +
> +	info = drm_format_info(pixel_format);
> +
> +	if (src_h >= (4 * dst_h)) {
> +		gt4 = 1;
> +		src_h >>= 2;
> +	} else if (src_h >= (2 * dst_h)) {
> +		gt2 = 1;
> +		src_h >>= 1;
> +	}
> +
> +	hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> +	ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> +
> +	if (hor_scl_mode == SCALE_UP)
> +		hscl_filter_mode = VOP2_SCALE_UP_BIC;
> +	else
> +		hscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> +
> +	if (ver_scl_mode == SCALE_UP)
> +		vscl_filter_mode = VOP2_SCALE_UP_BIL;
> +	else
> +		vscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> +
> +	/*
> +	 * RK3568 VOP Esmart/Smart dsp_w should be even pixel
> +	 * at scale down mode
> +	 */
> +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> +		if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) {
> +			drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n",
> +				win->data->name, dst_w);
> +			dst_w++;
> +		}
> +	}
> +
> +	val = vop2_scale_factor(src_w, dst_w);
> +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val);
> +	val = vop2_scale_factor(src_h, dst_h);
> +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val);
> +
> +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4);
> +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2);
> +
> +	vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode);
> +	vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode);
> +
> +	if (vop2_cluster_window(win))
> +		return;
> +
> +	vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode);
> +	vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode);
> +
> +	if (info->is_yuv) {
> +		src_w /= info->hsub;
> +		src_h /= info->vsub;
> +
> +		gt4 = gt2 = 0;
> +
> +		if (src_h >= (4 * dst_h)) {
> +			gt4 = 1;
> +			src_h >>= 2;
> +		} else if (src_h >= (2 * dst_h)) {
> +			gt2 = 1;
> +			src_h >>= 1;
> +		}
> +
> +		hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> +		ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> +
> +		val = vop2_scale_factor(src_w, dst_w);
> +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val);
> +
> +		val = vop2_scale_factor(src_h, dst_h);
> +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val);
> +
> +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4);
> +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2);
> +		vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode);
> +	}
> +}
> +
> +static int vop2_convert_csc_mode(int csc_mode)
> +{
> +	switch (csc_mode) {
> +	case V4L2_COLORSPACE_SMPTE170M:
> +	case V4L2_COLORSPACE_470_SYSTEM_M:
> +	case V4L2_COLORSPACE_470_SYSTEM_BG:
> +		return CSC_BT601L;
> +	case V4L2_COLORSPACE_REC709:
> +	case V4L2_COLORSPACE_SMPTE240M:
> +	case V4L2_COLORSPACE_DEFAULT:
> +		return CSC_BT709L;
> +	case V4L2_COLORSPACE_JPEG:
> +		return CSC_BT601F;
> +	case V4L2_COLORSPACE_BT2020:
> +		return CSC_BT2020;
> +	default:
> +		return CSC_BT709L;
> +	}
> +}
> +
> +/*
> + * colorspace path:
> + *      Input        Win csc                     Output
> + * 1. YUV(2020)  --> Y2R->2020To709->R2Y   --> YUV_OUTPUT(601/709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 2. YUV(2020)  --> bypasss               --> YUV_OUTPUT(2020)
> + *    RGB        --> 709To2020->R2Y       __/
> + *
> + * 3. YUV(2020)  --> Y2R->2020To709        --> RGB_OUTPUT(709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 4. YUV(601/709)-> Y2R->709To2020->R2Y   --> YUV_OUTPUT(2020)
> + *    RGB        --> 709To2020->R2Y       __/
> + *
> + * 5. YUV(601/709)-> bypass                --> YUV_OUTPUT(709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 6. YUV(601/709)-> bypass                --> YUV_OUTPUT(601)
> + *    RGB        --> R2Y(601)             __/
> + *
> + * 7. YUV        --> Y2R(709)              --> RGB_OUTPUT(709)
> + *    RGB        --> bypass               __/
> + *
> + * 8. RGB        --> 709To2020->R2Y        --> YUV_OUTPUT(2020)
> + *
> + * 9. RGB        --> R2Y(709)              --> YUV_OUTPUT(709)
> + *
> + * 10. RGB       --> R2Y(601)              --> YUV_OUTPUT(601)
> + *
> + * 11. RGB       --> bypass                --> RGB_OUTPUT(709)
> + */
> +
> +static void vop2_setup_csc_mode(struct vop2_video_port *vp,
> +				struct vop2_win *win,
> +				struct drm_plane_state *pstate)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
> +	int is_input_yuv = pstate->fb->format->is_yuv;
> +	int is_output_yuv = is_yuv_output(vcstate->bus_format);
> +	int input_csc = V4L2_COLORSPACE_DEFAULT;
> +	int output_csc = vcstate->color_space;
> +	bool r2y_en, y2r_en;
> +	int csc_mode;
> +
> +	if (is_input_yuv && !is_output_yuv) {
> +		y2r_en = true;
> +		r2y_en = false;
> +		csc_mode = vop2_convert_csc_mode(input_csc);
> +	} else if (!is_input_yuv && is_output_yuv) {
> +		y2r_en = false;
> +		r2y_en = true;
> +		csc_mode = vop2_convert_csc_mode(output_csc);
> +	} else {
> +		y2r_en = false;
> +		r2y_en = false;
> +		csc_mode = false;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en);
> +	vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en);
> +	vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
> +}
> +
> +static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq);
> +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq);
> +}
> +
> +static void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16);
> +}
> +
> +static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
> +{
> +	int ret;
> +
> +	ret = clk_prepare_enable(vop2->hclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable hclk - %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(vop2->aclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable aclk - %d\n", ret);
> +		goto err;
> +	}
> +
> +	return 0;
> +err:
> +	clk_disable_unprepare(vop2->hclk);
> +
> +	return ret;
> +}
> +
> +static void vop2_enable(struct vop2 *vop2)
> +{
> +	int ret;
> +
> +	ret = pm_runtime_get_sync(vop2->dev);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret);
> +		return;
> +	}
> +
> +	ret = vop2_core_clks_prepare_enable(vop2);
> +	if (ret) {
> +		pm_runtime_put_sync(vop2->dev);
> +		return;
> +	}
> +
> +	if (vop2->data->soc_id == 3566)
> +		vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
> +
> +	vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> +
> +	/*
> +	 * Disable auto gating, this is a workaround to
> +	 * avoid display image shift when a window enabled.
> +	 */
> +	regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL,
> +			  RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN);
> +
> +	vop2_writel(vop2, RK3568_SYS0_INT_CLR,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS0_INT_EN,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS1_INT_CLR,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS1_INT_EN,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +}
> +
> +static void vop2_disable(struct vop2 *vop2)
> +{
> +	pm_runtime_put_sync(vop2->dev);
> +
> +	clk_disable_unprepare(vop2->aclk);
> +	clk_disable_unprepare(vop2->hclk);
> +}
> +
> +static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
> +				     struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	int ret;
> +
> +	vop2_lock(vop2);
> +
> +	drm_crtc_vblank_off(crtc);
> +
> +	/*
> +	 * Vop standby will take effect at end of current frame,
> +	 * if dsp hold valid irq happen, it means standby complete.
> +	 *
> +	 * we must wait standby complete when we want to disable aclk,
> +	 * if not, memory bus maybe dead.
> +	 */
> +	reinit_completion(&vp->dsp_hold_completion);
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
> +
> +	ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
> +					  msecs_to_jiffies(50));
> +	if (!ret)
> +		drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
> +
> +	vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
> +
> +	clk_disable_unprepare(vp->dclk);
> +
> +	vop2->enable_count--;
> +
> +	if (!vop2->enable_count)
> +		vop2_disable(vop2);
> +
> +	vop2_unlock(vop2);
> +
> +	if (crtc->state->event && !crtc->state->active) {
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +
> +		crtc->state->event = NULL;
> +	}
> +}
> +
> +static int vop2_plane_atomic_check(struct drm_plane *plane,
> +				   struct drm_atomic_state *astate)
> +{
> +	struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane);
> +	struct drm_framebuffer *fb = pstate->fb;
> +	struct drm_crtc *crtc = pstate->crtc;
> +	struct drm_crtc_state *cstate;
> +	struct vop2_video_port *vp;
> +	struct vop2 *vop2;
> +	const struct vop2_data *vop2_data;
> +	struct drm_rect *dest = &pstate->dst;
> +	struct drm_rect *src = &pstate->src;
> +	int min_scale = FRAC_16_16(1, 8);
> +	int max_scale = FRAC_16_16(8, 1);
> +	int format;
> +	int ret;
> +
> +	if (!crtc)
> +		return 0;
> +
> +	vp = to_vop2_video_port(crtc);
> +	vop2 = vp->vop2;
> +	vop2_data = vop2->data;
> +
> +	cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc);
> +	if (WARN_ON(!cstate))
> +		return -EINVAL;
> +
> +	ret = drm_atomic_helper_check_plane_state(pstate, cstate,
> +						  min_scale, max_scale,
> +						  true, true);
> +	if (ret)
> +		return ret;
> +
> +	if (!pstate->visible)
> +		return 0;
> +
> +	format = vop2_convert_format(fb->format->format);
> +	if (format < 0)
> +		return format;
> +
> +	if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 ||
> +	    drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) {
> +		drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
> +			  drm_rect_width(src) >> 16, drm_rect_height(src) >> 16,
> +			  drm_rect_width(dest), drm_rect_height(dest));
> +		pstate->visible = false;
> +		return 0;
> +	}
> +
> +	if (drm_rect_width(src) >> 16 > vop2_data->max_input.width ||
> +	    drm_rect_height(src) >> 16 > vop2_data->max_input.height) {
> +		drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
> +			  drm_rect_width(src) >> 16,
> +			  drm_rect_height(src) >> 16,
> +			  vop2_data->max_input.width,
> +			  vop2_data->max_input.height);
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Src.x1 can be odd when do clip, but yuv plane start point
> +	 * need align with 2 pixel.
> +	 */
> +	if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
> +		drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vop2_plane_atomic_disable(struct drm_plane *plane,
> +				      struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state, plane);
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2 *vop2 = win->vop2;
> +
> +	drm_dbg(vop2->drm, "%s disable\n", win->data->name);
> +
> +	if (!old_pstate->crtc)
> +		return;
> +
> +	vop2_win_disable(win);
> +	vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0);
> +}
> +
> +/*
> + * The color key is 10 bit, so all format should
> + * convert to 10 bit here.
> + */
> +static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
> +{
> +	struct drm_plane_state *pstate = plane->state;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	struct vop2_win *win = to_vop2_win(plane);
> +	u32 color_key_en = 0;
> +	u32 r = 0;
> +	u32 g = 0;
> +	u32 b = 0;
> +
> +	if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) {
> +		vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0);
> +		return;
> +	}
> +
> +	switch (fb->format->format) {
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		r = (color_key & 0xf800) >> 11;
> +		g = (color_key & 0x7e0) >> 5;
> +		b = (color_key & 0x1f);
> +		r <<= 5;
> +		g <<= 4;
> +		b <<= 5;
> +		color_key_en = 1;
> +		break;
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		r = (color_key & 0xff0000) >> 16;
> +		g = (color_key & 0xff00) >> 8;
> +		b = (color_key & 0xff);
> +		r <<= 2;
> +		g <<= 2;
> +		b <<= 2;
> +		color_key_en = 1;
> +		break;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en);
> +	vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b);
> +}
> +
> +static void vop2_plane_atomic_update(struct drm_plane *plane,
> +				     struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *pstate = plane->state;
> +	struct drm_crtc *crtc = pstate->crtc;
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
> +	struct vop2 *vop2 = win->vop2;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	u32 bpp = fb->format->cpp[0] * 8;
> +	u32 actual_w, actual_h, dsp_w, dsp_h;
> +	u32 act_info, dsp_info;
> +	u32 format;
> +	u32 afbc_format;
> +	u32 rb_swap;
> +	u32 uv_swap;
> +	struct drm_rect *src = &pstate->src;
> +	struct drm_rect *dest = &pstate->dst;
> +	u32 afbc_tile_num;
> +	u32 transform_offset;
> +	bool dither_up;
> +	bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
> +	bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
> +	bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
> +	bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
> +	struct rockchip_gem_object *rk_obj;
> +	unsigned long offset;
> +	bool afbc_en;
> +	dma_addr_t yrgb_mst;
> +	dma_addr_t uv_mst;
> +
> +	/*
> +	 * can't update plane when vop2 is disabled.
> +	 */
> +	if (WARN_ON(!crtc))
> +		return;
> +
> +	if (!pstate->visible) {
> +		vop2_plane_atomic_disable(plane, state);
> +		return;
> +	}
> +
> +	afbc_en = rockchip_afbc(plane, fb->modifier);
> +
> +	offset = (src->x1 >> 16) * fb->format->cpp[0];
> +
> +	/*
> +	 * AFBC HDR_PTR must set to the zero offset of the framebuffer.
> +	 */
> +	if (afbc_en)
> +		offset = 0;
> +	else if (pstate->rotation & DRM_MODE_REFLECT_Y)
> +		offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
> +	else
> +		offset += (src->y1 >> 16) * fb->pitches[0];
> +
> +	rk_obj = to_rockchip_obj(fb->obj[0]);
> +
> +	yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
> +	if (fb->format->is_yuv) {
> +		int hsub = fb->format->hsub;
> +		int vsub = fb->format->vsub;
> +
> +		offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub;
> +		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
> +
> +		if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en)
> +			offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub;
> +
> +		rk_obj = to_rockchip_obj(fb->obj[0]);
> +		uv_mst = rk_obj->dma_addr + offset + fb->offsets[1];
> +	}
> +
> +	actual_w = drm_rect_width(src) >> 16;
> +	actual_h = drm_rect_height(src) >> 16;
> +	dsp_w = drm_rect_width(dest);
> +
> +	if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
> +		drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
> +			  vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
> +		dsp_w = adjusted_mode->hdisplay - dest->x1;
> +		if (dsp_w < 4)
> +			dsp_w = 4;
> +		actual_w = dsp_w * actual_w / drm_rect_width(dest);
> +	}
> +
> +	dsp_h = drm_rect_height(dest);
> +
> +	if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
> +		drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
> +			  vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
> +		dsp_h = adjusted_mode->vdisplay - dest->y1;
> +		if (dsp_h < 4)
> +			dsp_h = 4;
> +		actual_h = dsp_h * actual_h / drm_rect_height(dest);
> +	}
> +
> +	/*
> +	 * This is workaround solution for IC design:
> +	 * esmart can't support scale down when actual_w % 16 == 1.
> +	 */
> +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> +		if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
> +			drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
> +				vp->id, win->data->name, actual_w);
> +			actual_w -= 1;
> +		}
> +	}
> +
> +	if (afbc_en && actual_w % 4) {
> +		drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
> +			  vp->id, win->data->name, actual_w);
> +		actual_w = ALIGN_DOWN(actual_w, 4);
> +	}
> +
> +	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
> +	dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff);
> +
> +	format = vop2_convert_format(fb->format->format);
> +
> +	drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n",
> +		      vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h,
> +		      dest->x1, dest->y1,
> +		      &fb->format->format,
> +		      afbc_en ? "AFBC" : "", &yrgb_mst);
> +
> +	if (afbc_en) {
> +		u32 stride;
> +
> +		/* the afbc superblock is 16 x 16 */
> +		afbc_format = vop2_convert_afbc_format(fb->format->format);
> +
> +		/* Enable color transform for YTR */
> +		if (fb->modifier & AFBC_FORMAT_MOD_YTR)
> +			afbc_format |= (1 << 4);
> +
> +		afbc_tile_num = ALIGN(actual_w, 16) >> 4;
> +
> +		/*
> +		 * AFBC pic_vir_width is count by pixel, this is different
> +		 * with WIN_VIR_STRIDE.
> +		 */
> +		stride = (fb->pitches[0] << 3) / bpp;
> +		if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
> +			drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligened\n",
> +				  vp->id, win->data->name, stride);
> +
> +		rb_swap = vop2_afbc_rb_swap(fb->format->format);
> +		uv_swap = vop2_afbc_uv_swap(fb->format->format);
> +		/*
> +		 * This is a workaround for crazy IC design, Cluster
> +		 * and Esmart/Smart use different format configuration map:
> +		 * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart.
> +		 *
> +		 * This is one thing we can make the convert simple:
> +		 * AFBCD decode all the YUV data to YUV444. So we just
> +		 * set all the yuv 10 bit to YUV444_10.
> +		 */
> +		if (fb->format->is_yuv && (bpp == 10))
> +			format = VOP2_CLUSTER_YUV444_10;
> +
> +		if (vop2_cluster_window(win))
> +			vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
> +		vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
> +		vop2_win_write(win, VOP2_WIN_AFBC_RB_SWAP, rb_swap);
> +		vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap);
> +		vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
> +		vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
> +		if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) {
> +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0);
> +			transform_offset = vop2_afbc_transform_offset(pstate, false);
> +		} else {
> +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 1);
> +			transform_offset = vop2_afbc_transform_offset(pstate, true);
> +		}
> +		vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
> +		vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset);
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1));
> +		vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16)));
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride);
> +		vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num);
> +		vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror);
> +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270);
> +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90);
> +	} else {
> +		vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4));
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror);
> +
> +	if (rotate_90 || rotate_270) {
> +		act_info = swahw32(act_info);
> +		actual_w = drm_rect_height(src) >> 16;
> +		actual_h = drm_rect_width(src) >> 16;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_FORMAT, format);
> +	vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst);
> +
> +	rb_swap = vop2_win_rb_swap(fb->format->format);
> +	vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap);
> +	if (!vop2_cluster_window(win)) {
> +		uv_swap = vop2_win_uv_swap(fb->format->format);
> +		vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
> +	}
> +
> +	if (fb->format->is_yuv) {
> +		vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4));
> +		vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst);
> +	}
> +
> +	vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format);
> +	if (!vop2_cluster_window(win))
> +		vop2_plane_setup_color_key(plane, 0);
> +	vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info);
> +	vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info);
> +	vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff));
> +
> +	vop2_setup_csc_mode(vp, win, pstate);
> +
> +	dither_up = vop2_win_dither_up(fb->format->format);
> +	vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up);
> +
> +	vop2_win_write(win, VOP2_WIN_ENABLE, 1);
> +
> +	if (vop2_cluster_window(win)) {
> +		int lb_mode = vop2_get_cluster_lb_mode(win, pstate);
> +
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode);
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1);
> +	}
> +}
> +
> +static const struct drm_plane_helper_funcs vop2_plane_helper_funcs = {
> +	.atomic_check = vop2_plane_atomic_check,
> +	.atomic_update = vop2_plane_atomic_update,
> +	.atomic_disable = vop2_plane_atomic_disable,
> +};
> +
> +static const struct drm_plane_funcs vop2_plane_funcs = {
> +	.update_plane	= drm_atomic_helper_update_plane,
> +	.disable_plane	= drm_atomic_helper_disable_plane,
> +	.destroy = drm_plane_cleanup,
> +	.reset = drm_atomic_helper_plane_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> +	.format_mod_supported = rockchip_vop2_mod_supported,
> +};
> +
> +static int vop2_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD);
> +
> +	return 0;
> +}
> +
> +static void vop2_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD);
> +}
> +
> +static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
> +				 const struct drm_display_mode *mode,
> +				 struct drm_display_mode *adj_mode)
> +{
> +	drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V |
> +					CRTC_STEREO_DOUBLE);
> +
> +	return true;
> +}
> +
> +static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	switch (vcstate->bus_format) {
> +	case MEDIA_BUS_FMT_RGB565_1X16:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> +		break;
> +	case MEDIA_BUS_FMT_RGB666_1X18:
> +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> +	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> +		*dsp_ctrl |= RGB888_TO_RGB666;
> +		break;
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA)
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> +
> +	*dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL,
> +				DITHER_DOWN_ALLEGRO);
> +}
> +
> +static void vop2_post_config(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	u16 vtotal = mode->crtc_vtotal;
> +	u16 hdisplay = mode->crtc_hdisplay;
> +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> +	u16 vdisplay = mode->crtc_vdisplay;
> +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> +	u32 left_margin = 100, right_margin = 100;
> +	u32 top_margin = 100, bottom_margin = 100;
> +	u16 hsize = hdisplay * (left_margin + right_margin) / 200;
> +	u16 vsize = vdisplay * (top_margin + bottom_margin) / 200;
> +	u16 hact_end, vact_end;
> +	u32 val;
> +
> +	vsize = rounddown(vsize, 2);
> +	hsize = rounddown(hsize, 2);
> +	hact_st += hdisplay * (100 - left_margin) / 200;
> +	hact_end = hact_st + hsize;
> +	val = hact_st << 16;
> +	val |= hact_end;
> +	vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val);
> +	vact_st += vdisplay * (100 - top_margin) / 200;
> +	vact_end = vact_st + vsize;
> +	val = vact_st << 16;
> +	val |= vact_end;
> +	vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val);
> +	val = scl_cal_scale2(vdisplay, vsize) << 16;
> +	val |= scl_cal_scale2(hdisplay, hsize);
> +	vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val);
> +
> +	val = 0;
> +	if (hdisplay != hsize)
> +		val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN;
> +	if (vdisplay != vsize)
> +		val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN;
> +	vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val);
> +
> +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> +		u16 vact_st_f1 = vtotal + vact_st + 1;
> +		u16 vact_end_f1 = vact_st_f1 + vsize;
> +
> +		val = vact_st_f1 << 16 | vact_end_f1;
> +		vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
> +	}
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_BG, 0);
> +}
> +
> +static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
> +				u32 polflags)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	u32 die, dip;
> +
> +	die = vop2_readl(vop2, RK3568_DSP_IF_EN);
> +	dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
> +
> +	switch (id) {
> +	case RK3568_VOP2_EP_RGB:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_RGB |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
> +		if (polflags & POLFLAG_DCLK_INV)
> +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
> +		else
> +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16));
> +		break;
> +	case RK3568_VOP2_EP_HDMI:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_HDMI |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id);
> +		break;
> +	case RK3568_VOP2_EP_EDP:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_EDP |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id);
> +		break;
> +	case RK3568_VOP2_EP_MIPI0:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_MIPI1:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_LVDS0:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_LVDS1:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> +		break;
> +	default:
> +		return;
> +	};
> +
> +	dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
> +
> +	vop2_writel(vop2, RK3568_DSP_IF_EN, die);
> +	vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
> +}
> +
> +static int us_to_vertical_line(struct drm_display_mode *mode, int us)
> +{
> +	return us * mode->clock / mode->htotal / 1000;
> +}
> +
> +static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
> +				    struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	const struct vop2_data *vop2_data = vop2->data;
> +	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	unsigned long clock = mode->crtc_clock * 1000;
> +	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
> +	u16 hdisplay = mode->crtc_hdisplay;
> +	u16 htotal = mode->crtc_htotal;
> +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> +	u16 hact_end = hact_st + hdisplay;
> +	u16 vdisplay = mode->crtc_vdisplay;
> +	u16 vtotal = mode->crtc_vtotal;
> +	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
> +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> +	u16 vact_end = vact_st + vdisplay;
> +	u8 out_mode;
> +	u32 dsp_ctrl = 0;
> +	int act_end;
> +	u32 val, polflags;
> +	int ret;
> +	struct drm_encoder *encoder;
> +
> +	drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n",
> +		hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
> +		drm_mode_vrefresh(mode), vcstate->output_type, vp->id);
> +
> +	vop2_lock(vop2);
> +
> +	ret = clk_prepare_enable(vp->dclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
> +			      vp->id, ret);
> +		return;
> +	}
> +
> +	if (!vop2->enable_count)
> +		vop2_enable(vop2);
> +
> +	vop2->enable_count++;
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY);
> +
> +	polflags = 0;
> +	if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
> +		polflags |= POLFLAG_DCLK_INV;
> +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> +		polflags |= BIT(HSYNC_POSITIVE);
> +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> +		polflags |= BIT(VSYNC_POSITIVE);
> +
> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> +		struct device_node *node, *parent;
> +
> +		parent = of_get_parent(rkencoder->port);
> +
> +		for_each_endpoint_of_node(parent, node) {

Is there any hurt directly use our downstream vendor kernel method here: 
use vcstate->output_if set by encoder driver to get which interface we 
should enable here?

You method is ok with device tree,  but it tied up this driver to device 
tree, we are now tring to extend vop2 driver work with ACPI, so we hope 
this driver can be much more flexible.

> +			struct device_node *crtc_port = of_graph_get_remote_port(node);
> +			struct device_node *epn;
> +			struct of_endpoint endpoint;
> +
> +			if (crtc->port != crtc_port) {
> +				of_node_put(crtc_port);
> +				continue;
> +			}
> +
> +			of_node_put(crtc_port);
> +
> +			epn = of_graph_get_remote_endpoint(node);
> +			of_graph_parse_endpoint(epn, &endpoint);
> +			of_node_put(epn);
> +
> +			drm_dbg(vop2->drm, "vp%d is connected to %s, id %d\n",
> +					   vp->id, encoder->name, endpoint.id);
> +			rk3568_set_intf_mux(vp, endpoint.id, polflags);
> +		}
> +		of_node_put(parent);
> +	}
> +
> +	if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
> +	     !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT))
> +		out_mode = ROCKCHIP_OUT_MODE_P888;
> +	else
> +		out_mode = vcstate->output_mode;
> +
> +	dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode);
> +
> +	if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP;
> +
> +	if (is_yuv_output(vcstate->bus_format))
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y;
> +
> +	vop2_dither_setup(crtc, &dsp_ctrl);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len);
> +	val = hact_st << 16;
> +	val |= hact_end;
> +	vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val);
> +
> +	val = vact_st << 16;
> +	val |= vact_end;
> +	vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val);
> +
> +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> +		u16 vact_st_f1 = vtotal + vact_st + 1;
> +		u16 vact_end_f1 = vact_st_f1 + vdisplay;
> +
> +		val = vact_st_f1 << 16 | vact_end_f1;
> +		vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val);
> +
> +		val = vtotal << 16 | (vtotal + vsync_len);
> +		vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val);
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE;
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL;
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN;
> +		vtotal += vtotal + 1;
> +		act_end = vact_end_f1;
> +	} else {
> +		act_end = vact_end;
> +	}
> +
> +	vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id),
> +		    (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len);
> +
> +	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
> +		clock *= 2;
> +	}
> +
> +	vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
> +
> +	clk_set_rate(vp->dclk, clock);
> +
> +	vop2_post_config(crtc);
> +
> +	vop2_cfg_done(vp);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
> +
> +	drm_crtc_vblank_on(crtc);
> +
> +	vop2_unlock(vop2);
> +}
> +
> +static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
> +				  struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_plane *plane;
> +	int nplanes = 0;
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +
> +	drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
> +		nplanes++;
> +
> +	if (nplanes > vp->nlayers)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static bool is_opaque(u16 alpha)
> +{
> +	return (alpha >> 8) == 0xff;
> +}
> +
> +static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config,
> +			     struct vop2_alpha *alpha)
> +{
> +	int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1;
> +	int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1;
> +	int src_color_mode = alpha_config->src_premulti_en ?
> +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> +	int dst_color_mode = alpha_config->dst_premulti_en ?
> +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> +
> +	alpha->src_color_ctrl.val = 0;
> +	alpha->dst_color_ctrl.val = 0;
> +	alpha->src_alpha_ctrl.val = 0;
> +	alpha->dst_alpha_ctrl.val = 0;
> +
> +	if (!alpha_config->src_pixel_alpha_en)
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> +	else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en)
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> +	else
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> +
> +	alpha->src_color_ctrl.bits.alpha_en = 1;
> +
> +	if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) {
> +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> +	} else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) {
> +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE;
> +	} else {
> +		alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> +	}
> +	alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8;
> +	alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +
> +	alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +	alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> +	alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8;
> +	alpha->dst_color_ctrl.bits.color_mode = dst_color_mode;
> +	alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> +
> +	alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode;
> +	alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +	alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE;
> +
> +	alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en)
> +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> +	else
> +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> +	alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION;
> +	alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> +}
> +
> +static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id)
> +{
> +	struct vop2_video_port *vp;
> +	int used_layer = 0;
> +	int i;
> +
> +	for (i = 0; i < port_id; i++) {
> +		vp = &vop2->vps[i];
> +		used_layer += hweight32(vp->win_mask);
> +	}
> +
> +	return used_layer;
> +}
> +
> +static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win)
> +{
> +	u32 offset = (main_win->data->phys_id * 0x10);
> +	struct vop2_alpha_config alpha_config;
> +	struct vop2_alpha alpha;
> +	struct drm_plane_state *bottom_win_pstate;
> +	bool src_pixel_alpha_en = false;
> +	u16 src_glb_alpha_val, dst_glb_alpha_val;
> +	bool premulti_en = false;
> +	bool swap = false;
> +
> +	/* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */
> +	bottom_win_pstate = main_win->base.state;
> +	src_glb_alpha_val = 0;
> +	dst_glb_alpha_val = main_win->base.state->alpha;
> +
> +	if (!bottom_win_pstate->fb)
> +		return;
> +
> +	alpha_config.src_premulti_en = premulti_en;
> +	alpha_config.dst_premulti_en = false;
> +	alpha_config.src_pixel_alpha_en = src_pixel_alpha_en;
> +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> +	alpha_config.src_glb_alpha_value = src_glb_alpha_val;
> +	alpha_config.dst_glb_alpha_value = dst_glb_alpha_val;
> +	vop2_parse_alpha(&alpha_config, &alpha);
> +
> +	alpha.src_color_ctrl.bits.src_dst_swap = swap;
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset,
> +		    alpha.src_color_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset,
> +		    alpha.dst_color_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset,
> +		    alpha.src_alpha_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset,
> +		    alpha.dst_alpha_ctrl.val);
> +}
> +
> +static void vop2_setup_alpha(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_framebuffer *fb;
> +	struct vop2_alpha_config alpha_config;
> +	struct vop2_alpha alpha;
> +	struct drm_plane *plane;
> +	int pixel_alpha_en;
> +	int premulti_en, gpremulti_en = 0;
> +	int mixer_id;
> +	u32 offset;
> +	bool bottom_layer_alpha_en = false;
> +	u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE;
> +
> +	mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id);
> +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> +
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		if (plane->state->normalized_zpos == 0 &&
> +		    !is_opaque(plane->state->alpha) &&
> +		    !vop2_cluster_window(win)) {
> +			/*
> +			 * If bottom layer have global alpha effect [except cluster layer,
> +			 * because cluster have deal with bottom layer global alpha value
> +			 * at cluster mix], bottom layer mix need deal with global alpha.
> +			 */
> +			bottom_layer_alpha_en = true;
> +			dst_global_alpha = plane->state->alpha;
> +		}
> +	}
> +
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +		int zpos = plane->state->normalized_zpos;
> +
> +		if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
> +			premulti_en = 1;
> +		else
> +			premulti_en = 0;
> +
> +		plane = &win->base;
> +		fb = plane->state->fb;
> +
> +		pixel_alpha_en = fb->format->has_alpha;
> +
> +		alpha_config.src_premulti_en = premulti_en;
> +
> +		if (bottom_layer_alpha_en && zpos == 1) {
> +			gpremulti_en = premulti_en;
> +			/* Cd = Cs + (1 - As) * Cd * Agd */
> +			alpha_config.dst_premulti_en = false;
> +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> +			alpha_config.dst_glb_alpha_value = dst_global_alpha;
> +		} else if (vop2_cluster_window(win)) {
> +			/* Mix output data only have pixel alpha */
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = true;
> +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +		} else {
> +			/* Cd = Cs + (1 - As) * Cd */
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +		}
> +
> +		vop2_parse_alpha(&alpha_config, &alpha);
> +
> +		offset = (mixer_id + zpos - 1) * 0x10;
> +		vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset,
> +			    alpha.src_color_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset,
> +			    alpha.dst_color_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset,
> +			    alpha.src_alpha_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset,
> +			    alpha.dst_alpha_ctrl.val);
> +	}
> +
> +	if (vp->id == 0) {
> +		if (bottom_layer_alpha_en) {
> +			/* Transfer pixel alpha to hdr mix */
> +			alpha_config.src_premulti_en = gpremulti_en;
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = true;
> +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			vop2_parse_alpha(&alpha_config, &alpha);
> +
> +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL,
> +				alpha.src_color_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL,
> +				alpha.dst_color_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL,
> +				alpha.src_alpha_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL,
> +				alpha.dst_alpha_ctrl.val);
> +		} else {
> +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0);
> +		}
> +	}
> +}
> +
> +static void vop2_setup_layer_mixer(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_plane *plane;
> +	u32 layer_sel = 0;
> +	u32 port_sel;
> +	unsigned int nlayer, ofs;
> +	struct drm_display_mode *adjusted_mode;
> +	u16 hsync_len;
> +	u16 hdisplay;
> +	u32 bg_dly;
> +	u32 pre_scan_dly;
> +	int i;
> +	struct vop2_video_port *vp0 = &vop2->vps[0];
> +	struct vop2_video_port *vp1 = &vop2->vps[1];
> +	struct vop2_video_port *vp2 = &vop2->vps[2];
> +
> +	adjusted_mode = &vp->crtc.state->adjusted_mode;
> +	hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
> +	hdisplay = adjusted_mode->crtc_hdisplay;
> +
> +	bg_dly = vp->data->pre_scan_max_dly[3];
> +	vop2_writel(vop2, RK3568_VP_BG_MIX_CTRL(vp->id),
> +			    FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly));
> +
> +	pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len;
> +	vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly);
> +
> +	vop2_writel(vop2, RK3568_OVL_CTRL, 0);
> +	port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
> +	port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT;
> +
> +	if (vp0->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX,
> +				     vp0->nlayers - 1);
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8);
> +
> +	if (vp1->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX,
> +				     (vp0->nlayers + vp1->nlayers - 1));
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> +
> +	if (vp2->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX,
> +			(vp2->nlayers + vp1->nlayers + vp0->nlayers - 1));
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> +
> +	layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
> +
> +	ofs = 0;
> +	for (i = 0; i < vp->id; i++)
> +		ofs += vop2->vps[i].nlayers;
> +
> +	nlayer = 0;
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		switch (win->data->phys_id) {
> +		case ROCKCHIP_VOP2_CLUSTER0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_CLUSTER1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_SMART0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_SMART1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id);
> +			break;
> +		}
> +
> +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, win->data->layer_sel_id);
> +		nlayer++;
> +	}
> +
> +	/* configure unused layers to 0x5 (reserved) */
> +	for (; nlayer < 3; nlayer++) {
> +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5);
> +	}
> +
> +	vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel);
> +	vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
> +	vop2_writel(vop2, RK3568_OVL_CTRL, RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD);
> +}
> +
> +static void vop2_setup_dly_for_windows(struct vop2 *vop2)
> +{
> +	struct vop2_win *win;
> +	int i = 0;
> +	u32 cdly = 0, sdly = 0;
> +
> +	for (i = 0; i < vop2->data->win_size; i++) {
> +		u32 dly;
> +
> +		win = &vop2->win[i];
> +		dly = win->delay;
> +
> +		switch (win->data->phys_id) {
> +		case ROCKCHIP_VOP2_CLUSTER0:
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly);
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_CLUSTER1:
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly);
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART0:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART1:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_SMART0:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_SMART1:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly);
> +			break;
> +		}
> +	}
> +
> +	vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly);
> +	vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly);
> +}
> +
> +static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_plane *plane;
> +
> +	vp->win_mask = 0;
> +
> +	drm_atomic_crtc_for_each_plane(plane, crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT];
> +
> +		vp->win_mask |= BIT(win->data->phys_id);
> +
> +		if (vop2_cluster_window(win))
> +			vop2_setup_cluster_alpha(vop2, win);
> +	}
> +
> +	if (!vp->win_mask)
> +		return;
> +
> +	vop2_setup_layer_mixer(vp);
> +	vop2_setup_alpha(vp);
> +	vop2_setup_dly_for_windows(vop2);
> +}
> +
> +static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_post_config(crtc);
> +
> +	vop2_cfg_done(vp);
> +
> +	spin_lock_irq(&crtc->dev->event_lock);
> +
> +	if (crtc->state->event) {
> +		WARN_ON(drm_crtc_vblank_get(crtc));
> +		vp->event = crtc->state->event;
> +		crtc->state->event = NULL;
> +	}
> +
> +	spin_unlock_irq(&crtc->dev->event_lock);
> +}
> +
> +static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
> +	.mode_fixup = vop2_crtc_mode_fixup,
> +	.atomic_check = vop2_crtc_atomic_check,
> +	.atomic_begin = vop2_crtc_atomic_begin,
> +	.atomic_flush = vop2_crtc_atomic_flush,
> +	.atomic_enable = vop2_crtc_atomic_enable,
> +	.atomic_disable = vop2_crtc_atomic_disable,
> +};
> +
> +static void vop2_crtc_reset(struct drm_crtc *crtc)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	if (crtc->state) {
> +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
> +		kfree(vcstate);
> +	}
> +
> +	vcstate = kzalloc(sizeof(*vcstate), GFP_KERNEL);
> +	if (!vcstate)
> +		return;
> +
> +	crtc->state = &vcstate->base;
> +	crtc->state->crtc = crtc;
> +}
> +
> +static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc)
> +{
> +	struct rockchip_crtc_state *vcstate, *old_vcstate;
> +
> +	old_vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	vcstate = kmemdup(old_vcstate, sizeof(*old_vcstate), GFP_KERNEL);
> +	if (!vcstate)
> +		return NULL;
> +
> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
> +
> +	return &vcstate->base;
> +}
> +
> +static void vop2_crtc_destroy_state(struct drm_crtc *crtc,
> +				    struct drm_crtc_state *state)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state);
> +
> +	__drm_atomic_helper_crtc_destroy_state(&vcstate->base);
> +	kfree(vcstate);
> +}
> +
> +static const struct drm_crtc_funcs vop2_crtc_funcs = {
> +	.set_config = drm_atomic_helper_set_config,
> +	.page_flip = drm_atomic_helper_page_flip,
> +	.destroy = drm_crtc_cleanup,
> +	.reset = vop2_crtc_reset,
> +	.atomic_duplicate_state = vop2_crtc_duplicate_state,
> +	.atomic_destroy_state = vop2_crtc_destroy_state,
> +	.enable_vblank = vop2_crtc_enable_vblank,
> +	.disable_vblank = vop2_crtc_disable_vblank,
> +};
> +
> +static irqreturn_t vop2_isr(int irq, void *data)
> +{
> +	struct vop2 *vop2 = data;
> +	const struct vop2_data *vop2_data = vop2->data;
> +	u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM];
> +	int ret = IRQ_NONE;
> +	int i;
> +
> +	/*
> +	 * The irq is shared with the iommu. If the runtime-pm state of the
> +	 * vop2-device is disabled the irq has to be targeted at the iommu.
> +	 */
> +	if (!pm_runtime_get_if_in_use(vop2->dev))
> +		return IRQ_NONE;
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +		struct drm_crtc *crtc = &vp->crtc;
> +		u32 irqs;
> +
> +		irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
> +		vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
> +
> +		if (irqs & VP_INT_DSP_HOLD_VALID) {
> +			complete(&vp->dsp_hold_completion);
> +			ret = IRQ_HANDLED;
> +		}
> +
> +		if (irqs & VP_INT_FS_FIELD) {
> +			drm_crtc_handle_vblank(crtc);
> +			spin_lock(&crtc->dev->event_lock);
> +			if (vp->event) {
> +				u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
> +
> +				if (!(val & BIT(vp->id))) {
> +					drm_crtc_send_vblank_event(crtc, vp->event);
> +					vp->event = NULL;
> +					drm_crtc_vblank_put(crtc);
> +				}
> +			}
> +			spin_unlock(&crtc->dev->event_lock);
> +
> +			ret = IRQ_HANDLED;
> +		}
> +
> +		if (irqs & VP_INT_POST_BUF_EMPTY) {
> +			drm_err_ratelimited(vop2->drm,
> +					    "POST_BUF_EMPTY irq err at vp%d\n",
> +					    vp->id);
> +			ret = IRQ_HANDLED;
> +		}
> +	}
> +
> +	axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS);
> +	vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]);
> +	axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS);
> +	vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]);
> +
> +	for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) {
> +		if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) {
> +			drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n");
> +			ret = IRQ_HANDLED;
> +		}
> +	}
> +
> +	pm_runtime_put(vop2->dev);
> +
> +	return ret;
> +}
> +
> +static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win,
> +			   unsigned long possible_crtcs)
> +{
> +	const struct vop2_win_data *win_data = win->data;
> +	unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +				  BIT(DRM_MODE_BLEND_PREMULTI) |
> +				  BIT(DRM_MODE_BLEND_COVERAGE);
> +	int ret;
> +
> +	ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs,
> +				       &vop2_plane_funcs, win_data->formats,
> +				       win_data->nformats,
> +				       win_data->format_modifiers,
> +				       win->type, win_data->name);
> +	if (ret) {
> +		drm_err(vop2->drm, "failed to initialize plane %d\n", ret);
> +		return ret;
> +	}
> +
> +	drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs);
> +
> +	if (win->data->supported_rotations)
> +		drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0,
> +						   DRM_MODE_ROTATE_0 |
> +						   win->data->supported_rotations);
> +	drm_plane_create_alpha_property(&win->base);
> +	drm_plane_create_blend_mode_property(&win->base, blend_caps);
> +	drm_plane_create_zpos_property(&win->base, win->win_id, 0,
> +				       vop2->registered_num_wins - 1);
> +
> +	return 0;
> +}
> +
> +static struct vop2_video_port *get_activated_vp(struct vop2 *vop2, int n)
> +{
> +	int i, id = 0;
> +
> +	for (i = 0; i < vop2->data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +
> +		if (!vp->crtc.port)
> +			continue;
> +
> +		if (n == id)
> +			return vp;
> +		id++;
> +	}
> +
> +	return NULL;
> +}
> +
> +#define NR_LAYERS 6
> +
> +static int vop2_create_crtc(struct vop2 *vop2)
> +{
> +	const struct vop2_data *vop2_data = vop2->data;
> +	struct drm_device *drm = vop2->drm;
> +	struct device *dev = vop2->dev;
> +	struct drm_plane *plane;
> +	struct device_node *port;
> +	struct vop2_video_port *vp;
> +	u32 possible_crtcs;
> +	int i, nvp, nvps = 0;
> +	int ret;
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		const struct vop2_video_port_data *vp_data;
> +		struct device_node *np;
> +		char dclk_name[9];
> +
> +		vp_data = &vop2_data->vp[i];
> +		vp = &vop2->vps[i];
> +		vp->vop2 = vop2;
> +		vp->id = vp_data->id;
> +		vp->regs = vp_data->regs;
> +		vp->data = vp_data;
> +
> +		snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
> +		vp->dclk = devm_clk_get(vop2->dev, dclk_name);
> +		if (IS_ERR(vp->dclk)) {
> +			drm_err(vop2->drm, "failed to get %s\n", dclk_name);
> +			return PTR_ERR(vp->dclk);
> +		}
> +
> +		np = of_graph_get_remote_node(dev->of_node, i, -1);
> +		if (!np) {
> +			drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
> +			continue;
> +		}
> +		of_node_put(np);
> +
> +		port = of_graph_get_port_by_id(dev->of_node, i);
> +		if (!port) {
> +			drm_err(vop2->drm, "no port node found for video_port%d\n", i);
> +			return -ENOENT;
> +		}
> +
> +		vp->crtc.port = port;
> +		nvps++;
> +	}
> +
> +	nvp = 0;
> +	for (i = 0; i < vop2->registered_num_wins; i++) {
> +		struct vop2_win *win = &vop2->win[i];
> +
> +		if (win->type == DRM_PLANE_TYPE_PRIMARY) {
> +			vp = get_activated_vp(vop2, nvp);
> +
> +			if (vp) {
> +				possible_crtcs = BIT(nvp);
> +				vp->primary_plane = win;
> +
> +				nvp++;
> +			} else {
> +				/* change the unused primary window to overlay window */
> +				win->type = DRM_PLANE_TYPE_OVERLAY;
> +			}
> +		}
> +
> +		if (win->type == DRM_PLANE_TYPE_OVERLAY)
> +			possible_crtcs = (1 << vop2_data->nr_vps) - 1;
> +
> +		ret = vop2_plane_init(vop2, win, possible_crtcs);
> +
> +		if (ret) {
> +			drm_err(vop2->drm, "failed to init plane %s: %d\n",
> +				win->data->name, ret);
> +			return ret;
> +		}
> +	}
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		vp = &vop2->vps[i];
> +
> +		if (!vp->crtc.port)
> +			continue;
> +
> +		plane = &vp->primary_plane->base;
> +
> +		ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL,
> +						&vop2_crtc_funcs,
> +						"video_port%d", vp->id);
> +		if (ret) {
> +			drm_err(vop2->drm, "crtc init for video_port%d failed\n", i);
> +			return ret;
> +		}
> +
> +		drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs);
> +
> +		init_completion(&vp->dsp_hold_completion);
> +	}
> +
> +	for (i = 0; i < vop2->data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +
> +		if (vp->crtc.port)
> +			vp->nlayers = NR_LAYERS / nvps;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vop2_destroy_crtc(struct drm_crtc *crtc)
> +{
> +	of_node_put(crtc->port);
> +
> +	/*
> +	 * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane()
> +	 * references the CRTC.
> +	 */
> +	drm_crtc_cleanup(crtc);
> +}
> +
> +static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = {
> +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0),
> +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5),
> +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14),
> +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18),
> +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31),
> +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31),
> +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31),
> +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31),
> +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31),
> +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19),
> +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15),
> +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31),
> +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8),
> +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9),
> +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11),
> +
> +	/* Scale */
> +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15),
> +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31),
> +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15),
> +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13),
> +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3),
> +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28),
> +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29),
> +
> +	/* cluster regs */
> +	[VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1),
> +	[VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0),
> +	[VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7),
> +
> +	/* afbc regs */
> +	[VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6),
> +	[VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9),
> +	[VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10),
> +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4),
> +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7),
> +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8),
> +	[VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31),
> +	[VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31),
> +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15),
> +	[VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31),
> +	[VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0),
> +	[VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1),
> +	[VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2),
> +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3),
> +	[VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff },
> +	[VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
> +	[VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
> +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff },
> +	[VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff },
> +};
> +
> +static int vop2_cluster_init(struct vop2_win *win)
> +{
> +	struct vop2 *vop2 = win->vop2;
> +	struct reg_field *cluster_regs;
> +	int ret, i;
> +
> +	cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs),
> +			       GFP_KERNEL);
> +	if (!cluster_regs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++)
> +		if (cluster_regs[i].reg != 0xffffffff)
> +			cluster_regs[i].reg += win->offset;
> +
> +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> +					    cluster_regs,
> +					    ARRAY_SIZE(vop2_cluster_regs));
> +
> +	kfree(cluster_regs);
> +
> +	return ret;
> +};
> +
> +static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = {
> +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0),
> +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5),
> +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12),
> +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14),
> +	[VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16),
> +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31),
> +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31),
> +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28),
> +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31),
> +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31),
> +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17),
> +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15),
> +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31),
> +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0),
> +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1),
> +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3),
> +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31),
> +	[VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29),
> +	[VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31),
> +
> +	/* Scale */
> +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15),
> +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31),
> +	[VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15),
> +	[VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31),
> +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1),
> +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3),
> +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5),
> +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7),
> +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9),
> +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11),
> +	[VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13),
> +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15),
> +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17),
> +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8),
> +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9),
> +	[VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10),
> +	[VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11),
> +	[VOP2_WIN_XMIRROR] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff },
> +};
> +
> +static int vop2_esmart_init(struct vop2_win *win)
> +{
> +	struct vop2 *vop2 = win->vop2;
> +	struct reg_field *esmart_regs;
> +	int ret, i;
> +
> +	esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs),
> +			       GFP_KERNEL);
> +	if (!esmart_regs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++)
> +		if (esmart_regs[i].reg != 0xffffffff)
> +			esmart_regs[i].reg += win->offset;
> +
> +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> +					    esmart_regs,
> +					    ARRAY_SIZE(vop2_esmart_regs));
> +
> +	kfree(esmart_regs);
> +
> +	return ret;
> +};
> +
> +static int vop2_win_init(struct vop2 *vop2)
> +{
> +	const struct vop2_data *vop2_data = vop2->data;
> +	struct vop2_win *win;
> +	int i, ret;
> +
> +	for (i = 0; i < vop2_data->win_size; i++) {
> +		const struct vop2_win_data *win_data = &vop2_data->win[i];
> +
> +		win = &vop2->win[i];
> +		win->data = win_data;
> +		win->type = win_data->type;
> +		win->offset = win_data->base;
> +		win->win_id = i;
> +		win->vop2 = vop2;
> +		if (vop2_cluster_window(win))
> +			ret = vop2_cluster_init(win);
> +		else
> +			ret = vop2_esmart_init(win);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	vop2->registered_num_wins = vop2_data->win_size;
> +
> +	return 0;
> +}
> +
> +/*
> + * The window registers are only updated when config done is written.
> + * Until that they read back the old value. As we read-modify-write
> + * these registers mark them as non-volatile. This makes sure we read
> + * the new values from the regmap register cache.
> + */
> +static const struct regmap_range vop2_nonvolatile_range[] = {
> +	regmap_reg_range(0x1000, 0x23ff),
> +};
> +
> +static const struct regmap_access_table vop2_volatile_table = {
> +	.no_ranges = vop2_nonvolatile_range,
> +	.n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range),
> +};
> +
> +static const struct regmap_config vop2_regmap_config = {
> +	.reg_bits	= 32,
> +	.val_bits	= 32,
> +	.reg_stride	= 4,
> +	.max_register	= 0x3000,
> +	.name		= "vop2",
> +	.volatile_table	= &vop2_volatile_table,
> +	.cache_type	= REGCACHE_RBTREE,
> +};
> +
> +static int vop2_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	const struct vop2_data *vop2_data;
> +	struct drm_device *drm = data;
> +	struct vop2 *vop2;
> +	struct resource *res;
> +	size_t alloc_size;
> +	int ret;
> +
> +	vop2_data = of_device_get_match_data(dev);
> +	if (!vop2_data)
> +		return -ENODEV;
> +
> +	/* Allocate vop2 struct and its vop2_win array */
> +	alloc_size = sizeof(*vop2) + sizeof(*vop2->win) * vop2_data->win_size;
> +	vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
> +	if (!vop2)
> +		return -ENOMEM;
> +
> +	vop2->dev = dev;
> +	vop2->data = vop2_data;
> +	vop2->drm = drm;
> +
> +	dev_set_drvdata(dev, vop2);
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
> +	if (!res) {
> +		drm_err(vop2->drm, "failed to get vop2 register byname\n");
> +		return -EINVAL;
> +	}
> +
> +	vop2->regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(vop2->regs))
> +		return PTR_ERR(vop2->regs);
> +	vop2->len = resource_size(res);
> +
> +	vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config);
> +
> +	ret = vop2_win_init(vop2);
> +	if (ret)
> +		return ret;
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma_lut");
> +	if (res) {
> +		vop2->lut_regs = devm_ioremap_resource(dev, res);
> +		if (IS_ERR(vop2->lut_regs))
> +			return PTR_ERR(vop2->lut_regs);
> +	}
> +
> +	vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
> +
> +	vop2->hclk = devm_clk_get(vop2->dev, "hclk");
> +	if (IS_ERR(vop2->hclk)) {
> +		drm_err(vop2->drm, "failed to get hclk source\n");
> +		return PTR_ERR(vop2->hclk);
> +	}
> +
> +	vop2->aclk = devm_clk_get(vop2->dev, "aclk");
> +	if (IS_ERR(vop2->aclk)) {
> +		drm_err(vop2->drm, "failed to get aclk source\n");
> +		return PTR_ERR(vop2->aclk);
> +	}
> +
> +	vop2->irq = platform_get_irq(pdev, 0);
> +	if (vop2->irq < 0) {
> +		drm_err(vop2->drm, "cannot find irq for vop2\n");
> +		return vop2->irq;
> +	}
> +
> +	mutex_init(&vop2->vop2_lock);
> +
> +	ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
> +	if (ret)
> +		return ret;
> +
> +	ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev);
> +	if (ret) {
> +		drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = vop2_create_crtc(vop2);
> +	if (ret)
> +		return ret;
> +
> +	pm_runtime_enable(&pdev->dev);
> +
> +	return 0;
> +}
> +
> +static void vop2_unbind(struct device *dev, struct device *master, void *data)
> +{
> +	struct vop2 *vop2 = dev_get_drvdata(dev);
> +	struct drm_device *drm = vop2->drm;
> +	struct list_head *plane_list = &drm->mode_config.plane_list;
> +	struct list_head *crtc_list = &drm->mode_config.crtc_list;
> +	struct drm_crtc *crtc, *tmpc;
> +	struct drm_plane *plane, *tmpp;
> +
> +	rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
> +
> +	pm_runtime_disable(dev);
> +
> +	list_for_each_entry_safe(plane, tmpp, plane_list, head)
> +		drm_plane_cleanup(plane);
> +
> +	list_for_each_entry_safe(crtc, tmpc, crtc_list, head)
> +		vop2_destroy_crtc(crtc);
> +}
> +
> +const struct component_ops vop2_component_ops = {
> +	.bind = vop2_bind,
> +	.unbind = vop2_unbind,
> +};
> +EXPORT_SYMBOL_GPL(vop2_component_ops);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> new file mode 100644
> index 0000000000000..c727093a06d68
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> @@ -0,0 +1,477 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao@rock-chips.com>
> + */
> +
> +#ifndef _ROCKCHIP_DRM_VOP2_H
> +#define _ROCKCHIP_DRM_VOP2_H
> +
> +#include "rockchip_drm_vop.h"
> +
> +#include <linux/regmap.h>
> +#include <drm/drm_modes.h>
> +
> +#define VOP_FEATURE_OUTPUT_10BIT        BIT(0)
> +
> +#define WIN_FEATURE_AFBDC		BIT(0)
> +#define WIN_FEATURE_CLUSTER		BIT(1)
> +
> +/*
> + *  the delay number of a window in different mode.
> + */
> +enum win_dly_mode {
> +	VOP2_DLY_MODE_DEFAULT,   /**< default mode */
> +	VOP2_DLY_MODE_HISO_S,    /** HDR in SDR out mode, as a SDR window */
> +	VOP2_DLY_MODE_HIHO_H,    /** HDR in HDR out mode, as a HDR window */
> +	VOP2_DLY_MODE_MAX,
> +};
> +
> +struct vop_rect {
> +	int width;
> +	int height;
> +};
> +
> +enum vop2_scale_up_mode {
> +	VOP2_SCALE_UP_NRST_NBOR,
> +	VOP2_SCALE_UP_BIL,
> +	VOP2_SCALE_UP_BIC,
> +};
> +
> +enum vop2_scale_down_mode {
> +	VOP2_SCALE_DOWN_NRST_NBOR,
> +	VOP2_SCALE_DOWN_BIL,
> +	VOP2_SCALE_DOWN_AVG,
> +};
> +
> +enum vop2_win_regs {
> +	VOP2_WIN_ENABLE,
> +	VOP2_WIN_FORMAT,
> +	VOP2_WIN_CSC_MODE,
> +	VOP2_WIN_XMIRROR,
> +	VOP2_WIN_YMIRROR,
> +	VOP2_WIN_RB_SWAP,
> +	VOP2_WIN_UV_SWAP,
> +	VOP2_WIN_ACT_INFO,
> +	VOP2_WIN_DSP_INFO,
> +	VOP2_WIN_DSP_ST,
> +	VOP2_WIN_YRGB_MST,
> +	VOP2_WIN_UV_MST,
> +	VOP2_WIN_YRGB_VIR,
> +	VOP2_WIN_UV_VIR,
> +	VOP2_WIN_YUV_CLIP,
> +	VOP2_WIN_Y2R_EN,
> +	VOP2_WIN_R2Y_EN,
> +	VOP2_WIN_COLOR_KEY,
> +	VOP2_WIN_COLOR_KEY_EN,
> +	VOP2_WIN_DITHER_UP,
> +
> +	/* scale regs */
> +	VOP2_WIN_SCALE_YRGB_X,
> +	VOP2_WIN_SCALE_YRGB_Y,
> +	VOP2_WIN_SCALE_CBCR_X,
> +	VOP2_WIN_SCALE_CBCR_Y,
> +	VOP2_WIN_YRGB_HOR_SCL_MODE,
> +	VOP2_WIN_YRGB_HSCL_FILTER_MODE,
> +	VOP2_WIN_YRGB_VER_SCL_MODE,
> +	VOP2_WIN_YRGB_VSCL_FILTER_MODE,
> +	VOP2_WIN_CBCR_VER_SCL_MODE,
> +	VOP2_WIN_CBCR_HSCL_FILTER_MODE,
> +	VOP2_WIN_CBCR_HOR_SCL_MODE,
> +	VOP2_WIN_CBCR_VSCL_FILTER_MODE,
> +	VOP2_WIN_VSD_CBCR_GT2,
> +	VOP2_WIN_VSD_CBCR_GT4,
> +	VOP2_WIN_VSD_YRGB_GT2,
> +	VOP2_WIN_VSD_YRGB_GT4,
> +	VOP2_WIN_BIC_COE_SEL,
> +
> +	/* cluster regs */
> +	VOP2_WIN_CLUSTER_ENABLE,
> +	VOP2_WIN_AFBC_ENABLE,
> +	VOP2_WIN_CLUSTER_LB_MODE,
> +
> +	/* afbc regs */
> +	VOP2_WIN_AFBC_FORMAT,
> +	VOP2_WIN_AFBC_RB_SWAP,
> +	VOP2_WIN_AFBC_UV_SWAP,
> +	VOP2_WIN_AFBC_AUTO_GATING_EN,
> +	VOP2_WIN_AFBC_BLOCK_SPLIT_EN,
> +	VOP2_WIN_AFBC_PIC_VIR_WIDTH,
> +	VOP2_WIN_AFBC_TILE_NUM,
> +	VOP2_WIN_AFBC_PIC_OFFSET,
> +	VOP2_WIN_AFBC_PIC_SIZE,
> +	VOP2_WIN_AFBC_DSP_OFFSET,
> +	VOP2_WIN_AFBC_TRANSFORM_OFFSET,
> +	VOP2_WIN_AFBC_HDR_PTR,
> +	VOP2_WIN_AFBC_HALF_BLOCK_EN,
> +	VOP2_WIN_AFBC_ROTATE_270,
> +	VOP2_WIN_AFBC_ROTATE_90,
> +	VOP2_WIN_MAX_REG,
> +};
> +
> +struct vop2_win_data {
> +	const char *name;
> +	unsigned int phys_id;
> +
> +	u32 base;
> +	enum drm_plane_type type;
> +
> +	u32 nformats;
> +	const u32 *formats;
> +	const uint64_t *format_modifiers;
> +	const unsigned int supported_rotations;
> +
> +	/**
> +	 * @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2
> +	 */
> +	unsigned int layer_sel_id;
> +	uint64_t feature;
> +
> +	unsigned int max_upscale_factor;
> +	unsigned int max_downscale_factor;
> +	const u8 dly[VOP2_DLY_MODE_MAX];
> +};
> +
> +struct vop2_video_port_data {
> +	unsigned int id;
> +	u32 feature;
> +	u16 gamma_lut_len;
> +	u16 cubic_lut_len;
> +	struct vop_rect max_output;
> +	const u8 pre_scan_max_dly[4];
> +	const struct vop2_video_port_regs *regs;
> +	unsigned int offset;
> +};
> +
> +struct vop2_data {
> +	u8 nr_vps;
> +	const struct vop2_ctrl *ctrl;
> +	const struct vop2_win_data *win;
> +	const struct vop2_video_port_data *vp;
> +	const struct vop_csc_table *csc_table;
> +	struct vop_rect max_input;
> +	struct vop_rect max_output;
> +
> +	unsigned int win_size;
> +	unsigned int soc_id;
> +};
> +
> +/* interrupt define */
> +#define FS_NEW_INTR			BIT(4)
> +#define ADDR_SAME_INTR			BIT(5)
> +#define LINE_FLAG1_INTR			BIT(6)
> +#define WIN0_EMPTY_INTR			BIT(7)
> +#define WIN1_EMPTY_INTR			BIT(8)
> +#define WIN2_EMPTY_INTR			BIT(9)
> +#define WIN3_EMPTY_INTR			BIT(10)
> +#define HWC_EMPTY_INTR			BIT(11)
> +#define POST_BUF_EMPTY_INTR		BIT(12)
> +#define PWM_GEN_INTR			BIT(13)
> +#define DMA_FINISH_INTR			BIT(14)
> +#define FS_FIELD_INTR			BIT(15)
> +#define FE_INTR				BIT(16)
> +#define WB_UV_FIFO_FULL_INTR		BIT(17)
> +#define WB_YRGB_FIFO_FULL_INTR		BIT(18)
> +#define WB_COMPLETE_INTR		BIT(19)
> +
> +/*
> + * display output interface supported by rockchip lcdc
> + */
> +#define ROCKCHIP_OUT_MODE_P888		0
> +#define ROCKCHIP_OUT_MODE_BT1120	0
> +#define ROCKCHIP_OUT_MODE_P666		1
> +#define ROCKCHIP_OUT_MODE_P565		2
> +#define ROCKCHIP_OUT_MODE_BT656		5
> +#define ROCKCHIP_OUT_MODE_S888		8
> +#define ROCKCHIP_OUT_MODE_S888_DUMMY	12
> +#define ROCKCHIP_OUT_MODE_YUV420	14
> +/* for use special outface */
> +#define ROCKCHIP_OUT_MODE_AAAA		15
> +
> +enum vop_csc_format {
> +	CSC_BT601L,
> +	CSC_BT709L,
> +	CSC_BT601F,
> +	CSC_BT2020,
> +};
> +
> +enum src_factor_mode {
> +	SRC_FAC_ALPHA_ZERO,
> +	SRC_FAC_ALPHA_ONE,
> +	SRC_FAC_ALPHA_DST,
> +	SRC_FAC_ALPHA_DST_INVERSE,
> +	SRC_FAC_ALPHA_SRC,
> +	SRC_FAC_ALPHA_SRC_GLOBAL,
> +};
> +
> +enum dst_factor_mode {
> +	DST_FAC_ALPHA_ZERO,
> +	DST_FAC_ALPHA_ONE,
> +	DST_FAC_ALPHA_SRC,
> +	DST_FAC_ALPHA_SRC_INVERSE,
> +	DST_FAC_ALPHA_DST,
> +	DST_FAC_ALPHA_DST_GLOBAL,
> +};
> +
> +#define RK3568_GRF_VO_CON1			0x0364
> +/* System registers definition */
> +#define RK3568_REG_CFG_DONE			0x000
> +#define RK3568_VERSION_INFO			0x004
> +#define RK3568_SYS_AUTO_GATING_CTRL		0x008
> +#define RK3568_SYS_AXI_LUT_CTRL			0x024
> +#define RK3568_DSP_IF_EN			0x028
> +#define RK3568_DSP_IF_CTRL			0x02c
> +#define RK3568_DSP_IF_POL			0x030
> +#define RK3568_WB_CTRL				0x40
> +#define RK3568_WB_XSCAL_FACTOR			0x44
> +#define RK3568_WB_YRGB_MST			0x48
> +#define RK3568_WB_CBR_MST			0x4C
> +#define RK3568_OTP_WIN_EN			0x050
> +#define RK3568_LUT_PORT_SEL			0x058
> +#define RK3568_SYS_STATUS0			0x060
> +#define RK3568_VP_LINE_FLAG(vp)			(0x70 + (vp) * 0x4)
> +#define RK3568_SYS0_INT_EN			0x80
> +#define RK3568_SYS0_INT_CLR			0x84
> +#define RK3568_SYS0_INT_STATUS			0x88
> +#define RK3568_SYS1_INT_EN			0x90
> +#define RK3568_SYS1_INT_CLR			0x94
> +#define RK3568_SYS1_INT_STATUS			0x98
> +#define RK3568_VP_INT_EN(vp)			(0xA0 + (vp) * 0x10)
> +#define RK3568_VP_INT_CLR(vp)			(0xA4 + (vp) * 0x10)
> +#define RK3568_VP_INT_STATUS(vp)		(0xA8 + (vp) * 0x10)
> +#define RK3568_VP_INT_RAW_STATUS(vp)		(0xAC + (vp) * 0x10)
> +
> +/* Video Port registers definition */
> +#define RK3568_VP_DSP_CTRL			0x00
> +#define RK3568_VP_MIPI_CTRL			0x04
> +#define RK3568_VP_COLOR_BAR_CTRL		0x08
> +#define RK3568_VP_3D_LUT_CTRL			0x10
> +#define RK3568_VP_3D_LUT_MST			0x20
> +#define RK3568_VP_DSP_BG			0x2C
> +#define RK3568_VP_PRE_SCAN_HTIMING		0x30
> +#define RK3568_VP_POST_DSP_HACT_INFO		0x34
> +#define RK3568_VP_POST_DSP_VACT_INFO		0x38
> +#define RK3568_VP_POST_SCL_FACTOR_YRGB		0x3C
> +#define RK3568_VP_POST_SCL_CTRL			0x40
> +#define RK3568_VP_POST_DSP_VACT_INFO_F1		0x44
> +#define RK3568_VP_DSP_HTOTAL_HS_END		0x48
> +#define RK3568_VP_DSP_HACT_ST_END		0x4C
> +#define RK3568_VP_DSP_VTOTAL_VS_END		0x50
> +#define RK3568_VP_DSP_VACT_ST_END		0x54
> +#define RK3568_VP_DSP_VS_ST_END_F1		0x58
> +#define RK3568_VP_DSP_VACT_ST_END_F1		0x5C
> +#define RK3568_VP_BCSH_CTRL			0x60
> +#define RK3568_VP_BCSH_BCS			0x64
> +#define RK3568_VP_BCSH_H			0x68
> +#define RK3568_VP_BCSH_COLOR_BAR		0x6C
> +
> +/* Overlay registers definition    */
> +#define RK3568_OVL_CTRL				0x600
> +#define RK3568_OVL_LAYER_SEL			0x604
> +#define RK3568_OVL_PORT_SEL			0x608
> +#define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL	0x610
> +#define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL	0x614
> +#define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL	0x618
> +#define RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL	0x61C
> +#define RK3568_MIX0_SRC_COLOR_CTRL		0x650
> +#define RK3568_MIX0_DST_COLOR_CTRL		0x654
> +#define RK3568_MIX0_SRC_ALPHA_CTRL		0x658
> +#define RK3568_MIX0_DST_ALPHA_CTRL		0x65C
> +#define RK3568_HDR0_SRC_COLOR_CTRL		0x6C0
> +#define RK3568_HDR0_DST_COLOR_CTRL		0x6C4
> +#define RK3568_HDR0_SRC_ALPHA_CTRL		0x6C8
> +#define RK3568_HDR0_DST_ALPHA_CTRL		0x6CC
> +#define RK3568_VP_BG_MIX_CTRL(vp)		(0x6E0 + (vp) * 4)
> +#define RK3568_CLUSTER_DLY_NUM			0x6F0
> +#define RK3568_SMART_DLY_NUM			0x6F8
> +
> +/* Cluster register definition, offset relative to window base */
> +#define RK3568_CLUSTER_WIN_CTRL0		0x00
> +#define RK3568_CLUSTER_WIN_CTRL1		0x04
> +#define RK3568_CLUSTER_WIN_YRGB_MST		0x10
> +#define RK3568_CLUSTER_WIN_CBR_MST		0x14
> +#define RK3568_CLUSTER_WIN_VIR			0x18
> +#define RK3568_CLUSTER_WIN_ACT_INFO		0x20
> +#define RK3568_CLUSTER_WIN_DSP_INFO		0x24
> +#define RK3568_CLUSTER_WIN_DSP_ST		0x28
> +#define RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB	0x30
> +#define RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET	0x3C
> +#define RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL	0x50
> +#define RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE	0x54
> +#define RK3568_CLUSTER_WIN_AFBCD_HDR_PTR	0x58
> +#define RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH	0x5C
> +#define RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE	0x60
> +#define RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET	0x64
> +#define RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET	0x68
> +#define RK3568_CLUSTER_WIN_AFBCD_CTRL		0x6C
> +
> +#define RK3568_CLUSTER_CTRL			0x100
> +
> +/* (E)smart register definition, offset relative to window base */
> +#define RK3568_SMART_CTRL0			0x00
> +#define RK3568_SMART_CTRL1			0x04
> +#define RK3568_SMART_REGION0_CTRL		0x10
> +#define RK3568_SMART_REGION0_YRGB_MST		0x14
> +#define RK3568_SMART_REGION0_CBR_MST		0x18
> +#define RK3568_SMART_REGION0_VIR		0x1C
> +#define RK3568_SMART_REGION0_ACT_INFO		0x20
> +#define RK3568_SMART_REGION0_DSP_INFO		0x24
> +#define RK3568_SMART_REGION0_DSP_ST		0x28
> +#define RK3568_SMART_REGION0_SCL_CTRL		0x30
> +#define RK3568_SMART_REGION0_SCL_FACTOR_YRGB	0x34
> +#define RK3568_SMART_REGION0_SCL_FACTOR_CBR	0x38
> +#define RK3568_SMART_REGION0_SCL_OFFSET		0x3C
> +#define RK3568_SMART_REGION1_CTRL		0x40
> +#define RK3568_SMART_REGION1_YRGB_MST		0x44
> +#define RK3568_SMART_REGION1_CBR_MST		0x48
> +#define RK3568_SMART_REGION1_VIR		0x4C
> +#define RK3568_SMART_REGION1_ACT_INFO		0x50
> +#define RK3568_SMART_REGION1_DSP_INFO		0x54
> +#define RK3568_SMART_REGION1_DSP_ST		0x58
> +#define RK3568_SMART_REGION1_SCL_CTRL		0x60
> +#define RK3568_SMART_REGION1_SCL_FACTOR_YRGB	0x64
> +#define RK3568_SMART_REGION1_SCL_FACTOR_CBR	0x68
> +#define RK3568_SMART_REGION1_SCL_OFFSET		0x6C
> +#define RK3568_SMART_REGION2_CTRL		0x70
> +#define RK3568_SMART_REGION2_YRGB_MST		0x74
> +#define RK3568_SMART_REGION2_CBR_MST		0x78
> +#define RK3568_SMART_REGION2_VIR		0x7C
> +#define RK3568_SMART_REGION2_ACT_INFO		0x80
> +#define RK3568_SMART_REGION2_DSP_INFO		0x84
> +#define RK3568_SMART_REGION2_DSP_ST		0x88
> +#define RK3568_SMART_REGION2_SCL_CTRL		0x90
> +#define RK3568_SMART_REGION2_SCL_FACTOR_YRGB	0x94
> +#define RK3568_SMART_REGION2_SCL_FACTOR_CBR	0x98
> +#define RK3568_SMART_REGION2_SCL_OFFSET		0x9C
> +#define RK3568_SMART_REGION3_CTRL		0xA0
> +#define RK3568_SMART_REGION3_YRGB_MST		0xA4
> +#define RK3568_SMART_REGION3_CBR_MST		0xA8
> +#define RK3568_SMART_REGION3_VIR		0xAC
> +#define RK3568_SMART_REGION3_ACT_INFO		0xB0
> +#define RK3568_SMART_REGION3_DSP_INFO		0xB4
> +#define RK3568_SMART_REGION3_DSP_ST		0xB8
> +#define RK3568_SMART_REGION3_SCL_CTRL		0xC0
> +#define RK3568_SMART_REGION3_SCL_FACTOR_YRGB	0xC4
> +#define RK3568_SMART_REGION3_SCL_FACTOR_CBR	0xC8
> +#define RK3568_SMART_REGION3_SCL_OFFSET		0xCC
> +#define RK3568_SMART_COLOR_KEY_CTRL		0xD0
> +
> +/* HDR register definition */
> +#define RK3568_HDR_LUT_CTRL			0x2000
> +#define RK3568_HDR_LUT_MST			0x2004
> +#define RK3568_SDR2HDR_CTRL			0x2010
> +#define RK3568_HDR2SDR_CTRL			0x2020
> +#define RK3568_HDR2SDR_SRC_RANGE		0x2024
> +#define RK3568_HDR2SDR_NORMFACEETF		0x2028
> +#define RK3568_HDR2SDR_DST_RANGE		0x202C
> +#define RK3568_HDR2SDR_NORMFACCGAMMA		0x2030
> +#define RK3568_HDR_EETF_OETF_Y0			0x203C
> +#define RK3568_HDR_SAT_Y0			0x20C0
> +#define RK3568_HDR_EOTF_OETF_Y0			0x20F0
> +#define RK3568_HDR_OETF_DX_POW1			0x2200
> +#define RK3568_HDR_OETF_XN1			0x2300
> +
> +#define RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN		BIT(15)
> +
> +#define RK3568_VP_DSP_CTRL__STANDBY			BIT(31)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_MODE		BIT(20)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL		GENMASK(19, 18)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_EN		BIT(17)
> +#define RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN		BIT(16)
> +#define RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y		BIT(15)
> +#define RK3568_VP_DSP_CTRL__DSP_RB_SWAP			BIT(9)
> +#define RK3568_VP_DSP_CTRL__DSP_INTERLACE		BIT(7)
> +#define RK3568_VP_DSP_CTRL__DSP_FILED_POL		BIT(6)
> +#define RK3568_VP_DSP_CTRL__P2I_EN			BIT(5)
> +#define RK3568_VP_DSP_CTRL__CORE_DCLK_DIV		BIT(4)
> +#define RK3568_VP_DSP_CTRL__OUT_MODE			GENMASK(3, 0)
> +
> +#define RK3568_VP_POST_SCL_CTRL__VSCALEDOWN		BIT(1)
> +#define RK3568_VP_POST_SCL_CTRL__HSCALEDOWN		BIT(0)
> +
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX		GENMASK(26, 25)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS1			BIT(24)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX		GENMASK(22, 21)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI1			BIT(20)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX		GENMASK(19, 18)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX		GENMASK(17, 16)
> +#define RK3568_SYS_DSP_INFACE_EN_EDP_MUX		GENMASK(15, 14)
> +#define RK3568_SYS_DSP_INFACE_EN_HDMI_MUX		GENMASK(11, 10)
> +#define RK3568_SYS_DSP_INFACE_EN_RGB_MUX		GENMASK(9, 8)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS0			BIT(5)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI0			BIT(4)
> +#define RK3568_SYS_DSP_INFACE_EN_EDP			BIT(3)
> +#define RK3568_SYS_DSP_INFACE_EN_HDMI			BIT(1)
> +#define RK3568_SYS_DSP_INFACE_EN_RGB			BIT(0)
> +
> +#define RK3568_DSP_IF_POL__MIPI_PIN_POL			GENMASK(19, 16)
> +#define RK3568_DSP_IF_POL__EDP_PIN_POL			GENMASK(15, 12)
> +#define RK3568_DSP_IF_POL__HDMI_PIN_POL			GENMASK(7, 4)
> +#define RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL		GENMASK(3, 0)
> +
> +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2_PHASE_LOCK	BIT(5)
> +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2			BIT(4)
> +
> +#define RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN	BIT(31)
> +
> +#define RK3568_DSP_IF_POL__CFG_DONE_IMD			BIT(28)
> +
> +#define VOP2_SYS_AXI_BUS_NUM				2
> +
> +#define VOP2_CLUSTER_YUV444_10				0x12
> +
> +#define VOP2_COLOR_KEY_MASK				BIT(31)
> +
> +#define RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD		BIT(28)
> +
> +#define RK3568_VP_BG_MIX_CTRL__BG_DLY			GENMASK(31, 24)
> +
> +#define RK3568_OVL_PORT_SEL__SEL_PORT			GENMASK(31, 16)
> +#define RK3568_OVL_PORT_SEL__SMART1			GENMASK(31, 30)
> +#define RK3568_OVL_PORT_SEL__SMART0			GENMASK(29, 28)
> +#define RK3568_OVL_PORT_SEL__ESMART1			GENMASK(27, 26)
> +#define RK3568_OVL_PORT_SEL__ESMART0			GENMASK(25, 24)
> +#define RK3568_OVL_PORT_SEL__CLUSTER1			GENMASK(19, 18)
> +#define RK3568_OVL_PORT_SEL__CLUSTER0			GENMASK(17, 16)
> +#define RK3568_OVL_PORT_SET__PORT2_MUX			GENMASK(11, 8)
> +#define RK3568_OVL_PORT_SET__PORT1_MUX			GENMASK(7, 4)
> +#define RK3568_OVL_PORT_SET__PORT0_MUX			GENMASK(3, 0)
> +#define RK3568_OVL_LAYER_SEL__LAYER(layer, x)		((x) << ((layer) * 4))
> +
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_1		GENMASK(31, 24)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_0		GENMASK(23, 16)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_1		GENMASK(15, 8)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_0		GENMASK(7, 0)
> +
> +#define RK3568_SMART_DLY_NUM__SMART1			GENMASK(31, 24)
> +#define RK3568_SMART_DLY_NUM__SMART0			GENMASK(23, 16)
> +#define RK3568_SMART_DLY_NUM__ESMART1			GENMASK(15, 8)
> +#define RK3568_SMART_DLY_NUM__ESMART0			GENMASK(7, 0)
> +
> +#define VP_INT_DSP_HOLD_VALID	BIT(6)
> +#define VP_INT_FS_FIELD		BIT(5)
> +#define VP_INT_POST_BUF_EMPTY	BIT(4)
> +#define VP_INT_LINE_FLAG1	BIT(3)
> +#define VP_INT_LINE_FLAG0	BIT(2)
> +#define VOP2_INT_BUS_ERRPR	BIT(1)
> +#define VP_INT_FS		BIT(0)
> +
> +#define POLFLAG_DCLK_INV	BIT(3)
> +
> +enum vop2_layer_phy_id {
> +	ROCKCHIP_VOP2_CLUSTER0 = 0,
> +	ROCKCHIP_VOP2_CLUSTER1,
> +	ROCKCHIP_VOP2_ESMART0,
> +	ROCKCHIP_VOP2_ESMART1,
> +	ROCKCHIP_VOP2_SMART0,
> +	ROCKCHIP_VOP2_SMART1,
> +	ROCKCHIP_VOP2_CLUSTER2,
> +	ROCKCHIP_VOP2_CLUSTER3,
> +	ROCKCHIP_VOP2_ESMART2,
> +	ROCKCHIP_VOP2_ESMART3,
> +	ROCKCHIP_VOP2_PHY_ID_INVALID = -1,
> +};
> +
> +extern const struct component_ops vop2_component_ops;
> +
> +#endif /* _ROCKCHIP_DRM_VOP2_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> new file mode 100644
> index 0000000000000..9bf0637bf8e26
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> @@ -0,0 +1,281 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) Rockchip Electronics Co.Ltd
> + * Author: Andy Yan <andy.yan@rock-chips.com>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/component.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_plane.h>
> +#include <drm/drm_print.h>
> +
> +#include "rockchip_drm_vop2.h"
> +
> +static const uint32_t formats_win_full_10bit[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV24,
> +};
> +
> +static const uint32_t formats_win_full_10bit_yuyv[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV24,
> +	DRM_FORMAT_YVYU,
> +	DRM_FORMAT_VYUY,
> +};
> +
> +static const uint32_t formats_win_lite[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +};
> +
> +static const uint64_t format_modifiers[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	DRM_FORMAT_MOD_INVALID,
> +};
> +
> +static const uint64_t format_modifiers_afbc[] = {
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_CBR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_CBR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_CBR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_CBR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	/* SPLIT mandates SPARSE, RGB modes mandates YTR */
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_SPARSE |
> +				AFBC_FORMAT_MOD_SPLIT),
> +	DRM_FORMAT_MOD_INVALID,
> +};
> +
> +static const struct vop2_video_port_data rk3568_vop_video_ports[] = {
> +	{
> +		.id = 0,
> +		.feature = VOP_FEATURE_OUTPUT_10BIT,
> +		.gamma_lut_len = 1024,
> +		.cubic_lut_len = 9 * 9 * 9,
> +		.max_output = { 4096, 2304 },
> +		.pre_scan_max_dly = { 69, 53, 53, 42 },
> +		.offset = 0xc00,
> +	}, {
> +		.id = 1,
> +		.gamma_lut_len = 1024,
> +		.max_output = { 2048, 1536 },
> +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> +		.offset = 0xd00,
> +	}, {
> +		.id = 2,
> +		.gamma_lut_len = 1024,
> +		.max_output = { 1920, 1080 },
> +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> +		.offset = 0xe00,
> +	},
> +};
> +
> +/*
> + * rk3568 vop with 2 cluster, 2 esmart win, 2 smart win.
> + * Every cluster can work as 4K win or split into two win.
> + * All win in cluster support AFBCD.
> + *
> + * Every esmart win and smart win support 4 Multi-region.
> + *
> + * Scale filter mode:
> + *
> + * * Cluster:  bicubic for horizontal scale up, others use bilinear
> + * * ESmart:
> + *    * nearest-neighbor/bilinear/bicubic for scale up
> + *    * nearest-neighbor/bilinear/average for scale down
> + *
> + *
> + * @TODO describe the wind like cpu-map dt nodes;
> + */
> +static const struct vop2_win_data rk3568_vop_win_data[] = {
> +	{
> +		.name = "Smart0-win0",
> +		.phys_id = ROCKCHIP_VOP2_SMART0,
> +		.base = 0x1c00,
> +		.formats = formats_win_lite,
> +		.nformats = ARRAY_SIZE(formats_win_lite),
> +		.format_modifiers = format_modifiers,
> +		.layer_sel_id = 3,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Smart1-win0",
> +		.phys_id = ROCKCHIP_VOP2_SMART1,
> +		.formats = formats_win_lite,
> +		.nformats = ARRAY_SIZE(formats_win_lite),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1e00,
> +		.layer_sel_id = 7,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Esmart1-win0",
> +		.phys_id = ROCKCHIP_VOP2_ESMART1,
> +		.formats = formats_win_full_10bit_yuyv,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1a00,
> +		.layer_sel_id = 6,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Esmart0-win0",
> +		.phys_id = ROCKCHIP_VOP2_ESMART0,
> +		.formats = formats_win_full_10bit_yuyv,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1800,
> +		.layer_sel_id = 2,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Cluster0-win0",
> +		.phys_id = ROCKCHIP_VOP2_CLUSTER0,
> +		.base = 0x1000,
> +		.formats = formats_win_full_10bit,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> +		.format_modifiers = format_modifiers_afbc,
> +		.layer_sel_id = 0,
> +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> +		.max_upscale_factor = 4,
> +		.max_downscale_factor = 4,
> +		.dly = { 0, 27, 21 },
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> +	}, {
> +		.name = "Cluster1-win0",
> +		.phys_id = ROCKCHIP_VOP2_CLUSTER1,
> +		.base = 0x1200,
> +		.formats = formats_win_full_10bit,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> +		.format_modifiers = format_modifiers_afbc,
> +		.layer_sel_id = 1,
> +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.max_upscale_factor = 4,
> +		.max_downscale_factor = 4,
> +		.dly = { 0, 27, 21 },
> +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> +	},
> +};
> +
> +static const struct vop2_data rk3566_vop = {
> +	.nr_vps = 3,
> +	.max_input = { 4096, 2304 },
> +	.max_output = { 4096, 2304 },
> +	.vp = rk3568_vop_video_ports,
> +	.win = rk3568_vop_win_data,
> +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> +	.soc_id = 3566,
> +};
> +
> +static const struct vop2_data rk3568_vop = {
> +	.nr_vps = 3,
> +	.max_input = { 4096, 2304 },
> +	.max_output = { 4096, 2304 },
> +	.vp = rk3568_vop_video_ports,
> +	.win = rk3568_vop_win_data,
> +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> +	.soc_id = 3568,
> +};
> +
> +static const struct of_device_id vop2_dt_match[] = {
> +	{
> +		.compatible = "rockchip,rk3566-vop",
> +		.data = &rk3566_vop,
> +	}, {
> +		.compatible = "rockchip,rk3568-vop",
> +		.data = &rk3568_vop,
> +	}, {
> +	},
> +};
> +MODULE_DEVICE_TABLE(of, vop2_dt_match);
> +
> +static int vop2_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +
> +	return component_add(dev, &vop2_component_ops);
> +}
> +
> +static int vop2_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &vop2_component_ops);
> +
> +	return 0;
> +}
> +
> +struct platform_driver vop2_platform_driver = {
> +	.probe = vop2_probe,
> +	.remove = vop2_remove,
> +	.driver = {
> +		.name = "rockchip-vop2",
> +		.of_match_table = of_match_ptr(vop2_dt_match),
> +	},
> +};

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

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

* Re: [PATCH v6 09/23] drm/rockchip: dw_hdmi: Add support for hclk
  2022-02-17  8:29   ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17 12:35     ` Dmitry Osipenko
  -1 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 12:35 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

17.02.2022 11:29, Sascha Hauer пишет:
> The rk3568 HDMI has an additional clock that needs to be enabled for the
> HDMI controller to work. The purpose of that clock is not clear. It is
> named "hclk" in the downstream driver, so use the same name.

Have you checked that DSI works without the enabled hclk? I'd expect the
whole VOP to be clock-gated.

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

* Re: [PATCH v6 09/23] drm/rockchip: dw_hdmi: Add support for hclk
@ 2022-02-17 12:35     ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 12:35 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

17.02.2022 11:29, Sascha Hauer пишет:
> The rk3568 HDMI has an additional clock that needs to be enabled for the
> HDMI controller to work. The purpose of that clock is not clear. It is
> named "hclk" in the downstream driver, so use the same name.

Have you checked that DSI works without the enabled hclk? I'd expect the
whole VOP to be clock-gated.

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

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

* Re: [PATCH v6 09/23] drm/rockchip: dw_hdmi: Add support for hclk
@ 2022-02-17 12:35     ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 12:35 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, kernel, Sandy Huang,
	linux-rockchip, Michael Riesch, Peter Geis, Andy Yan,
	linux-arm-kernel

17.02.2022 11:29, Sascha Hauer пишет:
> The rk3568 HDMI has an additional clock that needs to be enabled for the
> HDMI controller to work. The purpose of that clock is not clear. It is
> named "hclk" in the downstream driver, so use the same name.

Have you checked that DSI works without the enabled hclk? I'd expect the
whole VOP to be clock-gated.

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

* Re: [PATCH v6 09/23] drm/rockchip: dw_hdmi: Add support for hclk
@ 2022-02-17 12:35     ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 12:35 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

17.02.2022 11:29, Sascha Hauer пишет:
> The rk3568 HDMI has an additional clock that needs to be enabled for the
> HDMI controller to work. The purpose of that clock is not clear. It is
> named "hclk" in the downstream driver, so use the same name.

Have you checked that DSI works without the enabled hclk? I'd expect the
whole VOP to be clock-gated.

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 07/23] drm/rockchip: dw_hdmi: add regulator support
  2022-02-17  8:29   ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17 13:18     ` Dmitry Osipenko
  -1 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 13:18 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel


17.02.2022 11:29, Sascha Hauer пишет:
> The RK3568 has HDMI_TX_AVDD0V9 and HDMI_TX_AVDD_1V8 supply inputs needed
> for the HDMI port. add support for these to the driver for boards which
> have them supplied by switchable regulators.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 41 +++++++++++++++++++--
>  1 file changed, 38 insertions(+), 3 deletions(-)

Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>

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

* Re: [PATCH v6 07/23] drm/rockchip: dw_hdmi: add regulator support
@ 2022-02-17 13:18     ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 13:18 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel


17.02.2022 11:29, Sascha Hauer пишет:
> The RK3568 has HDMI_TX_AVDD0V9 and HDMI_TX_AVDD_1V8 supply inputs needed
> for the HDMI port. add support for these to the driver for boards which
> have them supplied by switchable regulators.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 41 +++++++++++++++++++--
>  1 file changed, 38 insertions(+), 3 deletions(-)

Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>

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

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

* Re: [PATCH v6 07/23] drm/rockchip: dw_hdmi: add regulator support
@ 2022-02-17 13:18     ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 13:18 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel


17.02.2022 11:29, Sascha Hauer пишет:
> The RK3568 has HDMI_TX_AVDD0V9 and HDMI_TX_AVDD_1V8 supply inputs needed
> for the HDMI port. add support for these to the driver for boards which
> have them supplied by switchable regulators.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 41 +++++++++++++++++++--
>  1 file changed, 38 insertions(+), 3 deletions(-)

Reviewed-by: Dmitry Osipenko <dmitry.osipenko@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] 201+ messages in thread

* Re: [PATCH v6 07/23] drm/rockchip: dw_hdmi: add regulator support
@ 2022-02-17 13:18     ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 13:18 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, kernel, Sandy Huang,
	linux-rockchip, Michael Riesch, Peter Geis, Andy Yan,
	linux-arm-kernel


17.02.2022 11:29, Sascha Hauer пишет:
> The RK3568 has HDMI_TX_AVDD0V9 and HDMI_TX_AVDD_1V8 supply inputs needed
> for the HDMI port. add support for these to the driver for boards which
> have them supplied by switchable regulators.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 41 +++++++++++++++++++--
>  1 file changed, 38 insertions(+), 3 deletions(-)

Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>

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

* Re: [PATCH v6 02/23] drm/rockchip: dw_hdmi: rename vpll clock to reference clock
  2022-02-17  8:29   ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17 13:20     ` Dmitry Osipenko
  -1 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 13:20 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

17.02.2022 11:29, Sascha Hauer пишет:
> +	hdmi->ref_clk = devm_clk_get(hdmi->dev, "ref");
> +	if (PTR_ERR(hdmi->ref_clk) == -ENOENT)
> +		hdmi->ref_clk = devm_clk_get(hdmi->dev, "vpll");
> +
> +	if (PTR_ERR(hdmi->ref_clk) == -ENOENT) {
> +		hdmi->ref_clk = NULL;

I missed in v5 that devm_clk_get_optional() could be used here. But this
doesn't worth the v7 by itself.

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

* Re: [PATCH v6 02/23] drm/rockchip: dw_hdmi: rename vpll clock to reference clock
@ 2022-02-17 13:20     ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 13:20 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

17.02.2022 11:29, Sascha Hauer пишет:
> +	hdmi->ref_clk = devm_clk_get(hdmi->dev, "ref");
> +	if (PTR_ERR(hdmi->ref_clk) == -ENOENT)
> +		hdmi->ref_clk = devm_clk_get(hdmi->dev, "vpll");
> +
> +	if (PTR_ERR(hdmi->ref_clk) == -ENOENT) {
> +		hdmi->ref_clk = NULL;

I missed in v5 that devm_clk_get_optional() could be used here. But this
doesn't worth the v7 by itself.

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

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

* Re: [PATCH v6 02/23] drm/rockchip: dw_hdmi: rename vpll clock to reference clock
@ 2022-02-17 13:20     ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 13:20 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

17.02.2022 11:29, Sascha Hauer пишет:
> +	hdmi->ref_clk = devm_clk_get(hdmi->dev, "ref");
> +	if (PTR_ERR(hdmi->ref_clk) == -ENOENT)
> +		hdmi->ref_clk = devm_clk_get(hdmi->dev, "vpll");
> +
> +	if (PTR_ERR(hdmi->ref_clk) == -ENOENT) {
> +		hdmi->ref_clk = NULL;

I missed in v5 that devm_clk_get_optional() could be used here. But this
doesn't worth the v7 by itself.

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 02/23] drm/rockchip: dw_hdmi: rename vpll clock to reference clock
@ 2022-02-17 13:20     ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 13:20 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, kernel, Sandy Huang,
	linux-rockchip, Michael Riesch, Peter Geis, Andy Yan,
	linux-arm-kernel

17.02.2022 11:29, Sascha Hauer пишет:
> +	hdmi->ref_clk = devm_clk_get(hdmi->dev, "ref");
> +	if (PTR_ERR(hdmi->ref_clk) == -ENOENT)
> +		hdmi->ref_clk = devm_clk_get(hdmi->dev, "vpll");
> +
> +	if (PTR_ERR(hdmi->ref_clk) == -ENOENT) {
> +		hdmi->ref_clk = NULL;

I missed in v5 that devm_clk_get_optional() could be used here. But this
doesn't worth the v7 by itself.

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
  2022-02-17  8:29   ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17 13:24     ` Dmitry Osipenko
  -1 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 13:24 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

17.02.2022 11:29, Sascha Hauer пишет:
> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
>  	  This selects support for the VOP driver. You should enable it
>  	  on all older SoCs up to RK3399.
>  
> +config ROCKCHIP_VOP2
> +	bool "Rockchip VOP2 driver"
> +	help
> +	  This selects support for the VOP2 driver. You should enable it
> +	  on all newer SoCs beginning form RK3568.

s/form/from/

The ROCKCHIP_VOP option is "default y". Do you really want "default n"
for the VOP2?

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-17 13:24     ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 13:24 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

17.02.2022 11:29, Sascha Hauer пишет:
> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
>  	  This selects support for the VOP driver. You should enable it
>  	  on all older SoCs up to RK3399.
>  
> +config ROCKCHIP_VOP2
> +	bool "Rockchip VOP2 driver"
> +	help
> +	  This selects support for the VOP2 driver. You should enable it
> +	  on all newer SoCs beginning form RK3568.

s/form/from/

The ROCKCHIP_VOP option is "default y". Do you really want "default n"
for the VOP2?

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

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-17 13:24     ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 13:24 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

17.02.2022 11:29, Sascha Hauer пишет:
> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
>  	  This selects support for the VOP driver. You should enable it
>  	  on all older SoCs up to RK3399.
>  
> +config ROCKCHIP_VOP2
> +	bool "Rockchip VOP2 driver"
> +	help
> +	  This selects support for the VOP2 driver. You should enable it
> +	  on all newer SoCs beginning form RK3568.

s/form/from/

The ROCKCHIP_VOP option is "default y". Do you really want "default n"
for the VOP2?

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-17 13:24     ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 13:24 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, kernel, Sandy Huang,
	linux-rockchip, Michael Riesch, Peter Geis, Andy Yan,
	linux-arm-kernel

17.02.2022 11:29, Sascha Hauer пишет:
> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
>  	  This selects support for the VOP driver. You should enable it
>  	  on all older SoCs up to RK3399.
>  
> +config ROCKCHIP_VOP2
> +	bool "Rockchip VOP2 driver"
> +	help
> +	  This selects support for the VOP2 driver. You should enable it
> +	  on all newer SoCs beginning form RK3568.

s/form/from/

The ROCKCHIP_VOP option is "default y". Do you really want "default n"
for the VOP2?

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

* Re: [PATCH v6 02/23] drm/rockchip: dw_hdmi: rename vpll clock to reference clock
  2022-02-17 13:20     ` Dmitry Osipenko
  (?)
  (?)
@ 2022-02-17 13:37       ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17 13:37 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

On Thu, Feb 17, 2022 at 04:20:15PM +0300, Dmitry Osipenko wrote:
> 17.02.2022 11:29, Sascha Hauer пишет:
> > +	hdmi->ref_clk = devm_clk_get(hdmi->dev, "ref");
> > +	if (PTR_ERR(hdmi->ref_clk) == -ENOENT)
> > +		hdmi->ref_clk = devm_clk_get(hdmi->dev, "vpll");
> > +
> > +	if (PTR_ERR(hdmi->ref_clk) == -ENOENT) {
> > +		hdmi->ref_clk = NULL;
> 
> I missed in v5 that devm_clk_get_optional() could be used here. But this
> doesn't worth the v7 by itself.

I looked into it and came to the conclusion that I can't use devm_clk_get_optional
for the "ref" clk because then I couldn't do the if (PTR_ERR(hdmi->ref_clk) == -ENOENT)
part.

Looking at it again I could do:

	hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "ref");
	if (!hdmi->ref_clk))
		hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "vpll");

I'll change that should I have to resend.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 02/23] drm/rockchip: dw_hdmi: rename vpll clock to reference clock
@ 2022-02-17 13:37       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17 13:37 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

On Thu, Feb 17, 2022 at 04:20:15PM +0300, Dmitry Osipenko wrote:
> 17.02.2022 11:29, Sascha Hauer пишет:
> > +	hdmi->ref_clk = devm_clk_get(hdmi->dev, "ref");
> > +	if (PTR_ERR(hdmi->ref_clk) == -ENOENT)
> > +		hdmi->ref_clk = devm_clk_get(hdmi->dev, "vpll");
> > +
> > +	if (PTR_ERR(hdmi->ref_clk) == -ENOENT) {
> > +		hdmi->ref_clk = NULL;
> 
> I missed in v5 that devm_clk_get_optional() could be used here. But this
> doesn't worth the v7 by itself.

I looked into it and came to the conclusion that I can't use devm_clk_get_optional
for the "ref" clk because then I couldn't do the if (PTR_ERR(hdmi->ref_clk) == -ENOENT)
part.

Looking at it again I could do:

	hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "ref");
	if (!hdmi->ref_clk))
		hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "vpll");

I'll change that should I have to resend.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

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

* Re: [PATCH v6 02/23] drm/rockchip: dw_hdmi: rename vpll clock to reference clock
@ 2022-02-17 13:37       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17 13:37 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: devicetree, Benjamin Gaignard, kernel, Sandy Huang, dri-devel,
	linux-rockchip, Michael Riesch, Peter Geis, Andy Yan,
	linux-arm-kernel

On Thu, Feb 17, 2022 at 04:20:15PM +0300, Dmitry Osipenko wrote:
> 17.02.2022 11:29, Sascha Hauer пишет:
> > +	hdmi->ref_clk = devm_clk_get(hdmi->dev, "ref");
> > +	if (PTR_ERR(hdmi->ref_clk) == -ENOENT)
> > +		hdmi->ref_clk = devm_clk_get(hdmi->dev, "vpll");
> > +
> > +	if (PTR_ERR(hdmi->ref_clk) == -ENOENT) {
> > +		hdmi->ref_clk = NULL;
> 
> I missed in v5 that devm_clk_get_optional() could be used here. But this
> doesn't worth the v7 by itself.

I looked into it and came to the conclusion that I can't use devm_clk_get_optional
for the "ref" clk because then I couldn't do the if (PTR_ERR(hdmi->ref_clk) == -ENOENT)
part.

Looking at it again I could do:

	hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "ref");
	if (!hdmi->ref_clk))
		hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "vpll");

I'll change that should I have to resend.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 02/23] drm/rockchip: dw_hdmi: rename vpll clock to reference clock
@ 2022-02-17 13:37       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17 13:37 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

On Thu, Feb 17, 2022 at 04:20:15PM +0300, Dmitry Osipenko wrote:
> 17.02.2022 11:29, Sascha Hauer пишет:
> > +	hdmi->ref_clk = devm_clk_get(hdmi->dev, "ref");
> > +	if (PTR_ERR(hdmi->ref_clk) == -ENOENT)
> > +		hdmi->ref_clk = devm_clk_get(hdmi->dev, "vpll");
> > +
> > +	if (PTR_ERR(hdmi->ref_clk) == -ENOENT) {
> > +		hdmi->ref_clk = NULL;
> 
> I missed in v5 that devm_clk_get_optional() could be used here. But this
> doesn't worth the v7 by itself.

I looked into it and came to the conclusion that I can't use devm_clk_get_optional
for the "ref" clk because then I couldn't do the if (PTR_ERR(hdmi->ref_clk) == -ENOENT)
part.

Looking at it again I could do:

	hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "ref");
	if (!hdmi->ref_clk))
		hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "vpll");

I'll change that should I have to resend.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
  2022-02-17 12:00     ` Andy Yan
  (?)
  (?)
@ 2022-02-17 13:58       ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17 13:58 UTC (permalink / raw)
  To: Andy Yan
  Cc: dri-devel, linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis

Hi Andy,

Please trim the context in your answers to the relevant parts, it makes
it easier to find the things you said.

On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> Hi Sascha:
> 
> > +
> > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > +		struct device_node *node, *parent;
> > +
> > +		parent = of_get_parent(rkencoder->port);
> > +
> > +		for_each_endpoint_of_node(parent, node) {
> 
> Is there any hurt directly use our downstream vendor kernel method here: use
> vcstate->output_if set by encoder driver to get which interface we should
> enable here?

There is no vcstate->output_if in mainline currently. Ok, we could add
that. The other thing is that there are multiple HDMI interfaces and
the id of the HDMI encoder is encoded into output_if. Downstream kernel
adds OF aliases to the HDMI ports. I didn't want to go that route
because it doesn't seem to be very elegant to me.

> 
> You method is ok with device tree,  but it tied up this driver to device
> tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> driver can be much more flexible.

The current rockchip drm driver seems to be pretty much tied to device
tree. There are probably many other places that need parallel paths for
ACPI support, I think we can delay this particular part until we see the
whole picture. In the end we can still retrieve the output_if
information differently with ACPI while still retrieving the information
from the device tree the way we are doing currently.

Sascha


-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-17 13:58       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17 13:58 UTC (permalink / raw)
  To: Andy Yan
  Cc: devicetree, Benjamin Gaignard, Sandy Huang, dri-devel,
	linux-rockchip, Michael Riesch, kernel, Peter Geis,
	linux-arm-kernel

Hi Andy,

Please trim the context in your answers to the relevant parts, it makes
it easier to find the things you said.

On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> Hi Sascha:
> 
> > +
> > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > +		struct device_node *node, *parent;
> > +
> > +		parent = of_get_parent(rkencoder->port);
> > +
> > +		for_each_endpoint_of_node(parent, node) {
> 
> Is there any hurt directly use our downstream vendor kernel method here: use
> vcstate->output_if set by encoder driver to get which interface we should
> enable here?

There is no vcstate->output_if in mainline currently. Ok, we could add
that. The other thing is that there are multiple HDMI interfaces and
the id of the HDMI encoder is encoded into output_if. Downstream kernel
adds OF aliases to the HDMI ports. I didn't want to go that route
because it doesn't seem to be very elegant to me.

> 
> You method is ok with device tree,  but it tied up this driver to device
> tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> driver can be much more flexible.

The current rockchip drm driver seems to be pretty much tied to device
tree. There are probably many other places that need parallel paths for
ACPI support, I think we can delay this particular part until we see the
whole picture. In the end we can still retrieve the output_if
information differently with ACPI while still retrieving the information
from the device tree the way we are doing currently.

Sascha


-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-17 13:58       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17 13:58 UTC (permalink / raw)
  To: Andy Yan
  Cc: dri-devel, linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis

Hi Andy,

Please trim the context in your answers to the relevant parts, it makes
it easier to find the things you said.

On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> Hi Sascha:
> 
> > +
> > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > +		struct device_node *node, *parent;
> > +
> > +		parent = of_get_parent(rkencoder->port);
> > +
> > +		for_each_endpoint_of_node(parent, node) {
> 
> Is there any hurt directly use our downstream vendor kernel method here: use
> vcstate->output_if set by encoder driver to get which interface we should
> enable here?

There is no vcstate->output_if in mainline currently. Ok, we could add
that. The other thing is that there are multiple HDMI interfaces and
the id of the HDMI encoder is encoded into output_if. Downstream kernel
adds OF aliases to the HDMI ports. I didn't want to go that route
because it doesn't seem to be very elegant to me.

> 
> You method is ok with device tree,  but it tied up this driver to device
> tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> driver can be much more flexible.

The current rockchip drm driver seems to be pretty much tied to device
tree. There are probably many other places that need parallel paths for
ACPI support, I think we can delay this particular part until we see the
whole picture. In the end we can still retrieve the output_if
information differently with ACPI while still retrieving the information
from the device tree the way we are doing currently.

Sascha


-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-17 13:58       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17 13:58 UTC (permalink / raw)
  To: Andy Yan
  Cc: dri-devel, linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis

Hi Andy,

Please trim the context in your answers to the relevant parts, it makes
it easier to find the things you said.

On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> Hi Sascha:
> 
> > +
> > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > +		struct device_node *node, *parent;
> > +
> > +		parent = of_get_parent(rkencoder->port);
> > +
> > +		for_each_endpoint_of_node(parent, node) {
> 
> Is there any hurt directly use our downstream vendor kernel method here: use
> vcstate->output_if set by encoder driver to get which interface we should
> enable here?

There is no vcstate->output_if in mainline currently. Ok, we could add
that. The other thing is that there are multiple HDMI interfaces and
the id of the HDMI encoder is encoded into output_if. Downstream kernel
adds OF aliases to the HDMI ports. I didn't want to go that route
because it doesn't seem to be very elegant to me.

> 
> You method is ok with device tree,  but it tied up this driver to device
> tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> driver can be much more flexible.

The current rockchip drm driver seems to be pretty much tied to device
tree. There are probably many other places that need parallel paths for
ACPI support, I think we can delay this particular part until we see the
whole picture. In the end we can still retrieve the output_if
information differently with ACPI while still retrieving the information
from the device tree the way we are doing currently.

Sascha


-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 13/23] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
  2022-02-17  8:29   ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17 14:00     ` Dmitry Osipenko
  -1 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 14:00 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	Douglas Anderson, linux-rockchip, Michael Riesch, kernel,
	Yakir Yang, Andy Yan, linux-arm-kernel

17.02.2022 11:29, Sascha Hauer пишет:
> From: Douglas Anderson <dianders@chromium.org>
> 
> Jitter was improved by lowering the MPLL bandwidth to account for high
> frequency noise in the rk3288 PLL.  In each case MPLL bandwidth was
> lowered only enough to get us a comfortable margin.  We believe that
> lowering the bandwidth like this is safe given sufficient testing.

There are no device-trees that use "rockchip,rk3288-cru", AFAICS..

Was this change tested on a non-RK3288 devices?

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

* Re: [PATCH v6 13/23] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
@ 2022-02-17 14:00     ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 14:00 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	Douglas Anderson, linux-rockchip, Michael Riesch, kernel,
	Yakir Yang, Andy Yan, linux-arm-kernel

17.02.2022 11:29, Sascha Hauer пишет:
> From: Douglas Anderson <dianders@chromium.org>
> 
> Jitter was improved by lowering the MPLL bandwidth to account for high
> frequency noise in the rk3288 PLL.  In each case MPLL bandwidth was
> lowered only enough to get us a comfortable margin.  We believe that
> lowering the bandwidth like this is safe given sufficient testing.

There are no device-trees that use "rockchip,rk3288-cru", AFAICS..

Was this change tested on a non-RK3288 devices?

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

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

* Re: [PATCH v6 13/23] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
@ 2022-02-17 14:00     ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 14:00 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	Douglas Anderson, linux-rockchip, Michael Riesch, kernel,
	Yakir Yang, Andy Yan, linux-arm-kernel

17.02.2022 11:29, Sascha Hauer пишет:
> From: Douglas Anderson <dianders@chromium.org>
> 
> Jitter was improved by lowering the MPLL bandwidth to account for high
> frequency noise in the rk3288 PLL.  In each case MPLL bandwidth was
> lowered only enough to get us a comfortable margin.  We believe that
> lowering the bandwidth like this is safe given sufficient testing.

There are no device-trees that use "rockchip,rk3288-cru", AFAICS..

Was this change tested on a non-RK3288 devices?

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 13/23] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
@ 2022-02-17 14:00     ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 14:00 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, kernel, Sandy Huang,
	Douglas Anderson, linux-rockchip, Michael Riesch, Peter Geis,
	Yakir Yang, Andy Yan, linux-arm-kernel

17.02.2022 11:29, Sascha Hauer пишет:
> From: Douglas Anderson <dianders@chromium.org>
> 
> Jitter was improved by lowering the MPLL bandwidth to account for high
> frequency noise in the rk3288 PLL.  In each case MPLL bandwidth was
> lowered only enough to get us a comfortable margin.  We believe that
> lowering the bandwidth like this is safe given sufficient testing.

There are no device-trees that use "rockchip,rk3288-cru", AFAICS..

Was this change tested on a non-RK3288 devices?

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
  2022-02-17 13:58       ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17 14:06         ` Heiko Stübner
  -1 siblings, 0 replies; 201+ messages in thread
From: Heiko Stübner @ 2022-02-17 14:06 UTC (permalink / raw)
  To: Andy Yan, Sascha Hauer
  Cc: dri-devel, linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Benjamin Gaignard, Michael Riesch, Sandy Huang, Peter Geis

Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
> Hi Andy,
> 
> Please trim the context in your answers to the relevant parts, it makes
> it easier to find the things you said.
> 
> On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> > Hi Sascha:
> > 
> > > +
> > > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > > +		struct device_node *node, *parent;
> > > +
> > > +		parent = of_get_parent(rkencoder->port);
> > > +
> > > +		for_each_endpoint_of_node(parent, node) {
> > 
> > Is there any hurt directly use our downstream vendor kernel method here: use
> > vcstate->output_if set by encoder driver to get which interface we should
> > enable here?
> 
> There is no vcstate->output_if in mainline currently. Ok, we could add
> that. The other thing is that there are multiple HDMI interfaces and
> the id of the HDMI encoder is encoded into output_if. Downstream kernel
> adds OF aliases to the HDMI ports. I didn't want to go that route
> because it doesn't seem to be very elegant to me.
> 
> > 
> > You method is ok with device tree,  but it tied up this driver to device
> > tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> > driver can be much more flexible.
> 
> The current rockchip drm driver seems to be pretty much tied to device
> tree. There are probably many other places that need parallel paths for
> ACPI support, I think we can delay this particular part until we see the
> whole picture. In the end we can still retrieve the output_if
> information differently with ACPI while still retrieving the information
> from the device tree the way we are doing currently.

agreed :-) .

I.e. adding ACPI support for Rockchip drivers separately later on
makes things way easier.

Having a separate discussion about ACPI changes at that point
also makes the whole process easier, as adding the whole thing
here will delay everything even more.

Also if a later series really only is about adding ACPI support, this
makes for easier discussion but also easier review of changes.
The new VOP2 driver is big enough as it is.


Heiko



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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-17 14:06         ` Heiko Stübner
  0 siblings, 0 replies; 201+ messages in thread
From: Heiko Stübner @ 2022-02-17 14:06 UTC (permalink / raw)
  To: Andy Yan, Sascha Hauer
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	dri-devel, linux-rockchip, Michael Riesch, kernel,
	linux-arm-kernel

Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
> Hi Andy,
> 
> Please trim the context in your answers to the relevant parts, it makes
> it easier to find the things you said.
> 
> On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> > Hi Sascha:
> > 
> > > +
> > > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > > +		struct device_node *node, *parent;
> > > +
> > > +		parent = of_get_parent(rkencoder->port);
> > > +
> > > +		for_each_endpoint_of_node(parent, node) {
> > 
> > Is there any hurt directly use our downstream vendor kernel method here: use
> > vcstate->output_if set by encoder driver to get which interface we should
> > enable here?
> 
> There is no vcstate->output_if in mainline currently. Ok, we could add
> that. The other thing is that there are multiple HDMI interfaces and
> the id of the HDMI encoder is encoded into output_if. Downstream kernel
> adds OF aliases to the HDMI ports. I didn't want to go that route
> because it doesn't seem to be very elegant to me.
> 
> > 
> > You method is ok with device tree,  but it tied up this driver to device
> > tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> > driver can be much more flexible.
> 
> The current rockchip drm driver seems to be pretty much tied to device
> tree. There are probably many other places that need parallel paths for
> ACPI support, I think we can delay this particular part until we see the
> whole picture. In the end we can still retrieve the output_if
> information differently with ACPI while still retrieving the information
> from the device tree the way we are doing currently.

agreed :-) .

I.e. adding ACPI support for Rockchip drivers separately later on
makes things way easier.

Having a separate discussion about ACPI changes at that point
also makes the whole process easier, as adding the whole thing
here will delay everything even more.

Also if a later series really only is about adding ACPI support, this
makes for easier discussion but also easier review of changes.
The new VOP2 driver is big enough as it is.


Heiko



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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-17 14:06         ` Heiko Stübner
  0 siblings, 0 replies; 201+ messages in thread
From: Heiko Stübner @ 2022-02-17 14:06 UTC (permalink / raw)
  To: Andy Yan, Sascha Hauer
  Cc: dri-devel, linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Benjamin Gaignard, Michael Riesch, Sandy Huang, Peter Geis

Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
> Hi Andy,
> 
> Please trim the context in your answers to the relevant parts, it makes
> it easier to find the things you said.
> 
> On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> > Hi Sascha:
> > 
> > > +
> > > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > > +		struct device_node *node, *parent;
> > > +
> > > +		parent = of_get_parent(rkencoder->port);
> > > +
> > > +		for_each_endpoint_of_node(parent, node) {
> > 
> > Is there any hurt directly use our downstream vendor kernel method here: use
> > vcstate->output_if set by encoder driver to get which interface we should
> > enable here?
> 
> There is no vcstate->output_if in mainline currently. Ok, we could add
> that. The other thing is that there are multiple HDMI interfaces and
> the id of the HDMI encoder is encoded into output_if. Downstream kernel
> adds OF aliases to the HDMI ports. I didn't want to go that route
> because it doesn't seem to be very elegant to me.
> 
> > 
> > You method is ok with device tree,  but it tied up this driver to device
> > tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> > driver can be much more flexible.
> 
> The current rockchip drm driver seems to be pretty much tied to device
> tree. There are probably many other places that need parallel paths for
> ACPI support, I think we can delay this particular part until we see the
> whole picture. In the end we can still retrieve the output_if
> information differently with ACPI while still retrieving the information
> from the device tree the way we are doing currently.

agreed :-) .

I.e. adding ACPI support for Rockchip drivers separately later on
makes things way easier.

Having a separate discussion about ACPI changes at that point
also makes the whole process easier, as adding the whole thing
here will delay everything even more.

Also if a later series really only is about adding ACPI support, this
makes for easier discussion but also easier review of changes.
The new VOP2 driver is big enough as it is.


Heiko



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

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-17 14:06         ` Heiko Stübner
  0 siblings, 0 replies; 201+ messages in thread
From: Heiko Stübner @ 2022-02-17 14:06 UTC (permalink / raw)
  To: Andy Yan, Sascha Hauer
  Cc: dri-devel, linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Benjamin Gaignard, Michael Riesch, Sandy Huang, Peter Geis

Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
> Hi Andy,
> 
> Please trim the context in your answers to the relevant parts, it makes
> it easier to find the things you said.
> 
> On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> > Hi Sascha:
> > 
> > > +
> > > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > > +		struct device_node *node, *parent;
> > > +
> > > +		parent = of_get_parent(rkencoder->port);
> > > +
> > > +		for_each_endpoint_of_node(parent, node) {
> > 
> > Is there any hurt directly use our downstream vendor kernel method here: use
> > vcstate->output_if set by encoder driver to get which interface we should
> > enable here?
> 
> There is no vcstate->output_if in mainline currently. Ok, we could add
> that. The other thing is that there are multiple HDMI interfaces and
> the id of the HDMI encoder is encoded into output_if. Downstream kernel
> adds OF aliases to the HDMI ports. I didn't want to go that route
> because it doesn't seem to be very elegant to me.
> 
> > 
> > You method is ok with device tree,  but it tied up this driver to device
> > tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> > driver can be much more flexible.
> 
> The current rockchip drm driver seems to be pretty much tied to device
> tree. There are probably many other places that need parallel paths for
> ACPI support, I think we can delay this particular part until we see the
> whole picture. In the end we can still retrieve the output_if
> information differently with ACPI while still retrieving the information
> from the device tree the way we are doing currently.

agreed :-) .

I.e. adding ACPI support for Rockchip drivers separately later on
makes things way easier.

Having a separate discussion about ACPI changes at that point
also makes the whole process easier, as adding the whole thing
here will delay everything even more.

Also if a later series really only is about adding ACPI support, this
makes for easier discussion but also easier review of changes.
The new VOP2 driver is big enough as it is.


Heiko



_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 09/23] drm/rockchip: dw_hdmi: Add support for hclk
  2022-02-17 12:35     ` Dmitry Osipenko
  (?)
  (?)
@ 2022-02-17 15:00       ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17 15:00 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

On Thu, Feb 17, 2022 at 03:35:08PM +0300, Dmitry Osipenko wrote:
> 17.02.2022 11:29, Sascha Hauer пишет:
> > The rk3568 HDMI has an additional clock that needs to be enabled for the
> > HDMI controller to work. The purpose of that clock is not clear. It is
> > named "hclk" in the downstream driver, so use the same name.
> 
> Have you checked that DSI works without the enabled hclk? I'd expect the
> whole VOP to be clock-gated.

No, I haven't checked that.

I am not sure where you aiming at. The HCLK_VOP is supplied to the vop2
as well and the vop2 driver also enables this clock. Still, when the
HDMI registers are accessed before the vop2 driver enables HCLK_VOP then
the system hangs, so the HDMI needs it also.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 09/23] drm/rockchip: dw_hdmi: Add support for hclk
@ 2022-02-17 15:00       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17 15:00 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: devicetree, Benjamin Gaignard, kernel, Sandy Huang, dri-devel,
	linux-rockchip, Michael Riesch, Peter Geis, Andy Yan,
	linux-arm-kernel

On Thu, Feb 17, 2022 at 03:35:08PM +0300, Dmitry Osipenko wrote:
> 17.02.2022 11:29, Sascha Hauer пишет:
> > The rk3568 HDMI has an additional clock that needs to be enabled for the
> > HDMI controller to work. The purpose of that clock is not clear. It is
> > named "hclk" in the downstream driver, so use the same name.
> 
> Have you checked that DSI works without the enabled hclk? I'd expect the
> whole VOP to be clock-gated.

No, I haven't checked that.

I am not sure where you aiming at. The HCLK_VOP is supplied to the vop2
as well and the vop2 driver also enables this clock. Still, when the
HDMI registers are accessed before the vop2 driver enables HCLK_VOP then
the system hangs, so the HDMI needs it also.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 09/23] drm/rockchip: dw_hdmi: Add support for hclk
@ 2022-02-17 15:00       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17 15:00 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

On Thu, Feb 17, 2022 at 03:35:08PM +0300, Dmitry Osipenko wrote:
> 17.02.2022 11:29, Sascha Hauer пишет:
> > The rk3568 HDMI has an additional clock that needs to be enabled for the
> > HDMI controller to work. The purpose of that clock is not clear. It is
> > named "hclk" in the downstream driver, so use the same name.
> 
> Have you checked that DSI works without the enabled hclk? I'd expect the
> whole VOP to be clock-gated.

No, I haven't checked that.

I am not sure where you aiming at. The HCLK_VOP is supplied to the vop2
as well and the vop2 driver also enables this clock. Still, when the
HDMI registers are accessed before the vop2 driver enables HCLK_VOP then
the system hangs, so the HDMI needs it also.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

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

* Re: [PATCH v6 09/23] drm/rockchip: dw_hdmi: Add support for hclk
@ 2022-02-17 15:00       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17 15:00 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

On Thu, Feb 17, 2022 at 03:35:08PM +0300, Dmitry Osipenko wrote:
> 17.02.2022 11:29, Sascha Hauer пишет:
> > The rk3568 HDMI has an additional clock that needs to be enabled for the
> > HDMI controller to work. The purpose of that clock is not clear. It is
> > named "hclk" in the downstream driver, so use the same name.
> 
> Have you checked that DSI works without the enabled hclk? I'd expect the
> whole VOP to be clock-gated.

No, I haven't checked that.

I am not sure where you aiming at. The HCLK_VOP is supplied to the vop2
as well and the vop2 driver also enables this clock. Still, when the
HDMI registers are accessed before the vop2 driver enables HCLK_VOP then
the system hangs, so the HDMI needs it also.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 13/23] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
  2022-02-17 14:00     ` Dmitry Osipenko
  (?)
  (?)
@ 2022-02-17 15:12       ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17 15:12 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, Douglas Anderson, linux-rockchip, Michael Riesch,
	kernel, Yakir Yang, Andy Yan, linux-arm-kernel

On Thu, Feb 17, 2022 at 05:00:37PM +0300, Dmitry Osipenko wrote:
> 17.02.2022 11:29, Sascha Hauer пишет:
> > From: Douglas Anderson <dianders@chromium.org>
> > 
> > Jitter was improved by lowering the MPLL bandwidth to account for high
> > frequency noise in the rk3288 PLL.  In each case MPLL bandwidth was
> > lowered only enough to get us a comfortable margin.  We believe that
> > lowering the bandwidth like this is safe given sufficient testing.
> 
> There are no device-trees that use "rockchip,rk3288-cru", AFAICS..

What do you mean? In my tree I have:

arch/arm/boot/dts/rk3288.dtsi:863:              compatible = "rockchip,rk3288-cru";
drivers/clk/rockchip/clk-rk3288.c:985:CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init);

> 
> Was this change tested on a non-RK3288 devices?

Yes, on a rk3568 ;)

The patch has been posted back in 2015 and was added to the Rockchip
downstream kernel in 2016. I don't know how thoroughly Rockchip tests
their kernels, but I assume the patch wouldn't be there if it caused
any problems.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 13/23] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
@ 2022-02-17 15:12       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17 15:12 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, Douglas Anderson, linux-rockchip, Michael Riesch,
	kernel, Yakir Yang, Andy Yan, linux-arm-kernel

On Thu, Feb 17, 2022 at 05:00:37PM +0300, Dmitry Osipenko wrote:
> 17.02.2022 11:29, Sascha Hauer пишет:
> > From: Douglas Anderson <dianders@chromium.org>
> > 
> > Jitter was improved by lowering the MPLL bandwidth to account for high
> > frequency noise in the rk3288 PLL.  In each case MPLL bandwidth was
> > lowered only enough to get us a comfortable margin.  We believe that
> > lowering the bandwidth like this is safe given sufficient testing.
> 
> There are no device-trees that use "rockchip,rk3288-cru", AFAICS..

What do you mean? In my tree I have:

arch/arm/boot/dts/rk3288.dtsi:863:              compatible = "rockchip,rk3288-cru";
drivers/clk/rockchip/clk-rk3288.c:985:CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init);

> 
> Was this change tested on a non-RK3288 devices?

Yes, on a rk3568 ;)

The patch has been posted back in 2015 and was added to the Rockchip
downstream kernel in 2016. I don't know how thoroughly Rockchip tests
their kernels, but I assume the patch wouldn't be there if it caused
any problems.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

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

* Re: [PATCH v6 13/23] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
@ 2022-02-17 15:12       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17 15:12 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: devicetree, Benjamin Gaignard, kernel, Douglas Anderson,
	dri-devel, Sandy Huang, linux-rockchip, Michael Riesch,
	Peter Geis, Yakir Yang, Andy Yan, linux-arm-kernel

On Thu, Feb 17, 2022 at 05:00:37PM +0300, Dmitry Osipenko wrote:
> 17.02.2022 11:29, Sascha Hauer пишет:
> > From: Douglas Anderson <dianders@chromium.org>
> > 
> > Jitter was improved by lowering the MPLL bandwidth to account for high
> > frequency noise in the rk3288 PLL.  In each case MPLL bandwidth was
> > lowered only enough to get us a comfortable margin.  We believe that
> > lowering the bandwidth like this is safe given sufficient testing.
> 
> There are no device-trees that use "rockchip,rk3288-cru", AFAICS..

What do you mean? In my tree I have:

arch/arm/boot/dts/rk3288.dtsi:863:              compatible = "rockchip,rk3288-cru";
drivers/clk/rockchip/clk-rk3288.c:985:CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init);

> 
> Was this change tested on a non-RK3288 devices?

Yes, on a rk3568 ;)

The patch has been posted back in 2015 and was added to the Rockchip
downstream kernel in 2016. I don't know how thoroughly Rockchip tests
their kernels, but I assume the patch wouldn't be there if it caused
any problems.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 13/23] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
@ 2022-02-17 15:12       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-17 15:12 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, Douglas Anderson, linux-rockchip, Michael Riesch,
	kernel, Yakir Yang, Andy Yan, linux-arm-kernel

On Thu, Feb 17, 2022 at 05:00:37PM +0300, Dmitry Osipenko wrote:
> 17.02.2022 11:29, Sascha Hauer пишет:
> > From: Douglas Anderson <dianders@chromium.org>
> > 
> > Jitter was improved by lowering the MPLL bandwidth to account for high
> > frequency noise in the rk3288 PLL.  In each case MPLL bandwidth was
> > lowered only enough to get us a comfortable margin.  We believe that
> > lowering the bandwidth like this is safe given sufficient testing.
> 
> There are no device-trees that use "rockchip,rk3288-cru", AFAICS..

What do you mean? In my tree I have:

arch/arm/boot/dts/rk3288.dtsi:863:              compatible = "rockchip,rk3288-cru";
drivers/clk/rockchip/clk-rk3288.c:985:CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init);

> 
> Was this change tested on a non-RK3288 devices?

Yes, on a rk3568 ;)

The patch has been posted back in 2015 and was added to the Rockchip
downstream kernel in 2016. I don't know how thoroughly Rockchip tests
their kernels, but I assume the patch wouldn't be there if it caused
any problems.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 09/23] drm/rockchip: dw_hdmi: Add support for hclk
  2022-02-17 15:00       ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17 16:59         ` Dmitry Osipenko
  -1 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 16:59 UTC (permalink / raw)
  To: Sascha Hauer, Dmitry Osipenko
  Cc: devicetree, Benjamin Gaignard, kernel, Sandy Huang, dri-devel,
	linux-rockchip, Michael Riesch, Peter Geis, Andy Yan,
	linux-arm-kernel

17.02.2022 18:00, Sascha Hauer пишет:
> On Thu, Feb 17, 2022 at 03:35:08PM +0300, Dmitry Osipenko wrote:
>> 17.02.2022 11:29, Sascha Hauer пишет:
>>> The rk3568 HDMI has an additional clock that needs to be enabled for the
>>> HDMI controller to work. The purpose of that clock is not clear. It is
>>> named "hclk" in the downstream driver, so use the same name.
>>
>> Have you checked that DSI works without the enabled hclk? I'd expect the
>> whole VOP to be clock-gated.
> 
> No, I haven't checked that.
> 
> I am not sure where you aiming at. The HCLK_VOP is supplied to the vop2
> as well and the vop2 driver also enables this clock. Still, when the
> HDMI registers are accessed before the vop2 driver enables HCLK_VOP then
> the system hangs, so the HDMI needs it also.

HDMI, MIPI and etc are a part of VOP. I'm curious whether MIPI should
also hang, at least datasheet suggests that it should since hclk ungates
the AHB part of the VOP's h/w module, which is used for registers access.

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

* Re: [PATCH v6 09/23] drm/rockchip: dw_hdmi: Add support for hclk
@ 2022-02-17 16:59         ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 16:59 UTC (permalink / raw)
  To: Sascha Hauer, Dmitry Osipenko
  Cc: devicetree, Benjamin Gaignard, kernel, Sandy Huang, dri-devel,
	linux-rockchip, Michael Riesch, Peter Geis, Andy Yan,
	linux-arm-kernel

17.02.2022 18:00, Sascha Hauer пишет:
> On Thu, Feb 17, 2022 at 03:35:08PM +0300, Dmitry Osipenko wrote:
>> 17.02.2022 11:29, Sascha Hauer пишет:
>>> The rk3568 HDMI has an additional clock that needs to be enabled for the
>>> HDMI controller to work. The purpose of that clock is not clear. It is
>>> named "hclk" in the downstream driver, so use the same name.
>>
>> Have you checked that DSI works without the enabled hclk? I'd expect the
>> whole VOP to be clock-gated.
> 
> No, I haven't checked that.
> 
> I am not sure where you aiming at. The HCLK_VOP is supplied to the vop2
> as well and the vop2 driver also enables this clock. Still, when the
> HDMI registers are accessed before the vop2 driver enables HCLK_VOP then
> the system hangs, so the HDMI needs it also.

HDMI, MIPI and etc are a part of VOP. I'm curious whether MIPI should
also hang, at least datasheet suggests that it should since hclk ungates
the AHB part of the VOP's h/w module, which is used for registers access.

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

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

* Re: [PATCH v6 09/23] drm/rockchip: dw_hdmi: Add support for hclk
@ 2022-02-17 16:59         ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 16:59 UTC (permalink / raw)
  To: Sascha Hauer, Dmitry Osipenko
  Cc: devicetree, Benjamin Gaignard, kernel, Sandy Huang, dri-devel,
	linux-rockchip, Michael Riesch, Peter Geis, Andy Yan,
	linux-arm-kernel

17.02.2022 18:00, Sascha Hauer пишет:
> On Thu, Feb 17, 2022 at 03:35:08PM +0300, Dmitry Osipenko wrote:
>> 17.02.2022 11:29, Sascha Hauer пишет:
>>> The rk3568 HDMI has an additional clock that needs to be enabled for the
>>> HDMI controller to work. The purpose of that clock is not clear. It is
>>> named "hclk" in the downstream driver, so use the same name.
>>
>> Have you checked that DSI works without the enabled hclk? I'd expect the
>> whole VOP to be clock-gated.
> 
> No, I haven't checked that.
> 
> I am not sure where you aiming at. The HCLK_VOP is supplied to the vop2
> as well and the vop2 driver also enables this clock. Still, when the
> HDMI registers are accessed before the vop2 driver enables HCLK_VOP then
> the system hangs, so the HDMI needs it also.

HDMI, MIPI and etc are a part of VOP. I'm curious whether MIPI should
also hang, at least datasheet suggests that it should since hclk ungates
the AHB part of the VOP's h/w module, which is used for registers access.

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 09/23] drm/rockchip: dw_hdmi: Add support for hclk
@ 2022-02-17 16:59         ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 16:59 UTC (permalink / raw)
  To: Sascha Hauer, Dmitry Osipenko
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	dri-devel, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

17.02.2022 18:00, Sascha Hauer пишет:
> On Thu, Feb 17, 2022 at 03:35:08PM +0300, Dmitry Osipenko wrote:
>> 17.02.2022 11:29, Sascha Hauer пишет:
>>> The rk3568 HDMI has an additional clock that needs to be enabled for the
>>> HDMI controller to work. The purpose of that clock is not clear. It is
>>> named "hclk" in the downstream driver, so use the same name.
>>
>> Have you checked that DSI works without the enabled hclk? I'd expect the
>> whole VOP to be clock-gated.
> 
> No, I haven't checked that.
> 
> I am not sure where you aiming at. The HCLK_VOP is supplied to the vop2
> as well and the vop2 driver also enables this clock. Still, when the
> HDMI registers are accessed before the vop2 driver enables HCLK_VOP then
> the system hangs, so the HDMI needs it also.

HDMI, MIPI and etc are a part of VOP. I'm curious whether MIPI should
also hang, at least datasheet suggests that it should since hclk ungates
the AHB part of the VOP's h/w module, which is used for registers access.

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

* Re: [PATCH v6 13/23] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
  2022-02-17 15:12       ` Sascha Hauer
  (?)
  (?)
@ 2022-02-17 17:00         ` Dmitry Osipenko
  -1 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 17:00 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, Douglas Anderson, linux-rockchip, Michael Riesch,
	kernel, Yakir Yang, Andy Yan, linux-arm-kernel



17.02.2022 18:12, Sascha Hauer пишет:
> On Thu, Feb 17, 2022 at 05:00:37PM +0300, Dmitry Osipenko wrote:
>> 17.02.2022 11:29, Sascha Hauer пишет:
>>> From: Douglas Anderson <dianders@chromium.org>
>>>
>>> Jitter was improved by lowering the MPLL bandwidth to account for high
>>> frequency noise in the rk3288 PLL.  In each case MPLL bandwidth was
>>> lowered only enough to get us a comfortable margin.  We believe that
>>> lowering the bandwidth like this is safe given sufficient testing.
>>
>> There are no device-trees that use "rockchip,rk3288-cru", AFAICS..
> 
> What do you mean? In my tree I have:
> 
> arch/arm/boot/dts/rk3288.dtsi:863:              compatible = "rockchip,rk3288-cru";
> drivers/clk/rockchip/clk-rk3288.c:985:CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init);

Oh, it is ARM32, I was looking at ARM64 only. My bad.

>> Was this change tested on a non-RK3288 devices?
> 
> Yes, on a rk3568 ;)
> 
> The patch has been posted back in 2015 and was added to the Rockchip
> downstream kernel in 2016. I don't know how thoroughly Rockchip tests
> their kernels, but I assume the patch wouldn't be there if it caused
> any problems.

Thank you for the clarification.

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

* Re: [PATCH v6 13/23] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
@ 2022-02-17 17:00         ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 17:00 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, Douglas Anderson, linux-rockchip, Michael Riesch,
	kernel, Yakir Yang, Andy Yan, linux-arm-kernel



17.02.2022 18:12, Sascha Hauer пишет:
> On Thu, Feb 17, 2022 at 05:00:37PM +0300, Dmitry Osipenko wrote:
>> 17.02.2022 11:29, Sascha Hauer пишет:
>>> From: Douglas Anderson <dianders@chromium.org>
>>>
>>> Jitter was improved by lowering the MPLL bandwidth to account for high
>>> frequency noise in the rk3288 PLL.  In each case MPLL bandwidth was
>>> lowered only enough to get us a comfortable margin.  We believe that
>>> lowering the bandwidth like this is safe given sufficient testing.
>>
>> There are no device-trees that use "rockchip,rk3288-cru", AFAICS..
> 
> What do you mean? In my tree I have:
> 
> arch/arm/boot/dts/rk3288.dtsi:863:              compatible = "rockchip,rk3288-cru";
> drivers/clk/rockchip/clk-rk3288.c:985:CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init);

Oh, it is ARM32, I was looking at ARM64 only. My bad.

>> Was this change tested on a non-RK3288 devices?
> 
> Yes, on a rk3568 ;)
> 
> The patch has been posted back in 2015 and was added to the Rockchip
> downstream kernel in 2016. I don't know how thoroughly Rockchip tests
> their kernels, but I assume the patch wouldn't be there if it caused
> any problems.

Thank you for the clarification.

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

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

* Re: [PATCH v6 13/23] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
@ 2022-02-17 17:00         ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 17:00 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, Douglas Anderson, linux-rockchip, Michael Riesch,
	kernel, Yakir Yang, Andy Yan, linux-arm-kernel



17.02.2022 18:12, Sascha Hauer пишет:
> On Thu, Feb 17, 2022 at 05:00:37PM +0300, Dmitry Osipenko wrote:
>> 17.02.2022 11:29, Sascha Hauer пишет:
>>> From: Douglas Anderson <dianders@chromium.org>
>>>
>>> Jitter was improved by lowering the MPLL bandwidth to account for high
>>> frequency noise in the rk3288 PLL.  In each case MPLL bandwidth was
>>> lowered only enough to get us a comfortable margin.  We believe that
>>> lowering the bandwidth like this is safe given sufficient testing.
>>
>> There are no device-trees that use "rockchip,rk3288-cru", AFAICS..
> 
> What do you mean? In my tree I have:
> 
> arch/arm/boot/dts/rk3288.dtsi:863:              compatible = "rockchip,rk3288-cru";
> drivers/clk/rockchip/clk-rk3288.c:985:CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init);

Oh, it is ARM32, I was looking at ARM64 only. My bad.

>> Was this change tested on a non-RK3288 devices?
> 
> Yes, on a rk3568 ;)
> 
> The patch has been posted back in 2015 and was added to the Rockchip
> downstream kernel in 2016. I don't know how thoroughly Rockchip tests
> their kernels, but I assume the patch wouldn't be there if it caused
> any problems.

Thank you for the clarification.

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 13/23] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
@ 2022-02-17 17:00         ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-17 17:00 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: devicetree, Benjamin Gaignard, kernel, Douglas Anderson,
	dri-devel, Sandy Huang, linux-rockchip, Michael Riesch,
	Peter Geis, Yakir Yang, Andy Yan, linux-arm-kernel



17.02.2022 18:12, Sascha Hauer пишет:
> On Thu, Feb 17, 2022 at 05:00:37PM +0300, Dmitry Osipenko wrote:
>> 17.02.2022 11:29, Sascha Hauer пишет:
>>> From: Douglas Anderson <dianders@chromium.org>
>>>
>>> Jitter was improved by lowering the MPLL bandwidth to account for high
>>> frequency noise in the rk3288 PLL.  In each case MPLL bandwidth was
>>> lowered only enough to get us a comfortable margin.  We believe that
>>> lowering the bandwidth like this is safe given sufficient testing.
>>
>> There are no device-trees that use "rockchip,rk3288-cru", AFAICS..
> 
> What do you mean? In my tree I have:
> 
> arch/arm/boot/dts/rk3288.dtsi:863:              compatible = "rockchip,rk3288-cru";
> drivers/clk/rockchip/clk-rk3288.c:985:CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init);

Oh, it is ARM32, I was looking at ARM64 only. My bad.

>> Was this change tested on a non-RK3288 devices?
> 
> Yes, on a rk3568 ;)
> 
> The patch has been posted back in 2015 and was added to the Rockchip
> downstream kernel in 2016. I don't know how thoroughly Rockchip tests
> their kernels, but I assume the patch wouldn't be there if it caused
> any problems.

Thank you for the clarification.

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
  2022-02-17 14:06         ` Heiko Stübner
  (?)
  (?)
@ 2022-02-18  3:50           ` Andy Yan
  -1 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-18  3:50 UTC (permalink / raw)
  To: Heiko Stübner, Sascha Hauer
  Cc: dri-devel, linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Benjamin Gaignard, Michael Riesch, Sandy Huang, Peter Geis

Hi Sascha:

On 2/17/22 22:06, Heiko Stübner wrote:
> Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
>> Hi Andy,
>>
>> Please trim the context in your answers to the relevant parts, it makes
>> it easier to find the things you said.
>>
>> On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
>>> Hi Sascha:
>>>
>>>> +
>>>> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
>>>> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
>>>> +		struct device_node *node, *parent;
>>>> +
>>>> +		parent = of_get_parent(rkencoder->port);
>>>> +
>>>> +		for_each_endpoint_of_node(parent, node) {
>>> Is there any hurt directly use our downstream vendor kernel method here: use
>>> vcstate->output_if set by encoder driver to get which interface we should
>>> enable here?
>> There is no vcstate->output_if in mainline currently. Ok, we could add
>> that. The other thing is that there are multiple HDMI interfaces and
>> the id of the HDMI encoder is encoded into output_if. Downstream kernel
>> adds OF aliases to the HDMI ports. I didn't want to go that route
>> because it doesn't seem to be very elegant to me.
aliases is a very comm strategy in device tree world.  And your method 
also add need additional dt binds to define RK3568_VOP2_EP_xxx
>>> You method is ok with device tree,  but it tied up this driver to device
>>> tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
>>> driver can be much more flexible.
>> The current rockchip drm driver seems to be pretty much tied to device
>> tree. There are probably many other places that need parallel paths for
>> ACPI support, I think we can delay this particular part until we see the
>> whole picture. In the end we can still retrieve the output_if
>> information differently with ACPI while still retrieving the information
>> from the device tree the way we are doing currently.

The current driver only reference device thee at driver initial, we not 
wrap

device tree related things in other parts, so if we extend it to support 
ACPI,

we just need modify the initial code, this make things easier.

> agreed :-) .
>
> I.e. adding ACPI support for Rockchip drivers separately later on
> makes things way easier.
>
> Having a separate discussion about ACPI changes at that point
> also makes the whole process easier, as adding the whole thing
> here will delay everything even more.


  Heiko: I am not  ask to add new code for future  ACPI support, I just

hope the original downstream method  can keep to make future work easier.

> Also if a later series really only is about adding ACPI support, this
> makes for easier discussion but also easier review of changes.
> The new VOP2 driver is big enough as it is.
>
>
> Heiko
>
>

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-18  3:50           ` Andy Yan
  0 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-18  3:50 UTC (permalink / raw)
  To: Heiko Stübner, Sascha Hauer
  Cc: dri-devel, linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Benjamin Gaignard, Michael Riesch, Sandy Huang, Peter Geis

Hi Sascha:

On 2/17/22 22:06, Heiko Stübner wrote:
> Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
>> Hi Andy,
>>
>> Please trim the context in your answers to the relevant parts, it makes
>> it easier to find the things you said.
>>
>> On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
>>> Hi Sascha:
>>>
>>>> +
>>>> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
>>>> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
>>>> +		struct device_node *node, *parent;
>>>> +
>>>> +		parent = of_get_parent(rkencoder->port);
>>>> +
>>>> +		for_each_endpoint_of_node(parent, node) {
>>> Is there any hurt directly use our downstream vendor kernel method here: use
>>> vcstate->output_if set by encoder driver to get which interface we should
>>> enable here?
>> There is no vcstate->output_if in mainline currently. Ok, we could add
>> that. The other thing is that there are multiple HDMI interfaces and
>> the id of the HDMI encoder is encoded into output_if. Downstream kernel
>> adds OF aliases to the HDMI ports. I didn't want to go that route
>> because it doesn't seem to be very elegant to me.
aliases is a very comm strategy in device tree world.  And your method 
also add need additional dt binds to define RK3568_VOP2_EP_xxx
>>> You method is ok with device tree,  but it tied up this driver to device
>>> tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
>>> driver can be much more flexible.
>> The current rockchip drm driver seems to be pretty much tied to device
>> tree. There are probably many other places that need parallel paths for
>> ACPI support, I think we can delay this particular part until we see the
>> whole picture. In the end we can still retrieve the output_if
>> information differently with ACPI while still retrieving the information
>> from the device tree the way we are doing currently.

The current driver only reference device thee at driver initial, we not 
wrap

device tree related things in other parts, so if we extend it to support 
ACPI,

we just need modify the initial code, this make things easier.

> agreed :-) .
>
> I.e. adding ACPI support for Rockchip drivers separately later on
> makes things way easier.
>
> Having a separate discussion about ACPI changes at that point
> also makes the whole process easier, as adding the whole thing
> here will delay everything even more.


  Heiko: I am not  ask to add new code for future  ACPI support, I just

hope the original downstream method  can keep to make future work easier.

> Also if a later series really only is about adding ACPI support, this
> makes for easier discussion but also easier review of changes.
> The new VOP2 driver is big enough as it is.
>
>
> Heiko
>
>

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

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-18  3:50           ` Andy Yan
  0 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-18  3:50 UTC (permalink / raw)
  To: Heiko Stübner, Sascha Hauer
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	dri-devel, linux-rockchip, Michael Riesch, kernel,
	linux-arm-kernel

Hi Sascha:

On 2/17/22 22:06, Heiko Stübner wrote:
> Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
>> Hi Andy,
>>
>> Please trim the context in your answers to the relevant parts, it makes
>> it easier to find the things you said.
>>
>> On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
>>> Hi Sascha:
>>>
>>>> +
>>>> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
>>>> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
>>>> +		struct device_node *node, *parent;
>>>> +
>>>> +		parent = of_get_parent(rkencoder->port);
>>>> +
>>>> +		for_each_endpoint_of_node(parent, node) {
>>> Is there any hurt directly use our downstream vendor kernel method here: use
>>> vcstate->output_if set by encoder driver to get which interface we should
>>> enable here?
>> There is no vcstate->output_if in mainline currently. Ok, we could add
>> that. The other thing is that there are multiple HDMI interfaces and
>> the id of the HDMI encoder is encoded into output_if. Downstream kernel
>> adds OF aliases to the HDMI ports. I didn't want to go that route
>> because it doesn't seem to be very elegant to me.
aliases is a very comm strategy in device tree world.  And your method 
also add need additional dt binds to define RK3568_VOP2_EP_xxx
>>> You method is ok with device tree,  but it tied up this driver to device
>>> tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
>>> driver can be much more flexible.
>> The current rockchip drm driver seems to be pretty much tied to device
>> tree. There are probably many other places that need parallel paths for
>> ACPI support, I think we can delay this particular part until we see the
>> whole picture. In the end we can still retrieve the output_if
>> information differently with ACPI while still retrieving the information
>> from the device tree the way we are doing currently.

The current driver only reference device thee at driver initial, we not 
wrap

device tree related things in other parts, so if we extend it to support 
ACPI,

we just need modify the initial code, this make things easier.

> agreed :-) .
>
> I.e. adding ACPI support for Rockchip drivers separately later on
> makes things way easier.
>
> Having a separate discussion about ACPI changes at that point
> also makes the whole process easier, as adding the whole thing
> here will delay everything even more.


  Heiko: I am not  ask to add new code for future  ACPI support, I just

hope the original downstream method  can keep to make future work easier.

> Also if a later series really only is about adding ACPI support, this
> makes for easier discussion but also easier review of changes.
> The new VOP2 driver is big enough as it is.
>
>
> Heiko
>
>

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-18  3:50           ` Andy Yan
  0 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-18  3:50 UTC (permalink / raw)
  To: Heiko Stübner, Sascha Hauer
  Cc: dri-devel, linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Benjamin Gaignard, Michael Riesch, Sandy Huang, Peter Geis

Hi Sascha:

On 2/17/22 22:06, Heiko Stübner wrote:
> Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
>> Hi Andy,
>>
>> Please trim the context in your answers to the relevant parts, it makes
>> it easier to find the things you said.
>>
>> On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
>>> Hi Sascha:
>>>
>>>> +
>>>> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
>>>> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
>>>> +		struct device_node *node, *parent;
>>>> +
>>>> +		parent = of_get_parent(rkencoder->port);
>>>> +
>>>> +		for_each_endpoint_of_node(parent, node) {
>>> Is there any hurt directly use our downstream vendor kernel method here: use
>>> vcstate->output_if set by encoder driver to get which interface we should
>>> enable here?
>> There is no vcstate->output_if in mainline currently. Ok, we could add
>> that. The other thing is that there are multiple HDMI interfaces and
>> the id of the HDMI encoder is encoded into output_if. Downstream kernel
>> adds OF aliases to the HDMI ports. I didn't want to go that route
>> because it doesn't seem to be very elegant to me.
aliases is a very comm strategy in device tree world.  And your method 
also add need additional dt binds to define RK3568_VOP2_EP_xxx
>>> You method is ok with device tree,  but it tied up this driver to device
>>> tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
>>> driver can be much more flexible.
>> The current rockchip drm driver seems to be pretty much tied to device
>> tree. There are probably many other places that need parallel paths for
>> ACPI support, I think we can delay this particular part until we see the
>> whole picture. In the end we can still retrieve the output_if
>> information differently with ACPI while still retrieving the information
>> from the device tree the way we are doing currently.

The current driver only reference device thee at driver initial, we not 
wrap

device tree related things in other parts, so if we extend it to support 
ACPI,

we just need modify the initial code, this make things easier.

> agreed :-) .
>
> I.e. adding ACPI support for Rockchip drivers separately later on
> makes things way easier.
>
> Having a separate discussion about ACPI changes at that point
> also makes the whole process easier, as adding the whole thing
> here will delay everything even more.


  Heiko: I am not  ask to add new code for future  ACPI support, I just

hope the original downstream method  can keep to make future work easier.

> Also if a later series really only is about adding ACPI support, this
> makes for easier discussion but also easier review of changes.
> The new VOP2 driver is big enough as it is.
>
>
> Heiko
>
>

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
  2022-02-18  3:50           ` Andy Yan
  (?)
  (?)
@ 2022-02-18  8:00             ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-18  8:00 UTC (permalink / raw)
  To: Andy Yan
  Cc: Heiko Stübner, dri-devel, linux-arm-kernel, linux-rockchip,
	devicetree, kernel, Benjamin Gaignard, Michael Riesch,
	Sandy Huang, Peter Geis

On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
> Hi Sascha:
> 
> On 2/17/22 22:06, Heiko Stübner wrote:
> > Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
> > > Hi Andy,
> > > 
> > > Please trim the context in your answers to the relevant parts, it makes
> > > it easier to find the things you said.
> > > 
> > > On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> > > > Hi Sascha:
> > > > 
> > > > > +
> > > > > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > > > > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > > > > +		struct device_node *node, *parent;
> > > > > +
> > > > > +		parent = of_get_parent(rkencoder->port);
> > > > > +
> > > > > +		for_each_endpoint_of_node(parent, node) {
> > > > Is there any hurt directly use our downstream vendor kernel method here: use
> > > > vcstate->output_if set by encoder driver to get which interface we should
> > > > enable here?
> > > There is no vcstate->output_if in mainline currently. Ok, we could add
> > > that. The other thing is that there are multiple HDMI interfaces and
> > > the id of the HDMI encoder is encoded into output_if. Downstream kernel
> > > adds OF aliases to the HDMI ports. I didn't want to go that route
> > > because it doesn't seem to be very elegant to me.
> aliases is a very comm strategy in device tree world.

Yes, but not for retrieving bit offsets into registers. Normally aliases
can be changed at board level without confusing drivers.

> And your method also
> add need additional dt binds to define RK3568_VOP2_EP_xxx
> > > > You method is ok with device tree,  but it tied up this driver to device
> > > > tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> > > > driver can be much more flexible.
> > > The current rockchip drm driver seems to be pretty much tied to device
> > > tree. There are probably many other places that need parallel paths for
> > > ACPI support, I think we can delay this particular part until we see the
> > > whole picture. In the end we can still retrieve the output_if
> > > information differently with ACPI while still retrieving the information
> > > from the device tree the way we are doing currently.
> 
> The current driver only reference device thee at driver initial, we not wrap
> 
> device tree related things in other parts, so if we extend it to support
> ACPI,
> 
> we just need modify the initial code, this make things easier.

The device tree parsing could be moved out of vop2_crtc_atomic_enable()
into some initialisation path. In the end it's static information,
there's no need to do it repeatedly in atomic_enable.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-18  8:00             ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-18  8:00 UTC (permalink / raw)
  To: Andy Yan
  Cc: Heiko Stübner, dri-devel, linux-arm-kernel, linux-rockchip,
	devicetree, kernel, Benjamin Gaignard, Michael Riesch,
	Sandy Huang, Peter Geis

On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
> Hi Sascha:
> 
> On 2/17/22 22:06, Heiko Stübner wrote:
> > Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
> > > Hi Andy,
> > > 
> > > Please trim the context in your answers to the relevant parts, it makes
> > > it easier to find the things you said.
> > > 
> > > On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> > > > Hi Sascha:
> > > > 
> > > > > +
> > > > > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > > > > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > > > > +		struct device_node *node, *parent;
> > > > > +
> > > > > +		parent = of_get_parent(rkencoder->port);
> > > > > +
> > > > > +		for_each_endpoint_of_node(parent, node) {
> > > > Is there any hurt directly use our downstream vendor kernel method here: use
> > > > vcstate->output_if set by encoder driver to get which interface we should
> > > > enable here?
> > > There is no vcstate->output_if in mainline currently. Ok, we could add
> > > that. The other thing is that there are multiple HDMI interfaces and
> > > the id of the HDMI encoder is encoded into output_if. Downstream kernel
> > > adds OF aliases to the HDMI ports. I didn't want to go that route
> > > because it doesn't seem to be very elegant to me.
> aliases is a very comm strategy in device tree world.

Yes, but not for retrieving bit offsets into registers. Normally aliases
can be changed at board level without confusing drivers.

> And your method also
> add need additional dt binds to define RK3568_VOP2_EP_xxx
> > > > You method is ok with device tree,  but it tied up this driver to device
> > > > tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> > > > driver can be much more flexible.
> > > The current rockchip drm driver seems to be pretty much tied to device
> > > tree. There are probably many other places that need parallel paths for
> > > ACPI support, I think we can delay this particular part until we see the
> > > whole picture. In the end we can still retrieve the output_if
> > > information differently with ACPI while still retrieving the information
> > > from the device tree the way we are doing currently.
> 
> The current driver only reference device thee at driver initial, we not wrap
> 
> device tree related things in other parts, so if we extend it to support
> ACPI,
> 
> we just need modify the initial code, this make things easier.

The device tree parsing could be moved out of vop2_crtc_atomic_enable()
into some initialisation path. In the end it's static information,
there's no need to do it repeatedly in atomic_enable.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-18  8:00             ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-18  8:00 UTC (permalink / raw)
  To: Andy Yan
  Cc: devicetree, Benjamin Gaignard, Sandy Huang, dri-devel,
	linux-rockchip, Michael Riesch, kernel, Peter Geis,
	linux-arm-kernel

On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
> Hi Sascha:
> 
> On 2/17/22 22:06, Heiko Stübner wrote:
> > Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
> > > Hi Andy,
> > > 
> > > Please trim the context in your answers to the relevant parts, it makes
> > > it easier to find the things you said.
> > > 
> > > On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> > > > Hi Sascha:
> > > > 
> > > > > +
> > > > > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > > > > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > > > > +		struct device_node *node, *parent;
> > > > > +
> > > > > +		parent = of_get_parent(rkencoder->port);
> > > > > +
> > > > > +		for_each_endpoint_of_node(parent, node) {
> > > > Is there any hurt directly use our downstream vendor kernel method here: use
> > > > vcstate->output_if set by encoder driver to get which interface we should
> > > > enable here?
> > > There is no vcstate->output_if in mainline currently. Ok, we could add
> > > that. The other thing is that there are multiple HDMI interfaces and
> > > the id of the HDMI encoder is encoded into output_if. Downstream kernel
> > > adds OF aliases to the HDMI ports. I didn't want to go that route
> > > because it doesn't seem to be very elegant to me.
> aliases is a very comm strategy in device tree world.

Yes, but not for retrieving bit offsets into registers. Normally aliases
can be changed at board level without confusing drivers.

> And your method also
> add need additional dt binds to define RK3568_VOP2_EP_xxx
> > > > You method is ok with device tree,  but it tied up this driver to device
> > > > tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> > > > driver can be much more flexible.
> > > The current rockchip drm driver seems to be pretty much tied to device
> > > tree. There are probably many other places that need parallel paths for
> > > ACPI support, I think we can delay this particular part until we see the
> > > whole picture. In the end we can still retrieve the output_if
> > > information differently with ACPI while still retrieving the information
> > > from the device tree the way we are doing currently.
> 
> The current driver only reference device thee at driver initial, we not wrap
> 
> device tree related things in other parts, so if we extend it to support
> ACPI,
> 
> we just need modify the initial code, this make things easier.

The device tree parsing could be moved out of vop2_crtc_atomic_enable()
into some initialisation path. In the end it's static information,
there's no need to do it repeatedly in atomic_enable.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-18  8:00             ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-18  8:00 UTC (permalink / raw)
  To: Andy Yan
  Cc: Heiko Stübner, dri-devel, linux-arm-kernel, linux-rockchip,
	devicetree, kernel, Benjamin Gaignard, Michael Riesch,
	Sandy Huang, Peter Geis

On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
> Hi Sascha:
> 
> On 2/17/22 22:06, Heiko Stübner wrote:
> > Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
> > > Hi Andy,
> > > 
> > > Please trim the context in your answers to the relevant parts, it makes
> > > it easier to find the things you said.
> > > 
> > > On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> > > > Hi Sascha:
> > > > 
> > > > > +
> > > > > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > > > > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > > > > +		struct device_node *node, *parent;
> > > > > +
> > > > > +		parent = of_get_parent(rkencoder->port);
> > > > > +
> > > > > +		for_each_endpoint_of_node(parent, node) {
> > > > Is there any hurt directly use our downstream vendor kernel method here: use
> > > > vcstate->output_if set by encoder driver to get which interface we should
> > > > enable here?
> > > There is no vcstate->output_if in mainline currently. Ok, we could add
> > > that. The other thing is that there are multiple HDMI interfaces and
> > > the id of the HDMI encoder is encoded into output_if. Downstream kernel
> > > adds OF aliases to the HDMI ports. I didn't want to go that route
> > > because it doesn't seem to be very elegant to me.
> aliases is a very comm strategy in device tree world.

Yes, but not for retrieving bit offsets into registers. Normally aliases
can be changed at board level without confusing drivers.

> And your method also
> add need additional dt binds to define RK3568_VOP2_EP_xxx
> > > > You method is ok with device tree,  but it tied up this driver to device
> > > > tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> > > > driver can be much more flexible.
> > > The current rockchip drm driver seems to be pretty much tied to device
> > > tree. There are probably many other places that need parallel paths for
> > > ACPI support, I think we can delay this particular part until we see the
> > > whole picture. In the end we can still retrieve the output_if
> > > information differently with ACPI while still retrieving the information
> > > from the device tree the way we are doing currently.
> 
> The current driver only reference device thee at driver initial, we not wrap
> 
> device tree related things in other parts, so if we extend it to support
> ACPI,
> 
> we just need modify the initial code, this make things easier.

The device tree parsing could be moved out of vop2_crtc_atomic_enable()
into some initialisation path. In the end it's static information,
there's no need to do it repeatedly in atomic_enable.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
  2022-02-18  8:00             ` Sascha Hauer
  (?)
  (?)
@ 2022-02-19  7:35               ` Andy Yan
  -1 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-19  7:35 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: Heiko Stübner, dri-devel, linux-arm-kernel, linux-rockchip,
	devicetree, kernel, Benjamin Gaignard, Michael Riesch,
	Sandy Huang, Peter Geis

Hi Sascha:

On 2/18/22 16:00, Sascha Hauer wrote:
> On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
>> Hi Sascha:
>>
>> On 2/17/22 22:06, Heiko Stübner wrote:
>>> Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
>>>> Hi Andy,
>>>>
>>>> Please trim the context in your answers to the relevant parts, it makes
>>>> it easier to find the things you said.
>>>>
>>>> On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
>>>>> Hi Sascha:
>>>>>
>>>>>> +
>>>>>> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
>>>>>> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
>>>>>> +		struct device_node *node, *parent;
>>>>>> +
>>>>>> +		parent = of_get_parent(rkencoder->port);
>>>>>> +
>>>>>> +		for_each_endpoint_of_node(parent, node) {
>>>>> Is there any hurt directly use our downstream vendor kernel method here: use
>>>>> vcstate->output_if set by encoder driver to get which interface we should
>>>>> enable here?
>>>> There is no vcstate->output_if in mainline currently. Ok, we could add
>>>> that. The other thing is that there are multiple HDMI interfaces and
>>>> the id of the HDMI encoder is encoded into output_if. Downstream kernel
>>>> adds OF aliases to the HDMI ports. I didn't want to go that route
>>>> because it doesn't seem to be very elegant to me.
>> aliases is a very comm strategy in device tree world.
> Yes, but not for retrieving bit offsets into registers. Normally aliases
> can be changed at board level without confusing drivers.
>
>> And your method also
>> add need additional dt binds to define RK3568_VOP2_EP_xxx
>>>>> You method is ok with device tree,  but it tied up this driver to device
>>>>> tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
>>>>> driver can be much more flexible.
>>>> The current rockchip drm driver seems to be pretty much tied to device
>>>> tree. There are probably many other places that need parallel paths for
>>>> ACPI support, I think we can delay this particular part until we see the
>>>> whole picture. In the end we can still retrieve the output_if
>>>> information differently with ACPI while still retrieving the information
>>>> from the device tree the way we are doing currently.
>> The current driver only reference device thee at driver initial, we not wrap
>>
>> device tree related things in other parts, so if we extend it to support
>> ACPI,
>>
>> we just need modify the initial code, this make things easier.
> The device tree parsing could be moved out of vop2_crtc_atomic_enable()
> into some initialisation path. In the end it's static information,
> there's no need to do it repeatedly in atomic_enable.

This could be one solution, the repeatedly parsing device tree in 
atomic_enable is also my concern.

In addition, there are 2 HDMI, 2 eDP, 2 MIPI on the coming rk3588, so 
it's better to consider give position

for HDMI1, EDP1, in  include/dt-bindings/soc/rockchip,vop2.h

>
> Sascha
>

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-19  7:35               ` Andy Yan
  0 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-19  7:35 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: Heiko Stübner, dri-devel, linux-arm-kernel, linux-rockchip,
	devicetree, kernel, Benjamin Gaignard, Michael Riesch,
	Sandy Huang, Peter Geis

Hi Sascha:

On 2/18/22 16:00, Sascha Hauer wrote:
> On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
>> Hi Sascha:
>>
>> On 2/17/22 22:06, Heiko Stübner wrote:
>>> Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
>>>> Hi Andy,
>>>>
>>>> Please trim the context in your answers to the relevant parts, it makes
>>>> it easier to find the things you said.
>>>>
>>>> On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
>>>>> Hi Sascha:
>>>>>
>>>>>> +
>>>>>> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
>>>>>> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
>>>>>> +		struct device_node *node, *parent;
>>>>>> +
>>>>>> +		parent = of_get_parent(rkencoder->port);
>>>>>> +
>>>>>> +		for_each_endpoint_of_node(parent, node) {
>>>>> Is there any hurt directly use our downstream vendor kernel method here: use
>>>>> vcstate->output_if set by encoder driver to get which interface we should
>>>>> enable here?
>>>> There is no vcstate->output_if in mainline currently. Ok, we could add
>>>> that. The other thing is that there are multiple HDMI interfaces and
>>>> the id of the HDMI encoder is encoded into output_if. Downstream kernel
>>>> adds OF aliases to the HDMI ports. I didn't want to go that route
>>>> because it doesn't seem to be very elegant to me.
>> aliases is a very comm strategy in device tree world.
> Yes, but not for retrieving bit offsets into registers. Normally aliases
> can be changed at board level without confusing drivers.
>
>> And your method also
>> add need additional dt binds to define RK3568_VOP2_EP_xxx
>>>>> You method is ok with device tree,  but it tied up this driver to device
>>>>> tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
>>>>> driver can be much more flexible.
>>>> The current rockchip drm driver seems to be pretty much tied to device
>>>> tree. There are probably many other places that need parallel paths for
>>>> ACPI support, I think we can delay this particular part until we see the
>>>> whole picture. In the end we can still retrieve the output_if
>>>> information differently with ACPI while still retrieving the information
>>>> from the device tree the way we are doing currently.
>> The current driver only reference device thee at driver initial, we not wrap
>>
>> device tree related things in other parts, so if we extend it to support
>> ACPI,
>>
>> we just need modify the initial code, this make things easier.
> The device tree parsing could be moved out of vop2_crtc_atomic_enable()
> into some initialisation path. In the end it's static information,
> there's no need to do it repeatedly in atomic_enable.

This could be one solution, the repeatedly parsing device tree in 
atomic_enable is also my concern.

In addition, there are 2 HDMI, 2 eDP, 2 MIPI on the coming rk3588, so 
it's better to consider give position

for HDMI1, EDP1, in  include/dt-bindings/soc/rockchip,vop2.h

>
> Sascha
>

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

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-19  7:35               ` Andy Yan
  0 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-19  7:35 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: devicetree, Benjamin Gaignard, Sandy Huang, dri-devel,
	linux-rockchip, Michael Riesch, kernel, Peter Geis,
	linux-arm-kernel

Hi Sascha:

On 2/18/22 16:00, Sascha Hauer wrote:
> On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
>> Hi Sascha:
>>
>> On 2/17/22 22:06, Heiko Stübner wrote:
>>> Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
>>>> Hi Andy,
>>>>
>>>> Please trim the context in your answers to the relevant parts, it makes
>>>> it easier to find the things you said.
>>>>
>>>> On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
>>>>> Hi Sascha:
>>>>>
>>>>>> +
>>>>>> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
>>>>>> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
>>>>>> +		struct device_node *node, *parent;
>>>>>> +
>>>>>> +		parent = of_get_parent(rkencoder->port);
>>>>>> +
>>>>>> +		for_each_endpoint_of_node(parent, node) {
>>>>> Is there any hurt directly use our downstream vendor kernel method here: use
>>>>> vcstate->output_if set by encoder driver to get which interface we should
>>>>> enable here?
>>>> There is no vcstate->output_if in mainline currently. Ok, we could add
>>>> that. The other thing is that there are multiple HDMI interfaces and
>>>> the id of the HDMI encoder is encoded into output_if. Downstream kernel
>>>> adds OF aliases to the HDMI ports. I didn't want to go that route
>>>> because it doesn't seem to be very elegant to me.
>> aliases is a very comm strategy in device tree world.
> Yes, but not for retrieving bit offsets into registers. Normally aliases
> can be changed at board level without confusing drivers.
>
>> And your method also
>> add need additional dt binds to define RK3568_VOP2_EP_xxx
>>>>> You method is ok with device tree,  but it tied up this driver to device
>>>>> tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
>>>>> driver can be much more flexible.
>>>> The current rockchip drm driver seems to be pretty much tied to device
>>>> tree. There are probably many other places that need parallel paths for
>>>> ACPI support, I think we can delay this particular part until we see the
>>>> whole picture. In the end we can still retrieve the output_if
>>>> information differently with ACPI while still retrieving the information
>>>> from the device tree the way we are doing currently.
>> The current driver only reference device thee at driver initial, we not wrap
>>
>> device tree related things in other parts, so if we extend it to support
>> ACPI,
>>
>> we just need modify the initial code, this make things easier.
> The device tree parsing could be moved out of vop2_crtc_atomic_enable()
> into some initialisation path. In the end it's static information,
> there's no need to do it repeatedly in atomic_enable.

This could be one solution, the repeatedly parsing device tree in 
atomic_enable is also my concern.

In addition, there are 2 HDMI, 2 eDP, 2 MIPI on the coming rk3588, so 
it's better to consider give position

for HDMI1, EDP1, in  include/dt-bindings/soc/rockchip,vop2.h

>
> Sascha
>

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-19  7:35               ` Andy Yan
  0 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-19  7:35 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: Heiko Stübner, dri-devel, linux-arm-kernel, linux-rockchip,
	devicetree, kernel, Benjamin Gaignard, Michael Riesch,
	Sandy Huang, Peter Geis

Hi Sascha:

On 2/18/22 16:00, Sascha Hauer wrote:
> On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
>> Hi Sascha:
>>
>> On 2/17/22 22:06, Heiko Stübner wrote:
>>> Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
>>>> Hi Andy,
>>>>
>>>> Please trim the context in your answers to the relevant parts, it makes
>>>> it easier to find the things you said.
>>>>
>>>> On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
>>>>> Hi Sascha:
>>>>>
>>>>>> +
>>>>>> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
>>>>>> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
>>>>>> +		struct device_node *node, *parent;
>>>>>> +
>>>>>> +		parent = of_get_parent(rkencoder->port);
>>>>>> +
>>>>>> +		for_each_endpoint_of_node(parent, node) {
>>>>> Is there any hurt directly use our downstream vendor kernel method here: use
>>>>> vcstate->output_if set by encoder driver to get which interface we should
>>>>> enable here?
>>>> There is no vcstate->output_if in mainline currently. Ok, we could add
>>>> that. The other thing is that there are multiple HDMI interfaces and
>>>> the id of the HDMI encoder is encoded into output_if. Downstream kernel
>>>> adds OF aliases to the HDMI ports. I didn't want to go that route
>>>> because it doesn't seem to be very elegant to me.
>> aliases is a very comm strategy in device tree world.
> Yes, but not for retrieving bit offsets into registers. Normally aliases
> can be changed at board level without confusing drivers.
>
>> And your method also
>> add need additional dt binds to define RK3568_VOP2_EP_xxx
>>>>> You method is ok with device tree,  but it tied up this driver to device
>>>>> tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
>>>>> driver can be much more flexible.
>>>> The current rockchip drm driver seems to be pretty much tied to device
>>>> tree. There are probably many other places that need parallel paths for
>>>> ACPI support, I think we can delay this particular part until we see the
>>>> whole picture. In the end we can still retrieve the output_if
>>>> information differently with ACPI while still retrieving the information
>>>> from the device tree the way we are doing currently.
>> The current driver only reference device thee at driver initial, we not wrap
>>
>> device tree related things in other parts, so if we extend it to support
>> ACPI,
>>
>> we just need modify the initial code, this make things easier.
> The device tree parsing could be moved out of vop2_crtc_atomic_enable()
> into some initialisation path. In the end it's static information,
> there's no need to do it repeatedly in atomic_enable.

This could be one solution, the repeatedly parsing device tree in 
atomic_enable is also my concern.

In addition, there are 2 HDMI, 2 eDP, 2 MIPI on the coming rk3588, so 
it's better to consider give position

for HDMI1, EDP1, in  include/dt-bindings/soc/rockchip,vop2.h

>
> Sascha
>

_______________________________________________
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] 201+ messages in thread

* Aw: [PATCH v6 17/23] arm64: dts: rockchip: rk356x: Add HDMI nodes
  2022-02-17  8:29   ` Sascha Hauer
  (?)
  (?)
@ 2022-02-21  8:54     ` Frank Wunderlich
  -1 siblings, 0 replies; 201+ messages in thread
From: Frank Wunderlich @ 2022-02-21  8:54 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sascha Hauer,
	Sandy Huang, dri-devel, linux-rockchip, Michael Riesch, kernel,
	Andy Yan, linux-arm-kernel

Hi

> Gesendet: Donnerstag, 17. Februar 2022 um 09:29 Uhr
> Von: "Sascha Hauer" <s.hauer@pengutronix.de>

> --- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
> @@ -10,7 +10,6 @@
>  #include <dt-bindings/pinctrl/rockchip.h>
>  #include <dt-bindings/power/rk3568-power.h>
>  #include <dt-bindings/soc/rockchip,boot-mode.h>
> -#include <dt-bindings/soc/rockchip,vop2.h>

why dropping this after adding in part 16?

>  #include <dt-bindings/thermal/thermal.h>

it looks like you moved this to board includes...imho this should stay in the rk356x.dtsi, because compilation will fail if a board without the vop2 (and missing the include) is derived from rk356x.dtsi.

regards Frank

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

* Aw: [PATCH v6 17/23] arm64: dts: rockchip: rk356x: Add HDMI nodes
@ 2022-02-21  8:54     ` Frank Wunderlich
  0 siblings, 0 replies; 201+ messages in thread
From: Frank Wunderlich @ 2022-02-21  8:54 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: dri-devel, linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Andy Yan, Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

Hi

> Gesendet: Donnerstag, 17. Februar 2022 um 09:29 Uhr
> Von: "Sascha Hauer" <s.hauer@pengutronix.de>

> --- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
> @@ -10,7 +10,6 @@
>  #include <dt-bindings/pinctrl/rockchip.h>
>  #include <dt-bindings/power/rk3568-power.h>
>  #include <dt-bindings/soc/rockchip,boot-mode.h>
> -#include <dt-bindings/soc/rockchip,vop2.h>

why dropping this after adding in part 16?

>  #include <dt-bindings/thermal/thermal.h>

it looks like you moved this to board includes...imho this should stay in the rk356x.dtsi, because compilation will fail if a board without the vop2 (and missing the include) is derived from rk356x.dtsi.

regards Frank

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

* Aw: [PATCH v6 17/23] arm64: dts: rockchip: rk356x: Add HDMI nodes
@ 2022-02-21  8:54     ` Frank Wunderlich
  0 siblings, 0 replies; 201+ messages in thread
From: Frank Wunderlich @ 2022-02-21  8:54 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: dri-devel, linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Andy Yan, Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

Hi

> Gesendet: Donnerstag, 17. Februar 2022 um 09:29 Uhr
> Von: "Sascha Hauer" <s.hauer@pengutronix.de>

> --- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
> @@ -10,7 +10,6 @@
>  #include <dt-bindings/pinctrl/rockchip.h>
>  #include <dt-bindings/power/rk3568-power.h>
>  #include <dt-bindings/soc/rockchip,boot-mode.h>
> -#include <dt-bindings/soc/rockchip,vop2.h>

why dropping this after adding in part 16?

>  #include <dt-bindings/thermal/thermal.h>

it looks like you moved this to board includes...imho this should stay in the rk356x.dtsi, because compilation will fail if a board without the vop2 (and missing the include) is derived from rk356x.dtsi.

regards Frank

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

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

* Aw: [PATCH v6 17/23] arm64: dts: rockchip: rk356x: Add HDMI nodes
@ 2022-02-21  8:54     ` Frank Wunderlich
  0 siblings, 0 replies; 201+ messages in thread
From: Frank Wunderlich @ 2022-02-21  8:54 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: dri-devel, linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Andy Yan, Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Sascha Hauer

Hi

> Gesendet: Donnerstag, 17. Februar 2022 um 09:29 Uhr
> Von: "Sascha Hauer" <s.hauer@pengutronix.de>

> --- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
> @@ -10,7 +10,6 @@
>  #include <dt-bindings/pinctrl/rockchip.h>
>  #include <dt-bindings/power/rk3568-power.h>
>  #include <dt-bindings/soc/rockchip,boot-mode.h>
> -#include <dt-bindings/soc/rockchip,vop2.h>

why dropping this after adding in part 16?

>  #include <dt-bindings/thermal/thermal.h>

it looks like you moved this to board includes...imho this should stay in the rk356x.dtsi, because compilation will fail if a board without the vop2 (and missing the include) is derived from rk356x.dtsi.

regards Frank

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 17/23] arm64: dts: rockchip: rk356x: Add HDMI nodes
  2022-02-21  8:54     ` Frank Wunderlich
  (?)
  (?)
@ 2022-02-21  9:53       ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-21  9:53 UTC (permalink / raw)
  To: Frank Wunderlich
  Cc: devicetree, Benjamin Gaignard, Peter Geis, Sandy Huang,
	dri-devel, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

On Mon, Feb 21, 2022 at 09:54:28AM +0100, Frank Wunderlich wrote:
> Hi
> 
> > Gesendet: Donnerstag, 17. Februar 2022 um 09:29 Uhr
> > Von: "Sascha Hauer" <s.hauer@pengutronix.de>
> 
> > --- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
> > +++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
> > @@ -10,7 +10,6 @@
> >  #include <dt-bindings/pinctrl/rockchip.h>
> >  #include <dt-bindings/power/rk3568-power.h>
> >  #include <dt-bindings/soc/rockchip,boot-mode.h>
> > -#include <dt-bindings/soc/rockchip,vop2.h>
> 
> why dropping this after adding in part 16?
> 
> >  #include <dt-bindings/thermal/thermal.h>
> 
> it looks like you moved this to board includes...imho this should stay
> in the rk356x.dtsi, because compilation will fail if a board without
> the vop2 (and missing the include) is derived from rk356x.dtsi.

I dropped adding the include from Patch 16. The include is not needed by
rk356x.dtsi. When a board without vop2 support is added then it won't
need the include either.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 17/23] arm64: dts: rockchip: rk356x: Add HDMI nodes
@ 2022-02-21  9:53       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-21  9:53 UTC (permalink / raw)
  To: Frank Wunderlich
  Cc: dri-devel, linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Andy Yan, Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis

On Mon, Feb 21, 2022 at 09:54:28AM +0100, Frank Wunderlich wrote:
> Hi
> 
> > Gesendet: Donnerstag, 17. Februar 2022 um 09:29 Uhr
> > Von: "Sascha Hauer" <s.hauer@pengutronix.de>
> 
> > --- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
> > +++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
> > @@ -10,7 +10,6 @@
> >  #include <dt-bindings/pinctrl/rockchip.h>
> >  #include <dt-bindings/power/rk3568-power.h>
> >  #include <dt-bindings/soc/rockchip,boot-mode.h>
> > -#include <dt-bindings/soc/rockchip,vop2.h>
> 
> why dropping this after adding in part 16?
> 
> >  #include <dt-bindings/thermal/thermal.h>
> 
> it looks like you moved this to board includes...imho this should stay
> in the rk356x.dtsi, because compilation will fail if a board without
> the vop2 (and missing the include) is derived from rk356x.dtsi.

I dropped adding the include from Patch 16. The include is not needed by
rk356x.dtsi. When a board without vop2 support is added then it won't
need the include either.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

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

* Re: [PATCH v6 17/23] arm64: dts: rockchip: rk356x: Add HDMI nodes
@ 2022-02-21  9:53       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-21  9:53 UTC (permalink / raw)
  To: Frank Wunderlich
  Cc: dri-devel, linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Andy Yan, Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis

On Mon, Feb 21, 2022 at 09:54:28AM +0100, Frank Wunderlich wrote:
> Hi
> 
> > Gesendet: Donnerstag, 17. Februar 2022 um 09:29 Uhr
> > Von: "Sascha Hauer" <s.hauer@pengutronix.de>
> 
> > --- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
> > +++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
> > @@ -10,7 +10,6 @@
> >  #include <dt-bindings/pinctrl/rockchip.h>
> >  #include <dt-bindings/power/rk3568-power.h>
> >  #include <dt-bindings/soc/rockchip,boot-mode.h>
> > -#include <dt-bindings/soc/rockchip,vop2.h>
> 
> why dropping this after adding in part 16?
> 
> >  #include <dt-bindings/thermal/thermal.h>
> 
> it looks like you moved this to board includes...imho this should stay
> in the rk356x.dtsi, because compilation will fail if a board without
> the vop2 (and missing the include) is derived from rk356x.dtsi.

I dropped adding the include from Patch 16. The include is not needed by
rk356x.dtsi. When a board without vop2 support is added then it won't
need the include either.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 17/23] arm64: dts: rockchip: rk356x: Add HDMI nodes
@ 2022-02-21  9:53       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-21  9:53 UTC (permalink / raw)
  To: Frank Wunderlich
  Cc: dri-devel, linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Andy Yan, Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis

On Mon, Feb 21, 2022 at 09:54:28AM +0100, Frank Wunderlich wrote:
> Hi
> 
> > Gesendet: Donnerstag, 17. Februar 2022 um 09:29 Uhr
> > Von: "Sascha Hauer" <s.hauer@pengutronix.de>
> 
> > --- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
> > +++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
> > @@ -10,7 +10,6 @@
> >  #include <dt-bindings/pinctrl/rockchip.h>
> >  #include <dt-bindings/power/rk3568-power.h>
> >  #include <dt-bindings/soc/rockchip,boot-mode.h>
> > -#include <dt-bindings/soc/rockchip,vop2.h>
> 
> why dropping this after adding in part 16?
> 
> >  #include <dt-bindings/thermal/thermal.h>
> 
> it looks like you moved this to board includes...imho this should stay
> in the rk356x.dtsi, because compilation will fail if a board without
> the vop2 (and missing the include) is derived from rk356x.dtsi.

I dropped adding the include from Patch 16. The include is not needed by
rk356x.dtsi. When a board without vop2 support is added then it won't
need the include either.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
  2022-02-17  8:29   ` Sascha Hauer
  (?)
@ 2022-02-21 11:51     ` Andy Yan
  -1 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-21 11:51 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Kever Yang, tfiga

Hi Sascha:

On 2/17/22 16:29, Sascha Hauer wrote:
> From: Andy Yan <andy.yan@rock-chips.com>
>
> The VOP2 unit is found on Rockchip SoCs beginning with rk3566/rk3568.
> It replaces the VOP unit found in the older Rockchip SoCs.
>
> This driver has been derived from the downstream Rockchip Kernel and
> heavily modified:
>
> - All nonstandard DRM properties have been removed
> - dropped struct vop2_plane_state and pass around less data between
>    functions
> - Dropped all DRM_FORMAT_* not known on upstream
> - rework register access to get rid of excessively used macros
> - Drop all waiting for framesyncs

All the waiting sync in the downstream divers are try to fix special 
problems,

and some of them inherited from upstream vop driver, for example: the 
fb_unref_work,

It submitted by Tomas Figa to upstream vop driver to make the wait for 
flip and asynchronously cursor work

right.

VOP2 share the same hardware design in vblank, so I don't think these 
code are useless.

[0] 
https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-5-git-send-email-tfiga@chromium.org/

[1] 
https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-6-git-send-email-tfiga@chromium.org/

[2] 
https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-4-git-send-email-tfiga@chromium.org/


>
> The driver is tested with HDMI and MIPI-DSI display on a RK3568-EVB
> board. Overlay support is tested with the modetest utility. AFBC support
> on the cluster windows is tested with weston-simple-dmabuf-egl on
> weston using the (yet to be upstreamed) panfrost driver support.
>
> Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>
> Notes:
>      Changes since v5:
>      - consistently use u8/u16/u32 rather than uint8_t/uint16_t/uint32_t
>      - Use spin_lock rather than spin_lock_irqsave
>      - replace printk with drm_dbg
>      - break some overlong lines
>      
>      Changes since v4:
>      - Avoid stack frame overflow by not allocating big array on the stack
>      
>      Changes since v3:
>      - Sort includes
>      - fix typos
>      - Drop spinlock
>      - Use regmap_set_bits()/regmap_clear_bits()
>      - simplify vop2_scale_factor()
>      - simplify vop2_afbc_transform_offset()
>      
>      Changes since v4:
>      - Sort nodes alphabetically
>      
>      Changes since v3:
>      - Fix HDMI connector type
>
>   drivers/gpu/drm/rockchip/Kconfig             |    6 +
>   drivers/gpu/drm/rockchip/Makefile            |    1 +
>   drivers/gpu/drm/rockchip/rockchip_drm_drv.c  |    1 +
>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h  |    6 +-
>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c   |    2 +
>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h  |   15 +
>   drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2708 ++++++++++++++++++
>   drivers/gpu/drm/rockchip/rockchip_drm_vop2.h |  477 +++
>   drivers/gpu/drm/rockchip/rockchip_vop2_reg.c |  281 ++
>   9 files changed, 3496 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
>
> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> index b9b156308460a..4ff0043f0ee70 100644
> --- a/drivers/gpu/drm/rockchip/Kconfig
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
>   	  This selects support for the VOP driver. You should enable it
>   	  on all older SoCs up to RK3399.
>   
> +config ROCKCHIP_VOP2
> +	bool "Rockchip VOP2 driver"
> +	help
> +	  This selects support for the VOP2 driver. You should enable it
> +	  on all newer SoCs beginning form RK3568.
> +
>   config ROCKCHIP_ANALOGIX_DP
>   	bool "Rockchip specific extensions for Analogix DP driver"
>   	depends on ROCKCHIP_VOP
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index dfc5512fdb9f1..3ff7b21c04149 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -6,6 +6,7 @@
>   rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>   		rockchip_drm_gem.o
>   
> +rockchipdrm-$(CONFIG_ROCKCHIP_VOP2) += rockchip_drm_vop2.o rockchip_vop2_reg.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> index 82c8faf1fb6b8..95f6c5985fdd7 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -459,6 +459,7 @@ static int __init rockchip_drm_init(void)
>   
>   	num_rockchip_sub_drivers = 0;
>   	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
> +	ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2);
>   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
>   				CONFIG_ROCKCHIP_LVDS);
>   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> index d3e42410ae5da..5cb207aaaadd0 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -18,7 +18,7 @@
>   
>   #define ROCKCHIP_MAX_FB_BUFFER	3
>   #define ROCKCHIP_MAX_CONNECTOR	2
> -#define ROCKCHIP_MAX_CRTC	2
> +#define ROCKCHIP_MAX_CRTC	4
>   
>   struct drm_device;
>   struct drm_connector;
> @@ -31,6 +31,9 @@ struct rockchip_crtc_state {
>   	int output_bpc;
>   	int output_flags;
>   	bool enable_afbc;
> +	u32 bus_format;
> +	u32 bus_flags;
> +	int color_space;
>   };
>   #define to_rockchip_crtc_state(s) \
>   		container_of(s, struct rockchip_crtc_state, base)
> @@ -63,6 +66,7 @@ extern struct platform_driver rockchip_dp_driver;
>   extern struct platform_driver rockchip_lvds_driver;
>   extern struct platform_driver vop_platform_driver;
>   extern struct platform_driver rk3066_hdmi_driver;
> +extern struct platform_driver vop2_platform_driver;
>   
>   struct rockchip_encoder {
>   	struct device_node *port;
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> index 3aa37e177667e..0d2cb4f3922b8 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> @@ -134,4 +134,6 @@ void rockchip_drm_mode_config_init(struct drm_device *dev)
>   
>   	dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
>   	dev->mode_config.helper_private = &rockchip_mode_config_helpers;
> +
> +	dev->mode_config.normalize_zpos = true;
>   }
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> index 857d97cdc67c6..1e364d7b50e69 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -54,9 +54,23 @@ struct vop_afbc {
>   	struct vop_reg enable;
>   	struct vop_reg win_sel;
>   	struct vop_reg format;
> +	struct vop_reg rb_swap;
> +	struct vop_reg uv_swap;
> +	struct vop_reg auto_gating_en;
> +	struct vop_reg block_split_en;
> +	struct vop_reg pic_vir_width;
> +	struct vop_reg tile_num;
>   	struct vop_reg hreg_block_split;
> +	struct vop_reg pic_offset;
>   	struct vop_reg pic_size;
> +	struct vop_reg dsp_offset;
> +	struct vop_reg transform_offset;
>   	struct vop_reg hdr_ptr;
> +	struct vop_reg half_block_en;
> +	struct vop_reg xmirror;
> +	struct vop_reg ymirror;
> +	struct vop_reg rotate_270;
> +	struct vop_reg rotate_90;
>   	struct vop_reg rstn;
>   };
>   
> @@ -410,4 +424,5 @@ static inline int scl_vop_cal_lb_mode(int width, bool is_yuv)
>   }
>   
>   extern const struct component_ops vop_component_ops;
> +
>   #endif /* _ROCKCHIP_DRM_VOP_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> new file mode 100644
> index 0000000000000..394dd6c583682
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> @@ -0,0 +1,2708 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
> + * Author: Andy Yan <andy.yan@rock-chips.com>
> + */
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/delay.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/swab.h>
> +
> +#include <drm/drm.h>
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_uapi.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_debugfs.h>
> +#include <drm/drm_flip_work.h>
> +#include <drm/drm_plane_helper.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_vblank.h>
> +
> +#include <uapi/linux/videodev2.h>
> +#include <dt-bindings/soc/rockchip,vop2.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_gem.h"
> +#include "rockchip_drm_fb.h"
> +#include "rockchip_drm_vop2.h"
> +
> +/*
> + * VOP2 architecture
> + *
> + +----------+   +-------------+                                                        +-----------+
> + |  Cluster |   | Sel 1 from 6|                                                        | 1 from 3  |
> + |  window0 |   |    Layer0   |                                                        |    RGB    |
> + +----------+   +-------------+              +---------------+    +-------------+      +-----------+
> + +----------+   +-------------+              |N from 6 layers|    |             |
> + |  Cluster |   | Sel 1 from 6|              |   Overlay0    +--->| Video Port0 |      +-----------+
> + |  window1 |   |    Layer1   |              |               |    |             |      | 1 from 3  |
> + +----------+   +-------------+              +---------------+    +-------------+      |   LVDS    |
> + +----------+   +-------------+                                                        +-----------+
> + |  Esmart  |   | Sel 1 from 6|
> + |  window0 |   |   Layer2    |              +---------------+    +-------------+      +-----------+
> + +----------+   +-------------+              |N from 6 Layers|    |             | +--> | 1 from 3  |
> + +----------+   +-------------+   -------->  |   Overlay1    +--->| Video Port1 |      |   MIPI    |
> + |  Esmart  |   | Sel 1 from 6|   -------->  |               |    |             |      +-----------+
> + |  Window1 |   |   Layer3    |              +---------------+    +-------------+
> + +----------+   +-------------+                                                        +-----------+
> + +----------+   +-------------+                                                        | 1 from 3  |
> + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |   HDMI    |
> + |  Window0 |   |    Layer4   |              |N from 6 Layers|    |             |      +-----------+
> + +----------+   +-------------+              |   Overlay2    +--->| Video Port2 |
> + +----------+   +-------------+              |               |    |             |      +-----------+
> + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |  1 from 3 |
> + |  Window1 |   |    Layer5   |                                                        |    eDP    |
> + +----------+   +-------------+                                                        +-----------+
> + *
> + */
> +
> +enum vop2_data_format {
> +	VOP2_FMT_ARGB8888 = 0,
> +	VOP2_FMT_RGB888,
> +	VOP2_FMT_RGB565,
> +	VOP2_FMT_XRGB101010,
> +	VOP2_FMT_YUV420SP,
> +	VOP2_FMT_YUV422SP,
> +	VOP2_FMT_YUV444SP,
> +	VOP2_FMT_YUYV422 = 8,
> +	VOP2_FMT_YUYV420,
> +	VOP2_FMT_VYUY422,
> +	VOP2_FMT_VYUY420,
> +	VOP2_FMT_YUV420SP_TILE_8x4 = 0x10,
> +	VOP2_FMT_YUV420SP_TILE_16x2,
> +	VOP2_FMT_YUV422SP_TILE_8x4,
> +	VOP2_FMT_YUV422SP_TILE_16x2,
> +	VOP2_FMT_YUV420SP_10,
> +	VOP2_FMT_YUV422SP_10,
> +	VOP2_FMT_YUV444SP_10,
> +};
> +
> +enum vop2_afbc_format {
> +	VOP2_AFBC_FMT_RGB565,
> +	VOP2_AFBC_FMT_ARGB2101010 = 2,
> +	VOP2_AFBC_FMT_YUV420_10BIT,
> +	VOP2_AFBC_FMT_RGB888,
> +	VOP2_AFBC_FMT_ARGB8888,
> +	VOP2_AFBC_FMT_YUV420 = 9,
> +	VOP2_AFBC_FMT_YUV422 = 0xb,
> +	VOP2_AFBC_FMT_YUV422_10BIT = 0xe,
> +	VOP2_AFBC_FMT_INVALID = -1,
> +};
> +
> +union vop2_alpha_ctrl {
> +	u32 val;
> +	struct {
> +		/* [0:1] */
> +		u32 color_mode:1;
> +		u32 alpha_mode:1;
> +		/* [2:3] */
> +		u32 blend_mode:2;
> +		u32 alpha_cal_mode:1;
> +		/* [5:7] */
> +		u32 factor_mode:3;
> +		/* [8:9] */
> +		u32 alpha_en:1;
> +		u32 src_dst_swap:1;
> +		u32 reserved:6;
> +		/* [16:23] */
> +		u32 glb_alpha:8;
> +	} bits;
> +};
> +
> +struct vop2_alpha {
> +	union vop2_alpha_ctrl src_color_ctrl;
> +	union vop2_alpha_ctrl dst_color_ctrl;
> +	union vop2_alpha_ctrl src_alpha_ctrl;
> +	union vop2_alpha_ctrl dst_alpha_ctrl;
> +};
> +
> +struct vop2_alpha_config {
> +	bool src_premulti_en;
> +	bool dst_premulti_en;
> +	bool src_pixel_alpha_en;
> +	bool dst_pixel_alpha_en;
> +	u16 src_glb_alpha_value;
> +	u16 dst_glb_alpha_value;
> +};
> +
> +struct vop2_win {
> +	struct vop2 *vop2;
> +	struct drm_plane base;
> +	const struct vop2_win_data *data;
> +	struct regmap_field *reg[VOP2_WIN_MAX_REG];
> +
> +	/**
> +	 * @win_id: graphic window id, a cluster may be split into two
> +	 * graphics windows.
> +	 */
> +	u8 win_id;
> +	u8 delay;
> +	u32 offset;
> +
> +	enum drm_plane_type type;
> +};
> +
> +struct vop2_video_port {
> +	struct drm_crtc crtc;
> +	struct vop2 *vop2;
> +	struct clk *dclk;
> +	unsigned int id;
> +	const struct vop2_video_port_regs *regs;
> +	const struct vop2_video_port_data *data;
> +
> +	struct completion dsp_hold_completion;
> +
> +	/**
> +	 * @win_mask: Bitmask of windows attached to the video port;
> +	 */
> +	u32 win_mask;
> +
> +	struct vop2_win *primary_plane;
> +	struct drm_pending_vblank_event *event;
> +
> +	unsigned int nlayers;
> +};
> +
> +struct vop2 {
> +	struct device *dev;
> +	struct drm_device *drm;
> +	struct vop2_video_port vps[ROCKCHIP_MAX_CRTC];
> +
> +	const struct vop2_data *data;
> +	/*
> +	 * Number of windows that are registered as plane, may be less than the
> +	 * total number of hardware windows.
> +	 */
> +	u32 registered_num_wins;
> +
> +	void __iomem *regs;
> +	struct regmap *map;
> +
> +	struct regmap *grf;
> +
> +	/* physical map length of vop2 register */
> +	u32 len;
> +
> +	void __iomem *lut_regs;
> +
> +	/* protects crtc enable/disable */
> +	struct mutex vop2_lock;
> +
> +	int irq;
> +
> +	/*
> +	 * Some global resources are shared between all video ports(crtcs), so
> +	 * we need a ref counter here.
> +	 */
> +	unsigned int enable_count;
> +	struct clk *hclk;
> +	struct clk *aclk;
> +
> +	/* must be put at the end of the struct */
> +	struct vop2_win win[];
> +};
> +
> +static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
> +{
> +	return container_of(crtc, struct vop2_video_port, crtc);
> +}
> +
> +static struct vop2_win *to_vop2_win(struct drm_plane *p)
> +{
> +	return container_of(p, struct vop2_win, base);
> +}
> +
> +static void vop2_lock(struct vop2 *vop2)
> +{
> +	mutex_lock(&vop2->vop2_lock);
> +}
> +
> +static void vop2_unlock(struct vop2 *vop2)
> +{
> +	mutex_unlock(&vop2->vop2_lock);
> +}
> +
> +static void vop2_writel(struct vop2 *vop2, u32 offset, u32 v)
> +{
> +	regmap_write(vop2->map, offset, v);
> +}
> +
> +static void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
> +{
> +	regmap_write(vp->vop2->map, vp->data->offset + offset, v);
> +}
> +
> +static u32 vop2_readl(struct vop2 *vop2, u32 offset)
> +{
> +	u32 val;
> +
> +	regmap_read(vop2->map, offset, &val);
> +
> +	return val;
> +}
> +
> +static void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v)
> +{
> +	regmap_field_write(win->reg[reg], v);
> +}
> +
> +static bool vop2_cluster_window(const struct vop2_win *win)
> +{
> +	return win->data->feature & WIN_FEATURE_CLUSTER;
> +}
> +
> +static void vop2_cfg_done(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE,
> +			BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> +}
> +
> +static void vop2_win_disable(struct vop2_win *win)
> +{
> +	vop2_win_write(win, VOP2_WIN_ENABLE, 0);
> +
> +	if (vop2_cluster_window(win))
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0);
> +}
> +
> +static enum vop2_data_format vop2_convert_format(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +		return VOP2_FMT_ARGB8888;
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		return VOP2_FMT_RGB888;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		return VOP2_FMT_RGB565;
> +	case DRM_FORMAT_NV12:
> +		return VOP2_FMT_YUV420SP;
> +	case DRM_FORMAT_NV16:
> +		return VOP2_FMT_YUV422SP;
> +	case DRM_FORMAT_NV24:
> +		return VOP2_FMT_YUV444SP;
> +	case DRM_FORMAT_YUYV:
> +	case DRM_FORMAT_YVYU:
> +		return VOP2_FMT_VYUY422;
> +	case DRM_FORMAT_VYUY:
> +	case DRM_FORMAT_UYVY:
> +		return VOP2_FMT_YUYV422;
> +	default:
> +		DRM_ERROR("unsupported format[%08x]\n", format);
> +		return -EINVAL;
> +	}
> +}
> +
> +static enum vop2_afbc_format vop2_convert_afbc_format(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +		return VOP2_AFBC_FMT_ARGB8888;
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		return VOP2_AFBC_FMT_RGB888;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		return VOP2_AFBC_FMT_RGB565;
> +	case DRM_FORMAT_NV12:
> +		return VOP2_AFBC_FMT_YUV420;
> +	case DRM_FORMAT_NV16:
> +		return VOP2_AFBC_FMT_YUV422;
> +	default:
> +		return VOP2_AFBC_FMT_INVALID;
> +	}
> +
> +	return VOP2_AFBC_FMT_INVALID;
> +}
> +
> +static bool vop2_win_rb_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_BGR888:
> +	case DRM_FORMAT_BGR565:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_afbc_rb_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV24:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_afbc_uv_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV12:
> +	case DRM_FORMAT_NV16:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_win_uv_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV12:
> +	case DRM_FORMAT_NV16:
> +	case DRM_FORMAT_NV24:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_win_dither_up(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_BGR565:
> +	case DRM_FORMAT_RGB565:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
> +{
> +	/*
> +	 * FIXME:
> +	 *
> +	 * There is no media type for YUV444 output,
> +	 * so when out_mode is AAAA or P888, assume output is YUV444 on
> +	 * yuv format.
> +	 *
> +	 * From H/W testing, YUV444 mode need a rb swap.
> +	 */
> +	if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 ||
> +	    bus_format == MEDIA_BUS_FMT_VYUY8_1X16 ||
> +	    bus_format == MEDIA_BUS_FMT_YVYU8_2X8 ||
> +	    bus_format == MEDIA_BUS_FMT_VYUY8_2X8 ||
> +	    ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
> +	      bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
> +	     (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
> +	      output_mode == ROCKCHIP_OUT_MODE_P888)))
> +		return true;
> +	else
> +		return false;
> +}
> +
> +static bool is_yuv_output(u32 bus_format)
> +{
> +	switch (bus_format) {
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +	case MEDIA_BUS_FMT_YUV10_1X30:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> +	case MEDIA_BUS_FMT_YUYV8_2X8:
> +	case MEDIA_BUS_FMT_YVYU8_2X8:
> +	case MEDIA_BUS_FMT_UYVY8_2X8:
> +	case MEDIA_BUS_FMT_VYUY8_2X8:
> +	case MEDIA_BUS_FMT_YUYV8_1X16:
> +	case MEDIA_BUS_FMT_YVYU8_1X16:
> +	case MEDIA_BUS_FMT_UYVY8_1X16:
> +	case MEDIA_BUS_FMT_VYUY8_1X16:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool rockchip_afbc(struct drm_plane *plane, u64 modifier)
> +{
> +	int i;
> +
> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> +		return false;
> +
> +	for (i = 0 ; i < plane->modifier_count; i++)
> +		if (plane->modifiers[i] == modifier)
> +			return true;
> +
> +	return false;
> +
> +}
> +
> +static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
> +					u64 modifier)
> +{
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2 *vop2 = win->vop2;
> +
> +	if (modifier == DRM_FORMAT_MOD_INVALID)
> +		return false;
> +
> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> +		return true;
> +
> +	if (!rockchip_afbc(plane, modifier)) {
> +		drm_err(vop2->drm, "Unsupported format modifier 0x%llx\n",
> +			modifier);
> +
> +		return false;
> +	}
> +
> +	return vop2_convert_afbc_format(format) >= 0;
> +}
> +
> +static u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate,
> +				      bool afbc_half_block_en)
> +{
> +	struct drm_rect *src = &pstate->src;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	u32 bpp = fb->format->cpp[0] * 8;
> +	u32 vir_width = (fb->pitches[0] << 3) / bpp;
> +	u32 width = drm_rect_width(src) >> 16;
> +	u32 height = drm_rect_height(src) >> 16;
> +	u32 act_xoffset = src->x1 >> 16;
> +	u32 act_yoffset = src->y1 >> 16;
> +	u32 align16_crop = 0;
> +	u32 align64_crop = 0;
> +	u32 height_tmp;
> +	u8 tx, ty;
> +	u8 bottom_crop_line_num = 0;
> +
> +	/* 16 pixel align */
> +	if (height & 0xf)
> +		align16_crop = 16 - (height & 0xf);
> +
> +	height_tmp = height + align16_crop;
> +
> +	/* 64 pixel align */
> +	if (height_tmp & 0x3f)
> +		align64_crop = 64 - (height_tmp & 0x3f);
> +
> +	bottom_crop_line_num = align16_crop + align64_crop;
> +
> +	switch (pstate->rotation &
> +		(DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y |
> +		 DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) {
> +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> +		tx = 16 - ((act_xoffset + width) & 0xf);
> +		ty = bottom_crop_line_num - act_yoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90:
> +		tx = bottom_crop_line_num - act_yoffset;
> +		ty = vir_width - width - act_xoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270:
> +		tx = act_yoffset;
> +		ty = act_xoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X:
> +		tx = 16 - ((act_xoffset + width) & 0xf);
> +		ty = act_yoffset;
> +		break;
> +	case DRM_MODE_REFLECT_Y:
> +		tx = act_xoffset;
> +		ty = bottom_crop_line_num - act_yoffset;
> +		break;
> +	case DRM_MODE_ROTATE_90:
> +		tx = bottom_crop_line_num - act_yoffset;
> +		ty = act_xoffset;
> +		break;
> +	case DRM_MODE_ROTATE_270:
> +		tx = act_yoffset;
> +		ty = vir_width - width - act_xoffset;
> +		break;
> +	case 0:
> +		tx = act_xoffset;
> +		ty = act_yoffset;
> +		break;
> +	}
> +
> +	if (afbc_half_block_en)
> +		ty &= 0x7f;
> +
> +#define TRANSFORM_XOFFSET GENMASK(7, 0)
> +#define TRANSFORM_YOFFSET GENMASK(23, 16)
> +	return FIELD_PREP(TRANSFORM_XOFFSET, tx) |
> +		FIELD_PREP(TRANSFORM_YOFFSET, ty);
> +}
> +
> +/*
> + * A Cluster window has 2048 x 16 line buffer, which can
> + * works at 2048 x 16(Full) or 4096 x 8 (Half) mode.
> + * for Cluster_lb_mode register:
> + * 0: half mode, for plane input width range 2048 ~ 4096
> + * 1: half mode, for cluster work at 2 * 2048 plane mode
> + * 2: half mode, for rotate_90/270 mode
> + *
> + */
> +static int vop2_get_cluster_lb_mode(struct vop2_win *win,
> +				    struct drm_plane_state *pstate)
> +{
> +	if ((pstate->rotation & DRM_MODE_ROTATE_270) ||
> +	    (pstate->rotation & DRM_MODE_ROTATE_90))
> +		return 2;
> +	else
> +		return 0;
> +}
> +
> +static u16 vop2_scale_factor(u32 src, u32 dst)
> +{
> +	u32 fac;
> +	int shift;
> +
> +	if (src == dst)
> +		return 0;
> +
> +	if (dst < 2)
> +		return U16_MAX;
> +
> +	if (src < 2)
> +		return 0;
> +
> +	if (src > dst)
> +		shift = 12;
> +	else
> +		shift = 16;
> +
> +	src--;
> +	dst--;
> +
> +	fac = DIV_ROUND_UP(src << shift, dst) - 1;
> +
> +	if (fac > U16_MAX)
> +		return U16_MAX;
> +
> +	return fac;
> +}
> +
> +static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
> +			     u32 src_w, u32 src_h, u32 dst_w,
> +			     u32 dst_h, u32 pixel_format)
> +{
> +	const struct drm_format_info *info;
> +	u16 hor_scl_mode, ver_scl_mode;
> +	u16 hscl_filter_mode, vscl_filter_mode;
> +	u8 gt2 = 0;
> +	u8 gt4 = 0;
> +	u32 val;
> +
> +	info = drm_format_info(pixel_format);
> +
> +	if (src_h >= (4 * dst_h)) {
> +		gt4 = 1;
> +		src_h >>= 2;
> +	} else if (src_h >= (2 * dst_h)) {
> +		gt2 = 1;
> +		src_h >>= 1;
> +	}
> +
> +	hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> +	ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> +
> +	if (hor_scl_mode == SCALE_UP)
> +		hscl_filter_mode = VOP2_SCALE_UP_BIC;
> +	else
> +		hscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> +
> +	if (ver_scl_mode == SCALE_UP)
> +		vscl_filter_mode = VOP2_SCALE_UP_BIL;
> +	else
> +		vscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> +
> +	/*
> +	 * RK3568 VOP Esmart/Smart dsp_w should be even pixel
> +	 * at scale down mode
> +	 */
> +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> +		if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) {
> +			drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n",
> +				win->data->name, dst_w);
> +			dst_w++;
> +		}
> +	}
> +
> +	val = vop2_scale_factor(src_w, dst_w);
> +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val);
> +	val = vop2_scale_factor(src_h, dst_h);
> +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val);
> +
> +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4);
> +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2);
> +
> +	vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode);
> +	vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode);
> +
> +	if (vop2_cluster_window(win))
> +		return;
> +
> +	vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode);
> +	vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode);
> +
> +	if (info->is_yuv) {
> +		src_w /= info->hsub;
> +		src_h /= info->vsub;
> +
> +		gt4 = gt2 = 0;
> +
> +		if (src_h >= (4 * dst_h)) {
> +			gt4 = 1;
> +			src_h >>= 2;
> +		} else if (src_h >= (2 * dst_h)) {
> +			gt2 = 1;
> +			src_h >>= 1;
> +		}
> +
> +		hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> +		ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> +
> +		val = vop2_scale_factor(src_w, dst_w);
> +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val);
> +
> +		val = vop2_scale_factor(src_h, dst_h);
> +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val);
> +
> +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4);
> +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2);
> +		vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode);
> +	}
> +}
> +
> +static int vop2_convert_csc_mode(int csc_mode)
> +{
> +	switch (csc_mode) {
> +	case V4L2_COLORSPACE_SMPTE170M:
> +	case V4L2_COLORSPACE_470_SYSTEM_M:
> +	case V4L2_COLORSPACE_470_SYSTEM_BG:
> +		return CSC_BT601L;
> +	case V4L2_COLORSPACE_REC709:
> +	case V4L2_COLORSPACE_SMPTE240M:
> +	case V4L2_COLORSPACE_DEFAULT:
> +		return CSC_BT709L;
> +	case V4L2_COLORSPACE_JPEG:
> +		return CSC_BT601F;
> +	case V4L2_COLORSPACE_BT2020:
> +		return CSC_BT2020;
> +	default:
> +		return CSC_BT709L;
> +	}
> +}
> +
> +/*
> + * colorspace path:
> + *      Input        Win csc                     Output
> + * 1. YUV(2020)  --> Y2R->2020To709->R2Y   --> YUV_OUTPUT(601/709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 2. YUV(2020)  --> bypasss               --> YUV_OUTPUT(2020)
> + *    RGB        --> 709To2020->R2Y       __/
> + *
> + * 3. YUV(2020)  --> Y2R->2020To709        --> RGB_OUTPUT(709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 4. YUV(601/709)-> Y2R->709To2020->R2Y   --> YUV_OUTPUT(2020)
> + *    RGB        --> 709To2020->R2Y       __/
> + *
> + * 5. YUV(601/709)-> bypass                --> YUV_OUTPUT(709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 6. YUV(601/709)-> bypass                --> YUV_OUTPUT(601)
> + *    RGB        --> R2Y(601)             __/
> + *
> + * 7. YUV        --> Y2R(709)              --> RGB_OUTPUT(709)
> + *    RGB        --> bypass               __/
> + *
> + * 8. RGB        --> 709To2020->R2Y        --> YUV_OUTPUT(2020)
> + *
> + * 9. RGB        --> R2Y(709)              --> YUV_OUTPUT(709)
> + *
> + * 10. RGB       --> R2Y(601)              --> YUV_OUTPUT(601)
> + *
> + * 11. RGB       --> bypass                --> RGB_OUTPUT(709)
> + */
> +
> +static void vop2_setup_csc_mode(struct vop2_video_port *vp,
> +				struct vop2_win *win,
> +				struct drm_plane_state *pstate)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
> +	int is_input_yuv = pstate->fb->format->is_yuv;
> +	int is_output_yuv = is_yuv_output(vcstate->bus_format);
> +	int input_csc = V4L2_COLORSPACE_DEFAULT;
> +	int output_csc = vcstate->color_space;
> +	bool r2y_en, y2r_en;
> +	int csc_mode;
> +
> +	if (is_input_yuv && !is_output_yuv) {
> +		y2r_en = true;
> +		r2y_en = false;
> +		csc_mode = vop2_convert_csc_mode(input_csc);
> +	} else if (!is_input_yuv && is_output_yuv) {
> +		y2r_en = false;
> +		r2y_en = true;
> +		csc_mode = vop2_convert_csc_mode(output_csc);
> +	} else {
> +		y2r_en = false;
> +		r2y_en = false;
> +		csc_mode = false;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en);
> +	vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en);
> +	vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
> +}
> +
> +static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq);
> +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq);
> +}
> +
> +static void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16);
> +}
> +
> +static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
> +{
> +	int ret;
> +
> +	ret = clk_prepare_enable(vop2->hclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable hclk - %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(vop2->aclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable aclk - %d\n", ret);
> +		goto err;
> +	}
> +
> +	return 0;
> +err:
> +	clk_disable_unprepare(vop2->hclk);
> +
> +	return ret;
> +}
> +
> +static void vop2_enable(struct vop2 *vop2)
> +{
> +	int ret;
> +
> +	ret = pm_runtime_get_sync(vop2->dev);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret);
> +		return;
> +	}
> +
> +	ret = vop2_core_clks_prepare_enable(vop2);
> +	if (ret) {
> +		pm_runtime_put_sync(vop2->dev);
> +		return;
> +	}
> +
> +	if (vop2->data->soc_id == 3566)
> +		vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
> +
> +	vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> +
> +	/*
> +	 * Disable auto gating, this is a workaround to
> +	 * avoid display image shift when a window enabled.
> +	 */
> +	regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL,
> +			  RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN);
> +
> +	vop2_writel(vop2, RK3568_SYS0_INT_CLR,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS0_INT_EN,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS1_INT_CLR,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS1_INT_EN,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +}
> +
> +static void vop2_disable(struct vop2 *vop2)
> +{
> +	pm_runtime_put_sync(vop2->dev);
> +
> +	clk_disable_unprepare(vop2->aclk);
> +	clk_disable_unprepare(vop2->hclk);
> +}
> +
> +static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
> +				     struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	int ret;
> +
> +	vop2_lock(vop2);
> +
> +	drm_crtc_vblank_off(crtc);
> +
> +	/*
> +	 * Vop standby will take effect at end of current frame,
> +	 * if dsp hold valid irq happen, it means standby complete.
> +	 *
> +	 * we must wait standby complete when we want to disable aclk,
> +	 * if not, memory bus maybe dead.
> +	 */
> +	reinit_completion(&vp->dsp_hold_completion);
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
> +
> +	ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
> +					  msecs_to_jiffies(50));
> +	if (!ret)
> +		drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
> +
> +	vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
> +
> +	clk_disable_unprepare(vp->dclk);
> +
> +	vop2->enable_count--;
> +
> +	if (!vop2->enable_count)
> +		vop2_disable(vop2);
> +
> +	vop2_unlock(vop2);
> +
> +	if (crtc->state->event && !crtc->state->active) {
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +
> +		crtc->state->event = NULL;
> +	}
> +}
> +
> +static int vop2_plane_atomic_check(struct drm_plane *plane,
> +				   struct drm_atomic_state *astate)
> +{
> +	struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane);
> +	struct drm_framebuffer *fb = pstate->fb;
> +	struct drm_crtc *crtc = pstate->crtc;
> +	struct drm_crtc_state *cstate;
> +	struct vop2_video_port *vp;
> +	struct vop2 *vop2;
> +	const struct vop2_data *vop2_data;
> +	struct drm_rect *dest = &pstate->dst;
> +	struct drm_rect *src = &pstate->src;
> +	int min_scale = FRAC_16_16(1, 8);
> +	int max_scale = FRAC_16_16(8, 1);
> +	int format;
> +	int ret;
> +
> +	if (!crtc)
> +		return 0;
> +
> +	vp = to_vop2_video_port(crtc);
> +	vop2 = vp->vop2;
> +	vop2_data = vop2->data;
> +
> +	cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc);
> +	if (WARN_ON(!cstate))
> +		return -EINVAL;
> +
> +	ret = drm_atomic_helper_check_plane_state(pstate, cstate,
> +						  min_scale, max_scale,
> +						  true, true);
> +	if (ret)
> +		return ret;
> +
> +	if (!pstate->visible)
> +		return 0;
> +
> +	format = vop2_convert_format(fb->format->format);
> +	if (format < 0)
> +		return format;
> +
> +	if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 ||
> +	    drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) {
> +		drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
> +			  drm_rect_width(src) >> 16, drm_rect_height(src) >> 16,
> +			  drm_rect_width(dest), drm_rect_height(dest));
> +		pstate->visible = false;
> +		return 0;
> +	}
> +
> +	if (drm_rect_width(src) >> 16 > vop2_data->max_input.width ||
> +	    drm_rect_height(src) >> 16 > vop2_data->max_input.height) {
> +		drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
> +			  drm_rect_width(src) >> 16,
> +			  drm_rect_height(src) >> 16,
> +			  vop2_data->max_input.width,
> +			  vop2_data->max_input.height);
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Src.x1 can be odd when do clip, but yuv plane start point
> +	 * need align with 2 pixel.
> +	 */
> +	if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
> +		drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vop2_plane_atomic_disable(struct drm_plane *plane,
> +				      struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state, plane);
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2 *vop2 = win->vop2;
> +
> +	drm_dbg(vop2->drm, "%s disable\n", win->data->name);
> +
> +	if (!old_pstate->crtc)
> +		return;
> +
> +	vop2_win_disable(win);
> +	vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0);
> +}
> +
> +/*
> + * The color key is 10 bit, so all format should
> + * convert to 10 bit here.
> + */
> +static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
> +{
> +	struct drm_plane_state *pstate = plane->state;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	struct vop2_win *win = to_vop2_win(plane);
> +	u32 color_key_en = 0;
> +	u32 r = 0;
> +	u32 g = 0;
> +	u32 b = 0;
> +
> +	if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) {
> +		vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0);
> +		return;
> +	}
> +
> +	switch (fb->format->format) {
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		r = (color_key & 0xf800) >> 11;
> +		g = (color_key & 0x7e0) >> 5;
> +		b = (color_key & 0x1f);
> +		r <<= 5;
> +		g <<= 4;
> +		b <<= 5;
> +		color_key_en = 1;
> +		break;
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		r = (color_key & 0xff0000) >> 16;
> +		g = (color_key & 0xff00) >> 8;
> +		b = (color_key & 0xff);
> +		r <<= 2;
> +		g <<= 2;
> +		b <<= 2;
> +		color_key_en = 1;
> +		break;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en);
> +	vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b);
> +}
> +
> +static void vop2_plane_atomic_update(struct drm_plane *plane,
> +				     struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *pstate = plane->state;
> +	struct drm_crtc *crtc = pstate->crtc;
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
> +	struct vop2 *vop2 = win->vop2;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	u32 bpp = fb->format->cpp[0] * 8;
> +	u32 actual_w, actual_h, dsp_w, dsp_h;
> +	u32 act_info, dsp_info;
> +	u32 format;
> +	u32 afbc_format;
> +	u32 rb_swap;
> +	u32 uv_swap;
> +	struct drm_rect *src = &pstate->src;
> +	struct drm_rect *dest = &pstate->dst;
> +	u32 afbc_tile_num;
> +	u32 transform_offset;
> +	bool dither_up;
> +	bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
> +	bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
> +	bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
> +	bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
> +	struct rockchip_gem_object *rk_obj;
> +	unsigned long offset;
> +	bool afbc_en;
> +	dma_addr_t yrgb_mst;
> +	dma_addr_t uv_mst;
> +
> +	/*
> +	 * can't update plane when vop2 is disabled.
> +	 */
> +	if (WARN_ON(!crtc))
> +		return;
> +
> +	if (!pstate->visible) {
> +		vop2_plane_atomic_disable(plane, state);
> +		return;
> +	}
> +
> +	afbc_en = rockchip_afbc(plane, fb->modifier);
> +
> +	offset = (src->x1 >> 16) * fb->format->cpp[0];
> +
> +	/*
> +	 * AFBC HDR_PTR must set to the zero offset of the framebuffer.
> +	 */
> +	if (afbc_en)
> +		offset = 0;
> +	else if (pstate->rotation & DRM_MODE_REFLECT_Y)
> +		offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
> +	else
> +		offset += (src->y1 >> 16) * fb->pitches[0];
> +
> +	rk_obj = to_rockchip_obj(fb->obj[0]);
> +
> +	yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
> +	if (fb->format->is_yuv) {
> +		int hsub = fb->format->hsub;
> +		int vsub = fb->format->vsub;
> +
> +		offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub;
> +		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
> +
> +		if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en)
> +			offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub;
> +
> +		rk_obj = to_rockchip_obj(fb->obj[0]);
> +		uv_mst = rk_obj->dma_addr + offset + fb->offsets[1];
> +	}
> +
> +	actual_w = drm_rect_width(src) >> 16;
> +	actual_h = drm_rect_height(src) >> 16;
> +	dsp_w = drm_rect_width(dest);
> +
> +	if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
> +		drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
> +			  vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
> +		dsp_w = adjusted_mode->hdisplay - dest->x1;
> +		if (dsp_w < 4)
> +			dsp_w = 4;
> +		actual_w = dsp_w * actual_w / drm_rect_width(dest);
> +	}
> +
> +	dsp_h = drm_rect_height(dest);
> +
> +	if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
> +		drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
> +			  vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
> +		dsp_h = adjusted_mode->vdisplay - dest->y1;
> +		if (dsp_h < 4)
> +			dsp_h = 4;
> +		actual_h = dsp_h * actual_h / drm_rect_height(dest);
> +	}
> +
> +	/*
> +	 * This is workaround solution for IC design:
> +	 * esmart can't support scale down when actual_w % 16 == 1.
> +	 */
> +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> +		if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
> +			drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
> +				vp->id, win->data->name, actual_w);
> +			actual_w -= 1;
> +		}
> +	}
> +
> +	if (afbc_en && actual_w % 4) {
> +		drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
> +			  vp->id, win->data->name, actual_w);
> +		actual_w = ALIGN_DOWN(actual_w, 4);
> +	}
> +
> +	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
> +	dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff);
> +
> +	format = vop2_convert_format(fb->format->format);
> +
> +	drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n",
> +		      vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h,
> +		      dest->x1, dest->y1,
> +		      &fb->format->format,
> +		      afbc_en ? "AFBC" : "", &yrgb_mst);
> +
> +	if (afbc_en) {
> +		u32 stride;
> +
> +		/* the afbc superblock is 16 x 16 */
> +		afbc_format = vop2_convert_afbc_format(fb->format->format);
> +
> +		/* Enable color transform for YTR */
> +		if (fb->modifier & AFBC_FORMAT_MOD_YTR)
> +			afbc_format |= (1 << 4);
> +
> +		afbc_tile_num = ALIGN(actual_w, 16) >> 4;
> +
> +		/*
> +		 * AFBC pic_vir_width is count by pixel, this is different
> +		 * with WIN_VIR_STRIDE.
> +		 */
> +		stride = (fb->pitches[0] << 3) / bpp;
> +		if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
> +			drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligened\n",
> +				  vp->id, win->data->name, stride);
> +
> +		rb_swap = vop2_afbc_rb_swap(fb->format->format);
> +		uv_swap = vop2_afbc_uv_swap(fb->format->format);
> +		/*
> +		 * This is a workaround for crazy IC design, Cluster
> +		 * and Esmart/Smart use different format configuration map:
> +		 * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart.
> +		 *
> +		 * This is one thing we can make the convert simple:
> +		 * AFBCD decode all the YUV data to YUV444. So we just
> +		 * set all the yuv 10 bit to YUV444_10.
> +		 */
> +		if (fb->format->is_yuv && (bpp == 10))
> +			format = VOP2_CLUSTER_YUV444_10;
> +
> +		if (vop2_cluster_window(win))
> +			vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
> +		vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
> +		vop2_win_write(win, VOP2_WIN_AFBC_RB_SWAP, rb_swap);
> +		vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap);
> +		vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
> +		vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
> +		if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) {
> +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0);
> +			transform_offset = vop2_afbc_transform_offset(pstate, false);
> +		} else {
> +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 1);
> +			transform_offset = vop2_afbc_transform_offset(pstate, true);
> +		}
> +		vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
> +		vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset);
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1));
> +		vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16)));
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride);
> +		vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num);
> +		vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror);
> +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270);
> +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90);
> +	} else {
> +		vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4));
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror);
> +
> +	if (rotate_90 || rotate_270) {
> +		act_info = swahw32(act_info);
> +		actual_w = drm_rect_height(src) >> 16;
> +		actual_h = drm_rect_width(src) >> 16;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_FORMAT, format);
> +	vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst);
> +
> +	rb_swap = vop2_win_rb_swap(fb->format->format);
> +	vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap);
> +	if (!vop2_cluster_window(win)) {
> +		uv_swap = vop2_win_uv_swap(fb->format->format);
> +		vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
> +	}
> +
> +	if (fb->format->is_yuv) {
> +		vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4));
> +		vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst);
> +	}
> +
> +	vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format);
> +	if (!vop2_cluster_window(win))
> +		vop2_plane_setup_color_key(plane, 0);
> +	vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info);
> +	vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info);
> +	vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff));
> +
> +	vop2_setup_csc_mode(vp, win, pstate);
> +
> +	dither_up = vop2_win_dither_up(fb->format->format);
> +	vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up);
> +
> +	vop2_win_write(win, VOP2_WIN_ENABLE, 1);
> +
> +	if (vop2_cluster_window(win)) {
> +		int lb_mode = vop2_get_cluster_lb_mode(win, pstate);
> +
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode);
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1);
> +	}
> +}
> +
> +static const struct drm_plane_helper_funcs vop2_plane_helper_funcs = {
> +	.atomic_check = vop2_plane_atomic_check,
> +	.atomic_update = vop2_plane_atomic_update,
> +	.atomic_disable = vop2_plane_atomic_disable,
> +};
> +
> +static const struct drm_plane_funcs vop2_plane_funcs = {
> +	.update_plane	= drm_atomic_helper_update_plane,
> +	.disable_plane	= drm_atomic_helper_disable_plane,
> +	.destroy = drm_plane_cleanup,
> +	.reset = drm_atomic_helper_plane_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> +	.format_mod_supported = rockchip_vop2_mod_supported,
> +};
> +
> +static int vop2_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD);
> +
> +	return 0;
> +}
> +
> +static void vop2_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD);
> +}
> +
> +static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
> +				 const struct drm_display_mode *mode,
> +				 struct drm_display_mode *adj_mode)
> +{
> +	drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V |
> +					CRTC_STEREO_DOUBLE);
> +
> +	return true;
> +}
> +
> +static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	switch (vcstate->bus_format) {
> +	case MEDIA_BUS_FMT_RGB565_1X16:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> +		break;
> +	case MEDIA_BUS_FMT_RGB666_1X18:
> +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> +	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> +		*dsp_ctrl |= RGB888_TO_RGB666;
> +		break;
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA)
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> +
> +	*dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL,
> +				DITHER_DOWN_ALLEGRO);
> +}
> +
> +static void vop2_post_config(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	u16 vtotal = mode->crtc_vtotal;
> +	u16 hdisplay = mode->crtc_hdisplay;
> +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> +	u16 vdisplay = mode->crtc_vdisplay;
> +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> +	u32 left_margin = 100, right_margin = 100;
> +	u32 top_margin = 100, bottom_margin = 100;
> +	u16 hsize = hdisplay * (left_margin + right_margin) / 200;
> +	u16 vsize = vdisplay * (top_margin + bottom_margin) / 200;
> +	u16 hact_end, vact_end;
> +	u32 val;
> +
> +	vsize = rounddown(vsize, 2);
> +	hsize = rounddown(hsize, 2);
> +	hact_st += hdisplay * (100 - left_margin) / 200;
> +	hact_end = hact_st + hsize;
> +	val = hact_st << 16;
> +	val |= hact_end;
> +	vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val);
> +	vact_st += vdisplay * (100 - top_margin) / 200;
> +	vact_end = vact_st + vsize;
> +	val = vact_st << 16;
> +	val |= vact_end;
> +	vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val);
> +	val = scl_cal_scale2(vdisplay, vsize) << 16;
> +	val |= scl_cal_scale2(hdisplay, hsize);
> +	vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val);
> +
> +	val = 0;
> +	if (hdisplay != hsize)
> +		val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN;
> +	if (vdisplay != vsize)
> +		val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN;
> +	vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val);
> +
> +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> +		u16 vact_st_f1 = vtotal + vact_st + 1;
> +		u16 vact_end_f1 = vact_st_f1 + vsize;
> +
> +		val = vact_st_f1 << 16 | vact_end_f1;
> +		vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
> +	}
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_BG, 0);
> +}
> +
> +static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
> +				u32 polflags)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	u32 die, dip;
> +
> +	die = vop2_readl(vop2, RK3568_DSP_IF_EN);
> +	dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
> +
> +	switch (id) {
> +	case RK3568_VOP2_EP_RGB:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_RGB |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
> +		if (polflags & POLFLAG_DCLK_INV)
> +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
> +		else
> +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16));
> +		break;
> +	case RK3568_VOP2_EP_HDMI:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_HDMI |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id);
> +		break;
> +	case RK3568_VOP2_EP_EDP:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_EDP |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id);
> +		break;
> +	case RK3568_VOP2_EP_MIPI0:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_MIPI1:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_LVDS0:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_LVDS1:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> +		break;
> +	default:
> +		return;
> +	};
> +
> +	dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
> +
> +	vop2_writel(vop2, RK3568_DSP_IF_EN, die);
> +	vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
> +}
> +
> +static int us_to_vertical_line(struct drm_display_mode *mode, int us)
> +{
> +	return us * mode->clock / mode->htotal / 1000;
> +}
> +
> +static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
> +				    struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	const struct vop2_data *vop2_data = vop2->data;
> +	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	unsigned long clock = mode->crtc_clock * 1000;
> +	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
> +	u16 hdisplay = mode->crtc_hdisplay;
> +	u16 htotal = mode->crtc_htotal;
> +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> +	u16 hact_end = hact_st + hdisplay;
> +	u16 vdisplay = mode->crtc_vdisplay;
> +	u16 vtotal = mode->crtc_vtotal;
> +	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
> +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> +	u16 vact_end = vact_st + vdisplay;
> +	u8 out_mode;
> +	u32 dsp_ctrl = 0;
> +	int act_end;
> +	u32 val, polflags;
> +	int ret;
> +	struct drm_encoder *encoder;
> +
> +	drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n",
> +		hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
> +		drm_mode_vrefresh(mode), vcstate->output_type, vp->id);
> +
> +	vop2_lock(vop2);
> +
> +	ret = clk_prepare_enable(vp->dclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
> +			      vp->id, ret);
> +		return;
> +	}
> +
> +	if (!vop2->enable_count)
> +		vop2_enable(vop2);
> +
> +	vop2->enable_count++;
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY);
> +
> +	polflags = 0;
> +	if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
> +		polflags |= POLFLAG_DCLK_INV;
> +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> +		polflags |= BIT(HSYNC_POSITIVE);
> +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> +		polflags |= BIT(VSYNC_POSITIVE);
> +
> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> +		struct device_node *node, *parent;
> +
> +		parent = of_get_parent(rkencoder->port);
> +
> +		for_each_endpoint_of_node(parent, node) {
> +			struct device_node *crtc_port = of_graph_get_remote_port(node);
> +			struct device_node *epn;
> +			struct of_endpoint endpoint;
> +
> +			if (crtc->port != crtc_port) {
> +				of_node_put(crtc_port);
> +				continue;
> +			}
> +
> +			of_node_put(crtc_port);
> +
> +			epn = of_graph_get_remote_endpoint(node);
> +			of_graph_parse_endpoint(epn, &endpoint);
> +			of_node_put(epn);
> +
> +			drm_dbg(vop2->drm, "vp%d is connected to %s, id %d\n",
> +					   vp->id, encoder->name, endpoint.id);
> +			rk3568_set_intf_mux(vp, endpoint.id, polflags);
> +		}
> +		of_node_put(parent);
> +	}
> +
> +	if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
> +	     !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT))
> +		out_mode = ROCKCHIP_OUT_MODE_P888;
> +	else
> +		out_mode = vcstate->output_mode;
> +
> +	dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode);
> +
> +	if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP;
> +
> +	if (is_yuv_output(vcstate->bus_format))
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y;
> +
> +	vop2_dither_setup(crtc, &dsp_ctrl);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len);
> +	val = hact_st << 16;
> +	val |= hact_end;
> +	vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val);
> +
> +	val = vact_st << 16;
> +	val |= vact_end;
> +	vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val);
> +
> +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> +		u16 vact_st_f1 = vtotal + vact_st + 1;
> +		u16 vact_end_f1 = vact_st_f1 + vdisplay;
> +
> +		val = vact_st_f1 << 16 | vact_end_f1;
> +		vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val);
> +
> +		val = vtotal << 16 | (vtotal + vsync_len);
> +		vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val);
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE;
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL;
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN;
> +		vtotal += vtotal + 1;
> +		act_end = vact_end_f1;
> +	} else {
> +		act_end = vact_end;
> +	}
> +
> +	vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id),
> +		    (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len);
> +
> +	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
> +		clock *= 2;
> +	}
> +
> +	vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
> +
> +	clk_set_rate(vp->dclk, clock);
> +
> +	vop2_post_config(crtc);
> +
> +	vop2_cfg_done(vp);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
> +
> +	drm_crtc_vblank_on(crtc);
> +
> +	vop2_unlock(vop2);
> +}
> +
> +static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
> +				  struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_plane *plane;
> +	int nplanes = 0;
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +
> +	drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
> +		nplanes++;
> +
> +	if (nplanes > vp->nlayers)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static bool is_opaque(u16 alpha)
> +{
> +	return (alpha >> 8) == 0xff;
> +}
> +
> +static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config,
> +			     struct vop2_alpha *alpha)
> +{
> +	int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1;
> +	int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1;
> +	int src_color_mode = alpha_config->src_premulti_en ?
> +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> +	int dst_color_mode = alpha_config->dst_premulti_en ?
> +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> +
> +	alpha->src_color_ctrl.val = 0;
> +	alpha->dst_color_ctrl.val = 0;
> +	alpha->src_alpha_ctrl.val = 0;
> +	alpha->dst_alpha_ctrl.val = 0;
> +
> +	if (!alpha_config->src_pixel_alpha_en)
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> +	else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en)
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> +	else
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> +
> +	alpha->src_color_ctrl.bits.alpha_en = 1;
> +
> +	if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) {
> +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> +	} else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) {
> +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE;
> +	} else {
> +		alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> +	}
> +	alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8;
> +	alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +
> +	alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +	alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> +	alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8;
> +	alpha->dst_color_ctrl.bits.color_mode = dst_color_mode;
> +	alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> +
> +	alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode;
> +	alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +	alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE;
> +
> +	alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en)
> +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> +	else
> +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> +	alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION;
> +	alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> +}
> +
> +static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id)
> +{
> +	struct vop2_video_port *vp;
> +	int used_layer = 0;
> +	int i;
> +
> +	for (i = 0; i < port_id; i++) {
> +		vp = &vop2->vps[i];
> +		used_layer += hweight32(vp->win_mask);
> +	}
> +
> +	return used_layer;
> +}
> +
> +static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win)
> +{
> +	u32 offset = (main_win->data->phys_id * 0x10);
> +	struct vop2_alpha_config alpha_config;
> +	struct vop2_alpha alpha;
> +	struct drm_plane_state *bottom_win_pstate;
> +	bool src_pixel_alpha_en = false;
> +	u16 src_glb_alpha_val, dst_glb_alpha_val;
> +	bool premulti_en = false;
> +	bool swap = false;
> +
> +	/* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */
> +	bottom_win_pstate = main_win->base.state;
> +	src_glb_alpha_val = 0;
> +	dst_glb_alpha_val = main_win->base.state->alpha;
> +
> +	if (!bottom_win_pstate->fb)
> +		return;
> +
> +	alpha_config.src_premulti_en = premulti_en;
> +	alpha_config.dst_premulti_en = false;
> +	alpha_config.src_pixel_alpha_en = src_pixel_alpha_en;
> +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> +	alpha_config.src_glb_alpha_value = src_glb_alpha_val;
> +	alpha_config.dst_glb_alpha_value = dst_glb_alpha_val;
> +	vop2_parse_alpha(&alpha_config, &alpha);
> +
> +	alpha.src_color_ctrl.bits.src_dst_swap = swap;
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset,
> +		    alpha.src_color_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset,
> +		    alpha.dst_color_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset,
> +		    alpha.src_alpha_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset,
> +		    alpha.dst_alpha_ctrl.val);
> +}
> +
> +static void vop2_setup_alpha(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_framebuffer *fb;
> +	struct vop2_alpha_config alpha_config;
> +	struct vop2_alpha alpha;
> +	struct drm_plane *plane;
> +	int pixel_alpha_en;
> +	int premulti_en, gpremulti_en = 0;
> +	int mixer_id;
> +	u32 offset;
> +	bool bottom_layer_alpha_en = false;
> +	u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE;
> +
> +	mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id);
> +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> +
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		if (plane->state->normalized_zpos == 0 &&
> +		    !is_opaque(plane->state->alpha) &&
> +		    !vop2_cluster_window(win)) {
> +			/*
> +			 * If bottom layer have global alpha effect [except cluster layer,
> +			 * because cluster have deal with bottom layer global alpha value
> +			 * at cluster mix], bottom layer mix need deal with global alpha.
> +			 */
> +			bottom_layer_alpha_en = true;
> +			dst_global_alpha = plane->state->alpha;
> +		}
> +	}
> +
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +		int zpos = plane->state->normalized_zpos;
> +
> +		if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
> +			premulti_en = 1;
> +		else
> +			premulti_en = 0;
> +
> +		plane = &win->base;
> +		fb = plane->state->fb;
> +
> +		pixel_alpha_en = fb->format->has_alpha;
> +
> +		alpha_config.src_premulti_en = premulti_en;
> +
> +		if (bottom_layer_alpha_en && zpos == 1) {
> +			gpremulti_en = premulti_en;
> +			/* Cd = Cs + (1 - As) * Cd * Agd */
> +			alpha_config.dst_premulti_en = false;
> +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> +			alpha_config.dst_glb_alpha_value = dst_global_alpha;
> +		} else if (vop2_cluster_window(win)) {
> +			/* Mix output data only have pixel alpha */
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = true;
> +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +		} else {
> +			/* Cd = Cs + (1 - As) * Cd */
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +		}
> +
> +		vop2_parse_alpha(&alpha_config, &alpha);
> +
> +		offset = (mixer_id + zpos - 1) * 0x10;
> +		vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset,
> +			    alpha.src_color_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset,
> +			    alpha.dst_color_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset,
> +			    alpha.src_alpha_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset,
> +			    alpha.dst_alpha_ctrl.val);
> +	}
> +
> +	if (vp->id == 0) {
> +		if (bottom_layer_alpha_en) {
> +			/* Transfer pixel alpha to hdr mix */
> +			alpha_config.src_premulti_en = gpremulti_en;
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = true;
> +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			vop2_parse_alpha(&alpha_config, &alpha);
> +
> +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL,
> +				alpha.src_color_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL,
> +				alpha.dst_color_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL,
> +				alpha.src_alpha_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL,
> +				alpha.dst_alpha_ctrl.val);
> +		} else {
> +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0);
> +		}
> +	}
> +}
> +
> +static void vop2_setup_layer_mixer(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_plane *plane;
> +	u32 layer_sel = 0;
> +	u32 port_sel;
> +	unsigned int nlayer, ofs;
> +	struct drm_display_mode *adjusted_mode;
> +	u16 hsync_len;
> +	u16 hdisplay;
> +	u32 bg_dly;
> +	u32 pre_scan_dly;
> +	int i;
> +	struct vop2_video_port *vp0 = &vop2->vps[0];
> +	struct vop2_video_port *vp1 = &vop2->vps[1];
> +	struct vop2_video_port *vp2 = &vop2->vps[2];
> +
> +	adjusted_mode = &vp->crtc.state->adjusted_mode;
> +	hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
> +	hdisplay = adjusted_mode->crtc_hdisplay;
> +
> +	bg_dly = vp->data->pre_scan_max_dly[3];
> +	vop2_writel(vop2, RK3568_VP_BG_MIX_CTRL(vp->id),
> +			    FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly));
> +
> +	pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len;
> +	vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly);
> +
> +	vop2_writel(vop2, RK3568_OVL_CTRL, 0);
> +	port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
> +	port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT;
> +
> +	if (vp0->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX,
> +				     vp0->nlayers - 1);
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8);
> +
> +	if (vp1->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX,
> +				     (vp0->nlayers + vp1->nlayers - 1));
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> +
> +	if (vp2->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX,
> +			(vp2->nlayers + vp1->nlayers + vp0->nlayers - 1));
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> +
> +	layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
> +
> +	ofs = 0;
> +	for (i = 0; i < vp->id; i++)
> +		ofs += vop2->vps[i].nlayers;
> +
> +	nlayer = 0;
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		switch (win->data->phys_id) {
> +		case ROCKCHIP_VOP2_CLUSTER0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_CLUSTER1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_SMART0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_SMART1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id);
> +			break;
> +		}
> +
> +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, win->data->layer_sel_id);
> +		nlayer++;
> +	}
> +
> +	/* configure unused layers to 0x5 (reserved) */
> +	for (; nlayer < 3; nlayer++) {
> +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5);
> +	}
> +
> +	vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel);
> +	vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
> +	vop2_writel(vop2, RK3568_OVL_CTRL, RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD);
> +}
> +
> +static void vop2_setup_dly_for_windows(struct vop2 *vop2)
> +{
> +	struct vop2_win *win;
> +	int i = 0;
> +	u32 cdly = 0, sdly = 0;
> +
> +	for (i = 0; i < vop2->data->win_size; i++) {
> +		u32 dly;
> +
> +		win = &vop2->win[i];
> +		dly = win->delay;
> +
> +		switch (win->data->phys_id) {
> +		case ROCKCHIP_VOP2_CLUSTER0:
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly);
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_CLUSTER1:
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly);
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART0:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART1:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_SMART0:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_SMART1:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly);
> +			break;
> +		}
> +	}
> +
> +	vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly);
> +	vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly);
> +}
> +
> +static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_plane *plane;
> +
> +	vp->win_mask = 0;
> +
> +	drm_atomic_crtc_for_each_plane(plane, crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT];
> +
> +		vp->win_mask |= BIT(win->data->phys_id);
> +
> +		if (vop2_cluster_window(win))
> +			vop2_setup_cluster_alpha(vop2, win);
> +	}
> +
> +	if (!vp->win_mask)
> +		return;
> +
> +	vop2_setup_layer_mixer(vp);
> +	vop2_setup_alpha(vp);
> +	vop2_setup_dly_for_windows(vop2);
> +}
> +
> +static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_post_config(crtc);
> +
> +	vop2_cfg_done(vp);
> +
> +	spin_lock_irq(&crtc->dev->event_lock);
> +
> +	if (crtc->state->event) {
> +		WARN_ON(drm_crtc_vblank_get(crtc));
> +		vp->event = crtc->state->event;
> +		crtc->state->event = NULL;
> +	}
> +
> +	spin_unlock_irq(&crtc->dev->event_lock);
> +}
> +
> +static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
> +	.mode_fixup = vop2_crtc_mode_fixup,
> +	.atomic_check = vop2_crtc_atomic_check,
> +	.atomic_begin = vop2_crtc_atomic_begin,
> +	.atomic_flush = vop2_crtc_atomic_flush,
> +	.atomic_enable = vop2_crtc_atomic_enable,
> +	.atomic_disable = vop2_crtc_atomic_disable,
> +};
> +
> +static void vop2_crtc_reset(struct drm_crtc *crtc)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	if (crtc->state) {
> +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
> +		kfree(vcstate);
> +	}
> +
> +	vcstate = kzalloc(sizeof(*vcstate), GFP_KERNEL);
> +	if (!vcstate)
> +		return;
> +
> +	crtc->state = &vcstate->base;
> +	crtc->state->crtc = crtc;
> +}
> +
> +static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc)
> +{
> +	struct rockchip_crtc_state *vcstate, *old_vcstate;
> +
> +	old_vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	vcstate = kmemdup(old_vcstate, sizeof(*old_vcstate), GFP_KERNEL);
> +	if (!vcstate)
> +		return NULL;
> +
> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
> +
> +	return &vcstate->base;
> +}
> +
> +static void vop2_crtc_destroy_state(struct drm_crtc *crtc,
> +				    struct drm_crtc_state *state)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state);
> +
> +	__drm_atomic_helper_crtc_destroy_state(&vcstate->base);
> +	kfree(vcstate);
> +}
> +
> +static const struct drm_crtc_funcs vop2_crtc_funcs = {
> +	.set_config = drm_atomic_helper_set_config,
> +	.page_flip = drm_atomic_helper_page_flip,
> +	.destroy = drm_crtc_cleanup,
> +	.reset = vop2_crtc_reset,
> +	.atomic_duplicate_state = vop2_crtc_duplicate_state,
> +	.atomic_destroy_state = vop2_crtc_destroy_state,
> +	.enable_vblank = vop2_crtc_enable_vblank,
> +	.disable_vblank = vop2_crtc_disable_vblank,
> +};
> +
> +static irqreturn_t vop2_isr(int irq, void *data)
> +{
> +	struct vop2 *vop2 = data;
> +	const struct vop2_data *vop2_data = vop2->data;
> +	u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM];
> +	int ret = IRQ_NONE;
> +	int i;
> +
> +	/*
> +	 * The irq is shared with the iommu. If the runtime-pm state of the
> +	 * vop2-device is disabled the irq has to be targeted at the iommu.
> +	 */
> +	if (!pm_runtime_get_if_in_use(vop2->dev))
> +		return IRQ_NONE;
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +		struct drm_crtc *crtc = &vp->crtc;
> +		u32 irqs;
> +
> +		irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
> +		vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
> +
> +		if (irqs & VP_INT_DSP_HOLD_VALID) {
> +			complete(&vp->dsp_hold_completion);
> +			ret = IRQ_HANDLED;
> +		}
> +
> +		if (irqs & VP_INT_FS_FIELD) {
> +			drm_crtc_handle_vblank(crtc);
> +			spin_lock(&crtc->dev->event_lock);
> +			if (vp->event) {
> +				u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
> +
> +				if (!(val & BIT(vp->id))) {
> +					drm_crtc_send_vblank_event(crtc, vp->event);
> +					vp->event = NULL;
> +					drm_crtc_vblank_put(crtc);
> +				}
> +			}
> +			spin_unlock(&crtc->dev->event_lock);
> +
> +			ret = IRQ_HANDLED;
> +		}
> +
> +		if (irqs & VP_INT_POST_BUF_EMPTY) {
> +			drm_err_ratelimited(vop2->drm,
> +					    "POST_BUF_EMPTY irq err at vp%d\n",
> +					    vp->id);
> +			ret = IRQ_HANDLED;
> +		}
> +	}
> +
> +	axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS);
> +	vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]);
> +	axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS);
> +	vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]);
> +
> +	for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) {
> +		if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) {
> +			drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n");
> +			ret = IRQ_HANDLED;
> +		}
> +	}
> +
> +	pm_runtime_put(vop2->dev);
> +
> +	return ret;
> +}
> +
> +static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win,
> +			   unsigned long possible_crtcs)
> +{
> +	const struct vop2_win_data *win_data = win->data;
> +	unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +				  BIT(DRM_MODE_BLEND_PREMULTI) |
> +				  BIT(DRM_MODE_BLEND_COVERAGE);
> +	int ret;
> +
> +	ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs,
> +				       &vop2_plane_funcs, win_data->formats,
> +				       win_data->nformats,
> +				       win_data->format_modifiers,
> +				       win->type, win_data->name);
> +	if (ret) {
> +		drm_err(vop2->drm, "failed to initialize plane %d\n", ret);
> +		return ret;
> +	}
> +
> +	drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs);
> +
> +	if (win->data->supported_rotations)
> +		drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0,
> +						   DRM_MODE_ROTATE_0 |
> +						   win->data->supported_rotations);
> +	drm_plane_create_alpha_property(&win->base);
> +	drm_plane_create_blend_mode_property(&win->base, blend_caps);
> +	drm_plane_create_zpos_property(&win->base, win->win_id, 0,
> +				       vop2->registered_num_wins - 1);
> +
> +	return 0;
> +}
> +
> +static struct vop2_video_port *get_activated_vp(struct vop2 *vop2, int n)
> +{
> +	int i, id = 0;
> +
> +	for (i = 0; i < vop2->data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +
> +		if (!vp->crtc.port)
> +			continue;
> +
> +		if (n == id)
> +			return vp;
> +		id++;
> +	}
> +
> +	return NULL;
> +}
> +
> +#define NR_LAYERS 6
> +
> +static int vop2_create_crtc(struct vop2 *vop2)
> +{
> +	const struct vop2_data *vop2_data = vop2->data;
> +	struct drm_device *drm = vop2->drm;
> +	struct device *dev = vop2->dev;
> +	struct drm_plane *plane;
> +	struct device_node *port;
> +	struct vop2_video_port *vp;
> +	u32 possible_crtcs;
> +	int i, nvp, nvps = 0;
> +	int ret;
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		const struct vop2_video_port_data *vp_data;
> +		struct device_node *np;
> +		char dclk_name[9];
> +
> +		vp_data = &vop2_data->vp[i];
> +		vp = &vop2->vps[i];
> +		vp->vop2 = vop2;
> +		vp->id = vp_data->id;
> +		vp->regs = vp_data->regs;
> +		vp->data = vp_data;
> +
> +		snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
> +		vp->dclk = devm_clk_get(vop2->dev, dclk_name);
> +		if (IS_ERR(vp->dclk)) {
> +			drm_err(vop2->drm, "failed to get %s\n", dclk_name);
> +			return PTR_ERR(vp->dclk);
> +		}
> +
> +		np = of_graph_get_remote_node(dev->of_node, i, -1);
> +		if (!np) {
> +			drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
> +			continue;
> +		}
> +		of_node_put(np);
> +
> +		port = of_graph_get_port_by_id(dev->of_node, i);
> +		if (!port) {
> +			drm_err(vop2->drm, "no port node found for video_port%d\n", i);
> +			return -ENOENT;
> +		}
> +
> +		vp->crtc.port = port;
> +		nvps++;
> +	}
> +
> +	nvp = 0;
> +	for (i = 0; i < vop2->registered_num_wins; i++) {
> +		struct vop2_win *win = &vop2->win[i];
> +
> +		if (win->type == DRM_PLANE_TYPE_PRIMARY) {
> +			vp = get_activated_vp(vop2, nvp);
> +
> +			if (vp) {
> +				possible_crtcs = BIT(nvp);
> +				vp->primary_plane = win;
> +
> +				nvp++;
> +			} else {
> +				/* change the unused primary window to overlay window */
> +				win->type = DRM_PLANE_TYPE_OVERLAY;
> +			}
> +		}
> +
> +		if (win->type == DRM_PLANE_TYPE_OVERLAY)
> +			possible_crtcs = (1 << vop2_data->nr_vps) - 1;
> +
> +		ret = vop2_plane_init(vop2, win, possible_crtcs);
> +
> +		if (ret) {
> +			drm_err(vop2->drm, "failed to init plane %s: %d\n",
> +				win->data->name, ret);
> +			return ret;
> +		}
> +	}
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		vp = &vop2->vps[i];
> +
> +		if (!vp->crtc.port)
> +			continue;
> +
> +		plane = &vp->primary_plane->base;
> +
> +		ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL,
> +						&vop2_crtc_funcs,
> +						"video_port%d", vp->id);
> +		if (ret) {
> +			drm_err(vop2->drm, "crtc init for video_port%d failed\n", i);
> +			return ret;
> +		}
> +
> +		drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs);
> +
> +		init_completion(&vp->dsp_hold_completion);
> +	}
> +
> +	for (i = 0; i < vop2->data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +
> +		if (vp->crtc.port)
> +			vp->nlayers = NR_LAYERS / nvps;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vop2_destroy_crtc(struct drm_crtc *crtc)
> +{
> +	of_node_put(crtc->port);
> +
> +	/*
> +	 * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane()
> +	 * references the CRTC.
> +	 */
> +	drm_crtc_cleanup(crtc);
> +}
> +
> +static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = {
> +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0),
> +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5),
> +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14),
> +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18),
> +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31),
> +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31),
> +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31),
> +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31),
> +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31),
> +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19),
> +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15),
> +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31),
> +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8),
> +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9),
> +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11),
> +
> +	/* Scale */
> +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15),
> +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31),
> +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15),
> +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13),
> +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3),
> +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28),
> +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29),
> +
> +	/* cluster regs */
> +	[VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1),
> +	[VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0),
> +	[VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7),
> +
> +	/* afbc regs */
> +	[VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6),
> +	[VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9),
> +	[VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10),
> +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4),
> +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7),
> +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8),
> +	[VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31),
> +	[VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31),
> +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15),
> +	[VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31),
> +	[VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0),
> +	[VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1),
> +	[VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2),
> +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3),
> +	[VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff },
> +	[VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
> +	[VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
> +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff },
> +	[VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff },
> +};
> +
> +static int vop2_cluster_init(struct vop2_win *win)
> +{
> +	struct vop2 *vop2 = win->vop2;
> +	struct reg_field *cluster_regs;
> +	int ret, i;
> +
> +	cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs),
> +			       GFP_KERNEL);
> +	if (!cluster_regs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++)
> +		if (cluster_regs[i].reg != 0xffffffff)
> +			cluster_regs[i].reg += win->offset;
> +
> +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> +					    cluster_regs,
> +					    ARRAY_SIZE(vop2_cluster_regs));
> +
> +	kfree(cluster_regs);
> +
> +	return ret;
> +};
> +
> +static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = {
> +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0),
> +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5),
> +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12),
> +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14),
> +	[VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16),
> +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31),
> +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31),
> +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28),
> +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31),
> +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31),
> +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17),
> +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15),
> +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31),
> +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0),
> +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1),
> +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3),
> +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31),
> +	[VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29),
> +	[VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31),
> +
> +	/* Scale */
> +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15),
> +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31),
> +	[VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15),
> +	[VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31),
> +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1),
> +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3),
> +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5),
> +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7),
> +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9),
> +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11),
> +	[VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13),
> +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15),
> +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17),
> +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8),
> +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9),
> +	[VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10),
> +	[VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11),
> +	[VOP2_WIN_XMIRROR] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff },
> +};
> +
> +static int vop2_esmart_init(struct vop2_win *win)
> +{
> +	struct vop2 *vop2 = win->vop2;
> +	struct reg_field *esmart_regs;
> +	int ret, i;
> +
> +	esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs),
> +			       GFP_KERNEL);
> +	if (!esmart_regs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++)
> +		if (esmart_regs[i].reg != 0xffffffff)
> +			esmart_regs[i].reg += win->offset;
> +
> +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> +					    esmart_regs,
> +					    ARRAY_SIZE(vop2_esmart_regs));
> +
> +	kfree(esmart_regs);
> +
> +	return ret;
> +};
> +
> +static int vop2_win_init(struct vop2 *vop2)
> +{
> +	const struct vop2_data *vop2_data = vop2->data;
> +	struct vop2_win *win;
> +	int i, ret;
> +
> +	for (i = 0; i < vop2_data->win_size; i++) {
> +		const struct vop2_win_data *win_data = &vop2_data->win[i];
> +
> +		win = &vop2->win[i];
> +		win->data = win_data;
> +		win->type = win_data->type;
> +		win->offset = win_data->base;
> +		win->win_id = i;
> +		win->vop2 = vop2;
> +		if (vop2_cluster_window(win))
> +			ret = vop2_cluster_init(win);
> +		else
> +			ret = vop2_esmart_init(win);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	vop2->registered_num_wins = vop2_data->win_size;
> +
> +	return 0;
> +}
> +
> +/*
> + * The window registers are only updated when config done is written.
> + * Until that they read back the old value. As we read-modify-write
> + * these registers mark them as non-volatile. This makes sure we read
> + * the new values from the regmap register cache.
> + */
> +static const struct regmap_range vop2_nonvolatile_range[] = {
> +	regmap_reg_range(0x1000, 0x23ff),
> +};
> +
> +static const struct regmap_access_table vop2_volatile_table = {
> +	.no_ranges = vop2_nonvolatile_range,
> +	.n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range),
> +};
> +
> +static const struct regmap_config vop2_regmap_config = {
> +	.reg_bits	= 32,
> +	.val_bits	= 32,
> +	.reg_stride	= 4,
> +	.max_register	= 0x3000,
> +	.name		= "vop2",
> +	.volatile_table	= &vop2_volatile_table,
> +	.cache_type	= REGCACHE_RBTREE,
> +};
> +
> +static int vop2_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	const struct vop2_data *vop2_data;
> +	struct drm_device *drm = data;
> +	struct vop2 *vop2;
> +	struct resource *res;
> +	size_t alloc_size;
> +	int ret;
> +
> +	vop2_data = of_device_get_match_data(dev);
> +	if (!vop2_data)
> +		return -ENODEV;
> +
> +	/* Allocate vop2 struct and its vop2_win array */
> +	alloc_size = sizeof(*vop2) + sizeof(*vop2->win) * vop2_data->win_size;
> +	vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
> +	if (!vop2)
> +		return -ENOMEM;
> +
> +	vop2->dev = dev;
> +	vop2->data = vop2_data;
> +	vop2->drm = drm;
> +
> +	dev_set_drvdata(dev, vop2);
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
> +	if (!res) {
> +		drm_err(vop2->drm, "failed to get vop2 register byname\n");
> +		return -EINVAL;
> +	}
> +
> +	vop2->regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(vop2->regs))
> +		return PTR_ERR(vop2->regs);
> +	vop2->len = resource_size(res);
> +
> +	vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config);
> +
> +	ret = vop2_win_init(vop2);
> +	if (ret)
> +		return ret;
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma_lut");
> +	if (res) {
> +		vop2->lut_regs = devm_ioremap_resource(dev, res);
> +		if (IS_ERR(vop2->lut_regs))
> +			return PTR_ERR(vop2->lut_regs);
> +	}
> +
> +	vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
> +
> +	vop2->hclk = devm_clk_get(vop2->dev, "hclk");
> +	if (IS_ERR(vop2->hclk)) {
> +		drm_err(vop2->drm, "failed to get hclk source\n");
> +		return PTR_ERR(vop2->hclk);
> +	}
> +
> +	vop2->aclk = devm_clk_get(vop2->dev, "aclk");
> +	if (IS_ERR(vop2->aclk)) {
> +		drm_err(vop2->drm, "failed to get aclk source\n");
> +		return PTR_ERR(vop2->aclk);
> +	}
> +
> +	vop2->irq = platform_get_irq(pdev, 0);
> +	if (vop2->irq < 0) {
> +		drm_err(vop2->drm, "cannot find irq for vop2\n");
> +		return vop2->irq;
> +	}
> +
> +	mutex_init(&vop2->vop2_lock);
> +
> +	ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
> +	if (ret)
> +		return ret;
> +
> +	ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev);
> +	if (ret) {
> +		drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = vop2_create_crtc(vop2);
> +	if (ret)
> +		return ret;
> +
> +	pm_runtime_enable(&pdev->dev);
> +
> +	return 0;
> +}
> +
> +static void vop2_unbind(struct device *dev, struct device *master, void *data)
> +{
> +	struct vop2 *vop2 = dev_get_drvdata(dev);
> +	struct drm_device *drm = vop2->drm;
> +	struct list_head *plane_list = &drm->mode_config.plane_list;
> +	struct list_head *crtc_list = &drm->mode_config.crtc_list;
> +	struct drm_crtc *crtc, *tmpc;
> +	struct drm_plane *plane, *tmpp;
> +
> +	rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
> +
> +	pm_runtime_disable(dev);
> +
> +	list_for_each_entry_safe(plane, tmpp, plane_list, head)
> +		drm_plane_cleanup(plane);
> +
> +	list_for_each_entry_safe(crtc, tmpc, crtc_list, head)
> +		vop2_destroy_crtc(crtc);
> +}
> +
> +const struct component_ops vop2_component_ops = {
> +	.bind = vop2_bind,
> +	.unbind = vop2_unbind,
> +};
> +EXPORT_SYMBOL_GPL(vop2_component_ops);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> new file mode 100644
> index 0000000000000..c727093a06d68
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> @@ -0,0 +1,477 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao@rock-chips.com>
> + */
> +
> +#ifndef _ROCKCHIP_DRM_VOP2_H
> +#define _ROCKCHIP_DRM_VOP2_H
> +
> +#include "rockchip_drm_vop.h"
> +
> +#include <linux/regmap.h>
> +#include <drm/drm_modes.h>
> +
> +#define VOP_FEATURE_OUTPUT_10BIT        BIT(0)
> +
> +#define WIN_FEATURE_AFBDC		BIT(0)
> +#define WIN_FEATURE_CLUSTER		BIT(1)
> +
> +/*
> + *  the delay number of a window in different mode.
> + */
> +enum win_dly_mode {
> +	VOP2_DLY_MODE_DEFAULT,   /**< default mode */
> +	VOP2_DLY_MODE_HISO_S,    /** HDR in SDR out mode, as a SDR window */
> +	VOP2_DLY_MODE_HIHO_H,    /** HDR in HDR out mode, as a HDR window */
> +	VOP2_DLY_MODE_MAX,
> +};
> +
> +struct vop_rect {
> +	int width;
> +	int height;
> +};
> +
> +enum vop2_scale_up_mode {
> +	VOP2_SCALE_UP_NRST_NBOR,
> +	VOP2_SCALE_UP_BIL,
> +	VOP2_SCALE_UP_BIC,
> +};
> +
> +enum vop2_scale_down_mode {
> +	VOP2_SCALE_DOWN_NRST_NBOR,
> +	VOP2_SCALE_DOWN_BIL,
> +	VOP2_SCALE_DOWN_AVG,
> +};
> +
> +enum vop2_win_regs {
> +	VOP2_WIN_ENABLE,
> +	VOP2_WIN_FORMAT,
> +	VOP2_WIN_CSC_MODE,
> +	VOP2_WIN_XMIRROR,
> +	VOP2_WIN_YMIRROR,
> +	VOP2_WIN_RB_SWAP,
> +	VOP2_WIN_UV_SWAP,
> +	VOP2_WIN_ACT_INFO,
> +	VOP2_WIN_DSP_INFO,
> +	VOP2_WIN_DSP_ST,
> +	VOP2_WIN_YRGB_MST,
> +	VOP2_WIN_UV_MST,
> +	VOP2_WIN_YRGB_VIR,
> +	VOP2_WIN_UV_VIR,
> +	VOP2_WIN_YUV_CLIP,
> +	VOP2_WIN_Y2R_EN,
> +	VOP2_WIN_R2Y_EN,
> +	VOP2_WIN_COLOR_KEY,
> +	VOP2_WIN_COLOR_KEY_EN,
> +	VOP2_WIN_DITHER_UP,
> +
> +	/* scale regs */
> +	VOP2_WIN_SCALE_YRGB_X,
> +	VOP2_WIN_SCALE_YRGB_Y,
> +	VOP2_WIN_SCALE_CBCR_X,
> +	VOP2_WIN_SCALE_CBCR_Y,
> +	VOP2_WIN_YRGB_HOR_SCL_MODE,
> +	VOP2_WIN_YRGB_HSCL_FILTER_MODE,
> +	VOP2_WIN_YRGB_VER_SCL_MODE,
> +	VOP2_WIN_YRGB_VSCL_FILTER_MODE,
> +	VOP2_WIN_CBCR_VER_SCL_MODE,
> +	VOP2_WIN_CBCR_HSCL_FILTER_MODE,
> +	VOP2_WIN_CBCR_HOR_SCL_MODE,
> +	VOP2_WIN_CBCR_VSCL_FILTER_MODE,
> +	VOP2_WIN_VSD_CBCR_GT2,
> +	VOP2_WIN_VSD_CBCR_GT4,
> +	VOP2_WIN_VSD_YRGB_GT2,
> +	VOP2_WIN_VSD_YRGB_GT4,
> +	VOP2_WIN_BIC_COE_SEL,
> +
> +	/* cluster regs */
> +	VOP2_WIN_CLUSTER_ENABLE,
> +	VOP2_WIN_AFBC_ENABLE,
> +	VOP2_WIN_CLUSTER_LB_MODE,
> +
> +	/* afbc regs */
> +	VOP2_WIN_AFBC_FORMAT,
> +	VOP2_WIN_AFBC_RB_SWAP,
> +	VOP2_WIN_AFBC_UV_SWAP,
> +	VOP2_WIN_AFBC_AUTO_GATING_EN,
> +	VOP2_WIN_AFBC_BLOCK_SPLIT_EN,
> +	VOP2_WIN_AFBC_PIC_VIR_WIDTH,
> +	VOP2_WIN_AFBC_TILE_NUM,
> +	VOP2_WIN_AFBC_PIC_OFFSET,
> +	VOP2_WIN_AFBC_PIC_SIZE,
> +	VOP2_WIN_AFBC_DSP_OFFSET,
> +	VOP2_WIN_AFBC_TRANSFORM_OFFSET,
> +	VOP2_WIN_AFBC_HDR_PTR,
> +	VOP2_WIN_AFBC_HALF_BLOCK_EN,
> +	VOP2_WIN_AFBC_ROTATE_270,
> +	VOP2_WIN_AFBC_ROTATE_90,
> +	VOP2_WIN_MAX_REG,
> +};
> +
> +struct vop2_win_data {
> +	const char *name;
> +	unsigned int phys_id;
> +
> +	u32 base;
> +	enum drm_plane_type type;
> +
> +	u32 nformats;
> +	const u32 *formats;
> +	const uint64_t *format_modifiers;
> +	const unsigned int supported_rotations;
> +
> +	/**
> +	 * @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2
> +	 */
> +	unsigned int layer_sel_id;
> +	uint64_t feature;
> +
> +	unsigned int max_upscale_factor;
> +	unsigned int max_downscale_factor;
> +	const u8 dly[VOP2_DLY_MODE_MAX];
> +};
> +
> +struct vop2_video_port_data {
> +	unsigned int id;
> +	u32 feature;
> +	u16 gamma_lut_len;
> +	u16 cubic_lut_len;
> +	struct vop_rect max_output;
> +	const u8 pre_scan_max_dly[4];
> +	const struct vop2_video_port_regs *regs;
> +	unsigned int offset;
> +};
> +
> +struct vop2_data {
> +	u8 nr_vps;
> +	const struct vop2_ctrl *ctrl;
> +	const struct vop2_win_data *win;
> +	const struct vop2_video_port_data *vp;
> +	const struct vop_csc_table *csc_table;
> +	struct vop_rect max_input;
> +	struct vop_rect max_output;
> +
> +	unsigned int win_size;
> +	unsigned int soc_id;
> +};
> +
> +/* interrupt define */
> +#define FS_NEW_INTR			BIT(4)
> +#define ADDR_SAME_INTR			BIT(5)
> +#define LINE_FLAG1_INTR			BIT(6)
> +#define WIN0_EMPTY_INTR			BIT(7)
> +#define WIN1_EMPTY_INTR			BIT(8)
> +#define WIN2_EMPTY_INTR			BIT(9)
> +#define WIN3_EMPTY_INTR			BIT(10)
> +#define HWC_EMPTY_INTR			BIT(11)
> +#define POST_BUF_EMPTY_INTR		BIT(12)
> +#define PWM_GEN_INTR			BIT(13)
> +#define DMA_FINISH_INTR			BIT(14)
> +#define FS_FIELD_INTR			BIT(15)
> +#define FE_INTR				BIT(16)
> +#define WB_UV_FIFO_FULL_INTR		BIT(17)
> +#define WB_YRGB_FIFO_FULL_INTR		BIT(18)
> +#define WB_COMPLETE_INTR		BIT(19)
> +
> +/*
> + * display output interface supported by rockchip lcdc
> + */
> +#define ROCKCHIP_OUT_MODE_P888		0
> +#define ROCKCHIP_OUT_MODE_BT1120	0
> +#define ROCKCHIP_OUT_MODE_P666		1
> +#define ROCKCHIP_OUT_MODE_P565		2
> +#define ROCKCHIP_OUT_MODE_BT656		5
> +#define ROCKCHIP_OUT_MODE_S888		8
> +#define ROCKCHIP_OUT_MODE_S888_DUMMY	12
> +#define ROCKCHIP_OUT_MODE_YUV420	14
> +/* for use special outface */
> +#define ROCKCHIP_OUT_MODE_AAAA		15
> +
> +enum vop_csc_format {
> +	CSC_BT601L,
> +	CSC_BT709L,
> +	CSC_BT601F,
> +	CSC_BT2020,
> +};
> +
> +enum src_factor_mode {
> +	SRC_FAC_ALPHA_ZERO,
> +	SRC_FAC_ALPHA_ONE,
> +	SRC_FAC_ALPHA_DST,
> +	SRC_FAC_ALPHA_DST_INVERSE,
> +	SRC_FAC_ALPHA_SRC,
> +	SRC_FAC_ALPHA_SRC_GLOBAL,
> +};
> +
> +enum dst_factor_mode {
> +	DST_FAC_ALPHA_ZERO,
> +	DST_FAC_ALPHA_ONE,
> +	DST_FAC_ALPHA_SRC,
> +	DST_FAC_ALPHA_SRC_INVERSE,
> +	DST_FAC_ALPHA_DST,
> +	DST_FAC_ALPHA_DST_GLOBAL,
> +};
> +
> +#define RK3568_GRF_VO_CON1			0x0364
> +/* System registers definition */
> +#define RK3568_REG_CFG_DONE			0x000
> +#define RK3568_VERSION_INFO			0x004
> +#define RK3568_SYS_AUTO_GATING_CTRL		0x008
> +#define RK3568_SYS_AXI_LUT_CTRL			0x024
> +#define RK3568_DSP_IF_EN			0x028
> +#define RK3568_DSP_IF_CTRL			0x02c
> +#define RK3568_DSP_IF_POL			0x030
> +#define RK3568_WB_CTRL				0x40
> +#define RK3568_WB_XSCAL_FACTOR			0x44
> +#define RK3568_WB_YRGB_MST			0x48
> +#define RK3568_WB_CBR_MST			0x4C
> +#define RK3568_OTP_WIN_EN			0x050
> +#define RK3568_LUT_PORT_SEL			0x058
> +#define RK3568_SYS_STATUS0			0x060
> +#define RK3568_VP_LINE_FLAG(vp)			(0x70 + (vp) * 0x4)
> +#define RK3568_SYS0_INT_EN			0x80
> +#define RK3568_SYS0_INT_CLR			0x84
> +#define RK3568_SYS0_INT_STATUS			0x88
> +#define RK3568_SYS1_INT_EN			0x90
> +#define RK3568_SYS1_INT_CLR			0x94
> +#define RK3568_SYS1_INT_STATUS			0x98
> +#define RK3568_VP_INT_EN(vp)			(0xA0 + (vp) * 0x10)
> +#define RK3568_VP_INT_CLR(vp)			(0xA4 + (vp) * 0x10)
> +#define RK3568_VP_INT_STATUS(vp)		(0xA8 + (vp) * 0x10)
> +#define RK3568_VP_INT_RAW_STATUS(vp)		(0xAC + (vp) * 0x10)
> +
> +/* Video Port registers definition */
> +#define RK3568_VP_DSP_CTRL			0x00
> +#define RK3568_VP_MIPI_CTRL			0x04
> +#define RK3568_VP_COLOR_BAR_CTRL		0x08
> +#define RK3568_VP_3D_LUT_CTRL			0x10
> +#define RK3568_VP_3D_LUT_MST			0x20
> +#define RK3568_VP_DSP_BG			0x2C
> +#define RK3568_VP_PRE_SCAN_HTIMING		0x30
> +#define RK3568_VP_POST_DSP_HACT_INFO		0x34
> +#define RK3568_VP_POST_DSP_VACT_INFO		0x38
> +#define RK3568_VP_POST_SCL_FACTOR_YRGB		0x3C
> +#define RK3568_VP_POST_SCL_CTRL			0x40
> +#define RK3568_VP_POST_DSP_VACT_INFO_F1		0x44
> +#define RK3568_VP_DSP_HTOTAL_HS_END		0x48
> +#define RK3568_VP_DSP_HACT_ST_END		0x4C
> +#define RK3568_VP_DSP_VTOTAL_VS_END		0x50
> +#define RK3568_VP_DSP_VACT_ST_END		0x54
> +#define RK3568_VP_DSP_VS_ST_END_F1		0x58
> +#define RK3568_VP_DSP_VACT_ST_END_F1		0x5C
> +#define RK3568_VP_BCSH_CTRL			0x60
> +#define RK3568_VP_BCSH_BCS			0x64
> +#define RK3568_VP_BCSH_H			0x68
> +#define RK3568_VP_BCSH_COLOR_BAR		0x6C
> +
> +/* Overlay registers definition    */
> +#define RK3568_OVL_CTRL				0x600
> +#define RK3568_OVL_LAYER_SEL			0x604
> +#define RK3568_OVL_PORT_SEL			0x608
> +#define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL	0x610
> +#define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL	0x614
> +#define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL	0x618
> +#define RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL	0x61C
> +#define RK3568_MIX0_SRC_COLOR_CTRL		0x650
> +#define RK3568_MIX0_DST_COLOR_CTRL		0x654
> +#define RK3568_MIX0_SRC_ALPHA_CTRL		0x658
> +#define RK3568_MIX0_DST_ALPHA_CTRL		0x65C
> +#define RK3568_HDR0_SRC_COLOR_CTRL		0x6C0
> +#define RK3568_HDR0_DST_COLOR_CTRL		0x6C4
> +#define RK3568_HDR0_SRC_ALPHA_CTRL		0x6C8
> +#define RK3568_HDR0_DST_ALPHA_CTRL		0x6CC
> +#define RK3568_VP_BG_MIX_CTRL(vp)		(0x6E0 + (vp) * 4)
> +#define RK3568_CLUSTER_DLY_NUM			0x6F0
> +#define RK3568_SMART_DLY_NUM			0x6F8
> +
> +/* Cluster register definition, offset relative to window base */
> +#define RK3568_CLUSTER_WIN_CTRL0		0x00
> +#define RK3568_CLUSTER_WIN_CTRL1		0x04
> +#define RK3568_CLUSTER_WIN_YRGB_MST		0x10
> +#define RK3568_CLUSTER_WIN_CBR_MST		0x14
> +#define RK3568_CLUSTER_WIN_VIR			0x18
> +#define RK3568_CLUSTER_WIN_ACT_INFO		0x20
> +#define RK3568_CLUSTER_WIN_DSP_INFO		0x24
> +#define RK3568_CLUSTER_WIN_DSP_ST		0x28
> +#define RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB	0x30
> +#define RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET	0x3C
> +#define RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL	0x50
> +#define RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE	0x54
> +#define RK3568_CLUSTER_WIN_AFBCD_HDR_PTR	0x58
> +#define RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH	0x5C
> +#define RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE	0x60
> +#define RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET	0x64
> +#define RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET	0x68
> +#define RK3568_CLUSTER_WIN_AFBCD_CTRL		0x6C
> +
> +#define RK3568_CLUSTER_CTRL			0x100
> +
> +/* (E)smart register definition, offset relative to window base */
> +#define RK3568_SMART_CTRL0			0x00
> +#define RK3568_SMART_CTRL1			0x04
> +#define RK3568_SMART_REGION0_CTRL		0x10
> +#define RK3568_SMART_REGION0_YRGB_MST		0x14
> +#define RK3568_SMART_REGION0_CBR_MST		0x18
> +#define RK3568_SMART_REGION0_VIR		0x1C
> +#define RK3568_SMART_REGION0_ACT_INFO		0x20
> +#define RK3568_SMART_REGION0_DSP_INFO		0x24
> +#define RK3568_SMART_REGION0_DSP_ST		0x28
> +#define RK3568_SMART_REGION0_SCL_CTRL		0x30
> +#define RK3568_SMART_REGION0_SCL_FACTOR_YRGB	0x34
> +#define RK3568_SMART_REGION0_SCL_FACTOR_CBR	0x38
> +#define RK3568_SMART_REGION0_SCL_OFFSET		0x3C
> +#define RK3568_SMART_REGION1_CTRL		0x40
> +#define RK3568_SMART_REGION1_YRGB_MST		0x44
> +#define RK3568_SMART_REGION1_CBR_MST		0x48
> +#define RK3568_SMART_REGION1_VIR		0x4C
> +#define RK3568_SMART_REGION1_ACT_INFO		0x50
> +#define RK3568_SMART_REGION1_DSP_INFO		0x54
> +#define RK3568_SMART_REGION1_DSP_ST		0x58
> +#define RK3568_SMART_REGION1_SCL_CTRL		0x60
> +#define RK3568_SMART_REGION1_SCL_FACTOR_YRGB	0x64
> +#define RK3568_SMART_REGION1_SCL_FACTOR_CBR	0x68
> +#define RK3568_SMART_REGION1_SCL_OFFSET		0x6C
> +#define RK3568_SMART_REGION2_CTRL		0x70
> +#define RK3568_SMART_REGION2_YRGB_MST		0x74
> +#define RK3568_SMART_REGION2_CBR_MST		0x78
> +#define RK3568_SMART_REGION2_VIR		0x7C
> +#define RK3568_SMART_REGION2_ACT_INFO		0x80
> +#define RK3568_SMART_REGION2_DSP_INFO		0x84
> +#define RK3568_SMART_REGION2_DSP_ST		0x88
> +#define RK3568_SMART_REGION2_SCL_CTRL		0x90
> +#define RK3568_SMART_REGION2_SCL_FACTOR_YRGB	0x94
> +#define RK3568_SMART_REGION2_SCL_FACTOR_CBR	0x98
> +#define RK3568_SMART_REGION2_SCL_OFFSET		0x9C
> +#define RK3568_SMART_REGION3_CTRL		0xA0
> +#define RK3568_SMART_REGION3_YRGB_MST		0xA4
> +#define RK3568_SMART_REGION3_CBR_MST		0xA8
> +#define RK3568_SMART_REGION3_VIR		0xAC
> +#define RK3568_SMART_REGION3_ACT_INFO		0xB0
> +#define RK3568_SMART_REGION3_DSP_INFO		0xB4
> +#define RK3568_SMART_REGION3_DSP_ST		0xB8
> +#define RK3568_SMART_REGION3_SCL_CTRL		0xC0
> +#define RK3568_SMART_REGION3_SCL_FACTOR_YRGB	0xC4
> +#define RK3568_SMART_REGION3_SCL_FACTOR_CBR	0xC8
> +#define RK3568_SMART_REGION3_SCL_OFFSET		0xCC
> +#define RK3568_SMART_COLOR_KEY_CTRL		0xD0
> +
> +/* HDR register definition */
> +#define RK3568_HDR_LUT_CTRL			0x2000
> +#define RK3568_HDR_LUT_MST			0x2004
> +#define RK3568_SDR2HDR_CTRL			0x2010
> +#define RK3568_HDR2SDR_CTRL			0x2020
> +#define RK3568_HDR2SDR_SRC_RANGE		0x2024
> +#define RK3568_HDR2SDR_NORMFACEETF		0x2028
> +#define RK3568_HDR2SDR_DST_RANGE		0x202C
> +#define RK3568_HDR2SDR_NORMFACCGAMMA		0x2030
> +#define RK3568_HDR_EETF_OETF_Y0			0x203C
> +#define RK3568_HDR_SAT_Y0			0x20C0
> +#define RK3568_HDR_EOTF_OETF_Y0			0x20F0
> +#define RK3568_HDR_OETF_DX_POW1			0x2200
> +#define RK3568_HDR_OETF_XN1			0x2300
> +
> +#define RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN		BIT(15)
> +
> +#define RK3568_VP_DSP_CTRL__STANDBY			BIT(31)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_MODE		BIT(20)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL		GENMASK(19, 18)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_EN		BIT(17)
> +#define RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN		BIT(16)
> +#define RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y		BIT(15)
> +#define RK3568_VP_DSP_CTRL__DSP_RB_SWAP			BIT(9)
> +#define RK3568_VP_DSP_CTRL__DSP_INTERLACE		BIT(7)
> +#define RK3568_VP_DSP_CTRL__DSP_FILED_POL		BIT(6)
> +#define RK3568_VP_DSP_CTRL__P2I_EN			BIT(5)
> +#define RK3568_VP_DSP_CTRL__CORE_DCLK_DIV		BIT(4)
> +#define RK3568_VP_DSP_CTRL__OUT_MODE			GENMASK(3, 0)
> +
> +#define RK3568_VP_POST_SCL_CTRL__VSCALEDOWN		BIT(1)
> +#define RK3568_VP_POST_SCL_CTRL__HSCALEDOWN		BIT(0)
> +
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX		GENMASK(26, 25)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS1			BIT(24)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX		GENMASK(22, 21)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI1			BIT(20)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX		GENMASK(19, 18)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX		GENMASK(17, 16)
> +#define RK3568_SYS_DSP_INFACE_EN_EDP_MUX		GENMASK(15, 14)
> +#define RK3568_SYS_DSP_INFACE_EN_HDMI_MUX		GENMASK(11, 10)
> +#define RK3568_SYS_DSP_INFACE_EN_RGB_MUX		GENMASK(9, 8)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS0			BIT(5)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI0			BIT(4)
> +#define RK3568_SYS_DSP_INFACE_EN_EDP			BIT(3)
> +#define RK3568_SYS_DSP_INFACE_EN_HDMI			BIT(1)
> +#define RK3568_SYS_DSP_INFACE_EN_RGB			BIT(0)
> +
> +#define RK3568_DSP_IF_POL__MIPI_PIN_POL			GENMASK(19, 16)
> +#define RK3568_DSP_IF_POL__EDP_PIN_POL			GENMASK(15, 12)
> +#define RK3568_DSP_IF_POL__HDMI_PIN_POL			GENMASK(7, 4)
> +#define RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL		GENMASK(3, 0)
> +
> +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2_PHASE_LOCK	BIT(5)
> +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2			BIT(4)
> +
> +#define RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN	BIT(31)
> +
> +#define RK3568_DSP_IF_POL__CFG_DONE_IMD			BIT(28)
> +
> +#define VOP2_SYS_AXI_BUS_NUM				2
> +
> +#define VOP2_CLUSTER_YUV444_10				0x12
> +
> +#define VOP2_COLOR_KEY_MASK				BIT(31)
> +
> +#define RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD		BIT(28)
> +
> +#define RK3568_VP_BG_MIX_CTRL__BG_DLY			GENMASK(31, 24)
> +
> +#define RK3568_OVL_PORT_SEL__SEL_PORT			GENMASK(31, 16)
> +#define RK3568_OVL_PORT_SEL__SMART1			GENMASK(31, 30)
> +#define RK3568_OVL_PORT_SEL__SMART0			GENMASK(29, 28)
> +#define RK3568_OVL_PORT_SEL__ESMART1			GENMASK(27, 26)
> +#define RK3568_OVL_PORT_SEL__ESMART0			GENMASK(25, 24)
> +#define RK3568_OVL_PORT_SEL__CLUSTER1			GENMASK(19, 18)
> +#define RK3568_OVL_PORT_SEL__CLUSTER0			GENMASK(17, 16)
> +#define RK3568_OVL_PORT_SET__PORT2_MUX			GENMASK(11, 8)
> +#define RK3568_OVL_PORT_SET__PORT1_MUX			GENMASK(7, 4)
> +#define RK3568_OVL_PORT_SET__PORT0_MUX			GENMASK(3, 0)
> +#define RK3568_OVL_LAYER_SEL__LAYER(layer, x)		((x) << ((layer) * 4))
> +
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_1		GENMASK(31, 24)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_0		GENMASK(23, 16)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_1		GENMASK(15, 8)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_0		GENMASK(7, 0)
> +
> +#define RK3568_SMART_DLY_NUM__SMART1			GENMASK(31, 24)
> +#define RK3568_SMART_DLY_NUM__SMART0			GENMASK(23, 16)
> +#define RK3568_SMART_DLY_NUM__ESMART1			GENMASK(15, 8)
> +#define RK3568_SMART_DLY_NUM__ESMART0			GENMASK(7, 0)
> +
> +#define VP_INT_DSP_HOLD_VALID	BIT(6)
> +#define VP_INT_FS_FIELD		BIT(5)
> +#define VP_INT_POST_BUF_EMPTY	BIT(4)
> +#define VP_INT_LINE_FLAG1	BIT(3)
> +#define VP_INT_LINE_FLAG0	BIT(2)
> +#define VOP2_INT_BUS_ERRPR	BIT(1)
> +#define VP_INT_FS		BIT(0)
> +
> +#define POLFLAG_DCLK_INV	BIT(3)
> +
> +enum vop2_layer_phy_id {
> +	ROCKCHIP_VOP2_CLUSTER0 = 0,
> +	ROCKCHIP_VOP2_CLUSTER1,
> +	ROCKCHIP_VOP2_ESMART0,
> +	ROCKCHIP_VOP2_ESMART1,
> +	ROCKCHIP_VOP2_SMART0,
> +	ROCKCHIP_VOP2_SMART1,
> +	ROCKCHIP_VOP2_CLUSTER2,
> +	ROCKCHIP_VOP2_CLUSTER3,
> +	ROCKCHIP_VOP2_ESMART2,
> +	ROCKCHIP_VOP2_ESMART3,
> +	ROCKCHIP_VOP2_PHY_ID_INVALID = -1,
> +};
> +
> +extern const struct component_ops vop2_component_ops;
> +
> +#endif /* _ROCKCHIP_DRM_VOP2_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> new file mode 100644
> index 0000000000000..9bf0637bf8e26
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> @@ -0,0 +1,281 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) Rockchip Electronics Co.Ltd
> + * Author: Andy Yan <andy.yan@rock-chips.com>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/component.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_plane.h>
> +#include <drm/drm_print.h>
> +
> +#include "rockchip_drm_vop2.h"
> +
> +static const uint32_t formats_win_full_10bit[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV24,
> +};
> +
> +static const uint32_t formats_win_full_10bit_yuyv[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV24,
> +	DRM_FORMAT_YVYU,
> +	DRM_FORMAT_VYUY,
> +};
> +
> +static const uint32_t formats_win_lite[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +};
> +
> +static const uint64_t format_modifiers[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	DRM_FORMAT_MOD_INVALID,
> +};
> +
> +static const uint64_t format_modifiers_afbc[] = {
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_CBR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_CBR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_CBR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_CBR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	/* SPLIT mandates SPARSE, RGB modes mandates YTR */
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_SPARSE |
> +				AFBC_FORMAT_MOD_SPLIT),
> +	DRM_FORMAT_MOD_INVALID,
> +};
> +
> +static const struct vop2_video_port_data rk3568_vop_video_ports[] = {
> +	{
> +		.id = 0,
> +		.feature = VOP_FEATURE_OUTPUT_10BIT,
> +		.gamma_lut_len = 1024,
> +		.cubic_lut_len = 9 * 9 * 9,
> +		.max_output = { 4096, 2304 },
> +		.pre_scan_max_dly = { 69, 53, 53, 42 },
> +		.offset = 0xc00,
> +	}, {
> +		.id = 1,
> +		.gamma_lut_len = 1024,
> +		.max_output = { 2048, 1536 },
> +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> +		.offset = 0xd00,
> +	}, {
> +		.id = 2,
> +		.gamma_lut_len = 1024,
> +		.max_output = { 1920, 1080 },
> +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> +		.offset = 0xe00,
> +	},
> +};
> +
> +/*
> + * rk3568 vop with 2 cluster, 2 esmart win, 2 smart win.
> + * Every cluster can work as 4K win or split into two win.
> + * All win in cluster support AFBCD.
> + *
> + * Every esmart win and smart win support 4 Multi-region.
> + *
> + * Scale filter mode:
> + *
> + * * Cluster:  bicubic for horizontal scale up, others use bilinear
> + * * ESmart:
> + *    * nearest-neighbor/bilinear/bicubic for scale up
> + *    * nearest-neighbor/bilinear/average for scale down
> + *
> + *
> + * @TODO describe the wind like cpu-map dt nodes;
> + */
> +static const struct vop2_win_data rk3568_vop_win_data[] = {
> +	{
> +		.name = "Smart0-win0",
> +		.phys_id = ROCKCHIP_VOP2_SMART0,
> +		.base = 0x1c00,
> +		.formats = formats_win_lite,
> +		.nformats = ARRAY_SIZE(formats_win_lite),
> +		.format_modifiers = format_modifiers,
> +		.layer_sel_id = 3,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Smart1-win0",
> +		.phys_id = ROCKCHIP_VOP2_SMART1,
> +		.formats = formats_win_lite,
> +		.nformats = ARRAY_SIZE(formats_win_lite),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1e00,
> +		.layer_sel_id = 7,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Esmart1-win0",
> +		.phys_id = ROCKCHIP_VOP2_ESMART1,
> +		.formats = formats_win_full_10bit_yuyv,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1a00,
> +		.layer_sel_id = 6,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Esmart0-win0",
> +		.phys_id = ROCKCHIP_VOP2_ESMART0,
> +		.formats = formats_win_full_10bit_yuyv,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1800,
> +		.layer_sel_id = 2,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Cluster0-win0",
> +		.phys_id = ROCKCHIP_VOP2_CLUSTER0,
> +		.base = 0x1000,
> +		.formats = formats_win_full_10bit,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> +		.format_modifiers = format_modifiers_afbc,
> +		.layer_sel_id = 0,
> +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> +		.max_upscale_factor = 4,
> +		.max_downscale_factor = 4,
> +		.dly = { 0, 27, 21 },
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> +	}, {
> +		.name = "Cluster1-win0",
> +		.phys_id = ROCKCHIP_VOP2_CLUSTER1,
> +		.base = 0x1200,
> +		.formats = formats_win_full_10bit,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> +		.format_modifiers = format_modifiers_afbc,
> +		.layer_sel_id = 1,
> +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.max_upscale_factor = 4,
> +		.max_downscale_factor = 4,
> +		.dly = { 0, 27, 21 },
> +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> +	},
> +};
> +
> +static const struct vop2_data rk3566_vop = {
> +	.nr_vps = 3,
> +	.max_input = { 4096, 2304 },
> +	.max_output = { 4096, 2304 },
> +	.vp = rk3568_vop_video_ports,
> +	.win = rk3568_vop_win_data,
> +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> +	.soc_id = 3566,
> +};
> +
> +static const struct vop2_data rk3568_vop = {
> +	.nr_vps = 3,
> +	.max_input = { 4096, 2304 },
> +	.max_output = { 4096, 2304 },
> +	.vp = rk3568_vop_video_ports,
> +	.win = rk3568_vop_win_data,
> +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> +	.soc_id = 3568,
> +};
> +
> +static const struct of_device_id vop2_dt_match[] = {
> +	{
> +		.compatible = "rockchip,rk3566-vop",
> +		.data = &rk3566_vop,
> +	}, {
> +		.compatible = "rockchip,rk3568-vop",
> +		.data = &rk3568_vop,
> +	}, {
> +	},
> +};
> +MODULE_DEVICE_TABLE(of, vop2_dt_match);
> +
> +static int vop2_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +
> +	return component_add(dev, &vop2_component_ops);
> +}
> +
> +static int vop2_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &vop2_component_ops);
> +
> +	return 0;
> +}
> +
> +struct platform_driver vop2_platform_driver = {
> +	.probe = vop2_probe,
> +	.remove = vop2_remove,
> +	.driver = {
> +		.name = "rockchip-vop2",
> +		.of_match_table = of_match_ptr(vop2_dt_match),
> +	},
> +};

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-21 11:51     ` Andy Yan
  0 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-21 11:51 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: linux-arm-kernel, linux-rockchip, devicetree, kernel,
	Benjamin Gaignard, Michael Riesch, Sandy Huang,
	Heiko Stübner, Peter Geis, Kever Yang, tfiga

Hi Sascha:

On 2/17/22 16:29, Sascha Hauer wrote:
> From: Andy Yan <andy.yan@rock-chips.com>
>
> The VOP2 unit is found on Rockchip SoCs beginning with rk3566/rk3568.
> It replaces the VOP unit found in the older Rockchip SoCs.
>
> This driver has been derived from the downstream Rockchip Kernel and
> heavily modified:
>
> - All nonstandard DRM properties have been removed
> - dropped struct vop2_plane_state and pass around less data between
>    functions
> - Dropped all DRM_FORMAT_* not known on upstream
> - rework register access to get rid of excessively used macros
> - Drop all waiting for framesyncs

All the waiting sync in the downstream divers are try to fix special 
problems,

and some of them inherited from upstream vop driver, for example: the 
fb_unref_work,

It submitted by Tomas Figa to upstream vop driver to make the wait for 
flip and asynchronously cursor work

right.

VOP2 share the same hardware design in vblank, so I don't think these 
code are useless.

[0] 
https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-5-git-send-email-tfiga@chromium.org/

[1] 
https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-6-git-send-email-tfiga@chromium.org/

[2] 
https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-4-git-send-email-tfiga@chromium.org/


>
> The driver is tested with HDMI and MIPI-DSI display on a RK3568-EVB
> board. Overlay support is tested with the modetest utility. AFBC support
> on the cluster windows is tested with weston-simple-dmabuf-egl on
> weston using the (yet to be upstreamed) panfrost driver support.
>
> Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>
> Notes:
>      Changes since v5:
>      - consistently use u8/u16/u32 rather than uint8_t/uint16_t/uint32_t
>      - Use spin_lock rather than spin_lock_irqsave
>      - replace printk with drm_dbg
>      - break some overlong lines
>      
>      Changes since v4:
>      - Avoid stack frame overflow by not allocating big array on the stack
>      
>      Changes since v3:
>      - Sort includes
>      - fix typos
>      - Drop spinlock
>      - Use regmap_set_bits()/regmap_clear_bits()
>      - simplify vop2_scale_factor()
>      - simplify vop2_afbc_transform_offset()
>      
>      Changes since v4:
>      - Sort nodes alphabetically
>      
>      Changes since v3:
>      - Fix HDMI connector type
>
>   drivers/gpu/drm/rockchip/Kconfig             |    6 +
>   drivers/gpu/drm/rockchip/Makefile            |    1 +
>   drivers/gpu/drm/rockchip/rockchip_drm_drv.c  |    1 +
>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h  |    6 +-
>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c   |    2 +
>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h  |   15 +
>   drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2708 ++++++++++++++++++
>   drivers/gpu/drm/rockchip/rockchip_drm_vop2.h |  477 +++
>   drivers/gpu/drm/rockchip/rockchip_vop2_reg.c |  281 ++
>   9 files changed, 3496 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
>
> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> index b9b156308460a..4ff0043f0ee70 100644
> --- a/drivers/gpu/drm/rockchip/Kconfig
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
>   	  This selects support for the VOP driver. You should enable it
>   	  on all older SoCs up to RK3399.
>   
> +config ROCKCHIP_VOP2
> +	bool "Rockchip VOP2 driver"
> +	help
> +	  This selects support for the VOP2 driver. You should enable it
> +	  on all newer SoCs beginning form RK3568.
> +
>   config ROCKCHIP_ANALOGIX_DP
>   	bool "Rockchip specific extensions for Analogix DP driver"
>   	depends on ROCKCHIP_VOP
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index dfc5512fdb9f1..3ff7b21c04149 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -6,6 +6,7 @@
>   rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>   		rockchip_drm_gem.o
>   
> +rockchipdrm-$(CONFIG_ROCKCHIP_VOP2) += rockchip_drm_vop2.o rockchip_vop2_reg.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> index 82c8faf1fb6b8..95f6c5985fdd7 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -459,6 +459,7 @@ static int __init rockchip_drm_init(void)
>   
>   	num_rockchip_sub_drivers = 0;
>   	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
> +	ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2);
>   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
>   				CONFIG_ROCKCHIP_LVDS);
>   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> index d3e42410ae5da..5cb207aaaadd0 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -18,7 +18,7 @@
>   
>   #define ROCKCHIP_MAX_FB_BUFFER	3
>   #define ROCKCHIP_MAX_CONNECTOR	2
> -#define ROCKCHIP_MAX_CRTC	2
> +#define ROCKCHIP_MAX_CRTC	4
>   
>   struct drm_device;
>   struct drm_connector;
> @@ -31,6 +31,9 @@ struct rockchip_crtc_state {
>   	int output_bpc;
>   	int output_flags;
>   	bool enable_afbc;
> +	u32 bus_format;
> +	u32 bus_flags;
> +	int color_space;
>   };
>   #define to_rockchip_crtc_state(s) \
>   		container_of(s, struct rockchip_crtc_state, base)
> @@ -63,6 +66,7 @@ extern struct platform_driver rockchip_dp_driver;
>   extern struct platform_driver rockchip_lvds_driver;
>   extern struct platform_driver vop_platform_driver;
>   extern struct platform_driver rk3066_hdmi_driver;
> +extern struct platform_driver vop2_platform_driver;
>   
>   struct rockchip_encoder {
>   	struct device_node *port;
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> index 3aa37e177667e..0d2cb4f3922b8 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> @@ -134,4 +134,6 @@ void rockchip_drm_mode_config_init(struct drm_device *dev)
>   
>   	dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
>   	dev->mode_config.helper_private = &rockchip_mode_config_helpers;
> +
> +	dev->mode_config.normalize_zpos = true;
>   }
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> index 857d97cdc67c6..1e364d7b50e69 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -54,9 +54,23 @@ struct vop_afbc {
>   	struct vop_reg enable;
>   	struct vop_reg win_sel;
>   	struct vop_reg format;
> +	struct vop_reg rb_swap;
> +	struct vop_reg uv_swap;
> +	struct vop_reg auto_gating_en;
> +	struct vop_reg block_split_en;
> +	struct vop_reg pic_vir_width;
> +	struct vop_reg tile_num;
>   	struct vop_reg hreg_block_split;
> +	struct vop_reg pic_offset;
>   	struct vop_reg pic_size;
> +	struct vop_reg dsp_offset;
> +	struct vop_reg transform_offset;
>   	struct vop_reg hdr_ptr;
> +	struct vop_reg half_block_en;
> +	struct vop_reg xmirror;
> +	struct vop_reg ymirror;
> +	struct vop_reg rotate_270;
> +	struct vop_reg rotate_90;
>   	struct vop_reg rstn;
>   };
>   
> @@ -410,4 +424,5 @@ static inline int scl_vop_cal_lb_mode(int width, bool is_yuv)
>   }
>   
>   extern const struct component_ops vop_component_ops;
> +
>   #endif /* _ROCKCHIP_DRM_VOP_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> new file mode 100644
> index 0000000000000..394dd6c583682
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> @@ -0,0 +1,2708 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
> + * Author: Andy Yan <andy.yan@rock-chips.com>
> + */
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/delay.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/swab.h>
> +
> +#include <drm/drm.h>
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_uapi.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_debugfs.h>
> +#include <drm/drm_flip_work.h>
> +#include <drm/drm_plane_helper.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_vblank.h>
> +
> +#include <uapi/linux/videodev2.h>
> +#include <dt-bindings/soc/rockchip,vop2.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_gem.h"
> +#include "rockchip_drm_fb.h"
> +#include "rockchip_drm_vop2.h"
> +
> +/*
> + * VOP2 architecture
> + *
> + +----------+   +-------------+                                                        +-----------+
> + |  Cluster |   | Sel 1 from 6|                                                        | 1 from 3  |
> + |  window0 |   |    Layer0   |                                                        |    RGB    |
> + +----------+   +-------------+              +---------------+    +-------------+      +-----------+
> + +----------+   +-------------+              |N from 6 layers|    |             |
> + |  Cluster |   | Sel 1 from 6|              |   Overlay0    +--->| Video Port0 |      +-----------+
> + |  window1 |   |    Layer1   |              |               |    |             |      | 1 from 3  |
> + +----------+   +-------------+              +---------------+    +-------------+      |   LVDS    |
> + +----------+   +-------------+                                                        +-----------+
> + |  Esmart  |   | Sel 1 from 6|
> + |  window0 |   |   Layer2    |              +---------------+    +-------------+      +-----------+
> + +----------+   +-------------+              |N from 6 Layers|    |             | +--> | 1 from 3  |
> + +----------+   +-------------+   -------->  |   Overlay1    +--->| Video Port1 |      |   MIPI    |
> + |  Esmart  |   | Sel 1 from 6|   -------->  |               |    |             |      +-----------+
> + |  Window1 |   |   Layer3    |              +---------------+    +-------------+
> + +----------+   +-------------+                                                        +-----------+
> + +----------+   +-------------+                                                        | 1 from 3  |
> + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |   HDMI    |
> + |  Window0 |   |    Layer4   |              |N from 6 Layers|    |             |      +-----------+
> + +----------+   +-------------+              |   Overlay2    +--->| Video Port2 |
> + +----------+   +-------------+              |               |    |             |      +-----------+
> + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |  1 from 3 |
> + |  Window1 |   |    Layer5   |                                                        |    eDP    |
> + +----------+   +-------------+                                                        +-----------+
> + *
> + */
> +
> +enum vop2_data_format {
> +	VOP2_FMT_ARGB8888 = 0,
> +	VOP2_FMT_RGB888,
> +	VOP2_FMT_RGB565,
> +	VOP2_FMT_XRGB101010,
> +	VOP2_FMT_YUV420SP,
> +	VOP2_FMT_YUV422SP,
> +	VOP2_FMT_YUV444SP,
> +	VOP2_FMT_YUYV422 = 8,
> +	VOP2_FMT_YUYV420,
> +	VOP2_FMT_VYUY422,
> +	VOP2_FMT_VYUY420,
> +	VOP2_FMT_YUV420SP_TILE_8x4 = 0x10,
> +	VOP2_FMT_YUV420SP_TILE_16x2,
> +	VOP2_FMT_YUV422SP_TILE_8x4,
> +	VOP2_FMT_YUV422SP_TILE_16x2,
> +	VOP2_FMT_YUV420SP_10,
> +	VOP2_FMT_YUV422SP_10,
> +	VOP2_FMT_YUV444SP_10,
> +};
> +
> +enum vop2_afbc_format {
> +	VOP2_AFBC_FMT_RGB565,
> +	VOP2_AFBC_FMT_ARGB2101010 = 2,
> +	VOP2_AFBC_FMT_YUV420_10BIT,
> +	VOP2_AFBC_FMT_RGB888,
> +	VOP2_AFBC_FMT_ARGB8888,
> +	VOP2_AFBC_FMT_YUV420 = 9,
> +	VOP2_AFBC_FMT_YUV422 = 0xb,
> +	VOP2_AFBC_FMT_YUV422_10BIT = 0xe,
> +	VOP2_AFBC_FMT_INVALID = -1,
> +};
> +
> +union vop2_alpha_ctrl {
> +	u32 val;
> +	struct {
> +		/* [0:1] */
> +		u32 color_mode:1;
> +		u32 alpha_mode:1;
> +		/* [2:3] */
> +		u32 blend_mode:2;
> +		u32 alpha_cal_mode:1;
> +		/* [5:7] */
> +		u32 factor_mode:3;
> +		/* [8:9] */
> +		u32 alpha_en:1;
> +		u32 src_dst_swap:1;
> +		u32 reserved:6;
> +		/* [16:23] */
> +		u32 glb_alpha:8;
> +	} bits;
> +};
> +
> +struct vop2_alpha {
> +	union vop2_alpha_ctrl src_color_ctrl;
> +	union vop2_alpha_ctrl dst_color_ctrl;
> +	union vop2_alpha_ctrl src_alpha_ctrl;
> +	union vop2_alpha_ctrl dst_alpha_ctrl;
> +};
> +
> +struct vop2_alpha_config {
> +	bool src_premulti_en;
> +	bool dst_premulti_en;
> +	bool src_pixel_alpha_en;
> +	bool dst_pixel_alpha_en;
> +	u16 src_glb_alpha_value;
> +	u16 dst_glb_alpha_value;
> +};
> +
> +struct vop2_win {
> +	struct vop2 *vop2;
> +	struct drm_plane base;
> +	const struct vop2_win_data *data;
> +	struct regmap_field *reg[VOP2_WIN_MAX_REG];
> +
> +	/**
> +	 * @win_id: graphic window id, a cluster may be split into two
> +	 * graphics windows.
> +	 */
> +	u8 win_id;
> +	u8 delay;
> +	u32 offset;
> +
> +	enum drm_plane_type type;
> +};
> +
> +struct vop2_video_port {
> +	struct drm_crtc crtc;
> +	struct vop2 *vop2;
> +	struct clk *dclk;
> +	unsigned int id;
> +	const struct vop2_video_port_regs *regs;
> +	const struct vop2_video_port_data *data;
> +
> +	struct completion dsp_hold_completion;
> +
> +	/**
> +	 * @win_mask: Bitmask of windows attached to the video port;
> +	 */
> +	u32 win_mask;
> +
> +	struct vop2_win *primary_plane;
> +	struct drm_pending_vblank_event *event;
> +
> +	unsigned int nlayers;
> +};
> +
> +struct vop2 {
> +	struct device *dev;
> +	struct drm_device *drm;
> +	struct vop2_video_port vps[ROCKCHIP_MAX_CRTC];
> +
> +	const struct vop2_data *data;
> +	/*
> +	 * Number of windows that are registered as plane, may be less than the
> +	 * total number of hardware windows.
> +	 */
> +	u32 registered_num_wins;
> +
> +	void __iomem *regs;
> +	struct regmap *map;
> +
> +	struct regmap *grf;
> +
> +	/* physical map length of vop2 register */
> +	u32 len;
> +
> +	void __iomem *lut_regs;
> +
> +	/* protects crtc enable/disable */
> +	struct mutex vop2_lock;
> +
> +	int irq;
> +
> +	/*
> +	 * Some global resources are shared between all video ports(crtcs), so
> +	 * we need a ref counter here.
> +	 */
> +	unsigned int enable_count;
> +	struct clk *hclk;
> +	struct clk *aclk;
> +
> +	/* must be put at the end of the struct */
> +	struct vop2_win win[];
> +};
> +
> +static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
> +{
> +	return container_of(crtc, struct vop2_video_port, crtc);
> +}
> +
> +static struct vop2_win *to_vop2_win(struct drm_plane *p)
> +{
> +	return container_of(p, struct vop2_win, base);
> +}
> +
> +static void vop2_lock(struct vop2 *vop2)
> +{
> +	mutex_lock(&vop2->vop2_lock);
> +}
> +
> +static void vop2_unlock(struct vop2 *vop2)
> +{
> +	mutex_unlock(&vop2->vop2_lock);
> +}
> +
> +static void vop2_writel(struct vop2 *vop2, u32 offset, u32 v)
> +{
> +	regmap_write(vop2->map, offset, v);
> +}
> +
> +static void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
> +{
> +	regmap_write(vp->vop2->map, vp->data->offset + offset, v);
> +}
> +
> +static u32 vop2_readl(struct vop2 *vop2, u32 offset)
> +{
> +	u32 val;
> +
> +	regmap_read(vop2->map, offset, &val);
> +
> +	return val;
> +}
> +
> +static void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v)
> +{
> +	regmap_field_write(win->reg[reg], v);
> +}
> +
> +static bool vop2_cluster_window(const struct vop2_win *win)
> +{
> +	return win->data->feature & WIN_FEATURE_CLUSTER;
> +}
> +
> +static void vop2_cfg_done(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE,
> +			BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> +}
> +
> +static void vop2_win_disable(struct vop2_win *win)
> +{
> +	vop2_win_write(win, VOP2_WIN_ENABLE, 0);
> +
> +	if (vop2_cluster_window(win))
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0);
> +}
> +
> +static enum vop2_data_format vop2_convert_format(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +		return VOP2_FMT_ARGB8888;
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		return VOP2_FMT_RGB888;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		return VOP2_FMT_RGB565;
> +	case DRM_FORMAT_NV12:
> +		return VOP2_FMT_YUV420SP;
> +	case DRM_FORMAT_NV16:
> +		return VOP2_FMT_YUV422SP;
> +	case DRM_FORMAT_NV24:
> +		return VOP2_FMT_YUV444SP;
> +	case DRM_FORMAT_YUYV:
> +	case DRM_FORMAT_YVYU:
> +		return VOP2_FMT_VYUY422;
> +	case DRM_FORMAT_VYUY:
> +	case DRM_FORMAT_UYVY:
> +		return VOP2_FMT_YUYV422;
> +	default:
> +		DRM_ERROR("unsupported format[%08x]\n", format);
> +		return -EINVAL;
> +	}
> +}
> +
> +static enum vop2_afbc_format vop2_convert_afbc_format(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +		return VOP2_AFBC_FMT_ARGB8888;
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		return VOP2_AFBC_FMT_RGB888;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		return VOP2_AFBC_FMT_RGB565;
> +	case DRM_FORMAT_NV12:
> +		return VOP2_AFBC_FMT_YUV420;
> +	case DRM_FORMAT_NV16:
> +		return VOP2_AFBC_FMT_YUV422;
> +	default:
> +		return VOP2_AFBC_FMT_INVALID;
> +	}
> +
> +	return VOP2_AFBC_FMT_INVALID;
> +}
> +
> +static bool vop2_win_rb_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_BGR888:
> +	case DRM_FORMAT_BGR565:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_afbc_rb_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV24:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_afbc_uv_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV12:
> +	case DRM_FORMAT_NV16:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_win_uv_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV12:
> +	case DRM_FORMAT_NV16:
> +	case DRM_FORMAT_NV24:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_win_dither_up(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_BGR565:
> +	case DRM_FORMAT_RGB565:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
> +{
> +	/*
> +	 * FIXME:
> +	 *
> +	 * There is no media type for YUV444 output,
> +	 * so when out_mode is AAAA or P888, assume output is YUV444 on
> +	 * yuv format.
> +	 *
> +	 * From H/W testing, YUV444 mode need a rb swap.
> +	 */
> +	if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 ||
> +	    bus_format == MEDIA_BUS_FMT_VYUY8_1X16 ||
> +	    bus_format == MEDIA_BUS_FMT_YVYU8_2X8 ||
> +	    bus_format == MEDIA_BUS_FMT_VYUY8_2X8 ||
> +	    ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
> +	      bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
> +	     (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
> +	      output_mode == ROCKCHIP_OUT_MODE_P888)))
> +		return true;
> +	else
> +		return false;
> +}
> +
> +static bool is_yuv_output(u32 bus_format)
> +{
> +	switch (bus_format) {
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +	case MEDIA_BUS_FMT_YUV10_1X30:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> +	case MEDIA_BUS_FMT_YUYV8_2X8:
> +	case MEDIA_BUS_FMT_YVYU8_2X8:
> +	case MEDIA_BUS_FMT_UYVY8_2X8:
> +	case MEDIA_BUS_FMT_VYUY8_2X8:
> +	case MEDIA_BUS_FMT_YUYV8_1X16:
> +	case MEDIA_BUS_FMT_YVYU8_1X16:
> +	case MEDIA_BUS_FMT_UYVY8_1X16:
> +	case MEDIA_BUS_FMT_VYUY8_1X16:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool rockchip_afbc(struct drm_plane *plane, u64 modifier)
> +{
> +	int i;
> +
> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> +		return false;
> +
> +	for (i = 0 ; i < plane->modifier_count; i++)
> +		if (plane->modifiers[i] == modifier)
> +			return true;
> +
> +	return false;
> +
> +}
> +
> +static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
> +					u64 modifier)
> +{
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2 *vop2 = win->vop2;
> +
> +	if (modifier == DRM_FORMAT_MOD_INVALID)
> +		return false;
> +
> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> +		return true;
> +
> +	if (!rockchip_afbc(plane, modifier)) {
> +		drm_err(vop2->drm, "Unsupported format modifier 0x%llx\n",
> +			modifier);
> +
> +		return false;
> +	}
> +
> +	return vop2_convert_afbc_format(format) >= 0;
> +}
> +
> +static u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate,
> +				      bool afbc_half_block_en)
> +{
> +	struct drm_rect *src = &pstate->src;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	u32 bpp = fb->format->cpp[0] * 8;
> +	u32 vir_width = (fb->pitches[0] << 3) / bpp;
> +	u32 width = drm_rect_width(src) >> 16;
> +	u32 height = drm_rect_height(src) >> 16;
> +	u32 act_xoffset = src->x1 >> 16;
> +	u32 act_yoffset = src->y1 >> 16;
> +	u32 align16_crop = 0;
> +	u32 align64_crop = 0;
> +	u32 height_tmp;
> +	u8 tx, ty;
> +	u8 bottom_crop_line_num = 0;
> +
> +	/* 16 pixel align */
> +	if (height & 0xf)
> +		align16_crop = 16 - (height & 0xf);
> +
> +	height_tmp = height + align16_crop;
> +
> +	/* 64 pixel align */
> +	if (height_tmp & 0x3f)
> +		align64_crop = 64 - (height_tmp & 0x3f);
> +
> +	bottom_crop_line_num = align16_crop + align64_crop;
> +
> +	switch (pstate->rotation &
> +		(DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y |
> +		 DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) {
> +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> +		tx = 16 - ((act_xoffset + width) & 0xf);
> +		ty = bottom_crop_line_num - act_yoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90:
> +		tx = bottom_crop_line_num - act_yoffset;
> +		ty = vir_width - width - act_xoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270:
> +		tx = act_yoffset;
> +		ty = act_xoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X:
> +		tx = 16 - ((act_xoffset + width) & 0xf);
> +		ty = act_yoffset;
> +		break;
> +	case DRM_MODE_REFLECT_Y:
> +		tx = act_xoffset;
> +		ty = bottom_crop_line_num - act_yoffset;
> +		break;
> +	case DRM_MODE_ROTATE_90:
> +		tx = bottom_crop_line_num - act_yoffset;
> +		ty = act_xoffset;
> +		break;
> +	case DRM_MODE_ROTATE_270:
> +		tx = act_yoffset;
> +		ty = vir_width - width - act_xoffset;
> +		break;
> +	case 0:
> +		tx = act_xoffset;
> +		ty = act_yoffset;
> +		break;
> +	}
> +
> +	if (afbc_half_block_en)
> +		ty &= 0x7f;
> +
> +#define TRANSFORM_XOFFSET GENMASK(7, 0)
> +#define TRANSFORM_YOFFSET GENMASK(23, 16)
> +	return FIELD_PREP(TRANSFORM_XOFFSET, tx) |
> +		FIELD_PREP(TRANSFORM_YOFFSET, ty);
> +}
> +
> +/*
> + * A Cluster window has 2048 x 16 line buffer, which can
> + * works at 2048 x 16(Full) or 4096 x 8 (Half) mode.
> + * for Cluster_lb_mode register:
> + * 0: half mode, for plane input width range 2048 ~ 4096
> + * 1: half mode, for cluster work at 2 * 2048 plane mode
> + * 2: half mode, for rotate_90/270 mode
> + *
> + */
> +static int vop2_get_cluster_lb_mode(struct vop2_win *win,
> +				    struct drm_plane_state *pstate)
> +{
> +	if ((pstate->rotation & DRM_MODE_ROTATE_270) ||
> +	    (pstate->rotation & DRM_MODE_ROTATE_90))
> +		return 2;
> +	else
> +		return 0;
> +}
> +
> +static u16 vop2_scale_factor(u32 src, u32 dst)
> +{
> +	u32 fac;
> +	int shift;
> +
> +	if (src == dst)
> +		return 0;
> +
> +	if (dst < 2)
> +		return U16_MAX;
> +
> +	if (src < 2)
> +		return 0;
> +
> +	if (src > dst)
> +		shift = 12;
> +	else
> +		shift = 16;
> +
> +	src--;
> +	dst--;
> +
> +	fac = DIV_ROUND_UP(src << shift, dst) - 1;
> +
> +	if (fac > U16_MAX)
> +		return U16_MAX;
> +
> +	return fac;
> +}
> +
> +static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
> +			     u32 src_w, u32 src_h, u32 dst_w,
> +			     u32 dst_h, u32 pixel_format)
> +{
> +	const struct drm_format_info *info;
> +	u16 hor_scl_mode, ver_scl_mode;
> +	u16 hscl_filter_mode, vscl_filter_mode;
> +	u8 gt2 = 0;
> +	u8 gt4 = 0;
> +	u32 val;
> +
> +	info = drm_format_info(pixel_format);
> +
> +	if (src_h >= (4 * dst_h)) {
> +		gt4 = 1;
> +		src_h >>= 2;
> +	} else if (src_h >= (2 * dst_h)) {
> +		gt2 = 1;
> +		src_h >>= 1;
> +	}
> +
> +	hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> +	ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> +
> +	if (hor_scl_mode == SCALE_UP)
> +		hscl_filter_mode = VOP2_SCALE_UP_BIC;
> +	else
> +		hscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> +
> +	if (ver_scl_mode == SCALE_UP)
> +		vscl_filter_mode = VOP2_SCALE_UP_BIL;
> +	else
> +		vscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> +
> +	/*
> +	 * RK3568 VOP Esmart/Smart dsp_w should be even pixel
> +	 * at scale down mode
> +	 */
> +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> +		if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) {
> +			drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n",
> +				win->data->name, dst_w);
> +			dst_w++;
> +		}
> +	}
> +
> +	val = vop2_scale_factor(src_w, dst_w);
> +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val);
> +	val = vop2_scale_factor(src_h, dst_h);
> +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val);
> +
> +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4);
> +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2);
> +
> +	vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode);
> +	vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode);
> +
> +	if (vop2_cluster_window(win))
> +		return;
> +
> +	vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode);
> +	vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode);
> +
> +	if (info->is_yuv) {
> +		src_w /= info->hsub;
> +		src_h /= info->vsub;
> +
> +		gt4 = gt2 = 0;
> +
> +		if (src_h >= (4 * dst_h)) {
> +			gt4 = 1;
> +			src_h >>= 2;
> +		} else if (src_h >= (2 * dst_h)) {
> +			gt2 = 1;
> +			src_h >>= 1;
> +		}
> +
> +		hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> +		ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> +
> +		val = vop2_scale_factor(src_w, dst_w);
> +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val);
> +
> +		val = vop2_scale_factor(src_h, dst_h);
> +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val);
> +
> +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4);
> +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2);
> +		vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode);
> +	}
> +}
> +
> +static int vop2_convert_csc_mode(int csc_mode)
> +{
> +	switch (csc_mode) {
> +	case V4L2_COLORSPACE_SMPTE170M:
> +	case V4L2_COLORSPACE_470_SYSTEM_M:
> +	case V4L2_COLORSPACE_470_SYSTEM_BG:
> +		return CSC_BT601L;
> +	case V4L2_COLORSPACE_REC709:
> +	case V4L2_COLORSPACE_SMPTE240M:
> +	case V4L2_COLORSPACE_DEFAULT:
> +		return CSC_BT709L;
> +	case V4L2_COLORSPACE_JPEG:
> +		return CSC_BT601F;
> +	case V4L2_COLORSPACE_BT2020:
> +		return CSC_BT2020;
> +	default:
> +		return CSC_BT709L;
> +	}
> +}
> +
> +/*
> + * colorspace path:
> + *      Input        Win csc                     Output
> + * 1. YUV(2020)  --> Y2R->2020To709->R2Y   --> YUV_OUTPUT(601/709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 2. YUV(2020)  --> bypasss               --> YUV_OUTPUT(2020)
> + *    RGB        --> 709To2020->R2Y       __/
> + *
> + * 3. YUV(2020)  --> Y2R->2020To709        --> RGB_OUTPUT(709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 4. YUV(601/709)-> Y2R->709To2020->R2Y   --> YUV_OUTPUT(2020)
> + *    RGB        --> 709To2020->R2Y       __/
> + *
> + * 5. YUV(601/709)-> bypass                --> YUV_OUTPUT(709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 6. YUV(601/709)-> bypass                --> YUV_OUTPUT(601)
> + *    RGB        --> R2Y(601)             __/
> + *
> + * 7. YUV        --> Y2R(709)              --> RGB_OUTPUT(709)
> + *    RGB        --> bypass               __/
> + *
> + * 8. RGB        --> 709To2020->R2Y        --> YUV_OUTPUT(2020)
> + *
> + * 9. RGB        --> R2Y(709)              --> YUV_OUTPUT(709)
> + *
> + * 10. RGB       --> R2Y(601)              --> YUV_OUTPUT(601)
> + *
> + * 11. RGB       --> bypass                --> RGB_OUTPUT(709)
> + */
> +
> +static void vop2_setup_csc_mode(struct vop2_video_port *vp,
> +				struct vop2_win *win,
> +				struct drm_plane_state *pstate)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
> +	int is_input_yuv = pstate->fb->format->is_yuv;
> +	int is_output_yuv = is_yuv_output(vcstate->bus_format);
> +	int input_csc = V4L2_COLORSPACE_DEFAULT;
> +	int output_csc = vcstate->color_space;
> +	bool r2y_en, y2r_en;
> +	int csc_mode;
> +
> +	if (is_input_yuv && !is_output_yuv) {
> +		y2r_en = true;
> +		r2y_en = false;
> +		csc_mode = vop2_convert_csc_mode(input_csc);
> +	} else if (!is_input_yuv && is_output_yuv) {
> +		y2r_en = false;
> +		r2y_en = true;
> +		csc_mode = vop2_convert_csc_mode(output_csc);
> +	} else {
> +		y2r_en = false;
> +		r2y_en = false;
> +		csc_mode = false;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en);
> +	vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en);
> +	vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
> +}
> +
> +static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq);
> +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq);
> +}
> +
> +static void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16);
> +}
> +
> +static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
> +{
> +	int ret;
> +
> +	ret = clk_prepare_enable(vop2->hclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable hclk - %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(vop2->aclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable aclk - %d\n", ret);
> +		goto err;
> +	}
> +
> +	return 0;
> +err:
> +	clk_disable_unprepare(vop2->hclk);
> +
> +	return ret;
> +}
> +
> +static void vop2_enable(struct vop2 *vop2)
> +{
> +	int ret;
> +
> +	ret = pm_runtime_get_sync(vop2->dev);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret);
> +		return;
> +	}
> +
> +	ret = vop2_core_clks_prepare_enable(vop2);
> +	if (ret) {
> +		pm_runtime_put_sync(vop2->dev);
> +		return;
> +	}
> +
> +	if (vop2->data->soc_id == 3566)
> +		vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
> +
> +	vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> +
> +	/*
> +	 * Disable auto gating, this is a workaround to
> +	 * avoid display image shift when a window enabled.
> +	 */
> +	regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL,
> +			  RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN);
> +
> +	vop2_writel(vop2, RK3568_SYS0_INT_CLR,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS0_INT_EN,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS1_INT_CLR,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS1_INT_EN,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +}
> +
> +static void vop2_disable(struct vop2 *vop2)
> +{
> +	pm_runtime_put_sync(vop2->dev);
> +
> +	clk_disable_unprepare(vop2->aclk);
> +	clk_disable_unprepare(vop2->hclk);
> +}
> +
> +static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
> +				     struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	int ret;
> +
> +	vop2_lock(vop2);
> +
> +	drm_crtc_vblank_off(crtc);
> +
> +	/*
> +	 * Vop standby will take effect at end of current frame,
> +	 * if dsp hold valid irq happen, it means standby complete.
> +	 *
> +	 * we must wait standby complete when we want to disable aclk,
> +	 * if not, memory bus maybe dead.
> +	 */
> +	reinit_completion(&vp->dsp_hold_completion);
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
> +
> +	ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
> +					  msecs_to_jiffies(50));
> +	if (!ret)
> +		drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
> +
> +	vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
> +
> +	clk_disable_unprepare(vp->dclk);
> +
> +	vop2->enable_count--;
> +
> +	if (!vop2->enable_count)
> +		vop2_disable(vop2);
> +
> +	vop2_unlock(vop2);
> +
> +	if (crtc->state->event && !crtc->state->active) {
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +
> +		crtc->state->event = NULL;
> +	}
> +}
> +
> +static int vop2_plane_atomic_check(struct drm_plane *plane,
> +				   struct drm_atomic_state *astate)
> +{
> +	struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane);
> +	struct drm_framebuffer *fb = pstate->fb;
> +	struct drm_crtc *crtc = pstate->crtc;
> +	struct drm_crtc_state *cstate;
> +	struct vop2_video_port *vp;
> +	struct vop2 *vop2;
> +	const struct vop2_data *vop2_data;
> +	struct drm_rect *dest = &pstate->dst;
> +	struct drm_rect *src = &pstate->src;
> +	int min_scale = FRAC_16_16(1, 8);
> +	int max_scale = FRAC_16_16(8, 1);
> +	int format;
> +	int ret;
> +
> +	if (!crtc)
> +		return 0;
> +
> +	vp = to_vop2_video_port(crtc);
> +	vop2 = vp->vop2;
> +	vop2_data = vop2->data;
> +
> +	cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc);
> +	if (WARN_ON(!cstate))
> +		return -EINVAL;
> +
> +	ret = drm_atomic_helper_check_plane_state(pstate, cstate,
> +						  min_scale, max_scale,
> +						  true, true);
> +	if (ret)
> +		return ret;
> +
> +	if (!pstate->visible)
> +		return 0;
> +
> +	format = vop2_convert_format(fb->format->format);
> +	if (format < 0)
> +		return format;
> +
> +	if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 ||
> +	    drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) {
> +		drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
> +			  drm_rect_width(src) >> 16, drm_rect_height(src) >> 16,
> +			  drm_rect_width(dest), drm_rect_height(dest));
> +		pstate->visible = false;
> +		return 0;
> +	}
> +
> +	if (drm_rect_width(src) >> 16 > vop2_data->max_input.width ||
> +	    drm_rect_height(src) >> 16 > vop2_data->max_input.height) {
> +		drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
> +			  drm_rect_width(src) >> 16,
> +			  drm_rect_height(src) >> 16,
> +			  vop2_data->max_input.width,
> +			  vop2_data->max_input.height);
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Src.x1 can be odd when do clip, but yuv plane start point
> +	 * need align with 2 pixel.
> +	 */
> +	if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
> +		drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vop2_plane_atomic_disable(struct drm_plane *plane,
> +				      struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state, plane);
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2 *vop2 = win->vop2;
> +
> +	drm_dbg(vop2->drm, "%s disable\n", win->data->name);
> +
> +	if (!old_pstate->crtc)
> +		return;
> +
> +	vop2_win_disable(win);
> +	vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0);
> +}
> +
> +/*
> + * The color key is 10 bit, so all format should
> + * convert to 10 bit here.
> + */
> +static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
> +{
> +	struct drm_plane_state *pstate = plane->state;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	struct vop2_win *win = to_vop2_win(plane);
> +	u32 color_key_en = 0;
> +	u32 r = 0;
> +	u32 g = 0;
> +	u32 b = 0;
> +
> +	if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) {
> +		vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0);
> +		return;
> +	}
> +
> +	switch (fb->format->format) {
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		r = (color_key & 0xf800) >> 11;
> +		g = (color_key & 0x7e0) >> 5;
> +		b = (color_key & 0x1f);
> +		r <<= 5;
> +		g <<= 4;
> +		b <<= 5;
> +		color_key_en = 1;
> +		break;
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		r = (color_key & 0xff0000) >> 16;
> +		g = (color_key & 0xff00) >> 8;
> +		b = (color_key & 0xff);
> +		r <<= 2;
> +		g <<= 2;
> +		b <<= 2;
> +		color_key_en = 1;
> +		break;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en);
> +	vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b);
> +}
> +
> +static void vop2_plane_atomic_update(struct drm_plane *plane,
> +				     struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *pstate = plane->state;
> +	struct drm_crtc *crtc = pstate->crtc;
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
> +	struct vop2 *vop2 = win->vop2;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	u32 bpp = fb->format->cpp[0] * 8;
> +	u32 actual_w, actual_h, dsp_w, dsp_h;
> +	u32 act_info, dsp_info;
> +	u32 format;
> +	u32 afbc_format;
> +	u32 rb_swap;
> +	u32 uv_swap;
> +	struct drm_rect *src = &pstate->src;
> +	struct drm_rect *dest = &pstate->dst;
> +	u32 afbc_tile_num;
> +	u32 transform_offset;
> +	bool dither_up;
> +	bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
> +	bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
> +	bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
> +	bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
> +	struct rockchip_gem_object *rk_obj;
> +	unsigned long offset;
> +	bool afbc_en;
> +	dma_addr_t yrgb_mst;
> +	dma_addr_t uv_mst;
> +
> +	/*
> +	 * can't update plane when vop2 is disabled.
> +	 */
> +	if (WARN_ON(!crtc))
> +		return;
> +
> +	if (!pstate->visible) {
> +		vop2_plane_atomic_disable(plane, state);
> +		return;
> +	}
> +
> +	afbc_en = rockchip_afbc(plane, fb->modifier);
> +
> +	offset = (src->x1 >> 16) * fb->format->cpp[0];
> +
> +	/*
> +	 * AFBC HDR_PTR must set to the zero offset of the framebuffer.
> +	 */
> +	if (afbc_en)
> +		offset = 0;
> +	else if (pstate->rotation & DRM_MODE_REFLECT_Y)
> +		offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
> +	else
> +		offset += (src->y1 >> 16) * fb->pitches[0];
> +
> +	rk_obj = to_rockchip_obj(fb->obj[0]);
> +
> +	yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
> +	if (fb->format->is_yuv) {
> +		int hsub = fb->format->hsub;
> +		int vsub = fb->format->vsub;
> +
> +		offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub;
> +		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
> +
> +		if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en)
> +			offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub;
> +
> +		rk_obj = to_rockchip_obj(fb->obj[0]);
> +		uv_mst = rk_obj->dma_addr + offset + fb->offsets[1];
> +	}
> +
> +	actual_w = drm_rect_width(src) >> 16;
> +	actual_h = drm_rect_height(src) >> 16;
> +	dsp_w = drm_rect_width(dest);
> +
> +	if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
> +		drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
> +			  vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
> +		dsp_w = adjusted_mode->hdisplay - dest->x1;
> +		if (dsp_w < 4)
> +			dsp_w = 4;
> +		actual_w = dsp_w * actual_w / drm_rect_width(dest);
> +	}
> +
> +	dsp_h = drm_rect_height(dest);
> +
> +	if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
> +		drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
> +			  vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
> +		dsp_h = adjusted_mode->vdisplay - dest->y1;
> +		if (dsp_h < 4)
> +			dsp_h = 4;
> +		actual_h = dsp_h * actual_h / drm_rect_height(dest);
> +	}
> +
> +	/*
> +	 * This is workaround solution for IC design:
> +	 * esmart can't support scale down when actual_w % 16 == 1.
> +	 */
> +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> +		if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
> +			drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
> +				vp->id, win->data->name, actual_w);
> +			actual_w -= 1;
> +		}
> +	}
> +
> +	if (afbc_en && actual_w % 4) {
> +		drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
> +			  vp->id, win->data->name, actual_w);
> +		actual_w = ALIGN_DOWN(actual_w, 4);
> +	}
> +
> +	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
> +	dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff);
> +
> +	format = vop2_convert_format(fb->format->format);
> +
> +	drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n",
> +		      vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h,
> +		      dest->x1, dest->y1,
> +		      &fb->format->format,
> +		      afbc_en ? "AFBC" : "", &yrgb_mst);
> +
> +	if (afbc_en) {
> +		u32 stride;
> +
> +		/* the afbc superblock is 16 x 16 */
> +		afbc_format = vop2_convert_afbc_format(fb->format->format);
> +
> +		/* Enable color transform for YTR */
> +		if (fb->modifier & AFBC_FORMAT_MOD_YTR)
> +			afbc_format |= (1 << 4);
> +
> +		afbc_tile_num = ALIGN(actual_w, 16) >> 4;
> +
> +		/*
> +		 * AFBC pic_vir_width is count by pixel, this is different
> +		 * with WIN_VIR_STRIDE.
> +		 */
> +		stride = (fb->pitches[0] << 3) / bpp;
> +		if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
> +			drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligened\n",
> +				  vp->id, win->data->name, stride);
> +
> +		rb_swap = vop2_afbc_rb_swap(fb->format->format);
> +		uv_swap = vop2_afbc_uv_swap(fb->format->format);
> +		/*
> +		 * This is a workaround for crazy IC design, Cluster
> +		 * and Esmart/Smart use different format configuration map:
> +		 * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart.
> +		 *
> +		 * This is one thing we can make the convert simple:
> +		 * AFBCD decode all the YUV data to YUV444. So we just
> +		 * set all the yuv 10 bit to YUV444_10.
> +		 */
> +		if (fb->format->is_yuv && (bpp == 10))
> +			format = VOP2_CLUSTER_YUV444_10;
> +
> +		if (vop2_cluster_window(win))
> +			vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
> +		vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
> +		vop2_win_write(win, VOP2_WIN_AFBC_RB_SWAP, rb_swap);
> +		vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap);
> +		vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
> +		vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
> +		if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) {
> +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0);
> +			transform_offset = vop2_afbc_transform_offset(pstate, false);
> +		} else {
> +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 1);
> +			transform_offset = vop2_afbc_transform_offset(pstate, true);
> +		}
> +		vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
> +		vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset);
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1));
> +		vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16)));
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride);
> +		vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num);
> +		vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror);
> +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270);
> +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90);
> +	} else {
> +		vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4));
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror);
> +
> +	if (rotate_90 || rotate_270) {
> +		act_info = swahw32(act_info);
> +		actual_w = drm_rect_height(src) >> 16;
> +		actual_h = drm_rect_width(src) >> 16;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_FORMAT, format);
> +	vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst);
> +
> +	rb_swap = vop2_win_rb_swap(fb->format->format);
> +	vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap);
> +	if (!vop2_cluster_window(win)) {
> +		uv_swap = vop2_win_uv_swap(fb->format->format);
> +		vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
> +	}
> +
> +	if (fb->format->is_yuv) {
> +		vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4));
> +		vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst);
> +	}
> +
> +	vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format);
> +	if (!vop2_cluster_window(win))
> +		vop2_plane_setup_color_key(plane, 0);
> +	vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info);
> +	vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info);
> +	vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff));
> +
> +	vop2_setup_csc_mode(vp, win, pstate);
> +
> +	dither_up = vop2_win_dither_up(fb->format->format);
> +	vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up);
> +
> +	vop2_win_write(win, VOP2_WIN_ENABLE, 1);
> +
> +	if (vop2_cluster_window(win)) {
> +		int lb_mode = vop2_get_cluster_lb_mode(win, pstate);
> +
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode);
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1);
> +	}
> +}
> +
> +static const struct drm_plane_helper_funcs vop2_plane_helper_funcs = {
> +	.atomic_check = vop2_plane_atomic_check,
> +	.atomic_update = vop2_plane_atomic_update,
> +	.atomic_disable = vop2_plane_atomic_disable,
> +};
> +
> +static const struct drm_plane_funcs vop2_plane_funcs = {
> +	.update_plane	= drm_atomic_helper_update_plane,
> +	.disable_plane	= drm_atomic_helper_disable_plane,
> +	.destroy = drm_plane_cleanup,
> +	.reset = drm_atomic_helper_plane_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> +	.format_mod_supported = rockchip_vop2_mod_supported,
> +};
> +
> +static int vop2_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD);
> +
> +	return 0;
> +}
> +
> +static void vop2_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD);
> +}
> +
> +static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
> +				 const struct drm_display_mode *mode,
> +				 struct drm_display_mode *adj_mode)
> +{
> +	drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V |
> +					CRTC_STEREO_DOUBLE);
> +
> +	return true;
> +}
> +
> +static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	switch (vcstate->bus_format) {
> +	case MEDIA_BUS_FMT_RGB565_1X16:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> +		break;
> +	case MEDIA_BUS_FMT_RGB666_1X18:
> +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> +	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> +		*dsp_ctrl |= RGB888_TO_RGB666;
> +		break;
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA)
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> +
> +	*dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL,
> +				DITHER_DOWN_ALLEGRO);
> +}
> +
> +static void vop2_post_config(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	u16 vtotal = mode->crtc_vtotal;
> +	u16 hdisplay = mode->crtc_hdisplay;
> +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> +	u16 vdisplay = mode->crtc_vdisplay;
> +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> +	u32 left_margin = 100, right_margin = 100;
> +	u32 top_margin = 100, bottom_margin = 100;
> +	u16 hsize = hdisplay * (left_margin + right_margin) / 200;
> +	u16 vsize = vdisplay * (top_margin + bottom_margin) / 200;
> +	u16 hact_end, vact_end;
> +	u32 val;
> +
> +	vsize = rounddown(vsize, 2);
> +	hsize = rounddown(hsize, 2);
> +	hact_st += hdisplay * (100 - left_margin) / 200;
> +	hact_end = hact_st + hsize;
> +	val = hact_st << 16;
> +	val |= hact_end;
> +	vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val);
> +	vact_st += vdisplay * (100 - top_margin) / 200;
> +	vact_end = vact_st + vsize;
> +	val = vact_st << 16;
> +	val |= vact_end;
> +	vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val);
> +	val = scl_cal_scale2(vdisplay, vsize) << 16;
> +	val |= scl_cal_scale2(hdisplay, hsize);
> +	vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val);
> +
> +	val = 0;
> +	if (hdisplay != hsize)
> +		val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN;
> +	if (vdisplay != vsize)
> +		val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN;
> +	vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val);
> +
> +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> +		u16 vact_st_f1 = vtotal + vact_st + 1;
> +		u16 vact_end_f1 = vact_st_f1 + vsize;
> +
> +		val = vact_st_f1 << 16 | vact_end_f1;
> +		vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
> +	}
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_BG, 0);
> +}
> +
> +static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
> +				u32 polflags)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	u32 die, dip;
> +
> +	die = vop2_readl(vop2, RK3568_DSP_IF_EN);
> +	dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
> +
> +	switch (id) {
> +	case RK3568_VOP2_EP_RGB:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_RGB |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
> +		if (polflags & POLFLAG_DCLK_INV)
> +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
> +		else
> +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16));
> +		break;
> +	case RK3568_VOP2_EP_HDMI:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_HDMI |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id);
> +		break;
> +	case RK3568_VOP2_EP_EDP:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_EDP |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id);
> +		break;
> +	case RK3568_VOP2_EP_MIPI0:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_MIPI1:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_LVDS0:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_LVDS1:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> +		break;
> +	default:
> +		return;
> +	};
> +
> +	dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
> +
> +	vop2_writel(vop2, RK3568_DSP_IF_EN, die);
> +	vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
> +}
> +
> +static int us_to_vertical_line(struct drm_display_mode *mode, int us)
> +{
> +	return us * mode->clock / mode->htotal / 1000;
> +}
> +
> +static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
> +				    struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	const struct vop2_data *vop2_data = vop2->data;
> +	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	unsigned long clock = mode->crtc_clock * 1000;
> +	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
> +	u16 hdisplay = mode->crtc_hdisplay;
> +	u16 htotal = mode->crtc_htotal;
> +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> +	u16 hact_end = hact_st + hdisplay;
> +	u16 vdisplay = mode->crtc_vdisplay;
> +	u16 vtotal = mode->crtc_vtotal;
> +	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
> +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> +	u16 vact_end = vact_st + vdisplay;
> +	u8 out_mode;
> +	u32 dsp_ctrl = 0;
> +	int act_end;
> +	u32 val, polflags;
> +	int ret;
> +	struct drm_encoder *encoder;
> +
> +	drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n",
> +		hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
> +		drm_mode_vrefresh(mode), vcstate->output_type, vp->id);
> +
> +	vop2_lock(vop2);
> +
> +	ret = clk_prepare_enable(vp->dclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
> +			      vp->id, ret);
> +		return;
> +	}
> +
> +	if (!vop2->enable_count)
> +		vop2_enable(vop2);
> +
> +	vop2->enable_count++;
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY);
> +
> +	polflags = 0;
> +	if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
> +		polflags |= POLFLAG_DCLK_INV;
> +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> +		polflags |= BIT(HSYNC_POSITIVE);
> +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> +		polflags |= BIT(VSYNC_POSITIVE);
> +
> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> +		struct device_node *node, *parent;
> +
> +		parent = of_get_parent(rkencoder->port);
> +
> +		for_each_endpoint_of_node(parent, node) {
> +			struct device_node *crtc_port = of_graph_get_remote_port(node);
> +			struct device_node *epn;
> +			struct of_endpoint endpoint;
> +
> +			if (crtc->port != crtc_port) {
> +				of_node_put(crtc_port);
> +				continue;
> +			}
> +
> +			of_node_put(crtc_port);
> +
> +			epn = of_graph_get_remote_endpoint(node);
> +			of_graph_parse_endpoint(epn, &endpoint);
> +			of_node_put(epn);
> +
> +			drm_dbg(vop2->drm, "vp%d is connected to %s, id %d\n",
> +					   vp->id, encoder->name, endpoint.id);
> +			rk3568_set_intf_mux(vp, endpoint.id, polflags);
> +		}
> +		of_node_put(parent);
> +	}
> +
> +	if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
> +	     !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT))
> +		out_mode = ROCKCHIP_OUT_MODE_P888;
> +	else
> +		out_mode = vcstate->output_mode;
> +
> +	dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode);
> +
> +	if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP;
> +
> +	if (is_yuv_output(vcstate->bus_format))
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y;
> +
> +	vop2_dither_setup(crtc, &dsp_ctrl);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len);
> +	val = hact_st << 16;
> +	val |= hact_end;
> +	vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val);
> +
> +	val = vact_st << 16;
> +	val |= vact_end;
> +	vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val);
> +
> +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> +		u16 vact_st_f1 = vtotal + vact_st + 1;
> +		u16 vact_end_f1 = vact_st_f1 + vdisplay;
> +
> +		val = vact_st_f1 << 16 | vact_end_f1;
> +		vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val);
> +
> +		val = vtotal << 16 | (vtotal + vsync_len);
> +		vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val);
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE;
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL;
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN;
> +		vtotal += vtotal + 1;
> +		act_end = vact_end_f1;
> +	} else {
> +		act_end = vact_end;
> +	}
> +
> +	vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id),
> +		    (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len);
> +
> +	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
> +		clock *= 2;
> +	}
> +
> +	vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
> +
> +	clk_set_rate(vp->dclk, clock);
> +
> +	vop2_post_config(crtc);
> +
> +	vop2_cfg_done(vp);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
> +
> +	drm_crtc_vblank_on(crtc);
> +
> +	vop2_unlock(vop2);
> +}
> +
> +static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
> +				  struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_plane *plane;
> +	int nplanes = 0;
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +
> +	drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
> +		nplanes++;
> +
> +	if (nplanes > vp->nlayers)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static bool is_opaque(u16 alpha)
> +{
> +	return (alpha >> 8) == 0xff;
> +}
> +
> +static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config,
> +			     struct vop2_alpha *alpha)
> +{
> +	int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1;
> +	int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1;
> +	int src_color_mode = alpha_config->src_premulti_en ?
> +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> +	int dst_color_mode = alpha_config->dst_premulti_en ?
> +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> +
> +	alpha->src_color_ctrl.val = 0;
> +	alpha->dst_color_ctrl.val = 0;
> +	alpha->src_alpha_ctrl.val = 0;
> +	alpha->dst_alpha_ctrl.val = 0;
> +
> +	if (!alpha_config->src_pixel_alpha_en)
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> +	else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en)
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> +	else
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> +
> +	alpha->src_color_ctrl.bits.alpha_en = 1;
> +
> +	if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) {
> +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> +	} else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) {
> +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE;
> +	} else {
> +		alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> +	}
> +	alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8;
> +	alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +
> +	alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +	alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> +	alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8;
> +	alpha->dst_color_ctrl.bits.color_mode = dst_color_mode;
> +	alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> +
> +	alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode;
> +	alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +	alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE;
> +
> +	alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en)
> +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> +	else
> +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> +	alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION;
> +	alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> +}
> +
> +static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id)
> +{
> +	struct vop2_video_port *vp;
> +	int used_layer = 0;
> +	int i;
> +
> +	for (i = 0; i < port_id; i++) {
> +		vp = &vop2->vps[i];
> +		used_layer += hweight32(vp->win_mask);
> +	}
> +
> +	return used_layer;
> +}
> +
> +static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win)
> +{
> +	u32 offset = (main_win->data->phys_id * 0x10);
> +	struct vop2_alpha_config alpha_config;
> +	struct vop2_alpha alpha;
> +	struct drm_plane_state *bottom_win_pstate;
> +	bool src_pixel_alpha_en = false;
> +	u16 src_glb_alpha_val, dst_glb_alpha_val;
> +	bool premulti_en = false;
> +	bool swap = false;
> +
> +	/* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */
> +	bottom_win_pstate = main_win->base.state;
> +	src_glb_alpha_val = 0;
> +	dst_glb_alpha_val = main_win->base.state->alpha;
> +
> +	if (!bottom_win_pstate->fb)
> +		return;
> +
> +	alpha_config.src_premulti_en = premulti_en;
> +	alpha_config.dst_premulti_en = false;
> +	alpha_config.src_pixel_alpha_en = src_pixel_alpha_en;
> +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> +	alpha_config.src_glb_alpha_value = src_glb_alpha_val;
> +	alpha_config.dst_glb_alpha_value = dst_glb_alpha_val;
> +	vop2_parse_alpha(&alpha_config, &alpha);
> +
> +	alpha.src_color_ctrl.bits.src_dst_swap = swap;
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset,
> +		    alpha.src_color_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset,
> +		    alpha.dst_color_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset,
> +		    alpha.src_alpha_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset,
> +		    alpha.dst_alpha_ctrl.val);
> +}
> +
> +static void vop2_setup_alpha(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_framebuffer *fb;
> +	struct vop2_alpha_config alpha_config;
> +	struct vop2_alpha alpha;
> +	struct drm_plane *plane;
> +	int pixel_alpha_en;
> +	int premulti_en, gpremulti_en = 0;
> +	int mixer_id;
> +	u32 offset;
> +	bool bottom_layer_alpha_en = false;
> +	u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE;
> +
> +	mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id);
> +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> +
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		if (plane->state->normalized_zpos == 0 &&
> +		    !is_opaque(plane->state->alpha) &&
> +		    !vop2_cluster_window(win)) {
> +			/*
> +			 * If bottom layer have global alpha effect [except cluster layer,
> +			 * because cluster have deal with bottom layer global alpha value
> +			 * at cluster mix], bottom layer mix need deal with global alpha.
> +			 */
> +			bottom_layer_alpha_en = true;
> +			dst_global_alpha = plane->state->alpha;
> +		}
> +	}
> +
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +		int zpos = plane->state->normalized_zpos;
> +
> +		if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
> +			premulti_en = 1;
> +		else
> +			premulti_en = 0;
> +
> +		plane = &win->base;
> +		fb = plane->state->fb;
> +
> +		pixel_alpha_en = fb->format->has_alpha;
> +
> +		alpha_config.src_premulti_en = premulti_en;
> +
> +		if (bottom_layer_alpha_en && zpos == 1) {
> +			gpremulti_en = premulti_en;
> +			/* Cd = Cs + (1 - As) * Cd * Agd */
> +			alpha_config.dst_premulti_en = false;
> +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> +			alpha_config.dst_glb_alpha_value = dst_global_alpha;
> +		} else if (vop2_cluster_window(win)) {
> +			/* Mix output data only have pixel alpha */
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = true;
> +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +		} else {
> +			/* Cd = Cs + (1 - As) * Cd */
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +		}
> +
> +		vop2_parse_alpha(&alpha_config, &alpha);
> +
> +		offset = (mixer_id + zpos - 1) * 0x10;
> +		vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset,
> +			    alpha.src_color_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset,
> +			    alpha.dst_color_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset,
> +			    alpha.src_alpha_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset,
> +			    alpha.dst_alpha_ctrl.val);
> +	}
> +
> +	if (vp->id == 0) {
> +		if (bottom_layer_alpha_en) {
> +			/* Transfer pixel alpha to hdr mix */
> +			alpha_config.src_premulti_en = gpremulti_en;
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = true;
> +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			vop2_parse_alpha(&alpha_config, &alpha);
> +
> +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL,
> +				alpha.src_color_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL,
> +				alpha.dst_color_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL,
> +				alpha.src_alpha_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL,
> +				alpha.dst_alpha_ctrl.val);
> +		} else {
> +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0);
> +		}
> +	}
> +}
> +
> +static void vop2_setup_layer_mixer(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_plane *plane;
> +	u32 layer_sel = 0;
> +	u32 port_sel;
> +	unsigned int nlayer, ofs;
> +	struct drm_display_mode *adjusted_mode;
> +	u16 hsync_len;
> +	u16 hdisplay;
> +	u32 bg_dly;
> +	u32 pre_scan_dly;
> +	int i;
> +	struct vop2_video_port *vp0 = &vop2->vps[0];
> +	struct vop2_video_port *vp1 = &vop2->vps[1];
> +	struct vop2_video_port *vp2 = &vop2->vps[2];
> +
> +	adjusted_mode = &vp->crtc.state->adjusted_mode;
> +	hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
> +	hdisplay = adjusted_mode->crtc_hdisplay;
> +
> +	bg_dly = vp->data->pre_scan_max_dly[3];
> +	vop2_writel(vop2, RK3568_VP_BG_MIX_CTRL(vp->id),
> +			    FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly));
> +
> +	pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len;
> +	vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly);
> +
> +	vop2_writel(vop2, RK3568_OVL_CTRL, 0);
> +	port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
> +	port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT;
> +
> +	if (vp0->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX,
> +				     vp0->nlayers - 1);
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8);
> +
> +	if (vp1->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX,
> +				     (vp0->nlayers + vp1->nlayers - 1));
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> +
> +	if (vp2->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX,
> +			(vp2->nlayers + vp1->nlayers + vp0->nlayers - 1));
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> +
> +	layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
> +
> +	ofs = 0;
> +	for (i = 0; i < vp->id; i++)
> +		ofs += vop2->vps[i].nlayers;
> +
> +	nlayer = 0;
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		switch (win->data->phys_id) {
> +		case ROCKCHIP_VOP2_CLUSTER0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_CLUSTER1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_SMART0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_SMART1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id);
> +			break;
> +		}
> +
> +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, win->data->layer_sel_id);
> +		nlayer++;
> +	}
> +
> +	/* configure unused layers to 0x5 (reserved) */
> +	for (; nlayer < 3; nlayer++) {
> +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5);
> +	}
> +
> +	vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel);
> +	vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
> +	vop2_writel(vop2, RK3568_OVL_CTRL, RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD);
> +}
> +
> +static void vop2_setup_dly_for_windows(struct vop2 *vop2)
> +{
> +	struct vop2_win *win;
> +	int i = 0;
> +	u32 cdly = 0, sdly = 0;
> +
> +	for (i = 0; i < vop2->data->win_size; i++) {
> +		u32 dly;
> +
> +		win = &vop2->win[i];
> +		dly = win->delay;
> +
> +		switch (win->data->phys_id) {
> +		case ROCKCHIP_VOP2_CLUSTER0:
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly);
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_CLUSTER1:
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly);
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART0:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART1:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_SMART0:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_SMART1:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly);
> +			break;
> +		}
> +	}
> +
> +	vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly);
> +	vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly);
> +}
> +
> +static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_plane *plane;
> +
> +	vp->win_mask = 0;
> +
> +	drm_atomic_crtc_for_each_plane(plane, crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT];
> +
> +		vp->win_mask |= BIT(win->data->phys_id);
> +
> +		if (vop2_cluster_window(win))
> +			vop2_setup_cluster_alpha(vop2, win);
> +	}
> +
> +	if (!vp->win_mask)
> +		return;
> +
> +	vop2_setup_layer_mixer(vp);
> +	vop2_setup_alpha(vp);
> +	vop2_setup_dly_for_windows(vop2);
> +}
> +
> +static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_post_config(crtc);
> +
> +	vop2_cfg_done(vp);
> +
> +	spin_lock_irq(&crtc->dev->event_lock);
> +
> +	if (crtc->state->event) {
> +		WARN_ON(drm_crtc_vblank_get(crtc));
> +		vp->event = crtc->state->event;
> +		crtc->state->event = NULL;
> +	}
> +
> +	spin_unlock_irq(&crtc->dev->event_lock);
> +}
> +
> +static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
> +	.mode_fixup = vop2_crtc_mode_fixup,
> +	.atomic_check = vop2_crtc_atomic_check,
> +	.atomic_begin = vop2_crtc_atomic_begin,
> +	.atomic_flush = vop2_crtc_atomic_flush,
> +	.atomic_enable = vop2_crtc_atomic_enable,
> +	.atomic_disable = vop2_crtc_atomic_disable,
> +};
> +
> +static void vop2_crtc_reset(struct drm_crtc *crtc)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	if (crtc->state) {
> +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
> +		kfree(vcstate);
> +	}
> +
> +	vcstate = kzalloc(sizeof(*vcstate), GFP_KERNEL);
> +	if (!vcstate)
> +		return;
> +
> +	crtc->state = &vcstate->base;
> +	crtc->state->crtc = crtc;
> +}
> +
> +static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc)
> +{
> +	struct rockchip_crtc_state *vcstate, *old_vcstate;
> +
> +	old_vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	vcstate = kmemdup(old_vcstate, sizeof(*old_vcstate), GFP_KERNEL);
> +	if (!vcstate)
> +		return NULL;
> +
> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
> +
> +	return &vcstate->base;
> +}
> +
> +static void vop2_crtc_destroy_state(struct drm_crtc *crtc,
> +				    struct drm_crtc_state *state)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state);
> +
> +	__drm_atomic_helper_crtc_destroy_state(&vcstate->base);
> +	kfree(vcstate);
> +}
> +
> +static const struct drm_crtc_funcs vop2_crtc_funcs = {
> +	.set_config = drm_atomic_helper_set_config,
> +	.page_flip = drm_atomic_helper_page_flip,
> +	.destroy = drm_crtc_cleanup,
> +	.reset = vop2_crtc_reset,
> +	.atomic_duplicate_state = vop2_crtc_duplicate_state,
> +	.atomic_destroy_state = vop2_crtc_destroy_state,
> +	.enable_vblank = vop2_crtc_enable_vblank,
> +	.disable_vblank = vop2_crtc_disable_vblank,
> +};
> +
> +static irqreturn_t vop2_isr(int irq, void *data)
> +{
> +	struct vop2 *vop2 = data;
> +	const struct vop2_data *vop2_data = vop2->data;
> +	u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM];
> +	int ret = IRQ_NONE;
> +	int i;
> +
> +	/*
> +	 * The irq is shared with the iommu. If the runtime-pm state of the
> +	 * vop2-device is disabled the irq has to be targeted at the iommu.
> +	 */
> +	if (!pm_runtime_get_if_in_use(vop2->dev))
> +		return IRQ_NONE;
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +		struct drm_crtc *crtc = &vp->crtc;
> +		u32 irqs;
> +
> +		irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
> +		vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
> +
> +		if (irqs & VP_INT_DSP_HOLD_VALID) {
> +			complete(&vp->dsp_hold_completion);
> +			ret = IRQ_HANDLED;
> +		}
> +
> +		if (irqs & VP_INT_FS_FIELD) {
> +			drm_crtc_handle_vblank(crtc);
> +			spin_lock(&crtc->dev->event_lock);
> +			if (vp->event) {
> +				u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
> +
> +				if (!(val & BIT(vp->id))) {
> +					drm_crtc_send_vblank_event(crtc, vp->event);
> +					vp->event = NULL;
> +					drm_crtc_vblank_put(crtc);
> +				}
> +			}
> +			spin_unlock(&crtc->dev->event_lock);
> +
> +			ret = IRQ_HANDLED;
> +		}
> +
> +		if (irqs & VP_INT_POST_BUF_EMPTY) {
> +			drm_err_ratelimited(vop2->drm,
> +					    "POST_BUF_EMPTY irq err at vp%d\n",
> +					    vp->id);
> +			ret = IRQ_HANDLED;
> +		}
> +	}
> +
> +	axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS);
> +	vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]);
> +	axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS);
> +	vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]);
> +
> +	for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) {
> +		if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) {
> +			drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n");
> +			ret = IRQ_HANDLED;
> +		}
> +	}
> +
> +	pm_runtime_put(vop2->dev);
> +
> +	return ret;
> +}
> +
> +static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win,
> +			   unsigned long possible_crtcs)
> +{
> +	const struct vop2_win_data *win_data = win->data;
> +	unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +				  BIT(DRM_MODE_BLEND_PREMULTI) |
> +				  BIT(DRM_MODE_BLEND_COVERAGE);
> +	int ret;
> +
> +	ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs,
> +				       &vop2_plane_funcs, win_data->formats,
> +				       win_data->nformats,
> +				       win_data->format_modifiers,
> +				       win->type, win_data->name);
> +	if (ret) {
> +		drm_err(vop2->drm, "failed to initialize plane %d\n", ret);
> +		return ret;
> +	}
> +
> +	drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs);
> +
> +	if (win->data->supported_rotations)
> +		drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0,
> +						   DRM_MODE_ROTATE_0 |
> +						   win->data->supported_rotations);
> +	drm_plane_create_alpha_property(&win->base);
> +	drm_plane_create_blend_mode_property(&win->base, blend_caps);
> +	drm_plane_create_zpos_property(&win->base, win->win_id, 0,
> +				       vop2->registered_num_wins - 1);
> +
> +	return 0;
> +}
> +
> +static struct vop2_video_port *get_activated_vp(struct vop2 *vop2, int n)
> +{
> +	int i, id = 0;
> +
> +	for (i = 0; i < vop2->data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +
> +		if (!vp->crtc.port)
> +			continue;
> +
> +		if (n == id)
> +			return vp;
> +		id++;
> +	}
> +
> +	return NULL;
> +}
> +
> +#define NR_LAYERS 6
> +
> +static int vop2_create_crtc(struct vop2 *vop2)
> +{
> +	const struct vop2_data *vop2_data = vop2->data;
> +	struct drm_device *drm = vop2->drm;
> +	struct device *dev = vop2->dev;
> +	struct drm_plane *plane;
> +	struct device_node *port;
> +	struct vop2_video_port *vp;
> +	u32 possible_crtcs;
> +	int i, nvp, nvps = 0;
> +	int ret;
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		const struct vop2_video_port_data *vp_data;
> +		struct device_node *np;
> +		char dclk_name[9];
> +
> +		vp_data = &vop2_data->vp[i];
> +		vp = &vop2->vps[i];
> +		vp->vop2 = vop2;
> +		vp->id = vp_data->id;
> +		vp->regs = vp_data->regs;
> +		vp->data = vp_data;
> +
> +		snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
> +		vp->dclk = devm_clk_get(vop2->dev, dclk_name);
> +		if (IS_ERR(vp->dclk)) {
> +			drm_err(vop2->drm, "failed to get %s\n", dclk_name);
> +			return PTR_ERR(vp->dclk);
> +		}
> +
> +		np = of_graph_get_remote_node(dev->of_node, i, -1);
> +		if (!np) {
> +			drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
> +			continue;
> +		}
> +		of_node_put(np);
> +
> +		port = of_graph_get_port_by_id(dev->of_node, i);
> +		if (!port) {
> +			drm_err(vop2->drm, "no port node found for video_port%d\n", i);
> +			return -ENOENT;
> +		}
> +
> +		vp->crtc.port = port;
> +		nvps++;
> +	}
> +
> +	nvp = 0;
> +	for (i = 0; i < vop2->registered_num_wins; i++) {
> +		struct vop2_win *win = &vop2->win[i];
> +
> +		if (win->type == DRM_PLANE_TYPE_PRIMARY) {
> +			vp = get_activated_vp(vop2, nvp);
> +
> +			if (vp) {
> +				possible_crtcs = BIT(nvp);
> +				vp->primary_plane = win;
> +
> +				nvp++;
> +			} else {
> +				/* change the unused primary window to overlay window */
> +				win->type = DRM_PLANE_TYPE_OVERLAY;
> +			}
> +		}
> +
> +		if (win->type == DRM_PLANE_TYPE_OVERLAY)
> +			possible_crtcs = (1 << vop2_data->nr_vps) - 1;
> +
> +		ret = vop2_plane_init(vop2, win, possible_crtcs);
> +
> +		if (ret) {
> +			drm_err(vop2->drm, "failed to init plane %s: %d\n",
> +				win->data->name, ret);
> +			return ret;
> +		}
> +	}
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		vp = &vop2->vps[i];
> +
> +		if (!vp->crtc.port)
> +			continue;
> +
> +		plane = &vp->primary_plane->base;
> +
> +		ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL,
> +						&vop2_crtc_funcs,
> +						"video_port%d", vp->id);
> +		if (ret) {
> +			drm_err(vop2->drm, "crtc init for video_port%d failed\n", i);
> +			return ret;
> +		}
> +
> +		drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs);
> +
> +		init_completion(&vp->dsp_hold_completion);
> +	}
> +
> +	for (i = 0; i < vop2->data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +
> +		if (vp->crtc.port)
> +			vp->nlayers = NR_LAYERS / nvps;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vop2_destroy_crtc(struct drm_crtc *crtc)
> +{
> +	of_node_put(crtc->port);
> +
> +	/*
> +	 * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane()
> +	 * references the CRTC.
> +	 */
> +	drm_crtc_cleanup(crtc);
> +}
> +
> +static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = {
> +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0),
> +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5),
> +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14),
> +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18),
> +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31),
> +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31),
> +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31),
> +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31),
> +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31),
> +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19),
> +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15),
> +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31),
> +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8),
> +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9),
> +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11),
> +
> +	/* Scale */
> +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15),
> +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31),
> +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15),
> +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13),
> +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3),
> +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28),
> +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29),
> +
> +	/* cluster regs */
> +	[VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1),
> +	[VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0),
> +	[VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7),
> +
> +	/* afbc regs */
> +	[VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6),
> +	[VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9),
> +	[VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10),
> +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4),
> +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7),
> +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8),
> +	[VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31),
> +	[VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31),
> +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15),
> +	[VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31),
> +	[VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0),
> +	[VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1),
> +	[VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2),
> +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3),
> +	[VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff },
> +	[VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
> +	[VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
> +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff },
> +	[VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff },
> +};
> +
> +static int vop2_cluster_init(struct vop2_win *win)
> +{
> +	struct vop2 *vop2 = win->vop2;
> +	struct reg_field *cluster_regs;
> +	int ret, i;
> +
> +	cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs),
> +			       GFP_KERNEL);
> +	if (!cluster_regs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++)
> +		if (cluster_regs[i].reg != 0xffffffff)
> +			cluster_regs[i].reg += win->offset;
> +
> +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> +					    cluster_regs,
> +					    ARRAY_SIZE(vop2_cluster_regs));
> +
> +	kfree(cluster_regs);
> +
> +	return ret;
> +};
> +
> +static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = {
> +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0),
> +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5),
> +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12),
> +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14),
> +	[VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16),
> +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31),
> +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31),
> +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28),
> +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31),
> +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31),
> +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17),
> +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15),
> +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31),
> +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0),
> +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1),
> +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3),
> +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31),
> +	[VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29),
> +	[VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31),
> +
> +	/* Scale */
> +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15),
> +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31),
> +	[VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15),
> +	[VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31),
> +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1),
> +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3),
> +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5),
> +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7),
> +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9),
> +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11),
> +	[VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13),
> +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15),
> +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17),
> +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8),
> +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9),
> +	[VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10),
> +	[VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11),
> +	[VOP2_WIN_XMIRROR] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff },
> +};
> +
> +static int vop2_esmart_init(struct vop2_win *win)
> +{
> +	struct vop2 *vop2 = win->vop2;
> +	struct reg_field *esmart_regs;
> +	int ret, i;
> +
> +	esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs),
> +			       GFP_KERNEL);
> +	if (!esmart_regs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++)
> +		if (esmart_regs[i].reg != 0xffffffff)
> +			esmart_regs[i].reg += win->offset;
> +
> +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> +					    esmart_regs,
> +					    ARRAY_SIZE(vop2_esmart_regs));
> +
> +	kfree(esmart_regs);
> +
> +	return ret;
> +};
> +
> +static int vop2_win_init(struct vop2 *vop2)
> +{
> +	const struct vop2_data *vop2_data = vop2->data;
> +	struct vop2_win *win;
> +	int i, ret;
> +
> +	for (i = 0; i < vop2_data->win_size; i++) {
> +		const struct vop2_win_data *win_data = &vop2_data->win[i];
> +
> +		win = &vop2->win[i];
> +		win->data = win_data;
> +		win->type = win_data->type;
> +		win->offset = win_data->base;
> +		win->win_id = i;
> +		win->vop2 = vop2;
> +		if (vop2_cluster_window(win))
> +			ret = vop2_cluster_init(win);
> +		else
> +			ret = vop2_esmart_init(win);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	vop2->registered_num_wins = vop2_data->win_size;
> +
> +	return 0;
> +}
> +
> +/*
> + * The window registers are only updated when config done is written.
> + * Until that they read back the old value. As we read-modify-write
> + * these registers mark them as non-volatile. This makes sure we read
> + * the new values from the regmap register cache.
> + */
> +static const struct regmap_range vop2_nonvolatile_range[] = {
> +	regmap_reg_range(0x1000, 0x23ff),
> +};
> +
> +static const struct regmap_access_table vop2_volatile_table = {
> +	.no_ranges = vop2_nonvolatile_range,
> +	.n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range),
> +};
> +
> +static const struct regmap_config vop2_regmap_config = {
> +	.reg_bits	= 32,
> +	.val_bits	= 32,
> +	.reg_stride	= 4,
> +	.max_register	= 0x3000,
> +	.name		= "vop2",
> +	.volatile_table	= &vop2_volatile_table,
> +	.cache_type	= REGCACHE_RBTREE,
> +};
> +
> +static int vop2_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	const struct vop2_data *vop2_data;
> +	struct drm_device *drm = data;
> +	struct vop2 *vop2;
> +	struct resource *res;
> +	size_t alloc_size;
> +	int ret;
> +
> +	vop2_data = of_device_get_match_data(dev);
> +	if (!vop2_data)
> +		return -ENODEV;
> +
> +	/* Allocate vop2 struct and its vop2_win array */
> +	alloc_size = sizeof(*vop2) + sizeof(*vop2->win) * vop2_data->win_size;
> +	vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
> +	if (!vop2)
> +		return -ENOMEM;
> +
> +	vop2->dev = dev;
> +	vop2->data = vop2_data;
> +	vop2->drm = drm;
> +
> +	dev_set_drvdata(dev, vop2);
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
> +	if (!res) {
> +		drm_err(vop2->drm, "failed to get vop2 register byname\n");
> +		return -EINVAL;
> +	}
> +
> +	vop2->regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(vop2->regs))
> +		return PTR_ERR(vop2->regs);
> +	vop2->len = resource_size(res);
> +
> +	vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config);
> +
> +	ret = vop2_win_init(vop2);
> +	if (ret)
> +		return ret;
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma_lut");
> +	if (res) {
> +		vop2->lut_regs = devm_ioremap_resource(dev, res);
> +		if (IS_ERR(vop2->lut_regs))
> +			return PTR_ERR(vop2->lut_regs);
> +	}
> +
> +	vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
> +
> +	vop2->hclk = devm_clk_get(vop2->dev, "hclk");
> +	if (IS_ERR(vop2->hclk)) {
> +		drm_err(vop2->drm, "failed to get hclk source\n");
> +		return PTR_ERR(vop2->hclk);
> +	}
> +
> +	vop2->aclk = devm_clk_get(vop2->dev, "aclk");
> +	if (IS_ERR(vop2->aclk)) {
> +		drm_err(vop2->drm, "failed to get aclk source\n");
> +		return PTR_ERR(vop2->aclk);
> +	}
> +
> +	vop2->irq = platform_get_irq(pdev, 0);
> +	if (vop2->irq < 0) {
> +		drm_err(vop2->drm, "cannot find irq for vop2\n");
> +		return vop2->irq;
> +	}
> +
> +	mutex_init(&vop2->vop2_lock);
> +
> +	ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
> +	if (ret)
> +		return ret;
> +
> +	ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev);
> +	if (ret) {
> +		drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = vop2_create_crtc(vop2);
> +	if (ret)
> +		return ret;
> +
> +	pm_runtime_enable(&pdev->dev);
> +
> +	return 0;
> +}
> +
> +static void vop2_unbind(struct device *dev, struct device *master, void *data)
> +{
> +	struct vop2 *vop2 = dev_get_drvdata(dev);
> +	struct drm_device *drm = vop2->drm;
> +	struct list_head *plane_list = &drm->mode_config.plane_list;
> +	struct list_head *crtc_list = &drm->mode_config.crtc_list;
> +	struct drm_crtc *crtc, *tmpc;
> +	struct drm_plane *plane, *tmpp;
> +
> +	rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
> +
> +	pm_runtime_disable(dev);
> +
> +	list_for_each_entry_safe(plane, tmpp, plane_list, head)
> +		drm_plane_cleanup(plane);
> +
> +	list_for_each_entry_safe(crtc, tmpc, crtc_list, head)
> +		vop2_destroy_crtc(crtc);
> +}
> +
> +const struct component_ops vop2_component_ops = {
> +	.bind = vop2_bind,
> +	.unbind = vop2_unbind,
> +};
> +EXPORT_SYMBOL_GPL(vop2_component_ops);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> new file mode 100644
> index 0000000000000..c727093a06d68
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> @@ -0,0 +1,477 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao@rock-chips.com>
> + */
> +
> +#ifndef _ROCKCHIP_DRM_VOP2_H
> +#define _ROCKCHIP_DRM_VOP2_H
> +
> +#include "rockchip_drm_vop.h"
> +
> +#include <linux/regmap.h>
> +#include <drm/drm_modes.h>
> +
> +#define VOP_FEATURE_OUTPUT_10BIT        BIT(0)
> +
> +#define WIN_FEATURE_AFBDC		BIT(0)
> +#define WIN_FEATURE_CLUSTER		BIT(1)
> +
> +/*
> + *  the delay number of a window in different mode.
> + */
> +enum win_dly_mode {
> +	VOP2_DLY_MODE_DEFAULT,   /**< default mode */
> +	VOP2_DLY_MODE_HISO_S,    /** HDR in SDR out mode, as a SDR window */
> +	VOP2_DLY_MODE_HIHO_H,    /** HDR in HDR out mode, as a HDR window */
> +	VOP2_DLY_MODE_MAX,
> +};
> +
> +struct vop_rect {
> +	int width;
> +	int height;
> +};
> +
> +enum vop2_scale_up_mode {
> +	VOP2_SCALE_UP_NRST_NBOR,
> +	VOP2_SCALE_UP_BIL,
> +	VOP2_SCALE_UP_BIC,
> +};
> +
> +enum vop2_scale_down_mode {
> +	VOP2_SCALE_DOWN_NRST_NBOR,
> +	VOP2_SCALE_DOWN_BIL,
> +	VOP2_SCALE_DOWN_AVG,
> +};
> +
> +enum vop2_win_regs {
> +	VOP2_WIN_ENABLE,
> +	VOP2_WIN_FORMAT,
> +	VOP2_WIN_CSC_MODE,
> +	VOP2_WIN_XMIRROR,
> +	VOP2_WIN_YMIRROR,
> +	VOP2_WIN_RB_SWAP,
> +	VOP2_WIN_UV_SWAP,
> +	VOP2_WIN_ACT_INFO,
> +	VOP2_WIN_DSP_INFO,
> +	VOP2_WIN_DSP_ST,
> +	VOP2_WIN_YRGB_MST,
> +	VOP2_WIN_UV_MST,
> +	VOP2_WIN_YRGB_VIR,
> +	VOP2_WIN_UV_VIR,
> +	VOP2_WIN_YUV_CLIP,
> +	VOP2_WIN_Y2R_EN,
> +	VOP2_WIN_R2Y_EN,
> +	VOP2_WIN_COLOR_KEY,
> +	VOP2_WIN_COLOR_KEY_EN,
> +	VOP2_WIN_DITHER_UP,
> +
> +	/* scale regs */
> +	VOP2_WIN_SCALE_YRGB_X,
> +	VOP2_WIN_SCALE_YRGB_Y,
> +	VOP2_WIN_SCALE_CBCR_X,
> +	VOP2_WIN_SCALE_CBCR_Y,
> +	VOP2_WIN_YRGB_HOR_SCL_MODE,
> +	VOP2_WIN_YRGB_HSCL_FILTER_MODE,
> +	VOP2_WIN_YRGB_VER_SCL_MODE,
> +	VOP2_WIN_YRGB_VSCL_FILTER_MODE,
> +	VOP2_WIN_CBCR_VER_SCL_MODE,
> +	VOP2_WIN_CBCR_HSCL_FILTER_MODE,
> +	VOP2_WIN_CBCR_HOR_SCL_MODE,
> +	VOP2_WIN_CBCR_VSCL_FILTER_MODE,
> +	VOP2_WIN_VSD_CBCR_GT2,
> +	VOP2_WIN_VSD_CBCR_GT4,
> +	VOP2_WIN_VSD_YRGB_GT2,
> +	VOP2_WIN_VSD_YRGB_GT4,
> +	VOP2_WIN_BIC_COE_SEL,
> +
> +	/* cluster regs */
> +	VOP2_WIN_CLUSTER_ENABLE,
> +	VOP2_WIN_AFBC_ENABLE,
> +	VOP2_WIN_CLUSTER_LB_MODE,
> +
> +	/* afbc regs */
> +	VOP2_WIN_AFBC_FORMAT,
> +	VOP2_WIN_AFBC_RB_SWAP,
> +	VOP2_WIN_AFBC_UV_SWAP,
> +	VOP2_WIN_AFBC_AUTO_GATING_EN,
> +	VOP2_WIN_AFBC_BLOCK_SPLIT_EN,
> +	VOP2_WIN_AFBC_PIC_VIR_WIDTH,
> +	VOP2_WIN_AFBC_TILE_NUM,
> +	VOP2_WIN_AFBC_PIC_OFFSET,
> +	VOP2_WIN_AFBC_PIC_SIZE,
> +	VOP2_WIN_AFBC_DSP_OFFSET,
> +	VOP2_WIN_AFBC_TRANSFORM_OFFSET,
> +	VOP2_WIN_AFBC_HDR_PTR,
> +	VOP2_WIN_AFBC_HALF_BLOCK_EN,
> +	VOP2_WIN_AFBC_ROTATE_270,
> +	VOP2_WIN_AFBC_ROTATE_90,
> +	VOP2_WIN_MAX_REG,
> +};
> +
> +struct vop2_win_data {
> +	const char *name;
> +	unsigned int phys_id;
> +
> +	u32 base;
> +	enum drm_plane_type type;
> +
> +	u32 nformats;
> +	const u32 *formats;
> +	const uint64_t *format_modifiers;
> +	const unsigned int supported_rotations;
> +
> +	/**
> +	 * @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2
> +	 */
> +	unsigned int layer_sel_id;
> +	uint64_t feature;
> +
> +	unsigned int max_upscale_factor;
> +	unsigned int max_downscale_factor;
> +	const u8 dly[VOP2_DLY_MODE_MAX];
> +};
> +
> +struct vop2_video_port_data {
> +	unsigned int id;
> +	u32 feature;
> +	u16 gamma_lut_len;
> +	u16 cubic_lut_len;
> +	struct vop_rect max_output;
> +	const u8 pre_scan_max_dly[4];
> +	const struct vop2_video_port_regs *regs;
> +	unsigned int offset;
> +};
> +
> +struct vop2_data {
> +	u8 nr_vps;
> +	const struct vop2_ctrl *ctrl;
> +	const struct vop2_win_data *win;
> +	const struct vop2_video_port_data *vp;
> +	const struct vop_csc_table *csc_table;
> +	struct vop_rect max_input;
> +	struct vop_rect max_output;
> +
> +	unsigned int win_size;
> +	unsigned int soc_id;
> +};
> +
> +/* interrupt define */
> +#define FS_NEW_INTR			BIT(4)
> +#define ADDR_SAME_INTR			BIT(5)
> +#define LINE_FLAG1_INTR			BIT(6)
> +#define WIN0_EMPTY_INTR			BIT(7)
> +#define WIN1_EMPTY_INTR			BIT(8)
> +#define WIN2_EMPTY_INTR			BIT(9)
> +#define WIN3_EMPTY_INTR			BIT(10)
> +#define HWC_EMPTY_INTR			BIT(11)
> +#define POST_BUF_EMPTY_INTR		BIT(12)
> +#define PWM_GEN_INTR			BIT(13)
> +#define DMA_FINISH_INTR			BIT(14)
> +#define FS_FIELD_INTR			BIT(15)
> +#define FE_INTR				BIT(16)
> +#define WB_UV_FIFO_FULL_INTR		BIT(17)
> +#define WB_YRGB_FIFO_FULL_INTR		BIT(18)
> +#define WB_COMPLETE_INTR		BIT(19)
> +
> +/*
> + * display output interface supported by rockchip lcdc
> + */
> +#define ROCKCHIP_OUT_MODE_P888		0
> +#define ROCKCHIP_OUT_MODE_BT1120	0
> +#define ROCKCHIP_OUT_MODE_P666		1
> +#define ROCKCHIP_OUT_MODE_P565		2
> +#define ROCKCHIP_OUT_MODE_BT656		5
> +#define ROCKCHIP_OUT_MODE_S888		8
> +#define ROCKCHIP_OUT_MODE_S888_DUMMY	12
> +#define ROCKCHIP_OUT_MODE_YUV420	14
> +/* for use special outface */
> +#define ROCKCHIP_OUT_MODE_AAAA		15
> +
> +enum vop_csc_format {
> +	CSC_BT601L,
> +	CSC_BT709L,
> +	CSC_BT601F,
> +	CSC_BT2020,
> +};
> +
> +enum src_factor_mode {
> +	SRC_FAC_ALPHA_ZERO,
> +	SRC_FAC_ALPHA_ONE,
> +	SRC_FAC_ALPHA_DST,
> +	SRC_FAC_ALPHA_DST_INVERSE,
> +	SRC_FAC_ALPHA_SRC,
> +	SRC_FAC_ALPHA_SRC_GLOBAL,
> +};
> +
> +enum dst_factor_mode {
> +	DST_FAC_ALPHA_ZERO,
> +	DST_FAC_ALPHA_ONE,
> +	DST_FAC_ALPHA_SRC,
> +	DST_FAC_ALPHA_SRC_INVERSE,
> +	DST_FAC_ALPHA_DST,
> +	DST_FAC_ALPHA_DST_GLOBAL,
> +};
> +
> +#define RK3568_GRF_VO_CON1			0x0364
> +/* System registers definition */
> +#define RK3568_REG_CFG_DONE			0x000
> +#define RK3568_VERSION_INFO			0x004
> +#define RK3568_SYS_AUTO_GATING_CTRL		0x008
> +#define RK3568_SYS_AXI_LUT_CTRL			0x024
> +#define RK3568_DSP_IF_EN			0x028
> +#define RK3568_DSP_IF_CTRL			0x02c
> +#define RK3568_DSP_IF_POL			0x030
> +#define RK3568_WB_CTRL				0x40
> +#define RK3568_WB_XSCAL_FACTOR			0x44
> +#define RK3568_WB_YRGB_MST			0x48
> +#define RK3568_WB_CBR_MST			0x4C
> +#define RK3568_OTP_WIN_EN			0x050
> +#define RK3568_LUT_PORT_SEL			0x058
> +#define RK3568_SYS_STATUS0			0x060
> +#define RK3568_VP_LINE_FLAG(vp)			(0x70 + (vp) * 0x4)
> +#define RK3568_SYS0_INT_EN			0x80
> +#define RK3568_SYS0_INT_CLR			0x84
> +#define RK3568_SYS0_INT_STATUS			0x88
> +#define RK3568_SYS1_INT_EN			0x90
> +#define RK3568_SYS1_INT_CLR			0x94
> +#define RK3568_SYS1_INT_STATUS			0x98
> +#define RK3568_VP_INT_EN(vp)			(0xA0 + (vp) * 0x10)
> +#define RK3568_VP_INT_CLR(vp)			(0xA4 + (vp) * 0x10)
> +#define RK3568_VP_INT_STATUS(vp)		(0xA8 + (vp) * 0x10)
> +#define RK3568_VP_INT_RAW_STATUS(vp)		(0xAC + (vp) * 0x10)
> +
> +/* Video Port registers definition */
> +#define RK3568_VP_DSP_CTRL			0x00
> +#define RK3568_VP_MIPI_CTRL			0x04
> +#define RK3568_VP_COLOR_BAR_CTRL		0x08
> +#define RK3568_VP_3D_LUT_CTRL			0x10
> +#define RK3568_VP_3D_LUT_MST			0x20
> +#define RK3568_VP_DSP_BG			0x2C
> +#define RK3568_VP_PRE_SCAN_HTIMING		0x30
> +#define RK3568_VP_POST_DSP_HACT_INFO		0x34
> +#define RK3568_VP_POST_DSP_VACT_INFO		0x38
> +#define RK3568_VP_POST_SCL_FACTOR_YRGB		0x3C
> +#define RK3568_VP_POST_SCL_CTRL			0x40
> +#define RK3568_VP_POST_DSP_VACT_INFO_F1		0x44
> +#define RK3568_VP_DSP_HTOTAL_HS_END		0x48
> +#define RK3568_VP_DSP_HACT_ST_END		0x4C
> +#define RK3568_VP_DSP_VTOTAL_VS_END		0x50
> +#define RK3568_VP_DSP_VACT_ST_END		0x54
> +#define RK3568_VP_DSP_VS_ST_END_F1		0x58
> +#define RK3568_VP_DSP_VACT_ST_END_F1		0x5C
> +#define RK3568_VP_BCSH_CTRL			0x60
> +#define RK3568_VP_BCSH_BCS			0x64
> +#define RK3568_VP_BCSH_H			0x68
> +#define RK3568_VP_BCSH_COLOR_BAR		0x6C
> +
> +/* Overlay registers definition    */
> +#define RK3568_OVL_CTRL				0x600
> +#define RK3568_OVL_LAYER_SEL			0x604
> +#define RK3568_OVL_PORT_SEL			0x608
> +#define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL	0x610
> +#define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL	0x614
> +#define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL	0x618
> +#define RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL	0x61C
> +#define RK3568_MIX0_SRC_COLOR_CTRL		0x650
> +#define RK3568_MIX0_DST_COLOR_CTRL		0x654
> +#define RK3568_MIX0_SRC_ALPHA_CTRL		0x658
> +#define RK3568_MIX0_DST_ALPHA_CTRL		0x65C
> +#define RK3568_HDR0_SRC_COLOR_CTRL		0x6C0
> +#define RK3568_HDR0_DST_COLOR_CTRL		0x6C4
> +#define RK3568_HDR0_SRC_ALPHA_CTRL		0x6C8
> +#define RK3568_HDR0_DST_ALPHA_CTRL		0x6CC
> +#define RK3568_VP_BG_MIX_CTRL(vp)		(0x6E0 + (vp) * 4)
> +#define RK3568_CLUSTER_DLY_NUM			0x6F0
> +#define RK3568_SMART_DLY_NUM			0x6F8
> +
> +/* Cluster register definition, offset relative to window base */
> +#define RK3568_CLUSTER_WIN_CTRL0		0x00
> +#define RK3568_CLUSTER_WIN_CTRL1		0x04
> +#define RK3568_CLUSTER_WIN_YRGB_MST		0x10
> +#define RK3568_CLUSTER_WIN_CBR_MST		0x14
> +#define RK3568_CLUSTER_WIN_VIR			0x18
> +#define RK3568_CLUSTER_WIN_ACT_INFO		0x20
> +#define RK3568_CLUSTER_WIN_DSP_INFO		0x24
> +#define RK3568_CLUSTER_WIN_DSP_ST		0x28
> +#define RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB	0x30
> +#define RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET	0x3C
> +#define RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL	0x50
> +#define RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE	0x54
> +#define RK3568_CLUSTER_WIN_AFBCD_HDR_PTR	0x58
> +#define RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH	0x5C
> +#define RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE	0x60
> +#define RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET	0x64
> +#define RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET	0x68
> +#define RK3568_CLUSTER_WIN_AFBCD_CTRL		0x6C
> +
> +#define RK3568_CLUSTER_CTRL			0x100
> +
> +/* (E)smart register definition, offset relative to window base */
> +#define RK3568_SMART_CTRL0			0x00
> +#define RK3568_SMART_CTRL1			0x04
> +#define RK3568_SMART_REGION0_CTRL		0x10
> +#define RK3568_SMART_REGION0_YRGB_MST		0x14
> +#define RK3568_SMART_REGION0_CBR_MST		0x18
> +#define RK3568_SMART_REGION0_VIR		0x1C
> +#define RK3568_SMART_REGION0_ACT_INFO		0x20
> +#define RK3568_SMART_REGION0_DSP_INFO		0x24
> +#define RK3568_SMART_REGION0_DSP_ST		0x28
> +#define RK3568_SMART_REGION0_SCL_CTRL		0x30
> +#define RK3568_SMART_REGION0_SCL_FACTOR_YRGB	0x34
> +#define RK3568_SMART_REGION0_SCL_FACTOR_CBR	0x38
> +#define RK3568_SMART_REGION0_SCL_OFFSET		0x3C
> +#define RK3568_SMART_REGION1_CTRL		0x40
> +#define RK3568_SMART_REGION1_YRGB_MST		0x44
> +#define RK3568_SMART_REGION1_CBR_MST		0x48
> +#define RK3568_SMART_REGION1_VIR		0x4C
> +#define RK3568_SMART_REGION1_ACT_INFO		0x50
> +#define RK3568_SMART_REGION1_DSP_INFO		0x54
> +#define RK3568_SMART_REGION1_DSP_ST		0x58
> +#define RK3568_SMART_REGION1_SCL_CTRL		0x60
> +#define RK3568_SMART_REGION1_SCL_FACTOR_YRGB	0x64
> +#define RK3568_SMART_REGION1_SCL_FACTOR_CBR	0x68
> +#define RK3568_SMART_REGION1_SCL_OFFSET		0x6C
> +#define RK3568_SMART_REGION2_CTRL		0x70
> +#define RK3568_SMART_REGION2_YRGB_MST		0x74
> +#define RK3568_SMART_REGION2_CBR_MST		0x78
> +#define RK3568_SMART_REGION2_VIR		0x7C
> +#define RK3568_SMART_REGION2_ACT_INFO		0x80
> +#define RK3568_SMART_REGION2_DSP_INFO		0x84
> +#define RK3568_SMART_REGION2_DSP_ST		0x88
> +#define RK3568_SMART_REGION2_SCL_CTRL		0x90
> +#define RK3568_SMART_REGION2_SCL_FACTOR_YRGB	0x94
> +#define RK3568_SMART_REGION2_SCL_FACTOR_CBR	0x98
> +#define RK3568_SMART_REGION2_SCL_OFFSET		0x9C
> +#define RK3568_SMART_REGION3_CTRL		0xA0
> +#define RK3568_SMART_REGION3_YRGB_MST		0xA4
> +#define RK3568_SMART_REGION3_CBR_MST		0xA8
> +#define RK3568_SMART_REGION3_VIR		0xAC
> +#define RK3568_SMART_REGION3_ACT_INFO		0xB0
> +#define RK3568_SMART_REGION3_DSP_INFO		0xB4
> +#define RK3568_SMART_REGION3_DSP_ST		0xB8
> +#define RK3568_SMART_REGION3_SCL_CTRL		0xC0
> +#define RK3568_SMART_REGION3_SCL_FACTOR_YRGB	0xC4
> +#define RK3568_SMART_REGION3_SCL_FACTOR_CBR	0xC8
> +#define RK3568_SMART_REGION3_SCL_OFFSET		0xCC
> +#define RK3568_SMART_COLOR_KEY_CTRL		0xD0
> +
> +/* HDR register definition */
> +#define RK3568_HDR_LUT_CTRL			0x2000
> +#define RK3568_HDR_LUT_MST			0x2004
> +#define RK3568_SDR2HDR_CTRL			0x2010
> +#define RK3568_HDR2SDR_CTRL			0x2020
> +#define RK3568_HDR2SDR_SRC_RANGE		0x2024
> +#define RK3568_HDR2SDR_NORMFACEETF		0x2028
> +#define RK3568_HDR2SDR_DST_RANGE		0x202C
> +#define RK3568_HDR2SDR_NORMFACCGAMMA		0x2030
> +#define RK3568_HDR_EETF_OETF_Y0			0x203C
> +#define RK3568_HDR_SAT_Y0			0x20C0
> +#define RK3568_HDR_EOTF_OETF_Y0			0x20F0
> +#define RK3568_HDR_OETF_DX_POW1			0x2200
> +#define RK3568_HDR_OETF_XN1			0x2300
> +
> +#define RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN		BIT(15)
> +
> +#define RK3568_VP_DSP_CTRL__STANDBY			BIT(31)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_MODE		BIT(20)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL		GENMASK(19, 18)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_EN		BIT(17)
> +#define RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN		BIT(16)
> +#define RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y		BIT(15)
> +#define RK3568_VP_DSP_CTRL__DSP_RB_SWAP			BIT(9)
> +#define RK3568_VP_DSP_CTRL__DSP_INTERLACE		BIT(7)
> +#define RK3568_VP_DSP_CTRL__DSP_FILED_POL		BIT(6)
> +#define RK3568_VP_DSP_CTRL__P2I_EN			BIT(5)
> +#define RK3568_VP_DSP_CTRL__CORE_DCLK_DIV		BIT(4)
> +#define RK3568_VP_DSP_CTRL__OUT_MODE			GENMASK(3, 0)
> +
> +#define RK3568_VP_POST_SCL_CTRL__VSCALEDOWN		BIT(1)
> +#define RK3568_VP_POST_SCL_CTRL__HSCALEDOWN		BIT(0)
> +
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX		GENMASK(26, 25)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS1			BIT(24)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX		GENMASK(22, 21)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI1			BIT(20)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX		GENMASK(19, 18)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX		GENMASK(17, 16)
> +#define RK3568_SYS_DSP_INFACE_EN_EDP_MUX		GENMASK(15, 14)
> +#define RK3568_SYS_DSP_INFACE_EN_HDMI_MUX		GENMASK(11, 10)
> +#define RK3568_SYS_DSP_INFACE_EN_RGB_MUX		GENMASK(9, 8)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS0			BIT(5)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI0			BIT(4)
> +#define RK3568_SYS_DSP_INFACE_EN_EDP			BIT(3)
> +#define RK3568_SYS_DSP_INFACE_EN_HDMI			BIT(1)
> +#define RK3568_SYS_DSP_INFACE_EN_RGB			BIT(0)
> +
> +#define RK3568_DSP_IF_POL__MIPI_PIN_POL			GENMASK(19, 16)
> +#define RK3568_DSP_IF_POL__EDP_PIN_POL			GENMASK(15, 12)
> +#define RK3568_DSP_IF_POL__HDMI_PIN_POL			GENMASK(7, 4)
> +#define RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL		GENMASK(3, 0)
> +
> +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2_PHASE_LOCK	BIT(5)
> +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2			BIT(4)
> +
> +#define RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN	BIT(31)
> +
> +#define RK3568_DSP_IF_POL__CFG_DONE_IMD			BIT(28)
> +
> +#define VOP2_SYS_AXI_BUS_NUM				2
> +
> +#define VOP2_CLUSTER_YUV444_10				0x12
> +
> +#define VOP2_COLOR_KEY_MASK				BIT(31)
> +
> +#define RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD		BIT(28)
> +
> +#define RK3568_VP_BG_MIX_CTRL__BG_DLY			GENMASK(31, 24)
> +
> +#define RK3568_OVL_PORT_SEL__SEL_PORT			GENMASK(31, 16)
> +#define RK3568_OVL_PORT_SEL__SMART1			GENMASK(31, 30)
> +#define RK3568_OVL_PORT_SEL__SMART0			GENMASK(29, 28)
> +#define RK3568_OVL_PORT_SEL__ESMART1			GENMASK(27, 26)
> +#define RK3568_OVL_PORT_SEL__ESMART0			GENMASK(25, 24)
> +#define RK3568_OVL_PORT_SEL__CLUSTER1			GENMASK(19, 18)
> +#define RK3568_OVL_PORT_SEL__CLUSTER0			GENMASK(17, 16)
> +#define RK3568_OVL_PORT_SET__PORT2_MUX			GENMASK(11, 8)
> +#define RK3568_OVL_PORT_SET__PORT1_MUX			GENMASK(7, 4)
> +#define RK3568_OVL_PORT_SET__PORT0_MUX			GENMASK(3, 0)
> +#define RK3568_OVL_LAYER_SEL__LAYER(layer, x)		((x) << ((layer) * 4))
> +
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_1		GENMASK(31, 24)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_0		GENMASK(23, 16)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_1		GENMASK(15, 8)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_0		GENMASK(7, 0)
> +
> +#define RK3568_SMART_DLY_NUM__SMART1			GENMASK(31, 24)
> +#define RK3568_SMART_DLY_NUM__SMART0			GENMASK(23, 16)
> +#define RK3568_SMART_DLY_NUM__ESMART1			GENMASK(15, 8)
> +#define RK3568_SMART_DLY_NUM__ESMART0			GENMASK(7, 0)
> +
> +#define VP_INT_DSP_HOLD_VALID	BIT(6)
> +#define VP_INT_FS_FIELD		BIT(5)
> +#define VP_INT_POST_BUF_EMPTY	BIT(4)
> +#define VP_INT_LINE_FLAG1	BIT(3)
> +#define VP_INT_LINE_FLAG0	BIT(2)
> +#define VOP2_INT_BUS_ERRPR	BIT(1)
> +#define VP_INT_FS		BIT(0)
> +
> +#define POLFLAG_DCLK_INV	BIT(3)
> +
> +enum vop2_layer_phy_id {
> +	ROCKCHIP_VOP2_CLUSTER0 = 0,
> +	ROCKCHIP_VOP2_CLUSTER1,
> +	ROCKCHIP_VOP2_ESMART0,
> +	ROCKCHIP_VOP2_ESMART1,
> +	ROCKCHIP_VOP2_SMART0,
> +	ROCKCHIP_VOP2_SMART1,
> +	ROCKCHIP_VOP2_CLUSTER2,
> +	ROCKCHIP_VOP2_CLUSTER3,
> +	ROCKCHIP_VOP2_ESMART2,
> +	ROCKCHIP_VOP2_ESMART3,
> +	ROCKCHIP_VOP2_PHY_ID_INVALID = -1,
> +};
> +
> +extern const struct component_ops vop2_component_ops;
> +
> +#endif /* _ROCKCHIP_DRM_VOP2_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> new file mode 100644
> index 0000000000000..9bf0637bf8e26
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> @@ -0,0 +1,281 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) Rockchip Electronics Co.Ltd
> + * Author: Andy Yan <andy.yan@rock-chips.com>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/component.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_plane.h>
> +#include <drm/drm_print.h>
> +
> +#include "rockchip_drm_vop2.h"
> +
> +static const uint32_t formats_win_full_10bit[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV24,
> +};
> +
> +static const uint32_t formats_win_full_10bit_yuyv[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV24,
> +	DRM_FORMAT_YVYU,
> +	DRM_FORMAT_VYUY,
> +};
> +
> +static const uint32_t formats_win_lite[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +};
> +
> +static const uint64_t format_modifiers[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	DRM_FORMAT_MOD_INVALID,
> +};
> +
> +static const uint64_t format_modifiers_afbc[] = {
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_CBR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_CBR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_CBR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_CBR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	/* SPLIT mandates SPARSE, RGB modes mandates YTR */
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_SPARSE |
> +				AFBC_FORMAT_MOD_SPLIT),
> +	DRM_FORMAT_MOD_INVALID,
> +};
> +
> +static const struct vop2_video_port_data rk3568_vop_video_ports[] = {
> +	{
> +		.id = 0,
> +		.feature = VOP_FEATURE_OUTPUT_10BIT,
> +		.gamma_lut_len = 1024,
> +		.cubic_lut_len = 9 * 9 * 9,
> +		.max_output = { 4096, 2304 },
> +		.pre_scan_max_dly = { 69, 53, 53, 42 },
> +		.offset = 0xc00,
> +	}, {
> +		.id = 1,
> +		.gamma_lut_len = 1024,
> +		.max_output = { 2048, 1536 },
> +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> +		.offset = 0xd00,
> +	}, {
> +		.id = 2,
> +		.gamma_lut_len = 1024,
> +		.max_output = { 1920, 1080 },
> +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> +		.offset = 0xe00,
> +	},
> +};
> +
> +/*
> + * rk3568 vop with 2 cluster, 2 esmart win, 2 smart win.
> + * Every cluster can work as 4K win or split into two win.
> + * All win in cluster support AFBCD.
> + *
> + * Every esmart win and smart win support 4 Multi-region.
> + *
> + * Scale filter mode:
> + *
> + * * Cluster:  bicubic for horizontal scale up, others use bilinear
> + * * ESmart:
> + *    * nearest-neighbor/bilinear/bicubic for scale up
> + *    * nearest-neighbor/bilinear/average for scale down
> + *
> + *
> + * @TODO describe the wind like cpu-map dt nodes;
> + */
> +static const struct vop2_win_data rk3568_vop_win_data[] = {
> +	{
> +		.name = "Smart0-win0",
> +		.phys_id = ROCKCHIP_VOP2_SMART0,
> +		.base = 0x1c00,
> +		.formats = formats_win_lite,
> +		.nformats = ARRAY_SIZE(formats_win_lite),
> +		.format_modifiers = format_modifiers,
> +		.layer_sel_id = 3,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Smart1-win0",
> +		.phys_id = ROCKCHIP_VOP2_SMART1,
> +		.formats = formats_win_lite,
> +		.nformats = ARRAY_SIZE(formats_win_lite),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1e00,
> +		.layer_sel_id = 7,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Esmart1-win0",
> +		.phys_id = ROCKCHIP_VOP2_ESMART1,
> +		.formats = formats_win_full_10bit_yuyv,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1a00,
> +		.layer_sel_id = 6,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Esmart0-win0",
> +		.phys_id = ROCKCHIP_VOP2_ESMART0,
> +		.formats = formats_win_full_10bit_yuyv,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1800,
> +		.layer_sel_id = 2,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Cluster0-win0",
> +		.phys_id = ROCKCHIP_VOP2_CLUSTER0,
> +		.base = 0x1000,
> +		.formats = formats_win_full_10bit,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> +		.format_modifiers = format_modifiers_afbc,
> +		.layer_sel_id = 0,
> +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> +		.max_upscale_factor = 4,
> +		.max_downscale_factor = 4,
> +		.dly = { 0, 27, 21 },
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> +	}, {
> +		.name = "Cluster1-win0",
> +		.phys_id = ROCKCHIP_VOP2_CLUSTER1,
> +		.base = 0x1200,
> +		.formats = formats_win_full_10bit,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> +		.format_modifiers = format_modifiers_afbc,
> +		.layer_sel_id = 1,
> +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.max_upscale_factor = 4,
> +		.max_downscale_factor = 4,
> +		.dly = { 0, 27, 21 },
> +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> +	},
> +};
> +
> +static const struct vop2_data rk3566_vop = {
> +	.nr_vps = 3,
> +	.max_input = { 4096, 2304 },
> +	.max_output = { 4096, 2304 },
> +	.vp = rk3568_vop_video_ports,
> +	.win = rk3568_vop_win_data,
> +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> +	.soc_id = 3566,
> +};
> +
> +static const struct vop2_data rk3568_vop = {
> +	.nr_vps = 3,
> +	.max_input = { 4096, 2304 },
> +	.max_output = { 4096, 2304 },
> +	.vp = rk3568_vop_video_ports,
> +	.win = rk3568_vop_win_data,
> +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> +	.soc_id = 3568,
> +};
> +
> +static const struct of_device_id vop2_dt_match[] = {
> +	{
> +		.compatible = "rockchip,rk3566-vop",
> +		.data = &rk3566_vop,
> +	}, {
> +		.compatible = "rockchip,rk3568-vop",
> +		.data = &rk3568_vop,
> +	}, {
> +	},
> +};
> +MODULE_DEVICE_TABLE(of, vop2_dt_match);
> +
> +static int vop2_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +
> +	return component_add(dev, &vop2_component_ops);
> +}
> +
> +static int vop2_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &vop2_component_ops);
> +
> +	return 0;
> +}
> +
> +struct platform_driver vop2_platform_driver = {
> +	.probe = vop2_probe,
> +	.remove = vop2_remove,
> +	.driver = {
> +		.name = "rockchip-vop2",
> +		.of_match_table = of_match_ptr(vop2_dt_match),
> +	},
> +};

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

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-21 11:51     ` Andy Yan
  0 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-21 11:51 UTC (permalink / raw)
  To: Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Sandy Huang, Kever Yang,
	linux-rockchip, Michael Riesch, kernel, Peter Geis, tfiga,
	linux-arm-kernel

Hi Sascha:

On 2/17/22 16:29, Sascha Hauer wrote:
> From: Andy Yan <andy.yan@rock-chips.com>
>
> The VOP2 unit is found on Rockchip SoCs beginning with rk3566/rk3568.
> It replaces the VOP unit found in the older Rockchip SoCs.
>
> This driver has been derived from the downstream Rockchip Kernel and
> heavily modified:
>
> - All nonstandard DRM properties have been removed
> - dropped struct vop2_plane_state and pass around less data between
>    functions
> - Dropped all DRM_FORMAT_* not known on upstream
> - rework register access to get rid of excessively used macros
> - Drop all waiting for framesyncs

All the waiting sync in the downstream divers are try to fix special 
problems,

and some of them inherited from upstream vop driver, for example: the 
fb_unref_work,

It submitted by Tomas Figa to upstream vop driver to make the wait for 
flip and asynchronously cursor work

right.

VOP2 share the same hardware design in vblank, so I don't think these 
code are useless.

[0] 
https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-5-git-send-email-tfiga@chromium.org/

[1] 
https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-6-git-send-email-tfiga@chromium.org/

[2] 
https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-4-git-send-email-tfiga@chromium.org/


>
> The driver is tested with HDMI and MIPI-DSI display on a RK3568-EVB
> board. Overlay support is tested with the modetest utility. AFBC support
> on the cluster windows is tested with weston-simple-dmabuf-egl on
> weston using the (yet to be upstreamed) panfrost driver support.
>
> Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>
> Notes:
>      Changes since v5:
>      - consistently use u8/u16/u32 rather than uint8_t/uint16_t/uint32_t
>      - Use spin_lock rather than spin_lock_irqsave
>      - replace printk with drm_dbg
>      - break some overlong lines
>      
>      Changes since v4:
>      - Avoid stack frame overflow by not allocating big array on the stack
>      
>      Changes since v3:
>      - Sort includes
>      - fix typos
>      - Drop spinlock
>      - Use regmap_set_bits()/regmap_clear_bits()
>      - simplify vop2_scale_factor()
>      - simplify vop2_afbc_transform_offset()
>      
>      Changes since v4:
>      - Sort nodes alphabetically
>      
>      Changes since v3:
>      - Fix HDMI connector type
>
>   drivers/gpu/drm/rockchip/Kconfig             |    6 +
>   drivers/gpu/drm/rockchip/Makefile            |    1 +
>   drivers/gpu/drm/rockchip/rockchip_drm_drv.c  |    1 +
>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h  |    6 +-
>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c   |    2 +
>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h  |   15 +
>   drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2708 ++++++++++++++++++
>   drivers/gpu/drm/rockchip/rockchip_drm_vop2.h |  477 +++
>   drivers/gpu/drm/rockchip/rockchip_vop2_reg.c |  281 ++
>   9 files changed, 3496 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
>
> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> index b9b156308460a..4ff0043f0ee70 100644
> --- a/drivers/gpu/drm/rockchip/Kconfig
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
>   	  This selects support for the VOP driver. You should enable it
>   	  on all older SoCs up to RK3399.
>   
> +config ROCKCHIP_VOP2
> +	bool "Rockchip VOP2 driver"
> +	help
> +	  This selects support for the VOP2 driver. You should enable it
> +	  on all newer SoCs beginning form RK3568.
> +
>   config ROCKCHIP_ANALOGIX_DP
>   	bool "Rockchip specific extensions for Analogix DP driver"
>   	depends on ROCKCHIP_VOP
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index dfc5512fdb9f1..3ff7b21c04149 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -6,6 +6,7 @@
>   rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>   		rockchip_drm_gem.o
>   
> +rockchipdrm-$(CONFIG_ROCKCHIP_VOP2) += rockchip_drm_vop2.o rockchip_vop2_reg.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>   rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> index 82c8faf1fb6b8..95f6c5985fdd7 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -459,6 +459,7 @@ static int __init rockchip_drm_init(void)
>   
>   	num_rockchip_sub_drivers = 0;
>   	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
> +	ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2);
>   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
>   				CONFIG_ROCKCHIP_LVDS);
>   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> index d3e42410ae5da..5cb207aaaadd0 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -18,7 +18,7 @@
>   
>   #define ROCKCHIP_MAX_FB_BUFFER	3
>   #define ROCKCHIP_MAX_CONNECTOR	2
> -#define ROCKCHIP_MAX_CRTC	2
> +#define ROCKCHIP_MAX_CRTC	4
>   
>   struct drm_device;
>   struct drm_connector;
> @@ -31,6 +31,9 @@ struct rockchip_crtc_state {
>   	int output_bpc;
>   	int output_flags;
>   	bool enable_afbc;
> +	u32 bus_format;
> +	u32 bus_flags;
> +	int color_space;
>   };
>   #define to_rockchip_crtc_state(s) \
>   		container_of(s, struct rockchip_crtc_state, base)
> @@ -63,6 +66,7 @@ extern struct platform_driver rockchip_dp_driver;
>   extern struct platform_driver rockchip_lvds_driver;
>   extern struct platform_driver vop_platform_driver;
>   extern struct platform_driver rk3066_hdmi_driver;
> +extern struct platform_driver vop2_platform_driver;
>   
>   struct rockchip_encoder {
>   	struct device_node *port;
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> index 3aa37e177667e..0d2cb4f3922b8 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> @@ -134,4 +134,6 @@ void rockchip_drm_mode_config_init(struct drm_device *dev)
>   
>   	dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
>   	dev->mode_config.helper_private = &rockchip_mode_config_helpers;
> +
> +	dev->mode_config.normalize_zpos = true;
>   }
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> index 857d97cdc67c6..1e364d7b50e69 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -54,9 +54,23 @@ struct vop_afbc {
>   	struct vop_reg enable;
>   	struct vop_reg win_sel;
>   	struct vop_reg format;
> +	struct vop_reg rb_swap;
> +	struct vop_reg uv_swap;
> +	struct vop_reg auto_gating_en;
> +	struct vop_reg block_split_en;
> +	struct vop_reg pic_vir_width;
> +	struct vop_reg tile_num;
>   	struct vop_reg hreg_block_split;
> +	struct vop_reg pic_offset;
>   	struct vop_reg pic_size;
> +	struct vop_reg dsp_offset;
> +	struct vop_reg transform_offset;
>   	struct vop_reg hdr_ptr;
> +	struct vop_reg half_block_en;
> +	struct vop_reg xmirror;
> +	struct vop_reg ymirror;
> +	struct vop_reg rotate_270;
> +	struct vop_reg rotate_90;
>   	struct vop_reg rstn;
>   };
>   
> @@ -410,4 +424,5 @@ static inline int scl_vop_cal_lb_mode(int width, bool is_yuv)
>   }
>   
>   extern const struct component_ops vop_component_ops;
> +
>   #endif /* _ROCKCHIP_DRM_VOP_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> new file mode 100644
> index 0000000000000..394dd6c583682
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> @@ -0,0 +1,2708 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
> + * Author: Andy Yan <andy.yan@rock-chips.com>
> + */
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/delay.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/swab.h>
> +
> +#include <drm/drm.h>
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_uapi.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_debugfs.h>
> +#include <drm/drm_flip_work.h>
> +#include <drm/drm_plane_helper.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_vblank.h>
> +
> +#include <uapi/linux/videodev2.h>
> +#include <dt-bindings/soc/rockchip,vop2.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_gem.h"
> +#include "rockchip_drm_fb.h"
> +#include "rockchip_drm_vop2.h"
> +
> +/*
> + * VOP2 architecture
> + *
> + +----------+   +-------------+                                                        +-----------+
> + |  Cluster |   | Sel 1 from 6|                                                        | 1 from 3  |
> + |  window0 |   |    Layer0   |                                                        |    RGB    |
> + +----------+   +-------------+              +---------------+    +-------------+      +-----------+
> + +----------+   +-------------+              |N from 6 layers|    |             |
> + |  Cluster |   | Sel 1 from 6|              |   Overlay0    +--->| Video Port0 |      +-----------+
> + |  window1 |   |    Layer1   |              |               |    |             |      | 1 from 3  |
> + +----------+   +-------------+              +---------------+    +-------------+      |   LVDS    |
> + +----------+   +-------------+                                                        +-----------+
> + |  Esmart  |   | Sel 1 from 6|
> + |  window0 |   |   Layer2    |              +---------------+    +-------------+      +-----------+
> + +----------+   +-------------+              |N from 6 Layers|    |             | +--> | 1 from 3  |
> + +----------+   +-------------+   -------->  |   Overlay1    +--->| Video Port1 |      |   MIPI    |
> + |  Esmart  |   | Sel 1 from 6|   -------->  |               |    |             |      +-----------+
> + |  Window1 |   |   Layer3    |              +---------------+    +-------------+
> + +----------+   +-------------+                                                        +-----------+
> + +----------+   +-------------+                                                        | 1 from 3  |
> + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |   HDMI    |
> + |  Window0 |   |    Layer4   |              |N from 6 Layers|    |             |      +-----------+
> + +----------+   +-------------+              |   Overlay2    +--->| Video Port2 |
> + +----------+   +-------------+              |               |    |             |      +-----------+
> + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |  1 from 3 |
> + |  Window1 |   |    Layer5   |                                                        |    eDP    |
> + +----------+   +-------------+                                                        +-----------+
> + *
> + */
> +
> +enum vop2_data_format {
> +	VOP2_FMT_ARGB8888 = 0,
> +	VOP2_FMT_RGB888,
> +	VOP2_FMT_RGB565,
> +	VOP2_FMT_XRGB101010,
> +	VOP2_FMT_YUV420SP,
> +	VOP2_FMT_YUV422SP,
> +	VOP2_FMT_YUV444SP,
> +	VOP2_FMT_YUYV422 = 8,
> +	VOP2_FMT_YUYV420,
> +	VOP2_FMT_VYUY422,
> +	VOP2_FMT_VYUY420,
> +	VOP2_FMT_YUV420SP_TILE_8x4 = 0x10,
> +	VOP2_FMT_YUV420SP_TILE_16x2,
> +	VOP2_FMT_YUV422SP_TILE_8x4,
> +	VOP2_FMT_YUV422SP_TILE_16x2,
> +	VOP2_FMT_YUV420SP_10,
> +	VOP2_FMT_YUV422SP_10,
> +	VOP2_FMT_YUV444SP_10,
> +};
> +
> +enum vop2_afbc_format {
> +	VOP2_AFBC_FMT_RGB565,
> +	VOP2_AFBC_FMT_ARGB2101010 = 2,
> +	VOP2_AFBC_FMT_YUV420_10BIT,
> +	VOP2_AFBC_FMT_RGB888,
> +	VOP2_AFBC_FMT_ARGB8888,
> +	VOP2_AFBC_FMT_YUV420 = 9,
> +	VOP2_AFBC_FMT_YUV422 = 0xb,
> +	VOP2_AFBC_FMT_YUV422_10BIT = 0xe,
> +	VOP2_AFBC_FMT_INVALID = -1,
> +};
> +
> +union vop2_alpha_ctrl {
> +	u32 val;
> +	struct {
> +		/* [0:1] */
> +		u32 color_mode:1;
> +		u32 alpha_mode:1;
> +		/* [2:3] */
> +		u32 blend_mode:2;
> +		u32 alpha_cal_mode:1;
> +		/* [5:7] */
> +		u32 factor_mode:3;
> +		/* [8:9] */
> +		u32 alpha_en:1;
> +		u32 src_dst_swap:1;
> +		u32 reserved:6;
> +		/* [16:23] */
> +		u32 glb_alpha:8;
> +	} bits;
> +};
> +
> +struct vop2_alpha {
> +	union vop2_alpha_ctrl src_color_ctrl;
> +	union vop2_alpha_ctrl dst_color_ctrl;
> +	union vop2_alpha_ctrl src_alpha_ctrl;
> +	union vop2_alpha_ctrl dst_alpha_ctrl;
> +};
> +
> +struct vop2_alpha_config {
> +	bool src_premulti_en;
> +	bool dst_premulti_en;
> +	bool src_pixel_alpha_en;
> +	bool dst_pixel_alpha_en;
> +	u16 src_glb_alpha_value;
> +	u16 dst_glb_alpha_value;
> +};
> +
> +struct vop2_win {
> +	struct vop2 *vop2;
> +	struct drm_plane base;
> +	const struct vop2_win_data *data;
> +	struct regmap_field *reg[VOP2_WIN_MAX_REG];
> +
> +	/**
> +	 * @win_id: graphic window id, a cluster may be split into two
> +	 * graphics windows.
> +	 */
> +	u8 win_id;
> +	u8 delay;
> +	u32 offset;
> +
> +	enum drm_plane_type type;
> +};
> +
> +struct vop2_video_port {
> +	struct drm_crtc crtc;
> +	struct vop2 *vop2;
> +	struct clk *dclk;
> +	unsigned int id;
> +	const struct vop2_video_port_regs *regs;
> +	const struct vop2_video_port_data *data;
> +
> +	struct completion dsp_hold_completion;
> +
> +	/**
> +	 * @win_mask: Bitmask of windows attached to the video port;
> +	 */
> +	u32 win_mask;
> +
> +	struct vop2_win *primary_plane;
> +	struct drm_pending_vblank_event *event;
> +
> +	unsigned int nlayers;
> +};
> +
> +struct vop2 {
> +	struct device *dev;
> +	struct drm_device *drm;
> +	struct vop2_video_port vps[ROCKCHIP_MAX_CRTC];
> +
> +	const struct vop2_data *data;
> +	/*
> +	 * Number of windows that are registered as plane, may be less than the
> +	 * total number of hardware windows.
> +	 */
> +	u32 registered_num_wins;
> +
> +	void __iomem *regs;
> +	struct regmap *map;
> +
> +	struct regmap *grf;
> +
> +	/* physical map length of vop2 register */
> +	u32 len;
> +
> +	void __iomem *lut_regs;
> +
> +	/* protects crtc enable/disable */
> +	struct mutex vop2_lock;
> +
> +	int irq;
> +
> +	/*
> +	 * Some global resources are shared between all video ports(crtcs), so
> +	 * we need a ref counter here.
> +	 */
> +	unsigned int enable_count;
> +	struct clk *hclk;
> +	struct clk *aclk;
> +
> +	/* must be put at the end of the struct */
> +	struct vop2_win win[];
> +};
> +
> +static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
> +{
> +	return container_of(crtc, struct vop2_video_port, crtc);
> +}
> +
> +static struct vop2_win *to_vop2_win(struct drm_plane *p)
> +{
> +	return container_of(p, struct vop2_win, base);
> +}
> +
> +static void vop2_lock(struct vop2 *vop2)
> +{
> +	mutex_lock(&vop2->vop2_lock);
> +}
> +
> +static void vop2_unlock(struct vop2 *vop2)
> +{
> +	mutex_unlock(&vop2->vop2_lock);
> +}
> +
> +static void vop2_writel(struct vop2 *vop2, u32 offset, u32 v)
> +{
> +	regmap_write(vop2->map, offset, v);
> +}
> +
> +static void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
> +{
> +	regmap_write(vp->vop2->map, vp->data->offset + offset, v);
> +}
> +
> +static u32 vop2_readl(struct vop2 *vop2, u32 offset)
> +{
> +	u32 val;
> +
> +	regmap_read(vop2->map, offset, &val);
> +
> +	return val;
> +}
> +
> +static void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v)
> +{
> +	regmap_field_write(win->reg[reg], v);
> +}
> +
> +static bool vop2_cluster_window(const struct vop2_win *win)
> +{
> +	return win->data->feature & WIN_FEATURE_CLUSTER;
> +}
> +
> +static void vop2_cfg_done(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE,
> +			BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> +}
> +
> +static void vop2_win_disable(struct vop2_win *win)
> +{
> +	vop2_win_write(win, VOP2_WIN_ENABLE, 0);
> +
> +	if (vop2_cluster_window(win))
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0);
> +}
> +
> +static enum vop2_data_format vop2_convert_format(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +		return VOP2_FMT_ARGB8888;
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		return VOP2_FMT_RGB888;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		return VOP2_FMT_RGB565;
> +	case DRM_FORMAT_NV12:
> +		return VOP2_FMT_YUV420SP;
> +	case DRM_FORMAT_NV16:
> +		return VOP2_FMT_YUV422SP;
> +	case DRM_FORMAT_NV24:
> +		return VOP2_FMT_YUV444SP;
> +	case DRM_FORMAT_YUYV:
> +	case DRM_FORMAT_YVYU:
> +		return VOP2_FMT_VYUY422;
> +	case DRM_FORMAT_VYUY:
> +	case DRM_FORMAT_UYVY:
> +		return VOP2_FMT_YUYV422;
> +	default:
> +		DRM_ERROR("unsupported format[%08x]\n", format);
> +		return -EINVAL;
> +	}
> +}
> +
> +static enum vop2_afbc_format vop2_convert_afbc_format(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +		return VOP2_AFBC_FMT_ARGB8888;
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		return VOP2_AFBC_FMT_RGB888;
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		return VOP2_AFBC_FMT_RGB565;
> +	case DRM_FORMAT_NV12:
> +		return VOP2_AFBC_FMT_YUV420;
> +	case DRM_FORMAT_NV16:
> +		return VOP2_AFBC_FMT_YUV422;
> +	default:
> +		return VOP2_AFBC_FMT_INVALID;
> +	}
> +
> +	return VOP2_AFBC_FMT_INVALID;
> +}
> +
> +static bool vop2_win_rb_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_BGR888:
> +	case DRM_FORMAT_BGR565:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_afbc_rb_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV24:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_afbc_uv_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV12:
> +	case DRM_FORMAT_NV16:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_win_uv_swap(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV12:
> +	case DRM_FORMAT_NV16:
> +	case DRM_FORMAT_NV24:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_win_dither_up(u32 format)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_BGR565:
> +	case DRM_FORMAT_RGB565:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
> +{
> +	/*
> +	 * FIXME:
> +	 *
> +	 * There is no media type for YUV444 output,
> +	 * so when out_mode is AAAA or P888, assume output is YUV444 on
> +	 * yuv format.
> +	 *
> +	 * From H/W testing, YUV444 mode need a rb swap.
> +	 */
> +	if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 ||
> +	    bus_format == MEDIA_BUS_FMT_VYUY8_1X16 ||
> +	    bus_format == MEDIA_BUS_FMT_YVYU8_2X8 ||
> +	    bus_format == MEDIA_BUS_FMT_VYUY8_2X8 ||
> +	    ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
> +	      bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
> +	     (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
> +	      output_mode == ROCKCHIP_OUT_MODE_P888)))
> +		return true;
> +	else
> +		return false;
> +}
> +
> +static bool is_yuv_output(u32 bus_format)
> +{
> +	switch (bus_format) {
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +	case MEDIA_BUS_FMT_YUV10_1X30:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> +	case MEDIA_BUS_FMT_YUYV8_2X8:
> +	case MEDIA_BUS_FMT_YVYU8_2X8:
> +	case MEDIA_BUS_FMT_UYVY8_2X8:
> +	case MEDIA_BUS_FMT_VYUY8_2X8:
> +	case MEDIA_BUS_FMT_YUYV8_1X16:
> +	case MEDIA_BUS_FMT_YVYU8_1X16:
> +	case MEDIA_BUS_FMT_UYVY8_1X16:
> +	case MEDIA_BUS_FMT_VYUY8_1X16:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool rockchip_afbc(struct drm_plane *plane, u64 modifier)
> +{
> +	int i;
> +
> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> +		return false;
> +
> +	for (i = 0 ; i < plane->modifier_count; i++)
> +		if (plane->modifiers[i] == modifier)
> +			return true;
> +
> +	return false;
> +
> +}
> +
> +static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
> +					u64 modifier)
> +{
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2 *vop2 = win->vop2;
> +
> +	if (modifier == DRM_FORMAT_MOD_INVALID)
> +		return false;
> +
> +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> +		return true;
> +
> +	if (!rockchip_afbc(plane, modifier)) {
> +		drm_err(vop2->drm, "Unsupported format modifier 0x%llx\n",
> +			modifier);
> +
> +		return false;
> +	}
> +
> +	return vop2_convert_afbc_format(format) >= 0;
> +}
> +
> +static u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate,
> +				      bool afbc_half_block_en)
> +{
> +	struct drm_rect *src = &pstate->src;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	u32 bpp = fb->format->cpp[0] * 8;
> +	u32 vir_width = (fb->pitches[0] << 3) / bpp;
> +	u32 width = drm_rect_width(src) >> 16;
> +	u32 height = drm_rect_height(src) >> 16;
> +	u32 act_xoffset = src->x1 >> 16;
> +	u32 act_yoffset = src->y1 >> 16;
> +	u32 align16_crop = 0;
> +	u32 align64_crop = 0;
> +	u32 height_tmp;
> +	u8 tx, ty;
> +	u8 bottom_crop_line_num = 0;
> +
> +	/* 16 pixel align */
> +	if (height & 0xf)
> +		align16_crop = 16 - (height & 0xf);
> +
> +	height_tmp = height + align16_crop;
> +
> +	/* 64 pixel align */
> +	if (height_tmp & 0x3f)
> +		align64_crop = 64 - (height_tmp & 0x3f);
> +
> +	bottom_crop_line_num = align16_crop + align64_crop;
> +
> +	switch (pstate->rotation &
> +		(DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y |
> +		 DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) {
> +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> +		tx = 16 - ((act_xoffset + width) & 0xf);
> +		ty = bottom_crop_line_num - act_yoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90:
> +		tx = bottom_crop_line_num - act_yoffset;
> +		ty = vir_width - width - act_xoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270:
> +		tx = act_yoffset;
> +		ty = act_xoffset;
> +		break;
> +	case DRM_MODE_REFLECT_X:
> +		tx = 16 - ((act_xoffset + width) & 0xf);
> +		ty = act_yoffset;
> +		break;
> +	case DRM_MODE_REFLECT_Y:
> +		tx = act_xoffset;
> +		ty = bottom_crop_line_num - act_yoffset;
> +		break;
> +	case DRM_MODE_ROTATE_90:
> +		tx = bottom_crop_line_num - act_yoffset;
> +		ty = act_xoffset;
> +		break;
> +	case DRM_MODE_ROTATE_270:
> +		tx = act_yoffset;
> +		ty = vir_width - width - act_xoffset;
> +		break;
> +	case 0:
> +		tx = act_xoffset;
> +		ty = act_yoffset;
> +		break;
> +	}
> +
> +	if (afbc_half_block_en)
> +		ty &= 0x7f;
> +
> +#define TRANSFORM_XOFFSET GENMASK(7, 0)
> +#define TRANSFORM_YOFFSET GENMASK(23, 16)
> +	return FIELD_PREP(TRANSFORM_XOFFSET, tx) |
> +		FIELD_PREP(TRANSFORM_YOFFSET, ty);
> +}
> +
> +/*
> + * A Cluster window has 2048 x 16 line buffer, which can
> + * works at 2048 x 16(Full) or 4096 x 8 (Half) mode.
> + * for Cluster_lb_mode register:
> + * 0: half mode, for plane input width range 2048 ~ 4096
> + * 1: half mode, for cluster work at 2 * 2048 plane mode
> + * 2: half mode, for rotate_90/270 mode
> + *
> + */
> +static int vop2_get_cluster_lb_mode(struct vop2_win *win,
> +				    struct drm_plane_state *pstate)
> +{
> +	if ((pstate->rotation & DRM_MODE_ROTATE_270) ||
> +	    (pstate->rotation & DRM_MODE_ROTATE_90))
> +		return 2;
> +	else
> +		return 0;
> +}
> +
> +static u16 vop2_scale_factor(u32 src, u32 dst)
> +{
> +	u32 fac;
> +	int shift;
> +
> +	if (src == dst)
> +		return 0;
> +
> +	if (dst < 2)
> +		return U16_MAX;
> +
> +	if (src < 2)
> +		return 0;
> +
> +	if (src > dst)
> +		shift = 12;
> +	else
> +		shift = 16;
> +
> +	src--;
> +	dst--;
> +
> +	fac = DIV_ROUND_UP(src << shift, dst) - 1;
> +
> +	if (fac > U16_MAX)
> +		return U16_MAX;
> +
> +	return fac;
> +}
> +
> +static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
> +			     u32 src_w, u32 src_h, u32 dst_w,
> +			     u32 dst_h, u32 pixel_format)
> +{
> +	const struct drm_format_info *info;
> +	u16 hor_scl_mode, ver_scl_mode;
> +	u16 hscl_filter_mode, vscl_filter_mode;
> +	u8 gt2 = 0;
> +	u8 gt4 = 0;
> +	u32 val;
> +
> +	info = drm_format_info(pixel_format);
> +
> +	if (src_h >= (4 * dst_h)) {
> +		gt4 = 1;
> +		src_h >>= 2;
> +	} else if (src_h >= (2 * dst_h)) {
> +		gt2 = 1;
> +		src_h >>= 1;
> +	}
> +
> +	hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> +	ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> +
> +	if (hor_scl_mode == SCALE_UP)
> +		hscl_filter_mode = VOP2_SCALE_UP_BIC;
> +	else
> +		hscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> +
> +	if (ver_scl_mode == SCALE_UP)
> +		vscl_filter_mode = VOP2_SCALE_UP_BIL;
> +	else
> +		vscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> +
> +	/*
> +	 * RK3568 VOP Esmart/Smart dsp_w should be even pixel
> +	 * at scale down mode
> +	 */
> +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> +		if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) {
> +			drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n",
> +				win->data->name, dst_w);
> +			dst_w++;
> +		}
> +	}
> +
> +	val = vop2_scale_factor(src_w, dst_w);
> +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val);
> +	val = vop2_scale_factor(src_h, dst_h);
> +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val);
> +
> +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4);
> +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2);
> +
> +	vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode);
> +	vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode);
> +
> +	if (vop2_cluster_window(win))
> +		return;
> +
> +	vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode);
> +	vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode);
> +
> +	if (info->is_yuv) {
> +		src_w /= info->hsub;
> +		src_h /= info->vsub;
> +
> +		gt4 = gt2 = 0;
> +
> +		if (src_h >= (4 * dst_h)) {
> +			gt4 = 1;
> +			src_h >>= 2;
> +		} else if (src_h >= (2 * dst_h)) {
> +			gt2 = 1;
> +			src_h >>= 1;
> +		}
> +
> +		hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> +		ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> +
> +		val = vop2_scale_factor(src_w, dst_w);
> +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val);
> +
> +		val = vop2_scale_factor(src_h, dst_h);
> +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val);
> +
> +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4);
> +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2);
> +		vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode);
> +		vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode);
> +	}
> +}
> +
> +static int vop2_convert_csc_mode(int csc_mode)
> +{
> +	switch (csc_mode) {
> +	case V4L2_COLORSPACE_SMPTE170M:
> +	case V4L2_COLORSPACE_470_SYSTEM_M:
> +	case V4L2_COLORSPACE_470_SYSTEM_BG:
> +		return CSC_BT601L;
> +	case V4L2_COLORSPACE_REC709:
> +	case V4L2_COLORSPACE_SMPTE240M:
> +	case V4L2_COLORSPACE_DEFAULT:
> +		return CSC_BT709L;
> +	case V4L2_COLORSPACE_JPEG:
> +		return CSC_BT601F;
> +	case V4L2_COLORSPACE_BT2020:
> +		return CSC_BT2020;
> +	default:
> +		return CSC_BT709L;
> +	}
> +}
> +
> +/*
> + * colorspace path:
> + *      Input        Win csc                     Output
> + * 1. YUV(2020)  --> Y2R->2020To709->R2Y   --> YUV_OUTPUT(601/709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 2. YUV(2020)  --> bypasss               --> YUV_OUTPUT(2020)
> + *    RGB        --> 709To2020->R2Y       __/
> + *
> + * 3. YUV(2020)  --> Y2R->2020To709        --> RGB_OUTPUT(709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 4. YUV(601/709)-> Y2R->709To2020->R2Y   --> YUV_OUTPUT(2020)
> + *    RGB        --> 709To2020->R2Y       __/
> + *
> + * 5. YUV(601/709)-> bypass                --> YUV_OUTPUT(709)
> + *    RGB        --> R2Y                  __/
> + *
> + * 6. YUV(601/709)-> bypass                --> YUV_OUTPUT(601)
> + *    RGB        --> R2Y(601)             __/
> + *
> + * 7. YUV        --> Y2R(709)              --> RGB_OUTPUT(709)
> + *    RGB        --> bypass               __/
> + *
> + * 8. RGB        --> 709To2020->R2Y        --> YUV_OUTPUT(2020)
> + *
> + * 9. RGB        --> R2Y(709)              --> YUV_OUTPUT(709)
> + *
> + * 10. RGB       --> R2Y(601)              --> YUV_OUTPUT(601)
> + *
> + * 11. RGB       --> bypass                --> RGB_OUTPUT(709)
> + */
> +
> +static void vop2_setup_csc_mode(struct vop2_video_port *vp,
> +				struct vop2_win *win,
> +				struct drm_plane_state *pstate)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
> +	int is_input_yuv = pstate->fb->format->is_yuv;
> +	int is_output_yuv = is_yuv_output(vcstate->bus_format);
> +	int input_csc = V4L2_COLORSPACE_DEFAULT;
> +	int output_csc = vcstate->color_space;
> +	bool r2y_en, y2r_en;
> +	int csc_mode;
> +
> +	if (is_input_yuv && !is_output_yuv) {
> +		y2r_en = true;
> +		r2y_en = false;
> +		csc_mode = vop2_convert_csc_mode(input_csc);
> +	} else if (!is_input_yuv && is_output_yuv) {
> +		y2r_en = false;
> +		r2y_en = true;
> +		csc_mode = vop2_convert_csc_mode(output_csc);
> +	} else {
> +		y2r_en = false;
> +		r2y_en = false;
> +		csc_mode = false;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en);
> +	vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en);
> +	vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
> +}
> +
> +static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq);
> +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq);
> +}
> +
> +static void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +
> +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16);
> +}
> +
> +static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
> +{
> +	int ret;
> +
> +	ret = clk_prepare_enable(vop2->hclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable hclk - %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(vop2->aclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable aclk - %d\n", ret);
> +		goto err;
> +	}
> +
> +	return 0;
> +err:
> +	clk_disable_unprepare(vop2->hclk);
> +
> +	return ret;
> +}
> +
> +static void vop2_enable(struct vop2 *vop2)
> +{
> +	int ret;
> +
> +	ret = pm_runtime_get_sync(vop2->dev);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret);
> +		return;
> +	}
> +
> +	ret = vop2_core_clks_prepare_enable(vop2);
> +	if (ret) {
> +		pm_runtime_put_sync(vop2->dev);
> +		return;
> +	}
> +
> +	if (vop2->data->soc_id == 3566)
> +		vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
> +
> +	vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> +
> +	/*
> +	 * Disable auto gating, this is a workaround to
> +	 * avoid display image shift when a window enabled.
> +	 */
> +	regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL,
> +			  RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN);
> +
> +	vop2_writel(vop2, RK3568_SYS0_INT_CLR,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS0_INT_EN,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS1_INT_CLR,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +	vop2_writel(vop2, RK3568_SYS1_INT_EN,
> +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> +}
> +
> +static void vop2_disable(struct vop2 *vop2)
> +{
> +	pm_runtime_put_sync(vop2->dev);
> +
> +	clk_disable_unprepare(vop2->aclk);
> +	clk_disable_unprepare(vop2->hclk);
> +}
> +
> +static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
> +				     struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	int ret;
> +
> +	vop2_lock(vop2);
> +
> +	drm_crtc_vblank_off(crtc);
> +
> +	/*
> +	 * Vop standby will take effect at end of current frame,
> +	 * if dsp hold valid irq happen, it means standby complete.
> +	 *
> +	 * we must wait standby complete when we want to disable aclk,
> +	 * if not, memory bus maybe dead.
> +	 */
> +	reinit_completion(&vp->dsp_hold_completion);
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
> +
> +	ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
> +					  msecs_to_jiffies(50));
> +	if (!ret)
> +		drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
> +
> +	vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
> +
> +	clk_disable_unprepare(vp->dclk);
> +
> +	vop2->enable_count--;
> +
> +	if (!vop2->enable_count)
> +		vop2_disable(vop2);
> +
> +	vop2_unlock(vop2);
> +
> +	if (crtc->state->event && !crtc->state->active) {
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +
> +		crtc->state->event = NULL;
> +	}
> +}
> +
> +static int vop2_plane_atomic_check(struct drm_plane *plane,
> +				   struct drm_atomic_state *astate)
> +{
> +	struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane);
> +	struct drm_framebuffer *fb = pstate->fb;
> +	struct drm_crtc *crtc = pstate->crtc;
> +	struct drm_crtc_state *cstate;
> +	struct vop2_video_port *vp;
> +	struct vop2 *vop2;
> +	const struct vop2_data *vop2_data;
> +	struct drm_rect *dest = &pstate->dst;
> +	struct drm_rect *src = &pstate->src;
> +	int min_scale = FRAC_16_16(1, 8);
> +	int max_scale = FRAC_16_16(8, 1);
> +	int format;
> +	int ret;
> +
> +	if (!crtc)
> +		return 0;
> +
> +	vp = to_vop2_video_port(crtc);
> +	vop2 = vp->vop2;
> +	vop2_data = vop2->data;
> +
> +	cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc);
> +	if (WARN_ON(!cstate))
> +		return -EINVAL;
> +
> +	ret = drm_atomic_helper_check_plane_state(pstate, cstate,
> +						  min_scale, max_scale,
> +						  true, true);
> +	if (ret)
> +		return ret;
> +
> +	if (!pstate->visible)
> +		return 0;
> +
> +	format = vop2_convert_format(fb->format->format);
> +	if (format < 0)
> +		return format;
> +
> +	if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 ||
> +	    drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) {
> +		drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
> +			  drm_rect_width(src) >> 16, drm_rect_height(src) >> 16,
> +			  drm_rect_width(dest), drm_rect_height(dest));
> +		pstate->visible = false;
> +		return 0;
> +	}
> +
> +	if (drm_rect_width(src) >> 16 > vop2_data->max_input.width ||
> +	    drm_rect_height(src) >> 16 > vop2_data->max_input.height) {
> +		drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
> +			  drm_rect_width(src) >> 16,
> +			  drm_rect_height(src) >> 16,
> +			  vop2_data->max_input.width,
> +			  vop2_data->max_input.height);
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Src.x1 can be odd when do clip, but yuv plane start point
> +	 * need align with 2 pixel.
> +	 */
> +	if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
> +		drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vop2_plane_atomic_disable(struct drm_plane *plane,
> +				      struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state, plane);
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2 *vop2 = win->vop2;
> +
> +	drm_dbg(vop2->drm, "%s disable\n", win->data->name);
> +
> +	if (!old_pstate->crtc)
> +		return;
> +
> +	vop2_win_disable(win);
> +	vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0);
> +}
> +
> +/*
> + * The color key is 10 bit, so all format should
> + * convert to 10 bit here.
> + */
> +static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
> +{
> +	struct drm_plane_state *pstate = plane->state;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	struct vop2_win *win = to_vop2_win(plane);
> +	u32 color_key_en = 0;
> +	u32 r = 0;
> +	u32 g = 0;
> +	u32 b = 0;
> +
> +	if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) {
> +		vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0);
> +		return;
> +	}
> +
> +	switch (fb->format->format) {
> +	case DRM_FORMAT_RGB565:
> +	case DRM_FORMAT_BGR565:
> +		r = (color_key & 0xf800) >> 11;
> +		g = (color_key & 0x7e0) >> 5;
> +		b = (color_key & 0x1f);
> +		r <<= 5;
> +		g <<= 4;
> +		b <<= 5;
> +		color_key_en = 1;
> +		break;
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XBGR8888:
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_BGR888:
> +		r = (color_key & 0xff0000) >> 16;
> +		g = (color_key & 0xff00) >> 8;
> +		b = (color_key & 0xff);
> +		r <<= 2;
> +		g <<= 2;
> +		b <<= 2;
> +		color_key_en = 1;
> +		break;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en);
> +	vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b);
> +}
> +
> +static void vop2_plane_atomic_update(struct drm_plane *plane,
> +				     struct drm_atomic_state *state)
> +{
> +	struct drm_plane_state *pstate = plane->state;
> +	struct drm_crtc *crtc = pstate->crtc;
> +	struct vop2_win *win = to_vop2_win(plane);
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
> +	struct vop2 *vop2 = win->vop2;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	u32 bpp = fb->format->cpp[0] * 8;
> +	u32 actual_w, actual_h, dsp_w, dsp_h;
> +	u32 act_info, dsp_info;
> +	u32 format;
> +	u32 afbc_format;
> +	u32 rb_swap;
> +	u32 uv_swap;
> +	struct drm_rect *src = &pstate->src;
> +	struct drm_rect *dest = &pstate->dst;
> +	u32 afbc_tile_num;
> +	u32 transform_offset;
> +	bool dither_up;
> +	bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
> +	bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
> +	bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
> +	bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
> +	struct rockchip_gem_object *rk_obj;
> +	unsigned long offset;
> +	bool afbc_en;
> +	dma_addr_t yrgb_mst;
> +	dma_addr_t uv_mst;
> +
> +	/*
> +	 * can't update plane when vop2 is disabled.
> +	 */
> +	if (WARN_ON(!crtc))
> +		return;
> +
> +	if (!pstate->visible) {
> +		vop2_plane_atomic_disable(plane, state);
> +		return;
> +	}
> +
> +	afbc_en = rockchip_afbc(plane, fb->modifier);
> +
> +	offset = (src->x1 >> 16) * fb->format->cpp[0];
> +
> +	/*
> +	 * AFBC HDR_PTR must set to the zero offset of the framebuffer.
> +	 */
> +	if (afbc_en)
> +		offset = 0;
> +	else if (pstate->rotation & DRM_MODE_REFLECT_Y)
> +		offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
> +	else
> +		offset += (src->y1 >> 16) * fb->pitches[0];
> +
> +	rk_obj = to_rockchip_obj(fb->obj[0]);
> +
> +	yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
> +	if (fb->format->is_yuv) {
> +		int hsub = fb->format->hsub;
> +		int vsub = fb->format->vsub;
> +
> +		offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub;
> +		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
> +
> +		if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en)
> +			offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub;
> +
> +		rk_obj = to_rockchip_obj(fb->obj[0]);
> +		uv_mst = rk_obj->dma_addr + offset + fb->offsets[1];
> +	}
> +
> +	actual_w = drm_rect_width(src) >> 16;
> +	actual_h = drm_rect_height(src) >> 16;
> +	dsp_w = drm_rect_width(dest);
> +
> +	if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
> +		drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
> +			  vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
> +		dsp_w = adjusted_mode->hdisplay - dest->x1;
> +		if (dsp_w < 4)
> +			dsp_w = 4;
> +		actual_w = dsp_w * actual_w / drm_rect_width(dest);
> +	}
> +
> +	dsp_h = drm_rect_height(dest);
> +
> +	if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
> +		drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
> +			  vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
> +		dsp_h = adjusted_mode->vdisplay - dest->y1;
> +		if (dsp_h < 4)
> +			dsp_h = 4;
> +		actual_h = dsp_h * actual_h / drm_rect_height(dest);
> +	}
> +
> +	/*
> +	 * This is workaround solution for IC design:
> +	 * esmart can't support scale down when actual_w % 16 == 1.
> +	 */
> +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> +		if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
> +			drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
> +				vp->id, win->data->name, actual_w);
> +			actual_w -= 1;
> +		}
> +	}
> +
> +	if (afbc_en && actual_w % 4) {
> +		drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
> +			  vp->id, win->data->name, actual_w);
> +		actual_w = ALIGN_DOWN(actual_w, 4);
> +	}
> +
> +	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
> +	dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff);
> +
> +	format = vop2_convert_format(fb->format->format);
> +
> +	drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n",
> +		      vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h,
> +		      dest->x1, dest->y1,
> +		      &fb->format->format,
> +		      afbc_en ? "AFBC" : "", &yrgb_mst);
> +
> +	if (afbc_en) {
> +		u32 stride;
> +
> +		/* the afbc superblock is 16 x 16 */
> +		afbc_format = vop2_convert_afbc_format(fb->format->format);
> +
> +		/* Enable color transform for YTR */
> +		if (fb->modifier & AFBC_FORMAT_MOD_YTR)
> +			afbc_format |= (1 << 4);
> +
> +		afbc_tile_num = ALIGN(actual_w, 16) >> 4;
> +
> +		/*
> +		 * AFBC pic_vir_width is count by pixel, this is different
> +		 * with WIN_VIR_STRIDE.
> +		 */
> +		stride = (fb->pitches[0] << 3) / bpp;
> +		if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
> +			drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligened\n",
> +				  vp->id, win->data->name, stride);
> +
> +		rb_swap = vop2_afbc_rb_swap(fb->format->format);
> +		uv_swap = vop2_afbc_uv_swap(fb->format->format);
> +		/*
> +		 * This is a workaround for crazy IC design, Cluster
> +		 * and Esmart/Smart use different format configuration map:
> +		 * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart.
> +		 *
> +		 * This is one thing we can make the convert simple:
> +		 * AFBCD decode all the YUV data to YUV444. So we just
> +		 * set all the yuv 10 bit to YUV444_10.
> +		 */
> +		if (fb->format->is_yuv && (bpp == 10))
> +			format = VOP2_CLUSTER_YUV444_10;
> +
> +		if (vop2_cluster_window(win))
> +			vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
> +		vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
> +		vop2_win_write(win, VOP2_WIN_AFBC_RB_SWAP, rb_swap);
> +		vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap);
> +		vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
> +		vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
> +		if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) {
> +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0);
> +			transform_offset = vop2_afbc_transform_offset(pstate, false);
> +		} else {
> +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 1);
> +			transform_offset = vop2_afbc_transform_offset(pstate, true);
> +		}
> +		vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
> +		vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset);
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1));
> +		vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16)));
> +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride);
> +		vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num);
> +		vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror);
> +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270);
> +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90);
> +	} else {
> +		vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4));
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror);
> +
> +	if (rotate_90 || rotate_270) {
> +		act_info = swahw32(act_info);
> +		actual_w = drm_rect_height(src) >> 16;
> +		actual_h = drm_rect_width(src) >> 16;
> +	}
> +
> +	vop2_win_write(win, VOP2_WIN_FORMAT, format);
> +	vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst);
> +
> +	rb_swap = vop2_win_rb_swap(fb->format->format);
> +	vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap);
> +	if (!vop2_cluster_window(win)) {
> +		uv_swap = vop2_win_uv_swap(fb->format->format);
> +		vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
> +	}
> +
> +	if (fb->format->is_yuv) {
> +		vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4));
> +		vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst);
> +	}
> +
> +	vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format);
> +	if (!vop2_cluster_window(win))
> +		vop2_plane_setup_color_key(plane, 0);
> +	vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info);
> +	vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info);
> +	vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff));
> +
> +	vop2_setup_csc_mode(vp, win, pstate);
> +
> +	dither_up = vop2_win_dither_up(fb->format->format);
> +	vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up);
> +
> +	vop2_win_write(win, VOP2_WIN_ENABLE, 1);
> +
> +	if (vop2_cluster_window(win)) {
> +		int lb_mode = vop2_get_cluster_lb_mode(win, pstate);
> +
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode);
> +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1);
> +	}
> +}
> +
> +static const struct drm_plane_helper_funcs vop2_plane_helper_funcs = {
> +	.atomic_check = vop2_plane_atomic_check,
> +	.atomic_update = vop2_plane_atomic_update,
> +	.atomic_disable = vop2_plane_atomic_disable,
> +};
> +
> +static const struct drm_plane_funcs vop2_plane_funcs = {
> +	.update_plane	= drm_atomic_helper_update_plane,
> +	.disable_plane	= drm_atomic_helper_disable_plane,
> +	.destroy = drm_plane_cleanup,
> +	.reset = drm_atomic_helper_plane_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> +	.format_mod_supported = rockchip_vop2_mod_supported,
> +};
> +
> +static int vop2_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD);
> +
> +	return 0;
> +}
> +
> +static void vop2_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD);
> +}
> +
> +static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
> +				 const struct drm_display_mode *mode,
> +				 struct drm_display_mode *adj_mode)
> +{
> +	drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V |
> +					CRTC_STEREO_DOUBLE);
> +
> +	return true;
> +}
> +
> +static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	switch (vcstate->bus_format) {
> +	case MEDIA_BUS_FMT_RGB565_1X16:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> +		break;
> +	case MEDIA_BUS_FMT_RGB666_1X18:
> +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> +	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> +		*dsp_ctrl |= RGB888_TO_RGB666;
> +		break;
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA)
> +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> +
> +	*dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL,
> +				DITHER_DOWN_ALLEGRO);
> +}
> +
> +static void vop2_post_config(struct drm_crtc *crtc)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	u16 vtotal = mode->crtc_vtotal;
> +	u16 hdisplay = mode->crtc_hdisplay;
> +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> +	u16 vdisplay = mode->crtc_vdisplay;
> +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> +	u32 left_margin = 100, right_margin = 100;
> +	u32 top_margin = 100, bottom_margin = 100;
> +	u16 hsize = hdisplay * (left_margin + right_margin) / 200;
> +	u16 vsize = vdisplay * (top_margin + bottom_margin) / 200;
> +	u16 hact_end, vact_end;
> +	u32 val;
> +
> +	vsize = rounddown(vsize, 2);
> +	hsize = rounddown(hsize, 2);
> +	hact_st += hdisplay * (100 - left_margin) / 200;
> +	hact_end = hact_st + hsize;
> +	val = hact_st << 16;
> +	val |= hact_end;
> +	vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val);
> +	vact_st += vdisplay * (100 - top_margin) / 200;
> +	vact_end = vact_st + vsize;
> +	val = vact_st << 16;
> +	val |= vact_end;
> +	vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val);
> +	val = scl_cal_scale2(vdisplay, vsize) << 16;
> +	val |= scl_cal_scale2(hdisplay, hsize);
> +	vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val);
> +
> +	val = 0;
> +	if (hdisplay != hsize)
> +		val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN;
> +	if (vdisplay != vsize)
> +		val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN;
> +	vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val);
> +
> +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> +		u16 vact_st_f1 = vtotal + vact_st + 1;
> +		u16 vact_end_f1 = vact_st_f1 + vsize;
> +
> +		val = vact_st_f1 << 16 | vact_end_f1;
> +		vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
> +	}
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_BG, 0);
> +}
> +
> +static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
> +				u32 polflags)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	u32 die, dip;
> +
> +	die = vop2_readl(vop2, RK3568_DSP_IF_EN);
> +	dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
> +
> +	switch (id) {
> +	case RK3568_VOP2_EP_RGB:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_RGB |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
> +		if (polflags & POLFLAG_DCLK_INV)
> +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
> +		else
> +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16));
> +		break;
> +	case RK3568_VOP2_EP_HDMI:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_HDMI |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id);
> +		break;
> +	case RK3568_VOP2_EP_EDP:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_EDP |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id);
> +		break;
> +	case RK3568_VOP2_EP_MIPI0:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_MIPI1:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_LVDS0:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> +		break;
> +	case RK3568_VOP2_EP_LVDS1:
> +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX;
> +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 |
> +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id);
> +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> +		break;
> +	default:
> +		return;
> +	};
> +
> +	dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
> +
> +	vop2_writel(vop2, RK3568_DSP_IF_EN, die);
> +	vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
> +}
> +
> +static int us_to_vertical_line(struct drm_display_mode *mode, int us)
> +{
> +	return us * mode->clock / mode->htotal / 1000;
> +}
> +
> +static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
> +				    struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	const struct vop2_data *vop2_data = vop2->data;
> +	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	unsigned long clock = mode->crtc_clock * 1000;
> +	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
> +	u16 hdisplay = mode->crtc_hdisplay;
> +	u16 htotal = mode->crtc_htotal;
> +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> +	u16 hact_end = hact_st + hdisplay;
> +	u16 vdisplay = mode->crtc_vdisplay;
> +	u16 vtotal = mode->crtc_vtotal;
> +	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
> +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> +	u16 vact_end = vact_st + vdisplay;
> +	u8 out_mode;
> +	u32 dsp_ctrl = 0;
> +	int act_end;
> +	u32 val, polflags;
> +	int ret;
> +	struct drm_encoder *encoder;
> +
> +	drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n",
> +		hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
> +		drm_mode_vrefresh(mode), vcstate->output_type, vp->id);
> +
> +	vop2_lock(vop2);
> +
> +	ret = clk_prepare_enable(vp->dclk);
> +	if (ret < 0) {
> +		drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
> +			      vp->id, ret);
> +		return;
> +	}
> +
> +	if (!vop2->enable_count)
> +		vop2_enable(vop2);
> +
> +	vop2->enable_count++;
> +
> +	vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY);
> +
> +	polflags = 0;
> +	if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
> +		polflags |= POLFLAG_DCLK_INV;
> +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> +		polflags |= BIT(HSYNC_POSITIVE);
> +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> +		polflags |= BIT(VSYNC_POSITIVE);
> +
> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> +		struct device_node *node, *parent;
> +
> +		parent = of_get_parent(rkencoder->port);
> +
> +		for_each_endpoint_of_node(parent, node) {
> +			struct device_node *crtc_port = of_graph_get_remote_port(node);
> +			struct device_node *epn;
> +			struct of_endpoint endpoint;
> +
> +			if (crtc->port != crtc_port) {
> +				of_node_put(crtc_port);
> +				continue;
> +			}
> +
> +			of_node_put(crtc_port);
> +
> +			epn = of_graph_get_remote_endpoint(node);
> +			of_graph_parse_endpoint(epn, &endpoint);
> +			of_node_put(epn);
> +
> +			drm_dbg(vop2->drm, "vp%d is connected to %s, id %d\n",
> +					   vp->id, encoder->name, endpoint.id);
> +			rk3568_set_intf_mux(vp, endpoint.id, polflags);
> +		}
> +		of_node_put(parent);
> +	}
> +
> +	if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
> +	     !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT))
> +		out_mode = ROCKCHIP_OUT_MODE_P888;
> +	else
> +		out_mode = vcstate->output_mode;
> +
> +	dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode);
> +
> +	if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP;
> +
> +	if (is_yuv_output(vcstate->bus_format))
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y;
> +
> +	vop2_dither_setup(crtc, &dsp_ctrl);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len);
> +	val = hact_st << 16;
> +	val |= hact_end;
> +	vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val);
> +
> +	val = vact_st << 16;
> +	val |= vact_end;
> +	vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val);
> +
> +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> +		u16 vact_st_f1 = vtotal + vact_st + 1;
> +		u16 vact_end_f1 = vact_st_f1 + vdisplay;
> +
> +		val = vact_st_f1 << 16 | vact_end_f1;
> +		vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val);
> +
> +		val = vtotal << 16 | (vtotal + vsync_len);
> +		vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val);
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE;
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL;
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN;
> +		vtotal += vtotal + 1;
> +		act_end = vact_end_f1;
> +	} else {
> +		act_end = vact_end;
> +	}
> +
> +	vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id),
> +		    (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len);
> +
> +	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
> +		dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
> +		clock *= 2;
> +	}
> +
> +	vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
> +
> +	clk_set_rate(vp->dclk, clock);
> +
> +	vop2_post_config(crtc);
> +
> +	vop2_cfg_done(vp);
> +
> +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
> +
> +	drm_crtc_vblank_on(crtc);
> +
> +	vop2_unlock(vop2);
> +}
> +
> +static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
> +				  struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct drm_plane *plane;
> +	int nplanes = 0;
> +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +
> +	drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
> +		nplanes++;
> +
> +	if (nplanes > vp->nlayers)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static bool is_opaque(u16 alpha)
> +{
> +	return (alpha >> 8) == 0xff;
> +}
> +
> +static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config,
> +			     struct vop2_alpha *alpha)
> +{
> +	int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1;
> +	int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1;
> +	int src_color_mode = alpha_config->src_premulti_en ?
> +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> +	int dst_color_mode = alpha_config->dst_premulti_en ?
> +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> +
> +	alpha->src_color_ctrl.val = 0;
> +	alpha->dst_color_ctrl.val = 0;
> +	alpha->src_alpha_ctrl.val = 0;
> +	alpha->dst_alpha_ctrl.val = 0;
> +
> +	if (!alpha_config->src_pixel_alpha_en)
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> +	else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en)
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> +	else
> +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> +
> +	alpha->src_color_ctrl.bits.alpha_en = 1;
> +
> +	if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) {
> +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> +	} else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) {
> +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE;
> +	} else {
> +		alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL;
> +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> +	}
> +	alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8;
> +	alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +
> +	alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +	alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> +	alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8;
> +	alpha->dst_color_ctrl.bits.color_mode = dst_color_mode;
> +	alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> +
> +	alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode;
> +	alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> +	alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE;
> +
> +	alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> +	if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en)
> +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> +	else
> +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> +	alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION;
> +	alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> +}
> +
> +static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id)
> +{
> +	struct vop2_video_port *vp;
> +	int used_layer = 0;
> +	int i;
> +
> +	for (i = 0; i < port_id; i++) {
> +		vp = &vop2->vps[i];
> +		used_layer += hweight32(vp->win_mask);
> +	}
> +
> +	return used_layer;
> +}
> +
> +static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win)
> +{
> +	u32 offset = (main_win->data->phys_id * 0x10);
> +	struct vop2_alpha_config alpha_config;
> +	struct vop2_alpha alpha;
> +	struct drm_plane_state *bottom_win_pstate;
> +	bool src_pixel_alpha_en = false;
> +	u16 src_glb_alpha_val, dst_glb_alpha_val;
> +	bool premulti_en = false;
> +	bool swap = false;
> +
> +	/* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */
> +	bottom_win_pstate = main_win->base.state;
> +	src_glb_alpha_val = 0;
> +	dst_glb_alpha_val = main_win->base.state->alpha;
> +
> +	if (!bottom_win_pstate->fb)
> +		return;
> +
> +	alpha_config.src_premulti_en = premulti_en;
> +	alpha_config.dst_premulti_en = false;
> +	alpha_config.src_pixel_alpha_en = src_pixel_alpha_en;
> +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> +	alpha_config.src_glb_alpha_value = src_glb_alpha_val;
> +	alpha_config.dst_glb_alpha_value = dst_glb_alpha_val;
> +	vop2_parse_alpha(&alpha_config, &alpha);
> +
> +	alpha.src_color_ctrl.bits.src_dst_swap = swap;
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset,
> +		    alpha.src_color_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset,
> +		    alpha.dst_color_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset,
> +		    alpha.src_alpha_ctrl.val);
> +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset,
> +		    alpha.dst_alpha_ctrl.val);
> +}
> +
> +static void vop2_setup_alpha(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_framebuffer *fb;
> +	struct vop2_alpha_config alpha_config;
> +	struct vop2_alpha alpha;
> +	struct drm_plane *plane;
> +	int pixel_alpha_en;
> +	int premulti_en, gpremulti_en = 0;
> +	int mixer_id;
> +	u32 offset;
> +	bool bottom_layer_alpha_en = false;
> +	u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE;
> +
> +	mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id);
> +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> +
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		if (plane->state->normalized_zpos == 0 &&
> +		    !is_opaque(plane->state->alpha) &&
> +		    !vop2_cluster_window(win)) {
> +			/*
> +			 * If bottom layer have global alpha effect [except cluster layer,
> +			 * because cluster have deal with bottom layer global alpha value
> +			 * at cluster mix], bottom layer mix need deal with global alpha.
> +			 */
> +			bottom_layer_alpha_en = true;
> +			dst_global_alpha = plane->state->alpha;
> +		}
> +	}
> +
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +		int zpos = plane->state->normalized_zpos;
> +
> +		if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
> +			premulti_en = 1;
> +		else
> +			premulti_en = 0;
> +
> +		plane = &win->base;
> +		fb = plane->state->fb;
> +
> +		pixel_alpha_en = fb->format->has_alpha;
> +
> +		alpha_config.src_premulti_en = premulti_en;
> +
> +		if (bottom_layer_alpha_en && zpos == 1) {
> +			gpremulti_en = premulti_en;
> +			/* Cd = Cs + (1 - As) * Cd * Agd */
> +			alpha_config.dst_premulti_en = false;
> +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> +			alpha_config.dst_glb_alpha_value = dst_global_alpha;
> +		} else if (vop2_cluster_window(win)) {
> +			/* Mix output data only have pixel alpha */
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = true;
> +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +		} else {
> +			/* Cd = Cs + (1 - As) * Cd */
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +		}
> +
> +		vop2_parse_alpha(&alpha_config, &alpha);
> +
> +		offset = (mixer_id + zpos - 1) * 0x10;
> +		vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset,
> +			    alpha.src_color_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset,
> +			    alpha.dst_color_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset,
> +			    alpha.src_alpha_ctrl.val);
> +		vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset,
> +			    alpha.dst_alpha_ctrl.val);
> +	}
> +
> +	if (vp->id == 0) {
> +		if (bottom_layer_alpha_en) {
> +			/* Transfer pixel alpha to hdr mix */
> +			alpha_config.src_premulti_en = gpremulti_en;
> +			alpha_config.dst_premulti_en = true;
> +			alpha_config.src_pixel_alpha_en = true;
> +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> +			vop2_parse_alpha(&alpha_config, &alpha);
> +
> +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL,
> +				alpha.src_color_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL,
> +				alpha.dst_color_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL,
> +				alpha.src_alpha_ctrl.val);
> +			vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL,
> +				alpha.dst_alpha_ctrl.val);
> +		} else {
> +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0);
> +		}
> +	}
> +}
> +
> +static void vop2_setup_layer_mixer(struct vop2_video_port *vp)
> +{
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_plane *plane;
> +	u32 layer_sel = 0;
> +	u32 port_sel;
> +	unsigned int nlayer, ofs;
> +	struct drm_display_mode *adjusted_mode;
> +	u16 hsync_len;
> +	u16 hdisplay;
> +	u32 bg_dly;
> +	u32 pre_scan_dly;
> +	int i;
> +	struct vop2_video_port *vp0 = &vop2->vps[0];
> +	struct vop2_video_port *vp1 = &vop2->vps[1];
> +	struct vop2_video_port *vp2 = &vop2->vps[2];
> +
> +	adjusted_mode = &vp->crtc.state->adjusted_mode;
> +	hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
> +	hdisplay = adjusted_mode->crtc_hdisplay;
> +
> +	bg_dly = vp->data->pre_scan_max_dly[3];
> +	vop2_writel(vop2, RK3568_VP_BG_MIX_CTRL(vp->id),
> +			    FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly));
> +
> +	pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len;
> +	vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly);
> +
> +	vop2_writel(vop2, RK3568_OVL_CTRL, 0);
> +	port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
> +	port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT;
> +
> +	if (vp0->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX,
> +				     vp0->nlayers - 1);
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8);
> +
> +	if (vp1->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX,
> +				     (vp0->nlayers + vp1->nlayers - 1));
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> +
> +	if (vp2->nlayers)
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX,
> +			(vp2->nlayers + vp1->nlayers + vp0->nlayers - 1));
> +	else
> +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> +
> +	layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
> +
> +	ofs = 0;
> +	for (i = 0; i < vp->id; i++)
> +		ofs += vop2->vps[i].nlayers;
> +
> +	nlayer = 0;
> +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		switch (win->data->phys_id) {
> +		case ROCKCHIP_VOP2_CLUSTER0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_CLUSTER1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_SMART0:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART0;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id);
> +			break;
> +		case ROCKCHIP_VOP2_SMART1:
> +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART1;
> +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id);
> +			break;
> +		}
> +
> +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, win->data->layer_sel_id);
> +		nlayer++;
> +	}
> +
> +	/* configure unused layers to 0x5 (reserved) */
> +	for (; nlayer < 3; nlayer++) {
> +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5);
> +	}
> +
> +	vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel);
> +	vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
> +	vop2_writel(vop2, RK3568_OVL_CTRL, RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD);
> +}
> +
> +static void vop2_setup_dly_for_windows(struct vop2 *vop2)
> +{
> +	struct vop2_win *win;
> +	int i = 0;
> +	u32 cdly = 0, sdly = 0;
> +
> +	for (i = 0; i < vop2->data->win_size; i++) {
> +		u32 dly;
> +
> +		win = &vop2->win[i];
> +		dly = win->delay;
> +
> +		switch (win->data->phys_id) {
> +		case ROCKCHIP_VOP2_CLUSTER0:
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly);
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_CLUSTER1:
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly);
> +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART0:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly);
> +			break;
> +		case ROCKCHIP_VOP2_ESMART1:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_SMART0:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly);
> +			break;
> +		case ROCKCHIP_VOP2_SMART1:
> +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly);
> +			break;
> +		}
> +	}
> +
> +	vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly);
> +	vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly);
> +}
> +
> +static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +	struct vop2 *vop2 = vp->vop2;
> +	struct drm_plane *plane;
> +
> +	vp->win_mask = 0;
> +
> +	drm_atomic_crtc_for_each_plane(plane, crtc) {
> +		struct vop2_win *win = to_vop2_win(plane);
> +
> +		win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT];
> +
> +		vp->win_mask |= BIT(win->data->phys_id);
> +
> +		if (vop2_cluster_window(win))
> +			vop2_setup_cluster_alpha(vop2, win);
> +	}
> +
> +	if (!vp->win_mask)
> +		return;
> +
> +	vop2_setup_layer_mixer(vp);
> +	vop2_setup_alpha(vp);
> +	vop2_setup_dly_for_windows(vop2);
> +}
> +
> +static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
> +				   struct drm_atomic_state *state)
> +{
> +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> +
> +	vop2_post_config(crtc);
> +
> +	vop2_cfg_done(vp);
> +
> +	spin_lock_irq(&crtc->dev->event_lock);
> +
> +	if (crtc->state->event) {
> +		WARN_ON(drm_crtc_vblank_get(crtc));
> +		vp->event = crtc->state->event;
> +		crtc->state->event = NULL;
> +	}
> +
> +	spin_unlock_irq(&crtc->dev->event_lock);
> +}
> +
> +static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
> +	.mode_fixup = vop2_crtc_mode_fixup,
> +	.atomic_check = vop2_crtc_atomic_check,
> +	.atomic_begin = vop2_crtc_atomic_begin,
> +	.atomic_flush = vop2_crtc_atomic_flush,
> +	.atomic_enable = vop2_crtc_atomic_enable,
> +	.atomic_disable = vop2_crtc_atomic_disable,
> +};
> +
> +static void vop2_crtc_reset(struct drm_crtc *crtc)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	if (crtc->state) {
> +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
> +		kfree(vcstate);
> +	}
> +
> +	vcstate = kzalloc(sizeof(*vcstate), GFP_KERNEL);
> +	if (!vcstate)
> +		return;
> +
> +	crtc->state = &vcstate->base;
> +	crtc->state->crtc = crtc;
> +}
> +
> +static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc)
> +{
> +	struct rockchip_crtc_state *vcstate, *old_vcstate;
> +
> +	old_vcstate = to_rockchip_crtc_state(crtc->state);
> +
> +	vcstate = kmemdup(old_vcstate, sizeof(*old_vcstate), GFP_KERNEL);
> +	if (!vcstate)
> +		return NULL;
> +
> +	__drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
> +
> +	return &vcstate->base;
> +}
> +
> +static void vop2_crtc_destroy_state(struct drm_crtc *crtc,
> +				    struct drm_crtc_state *state)
> +{
> +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state);
> +
> +	__drm_atomic_helper_crtc_destroy_state(&vcstate->base);
> +	kfree(vcstate);
> +}
> +
> +static const struct drm_crtc_funcs vop2_crtc_funcs = {
> +	.set_config = drm_atomic_helper_set_config,
> +	.page_flip = drm_atomic_helper_page_flip,
> +	.destroy = drm_crtc_cleanup,
> +	.reset = vop2_crtc_reset,
> +	.atomic_duplicate_state = vop2_crtc_duplicate_state,
> +	.atomic_destroy_state = vop2_crtc_destroy_state,
> +	.enable_vblank = vop2_crtc_enable_vblank,
> +	.disable_vblank = vop2_crtc_disable_vblank,
> +};
> +
> +static irqreturn_t vop2_isr(int irq, void *data)
> +{
> +	struct vop2 *vop2 = data;
> +	const struct vop2_data *vop2_data = vop2->data;
> +	u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM];
> +	int ret = IRQ_NONE;
> +	int i;
> +
> +	/*
> +	 * The irq is shared with the iommu. If the runtime-pm state of the
> +	 * vop2-device is disabled the irq has to be targeted at the iommu.
> +	 */
> +	if (!pm_runtime_get_if_in_use(vop2->dev))
> +		return IRQ_NONE;
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +		struct drm_crtc *crtc = &vp->crtc;
> +		u32 irqs;
> +
> +		irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
> +		vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
> +
> +		if (irqs & VP_INT_DSP_HOLD_VALID) {
> +			complete(&vp->dsp_hold_completion);
> +			ret = IRQ_HANDLED;
> +		}
> +
> +		if (irqs & VP_INT_FS_FIELD) {
> +			drm_crtc_handle_vblank(crtc);
> +			spin_lock(&crtc->dev->event_lock);
> +			if (vp->event) {
> +				u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
> +
> +				if (!(val & BIT(vp->id))) {
> +					drm_crtc_send_vblank_event(crtc, vp->event);
> +					vp->event = NULL;
> +					drm_crtc_vblank_put(crtc);
> +				}
> +			}
> +			spin_unlock(&crtc->dev->event_lock);
> +
> +			ret = IRQ_HANDLED;
> +		}
> +
> +		if (irqs & VP_INT_POST_BUF_EMPTY) {
> +			drm_err_ratelimited(vop2->drm,
> +					    "POST_BUF_EMPTY irq err at vp%d\n",
> +					    vp->id);
> +			ret = IRQ_HANDLED;
> +		}
> +	}
> +
> +	axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS);
> +	vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]);
> +	axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS);
> +	vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]);
> +
> +	for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) {
> +		if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) {
> +			drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n");
> +			ret = IRQ_HANDLED;
> +		}
> +	}
> +
> +	pm_runtime_put(vop2->dev);
> +
> +	return ret;
> +}
> +
> +static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win,
> +			   unsigned long possible_crtcs)
> +{
> +	const struct vop2_win_data *win_data = win->data;
> +	unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> +				  BIT(DRM_MODE_BLEND_PREMULTI) |
> +				  BIT(DRM_MODE_BLEND_COVERAGE);
> +	int ret;
> +
> +	ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs,
> +				       &vop2_plane_funcs, win_data->formats,
> +				       win_data->nformats,
> +				       win_data->format_modifiers,
> +				       win->type, win_data->name);
> +	if (ret) {
> +		drm_err(vop2->drm, "failed to initialize plane %d\n", ret);
> +		return ret;
> +	}
> +
> +	drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs);
> +
> +	if (win->data->supported_rotations)
> +		drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0,
> +						   DRM_MODE_ROTATE_0 |
> +						   win->data->supported_rotations);
> +	drm_plane_create_alpha_property(&win->base);
> +	drm_plane_create_blend_mode_property(&win->base, blend_caps);
> +	drm_plane_create_zpos_property(&win->base, win->win_id, 0,
> +				       vop2->registered_num_wins - 1);
> +
> +	return 0;
> +}
> +
> +static struct vop2_video_port *get_activated_vp(struct vop2 *vop2, int n)
> +{
> +	int i, id = 0;
> +
> +	for (i = 0; i < vop2->data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +
> +		if (!vp->crtc.port)
> +			continue;
> +
> +		if (n == id)
> +			return vp;
> +		id++;
> +	}
> +
> +	return NULL;
> +}
> +
> +#define NR_LAYERS 6
> +
> +static int vop2_create_crtc(struct vop2 *vop2)
> +{
> +	const struct vop2_data *vop2_data = vop2->data;
> +	struct drm_device *drm = vop2->drm;
> +	struct device *dev = vop2->dev;
> +	struct drm_plane *plane;
> +	struct device_node *port;
> +	struct vop2_video_port *vp;
> +	u32 possible_crtcs;
> +	int i, nvp, nvps = 0;
> +	int ret;
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		const struct vop2_video_port_data *vp_data;
> +		struct device_node *np;
> +		char dclk_name[9];
> +
> +		vp_data = &vop2_data->vp[i];
> +		vp = &vop2->vps[i];
> +		vp->vop2 = vop2;
> +		vp->id = vp_data->id;
> +		vp->regs = vp_data->regs;
> +		vp->data = vp_data;
> +
> +		snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
> +		vp->dclk = devm_clk_get(vop2->dev, dclk_name);
> +		if (IS_ERR(vp->dclk)) {
> +			drm_err(vop2->drm, "failed to get %s\n", dclk_name);
> +			return PTR_ERR(vp->dclk);
> +		}
> +
> +		np = of_graph_get_remote_node(dev->of_node, i, -1);
> +		if (!np) {
> +			drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
> +			continue;
> +		}
> +		of_node_put(np);
> +
> +		port = of_graph_get_port_by_id(dev->of_node, i);
> +		if (!port) {
> +			drm_err(vop2->drm, "no port node found for video_port%d\n", i);
> +			return -ENOENT;
> +		}
> +
> +		vp->crtc.port = port;
> +		nvps++;
> +	}
> +
> +	nvp = 0;
> +	for (i = 0; i < vop2->registered_num_wins; i++) {
> +		struct vop2_win *win = &vop2->win[i];
> +
> +		if (win->type == DRM_PLANE_TYPE_PRIMARY) {
> +			vp = get_activated_vp(vop2, nvp);
> +
> +			if (vp) {
> +				possible_crtcs = BIT(nvp);
> +				vp->primary_plane = win;
> +
> +				nvp++;
> +			} else {
> +				/* change the unused primary window to overlay window */
> +				win->type = DRM_PLANE_TYPE_OVERLAY;
> +			}
> +		}
> +
> +		if (win->type == DRM_PLANE_TYPE_OVERLAY)
> +			possible_crtcs = (1 << vop2_data->nr_vps) - 1;
> +
> +		ret = vop2_plane_init(vop2, win, possible_crtcs);
> +
> +		if (ret) {
> +			drm_err(vop2->drm, "failed to init plane %s: %d\n",
> +				win->data->name, ret);
> +			return ret;
> +		}
> +	}
> +
> +	for (i = 0; i < vop2_data->nr_vps; i++) {
> +		vp = &vop2->vps[i];
> +
> +		if (!vp->crtc.port)
> +			continue;
> +
> +		plane = &vp->primary_plane->base;
> +
> +		ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL,
> +						&vop2_crtc_funcs,
> +						"video_port%d", vp->id);
> +		if (ret) {
> +			drm_err(vop2->drm, "crtc init for video_port%d failed\n", i);
> +			return ret;
> +		}
> +
> +		drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs);
> +
> +		init_completion(&vp->dsp_hold_completion);
> +	}
> +
> +	for (i = 0; i < vop2->data->nr_vps; i++) {
> +		struct vop2_video_port *vp = &vop2->vps[i];
> +
> +		if (vp->crtc.port)
> +			vp->nlayers = NR_LAYERS / nvps;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vop2_destroy_crtc(struct drm_crtc *crtc)
> +{
> +	of_node_put(crtc->port);
> +
> +	/*
> +	 * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane()
> +	 * references the CRTC.
> +	 */
> +	drm_crtc_cleanup(crtc);
> +}
> +
> +static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = {
> +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0),
> +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5),
> +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14),
> +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18),
> +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31),
> +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31),
> +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31),
> +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31),
> +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31),
> +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19),
> +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15),
> +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31),
> +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8),
> +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9),
> +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11),
> +
> +	/* Scale */
> +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15),
> +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31),
> +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15),
> +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13),
> +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3),
> +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28),
> +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29),
> +
> +	/* cluster regs */
> +	[VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1),
> +	[VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0),
> +	[VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7),
> +
> +	/* afbc regs */
> +	[VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6),
> +	[VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9),
> +	[VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10),
> +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4),
> +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7),
> +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8),
> +	[VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31),
> +	[VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31),
> +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15),
> +	[VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31),
> +	[VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31),
> +	[VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0),
> +	[VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1),
> +	[VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2),
> +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3),
> +	[VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff },
> +	[VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
> +	[VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
> +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff },
> +	[VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff },
> +};
> +
> +static int vop2_cluster_init(struct vop2_win *win)
> +{
> +	struct vop2 *vop2 = win->vop2;
> +	struct reg_field *cluster_regs;
> +	int ret, i;
> +
> +	cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs),
> +			       GFP_KERNEL);
> +	if (!cluster_regs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++)
> +		if (cluster_regs[i].reg != 0xffffffff)
> +			cluster_regs[i].reg += win->offset;
> +
> +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> +					    cluster_regs,
> +					    ARRAY_SIZE(vop2_cluster_regs));
> +
> +	kfree(cluster_regs);
> +
> +	return ret;
> +};
> +
> +static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = {
> +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0),
> +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5),
> +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12),
> +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14),
> +	[VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16),
> +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31),
> +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31),
> +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28),
> +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31),
> +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31),
> +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17),
> +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15),
> +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31),
> +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0),
> +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1),
> +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3),
> +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31),
> +	[VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29),
> +	[VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31),
> +
> +	/* Scale */
> +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15),
> +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31),
> +	[VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15),
> +	[VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31),
> +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1),
> +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3),
> +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5),
> +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7),
> +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9),
> +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11),
> +	[VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13),
> +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15),
> +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17),
> +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8),
> +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9),
> +	[VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10),
> +	[VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11),
> +	[VOP2_WIN_XMIRROR] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff },
> +	[VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff },
> +};
> +
> +static int vop2_esmart_init(struct vop2_win *win)
> +{
> +	struct vop2 *vop2 = win->vop2;
> +	struct reg_field *esmart_regs;
> +	int ret, i;
> +
> +	esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs),
> +			       GFP_KERNEL);
> +	if (!esmart_regs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++)
> +		if (esmart_regs[i].reg != 0xffffffff)
> +			esmart_regs[i].reg += win->offset;
> +
> +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> +					    esmart_regs,
> +					    ARRAY_SIZE(vop2_esmart_regs));
> +
> +	kfree(esmart_regs);
> +
> +	return ret;
> +};
> +
> +static int vop2_win_init(struct vop2 *vop2)
> +{
> +	const struct vop2_data *vop2_data = vop2->data;
> +	struct vop2_win *win;
> +	int i, ret;
> +
> +	for (i = 0; i < vop2_data->win_size; i++) {
> +		const struct vop2_win_data *win_data = &vop2_data->win[i];
> +
> +		win = &vop2->win[i];
> +		win->data = win_data;
> +		win->type = win_data->type;
> +		win->offset = win_data->base;
> +		win->win_id = i;
> +		win->vop2 = vop2;
> +		if (vop2_cluster_window(win))
> +			ret = vop2_cluster_init(win);
> +		else
> +			ret = vop2_esmart_init(win);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	vop2->registered_num_wins = vop2_data->win_size;
> +
> +	return 0;
> +}
> +
> +/*
> + * The window registers are only updated when config done is written.
> + * Until that they read back the old value. As we read-modify-write
> + * these registers mark them as non-volatile. This makes sure we read
> + * the new values from the regmap register cache.
> + */
> +static const struct regmap_range vop2_nonvolatile_range[] = {
> +	regmap_reg_range(0x1000, 0x23ff),
> +};
> +
> +static const struct regmap_access_table vop2_volatile_table = {
> +	.no_ranges = vop2_nonvolatile_range,
> +	.n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range),
> +};
> +
> +static const struct regmap_config vop2_regmap_config = {
> +	.reg_bits	= 32,
> +	.val_bits	= 32,
> +	.reg_stride	= 4,
> +	.max_register	= 0x3000,
> +	.name		= "vop2",
> +	.volatile_table	= &vop2_volatile_table,
> +	.cache_type	= REGCACHE_RBTREE,
> +};
> +
> +static int vop2_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	const struct vop2_data *vop2_data;
> +	struct drm_device *drm = data;
> +	struct vop2 *vop2;
> +	struct resource *res;
> +	size_t alloc_size;
> +	int ret;
> +
> +	vop2_data = of_device_get_match_data(dev);
> +	if (!vop2_data)
> +		return -ENODEV;
> +
> +	/* Allocate vop2 struct and its vop2_win array */
> +	alloc_size = sizeof(*vop2) + sizeof(*vop2->win) * vop2_data->win_size;
> +	vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
> +	if (!vop2)
> +		return -ENOMEM;
> +
> +	vop2->dev = dev;
> +	vop2->data = vop2_data;
> +	vop2->drm = drm;
> +
> +	dev_set_drvdata(dev, vop2);
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
> +	if (!res) {
> +		drm_err(vop2->drm, "failed to get vop2 register byname\n");
> +		return -EINVAL;
> +	}
> +
> +	vop2->regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(vop2->regs))
> +		return PTR_ERR(vop2->regs);
> +	vop2->len = resource_size(res);
> +
> +	vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config);
> +
> +	ret = vop2_win_init(vop2);
> +	if (ret)
> +		return ret;
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma_lut");
> +	if (res) {
> +		vop2->lut_regs = devm_ioremap_resource(dev, res);
> +		if (IS_ERR(vop2->lut_regs))
> +			return PTR_ERR(vop2->lut_regs);
> +	}
> +
> +	vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
> +
> +	vop2->hclk = devm_clk_get(vop2->dev, "hclk");
> +	if (IS_ERR(vop2->hclk)) {
> +		drm_err(vop2->drm, "failed to get hclk source\n");
> +		return PTR_ERR(vop2->hclk);
> +	}
> +
> +	vop2->aclk = devm_clk_get(vop2->dev, "aclk");
> +	if (IS_ERR(vop2->aclk)) {
> +		drm_err(vop2->drm, "failed to get aclk source\n");
> +		return PTR_ERR(vop2->aclk);
> +	}
> +
> +	vop2->irq = platform_get_irq(pdev, 0);
> +	if (vop2->irq < 0) {
> +		drm_err(vop2->drm, "cannot find irq for vop2\n");
> +		return vop2->irq;
> +	}
> +
> +	mutex_init(&vop2->vop2_lock);
> +
> +	ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
> +	if (ret)
> +		return ret;
> +
> +	ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev);
> +	if (ret) {
> +		drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = vop2_create_crtc(vop2);
> +	if (ret)
> +		return ret;
> +
> +	pm_runtime_enable(&pdev->dev);
> +
> +	return 0;
> +}
> +
> +static void vop2_unbind(struct device *dev, struct device *master, void *data)
> +{
> +	struct vop2 *vop2 = dev_get_drvdata(dev);
> +	struct drm_device *drm = vop2->drm;
> +	struct list_head *plane_list = &drm->mode_config.plane_list;
> +	struct list_head *crtc_list = &drm->mode_config.crtc_list;
> +	struct drm_crtc *crtc, *tmpc;
> +	struct drm_plane *plane, *tmpp;
> +
> +	rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
> +
> +	pm_runtime_disable(dev);
> +
> +	list_for_each_entry_safe(plane, tmpp, plane_list, head)
> +		drm_plane_cleanup(plane);
> +
> +	list_for_each_entry_safe(crtc, tmpc, crtc_list, head)
> +		vop2_destroy_crtc(crtc);
> +}
> +
> +const struct component_ops vop2_component_ops = {
> +	.bind = vop2_bind,
> +	.unbind = vop2_unbind,
> +};
> +EXPORT_SYMBOL_GPL(vop2_component_ops);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> new file mode 100644
> index 0000000000000..c727093a06d68
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> @@ -0,0 +1,477 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao@rock-chips.com>
> + */
> +
> +#ifndef _ROCKCHIP_DRM_VOP2_H
> +#define _ROCKCHIP_DRM_VOP2_H
> +
> +#include "rockchip_drm_vop.h"
> +
> +#include <linux/regmap.h>
> +#include <drm/drm_modes.h>
> +
> +#define VOP_FEATURE_OUTPUT_10BIT        BIT(0)
> +
> +#define WIN_FEATURE_AFBDC		BIT(0)
> +#define WIN_FEATURE_CLUSTER		BIT(1)
> +
> +/*
> + *  the delay number of a window in different mode.
> + */
> +enum win_dly_mode {
> +	VOP2_DLY_MODE_DEFAULT,   /**< default mode */
> +	VOP2_DLY_MODE_HISO_S,    /** HDR in SDR out mode, as a SDR window */
> +	VOP2_DLY_MODE_HIHO_H,    /** HDR in HDR out mode, as a HDR window */
> +	VOP2_DLY_MODE_MAX,
> +};
> +
> +struct vop_rect {
> +	int width;
> +	int height;
> +};
> +
> +enum vop2_scale_up_mode {
> +	VOP2_SCALE_UP_NRST_NBOR,
> +	VOP2_SCALE_UP_BIL,
> +	VOP2_SCALE_UP_BIC,
> +};
> +
> +enum vop2_scale_down_mode {
> +	VOP2_SCALE_DOWN_NRST_NBOR,
> +	VOP2_SCALE_DOWN_BIL,
> +	VOP2_SCALE_DOWN_AVG,
> +};
> +
> +enum vop2_win_regs {
> +	VOP2_WIN_ENABLE,
> +	VOP2_WIN_FORMAT,
> +	VOP2_WIN_CSC_MODE,
> +	VOP2_WIN_XMIRROR,
> +	VOP2_WIN_YMIRROR,
> +	VOP2_WIN_RB_SWAP,
> +	VOP2_WIN_UV_SWAP,
> +	VOP2_WIN_ACT_INFO,
> +	VOP2_WIN_DSP_INFO,
> +	VOP2_WIN_DSP_ST,
> +	VOP2_WIN_YRGB_MST,
> +	VOP2_WIN_UV_MST,
> +	VOP2_WIN_YRGB_VIR,
> +	VOP2_WIN_UV_VIR,
> +	VOP2_WIN_YUV_CLIP,
> +	VOP2_WIN_Y2R_EN,
> +	VOP2_WIN_R2Y_EN,
> +	VOP2_WIN_COLOR_KEY,
> +	VOP2_WIN_COLOR_KEY_EN,
> +	VOP2_WIN_DITHER_UP,
> +
> +	/* scale regs */
> +	VOP2_WIN_SCALE_YRGB_X,
> +	VOP2_WIN_SCALE_YRGB_Y,
> +	VOP2_WIN_SCALE_CBCR_X,
> +	VOP2_WIN_SCALE_CBCR_Y,
> +	VOP2_WIN_YRGB_HOR_SCL_MODE,
> +	VOP2_WIN_YRGB_HSCL_FILTER_MODE,
> +	VOP2_WIN_YRGB_VER_SCL_MODE,
> +	VOP2_WIN_YRGB_VSCL_FILTER_MODE,
> +	VOP2_WIN_CBCR_VER_SCL_MODE,
> +	VOP2_WIN_CBCR_HSCL_FILTER_MODE,
> +	VOP2_WIN_CBCR_HOR_SCL_MODE,
> +	VOP2_WIN_CBCR_VSCL_FILTER_MODE,
> +	VOP2_WIN_VSD_CBCR_GT2,
> +	VOP2_WIN_VSD_CBCR_GT4,
> +	VOP2_WIN_VSD_YRGB_GT2,
> +	VOP2_WIN_VSD_YRGB_GT4,
> +	VOP2_WIN_BIC_COE_SEL,
> +
> +	/* cluster regs */
> +	VOP2_WIN_CLUSTER_ENABLE,
> +	VOP2_WIN_AFBC_ENABLE,
> +	VOP2_WIN_CLUSTER_LB_MODE,
> +
> +	/* afbc regs */
> +	VOP2_WIN_AFBC_FORMAT,
> +	VOP2_WIN_AFBC_RB_SWAP,
> +	VOP2_WIN_AFBC_UV_SWAP,
> +	VOP2_WIN_AFBC_AUTO_GATING_EN,
> +	VOP2_WIN_AFBC_BLOCK_SPLIT_EN,
> +	VOP2_WIN_AFBC_PIC_VIR_WIDTH,
> +	VOP2_WIN_AFBC_TILE_NUM,
> +	VOP2_WIN_AFBC_PIC_OFFSET,
> +	VOP2_WIN_AFBC_PIC_SIZE,
> +	VOP2_WIN_AFBC_DSP_OFFSET,
> +	VOP2_WIN_AFBC_TRANSFORM_OFFSET,
> +	VOP2_WIN_AFBC_HDR_PTR,
> +	VOP2_WIN_AFBC_HALF_BLOCK_EN,
> +	VOP2_WIN_AFBC_ROTATE_270,
> +	VOP2_WIN_AFBC_ROTATE_90,
> +	VOP2_WIN_MAX_REG,
> +};
> +
> +struct vop2_win_data {
> +	const char *name;
> +	unsigned int phys_id;
> +
> +	u32 base;
> +	enum drm_plane_type type;
> +
> +	u32 nformats;
> +	const u32 *formats;
> +	const uint64_t *format_modifiers;
> +	const unsigned int supported_rotations;
> +
> +	/**
> +	 * @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2
> +	 */
> +	unsigned int layer_sel_id;
> +	uint64_t feature;
> +
> +	unsigned int max_upscale_factor;
> +	unsigned int max_downscale_factor;
> +	const u8 dly[VOP2_DLY_MODE_MAX];
> +};
> +
> +struct vop2_video_port_data {
> +	unsigned int id;
> +	u32 feature;
> +	u16 gamma_lut_len;
> +	u16 cubic_lut_len;
> +	struct vop_rect max_output;
> +	const u8 pre_scan_max_dly[4];
> +	const struct vop2_video_port_regs *regs;
> +	unsigned int offset;
> +};
> +
> +struct vop2_data {
> +	u8 nr_vps;
> +	const struct vop2_ctrl *ctrl;
> +	const struct vop2_win_data *win;
> +	const struct vop2_video_port_data *vp;
> +	const struct vop_csc_table *csc_table;
> +	struct vop_rect max_input;
> +	struct vop_rect max_output;
> +
> +	unsigned int win_size;
> +	unsigned int soc_id;
> +};
> +
> +/* interrupt define */
> +#define FS_NEW_INTR			BIT(4)
> +#define ADDR_SAME_INTR			BIT(5)
> +#define LINE_FLAG1_INTR			BIT(6)
> +#define WIN0_EMPTY_INTR			BIT(7)
> +#define WIN1_EMPTY_INTR			BIT(8)
> +#define WIN2_EMPTY_INTR			BIT(9)
> +#define WIN3_EMPTY_INTR			BIT(10)
> +#define HWC_EMPTY_INTR			BIT(11)
> +#define POST_BUF_EMPTY_INTR		BIT(12)
> +#define PWM_GEN_INTR			BIT(13)
> +#define DMA_FINISH_INTR			BIT(14)
> +#define FS_FIELD_INTR			BIT(15)
> +#define FE_INTR				BIT(16)
> +#define WB_UV_FIFO_FULL_INTR		BIT(17)
> +#define WB_YRGB_FIFO_FULL_INTR		BIT(18)
> +#define WB_COMPLETE_INTR		BIT(19)
> +
> +/*
> + * display output interface supported by rockchip lcdc
> + */
> +#define ROCKCHIP_OUT_MODE_P888		0
> +#define ROCKCHIP_OUT_MODE_BT1120	0
> +#define ROCKCHIP_OUT_MODE_P666		1
> +#define ROCKCHIP_OUT_MODE_P565		2
> +#define ROCKCHIP_OUT_MODE_BT656		5
> +#define ROCKCHIP_OUT_MODE_S888		8
> +#define ROCKCHIP_OUT_MODE_S888_DUMMY	12
> +#define ROCKCHIP_OUT_MODE_YUV420	14
> +/* for use special outface */
> +#define ROCKCHIP_OUT_MODE_AAAA		15
> +
> +enum vop_csc_format {
> +	CSC_BT601L,
> +	CSC_BT709L,
> +	CSC_BT601F,
> +	CSC_BT2020,
> +};
> +
> +enum src_factor_mode {
> +	SRC_FAC_ALPHA_ZERO,
> +	SRC_FAC_ALPHA_ONE,
> +	SRC_FAC_ALPHA_DST,
> +	SRC_FAC_ALPHA_DST_INVERSE,
> +	SRC_FAC_ALPHA_SRC,
> +	SRC_FAC_ALPHA_SRC_GLOBAL,
> +};
> +
> +enum dst_factor_mode {
> +	DST_FAC_ALPHA_ZERO,
> +	DST_FAC_ALPHA_ONE,
> +	DST_FAC_ALPHA_SRC,
> +	DST_FAC_ALPHA_SRC_INVERSE,
> +	DST_FAC_ALPHA_DST,
> +	DST_FAC_ALPHA_DST_GLOBAL,
> +};
> +
> +#define RK3568_GRF_VO_CON1			0x0364
> +/* System registers definition */
> +#define RK3568_REG_CFG_DONE			0x000
> +#define RK3568_VERSION_INFO			0x004
> +#define RK3568_SYS_AUTO_GATING_CTRL		0x008
> +#define RK3568_SYS_AXI_LUT_CTRL			0x024
> +#define RK3568_DSP_IF_EN			0x028
> +#define RK3568_DSP_IF_CTRL			0x02c
> +#define RK3568_DSP_IF_POL			0x030
> +#define RK3568_WB_CTRL				0x40
> +#define RK3568_WB_XSCAL_FACTOR			0x44
> +#define RK3568_WB_YRGB_MST			0x48
> +#define RK3568_WB_CBR_MST			0x4C
> +#define RK3568_OTP_WIN_EN			0x050
> +#define RK3568_LUT_PORT_SEL			0x058
> +#define RK3568_SYS_STATUS0			0x060
> +#define RK3568_VP_LINE_FLAG(vp)			(0x70 + (vp) * 0x4)
> +#define RK3568_SYS0_INT_EN			0x80
> +#define RK3568_SYS0_INT_CLR			0x84
> +#define RK3568_SYS0_INT_STATUS			0x88
> +#define RK3568_SYS1_INT_EN			0x90
> +#define RK3568_SYS1_INT_CLR			0x94
> +#define RK3568_SYS1_INT_STATUS			0x98
> +#define RK3568_VP_INT_EN(vp)			(0xA0 + (vp) * 0x10)
> +#define RK3568_VP_INT_CLR(vp)			(0xA4 + (vp) * 0x10)
> +#define RK3568_VP_INT_STATUS(vp)		(0xA8 + (vp) * 0x10)
> +#define RK3568_VP_INT_RAW_STATUS(vp)		(0xAC + (vp) * 0x10)
> +
> +/* Video Port registers definition */
> +#define RK3568_VP_DSP_CTRL			0x00
> +#define RK3568_VP_MIPI_CTRL			0x04
> +#define RK3568_VP_COLOR_BAR_CTRL		0x08
> +#define RK3568_VP_3D_LUT_CTRL			0x10
> +#define RK3568_VP_3D_LUT_MST			0x20
> +#define RK3568_VP_DSP_BG			0x2C
> +#define RK3568_VP_PRE_SCAN_HTIMING		0x30
> +#define RK3568_VP_POST_DSP_HACT_INFO		0x34
> +#define RK3568_VP_POST_DSP_VACT_INFO		0x38
> +#define RK3568_VP_POST_SCL_FACTOR_YRGB		0x3C
> +#define RK3568_VP_POST_SCL_CTRL			0x40
> +#define RK3568_VP_POST_DSP_VACT_INFO_F1		0x44
> +#define RK3568_VP_DSP_HTOTAL_HS_END		0x48
> +#define RK3568_VP_DSP_HACT_ST_END		0x4C
> +#define RK3568_VP_DSP_VTOTAL_VS_END		0x50
> +#define RK3568_VP_DSP_VACT_ST_END		0x54
> +#define RK3568_VP_DSP_VS_ST_END_F1		0x58
> +#define RK3568_VP_DSP_VACT_ST_END_F1		0x5C
> +#define RK3568_VP_BCSH_CTRL			0x60
> +#define RK3568_VP_BCSH_BCS			0x64
> +#define RK3568_VP_BCSH_H			0x68
> +#define RK3568_VP_BCSH_COLOR_BAR		0x6C
> +
> +/* Overlay registers definition    */
> +#define RK3568_OVL_CTRL				0x600
> +#define RK3568_OVL_LAYER_SEL			0x604
> +#define RK3568_OVL_PORT_SEL			0x608
> +#define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL	0x610
> +#define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL	0x614
> +#define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL	0x618
> +#define RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL	0x61C
> +#define RK3568_MIX0_SRC_COLOR_CTRL		0x650
> +#define RK3568_MIX0_DST_COLOR_CTRL		0x654
> +#define RK3568_MIX0_SRC_ALPHA_CTRL		0x658
> +#define RK3568_MIX0_DST_ALPHA_CTRL		0x65C
> +#define RK3568_HDR0_SRC_COLOR_CTRL		0x6C0
> +#define RK3568_HDR0_DST_COLOR_CTRL		0x6C4
> +#define RK3568_HDR0_SRC_ALPHA_CTRL		0x6C8
> +#define RK3568_HDR0_DST_ALPHA_CTRL		0x6CC
> +#define RK3568_VP_BG_MIX_CTRL(vp)		(0x6E0 + (vp) * 4)
> +#define RK3568_CLUSTER_DLY_NUM			0x6F0
> +#define RK3568_SMART_DLY_NUM			0x6F8
> +
> +/* Cluster register definition, offset relative to window base */
> +#define RK3568_CLUSTER_WIN_CTRL0		0x00
> +#define RK3568_CLUSTER_WIN_CTRL1		0x04
> +#define RK3568_CLUSTER_WIN_YRGB_MST		0x10
> +#define RK3568_CLUSTER_WIN_CBR_MST		0x14
> +#define RK3568_CLUSTER_WIN_VIR			0x18
> +#define RK3568_CLUSTER_WIN_ACT_INFO		0x20
> +#define RK3568_CLUSTER_WIN_DSP_INFO		0x24
> +#define RK3568_CLUSTER_WIN_DSP_ST		0x28
> +#define RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB	0x30
> +#define RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET	0x3C
> +#define RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL	0x50
> +#define RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE	0x54
> +#define RK3568_CLUSTER_WIN_AFBCD_HDR_PTR	0x58
> +#define RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH	0x5C
> +#define RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE	0x60
> +#define RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET	0x64
> +#define RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET	0x68
> +#define RK3568_CLUSTER_WIN_AFBCD_CTRL		0x6C
> +
> +#define RK3568_CLUSTER_CTRL			0x100
> +
> +/* (E)smart register definition, offset relative to window base */
> +#define RK3568_SMART_CTRL0			0x00
> +#define RK3568_SMART_CTRL1			0x04
> +#define RK3568_SMART_REGION0_CTRL		0x10
> +#define RK3568_SMART_REGION0_YRGB_MST		0x14
> +#define RK3568_SMART_REGION0_CBR_MST		0x18
> +#define RK3568_SMART_REGION0_VIR		0x1C
> +#define RK3568_SMART_REGION0_ACT_INFO		0x20
> +#define RK3568_SMART_REGION0_DSP_INFO		0x24
> +#define RK3568_SMART_REGION0_DSP_ST		0x28
> +#define RK3568_SMART_REGION0_SCL_CTRL		0x30
> +#define RK3568_SMART_REGION0_SCL_FACTOR_YRGB	0x34
> +#define RK3568_SMART_REGION0_SCL_FACTOR_CBR	0x38
> +#define RK3568_SMART_REGION0_SCL_OFFSET		0x3C
> +#define RK3568_SMART_REGION1_CTRL		0x40
> +#define RK3568_SMART_REGION1_YRGB_MST		0x44
> +#define RK3568_SMART_REGION1_CBR_MST		0x48
> +#define RK3568_SMART_REGION1_VIR		0x4C
> +#define RK3568_SMART_REGION1_ACT_INFO		0x50
> +#define RK3568_SMART_REGION1_DSP_INFO		0x54
> +#define RK3568_SMART_REGION1_DSP_ST		0x58
> +#define RK3568_SMART_REGION1_SCL_CTRL		0x60
> +#define RK3568_SMART_REGION1_SCL_FACTOR_YRGB	0x64
> +#define RK3568_SMART_REGION1_SCL_FACTOR_CBR	0x68
> +#define RK3568_SMART_REGION1_SCL_OFFSET		0x6C
> +#define RK3568_SMART_REGION2_CTRL		0x70
> +#define RK3568_SMART_REGION2_YRGB_MST		0x74
> +#define RK3568_SMART_REGION2_CBR_MST		0x78
> +#define RK3568_SMART_REGION2_VIR		0x7C
> +#define RK3568_SMART_REGION2_ACT_INFO		0x80
> +#define RK3568_SMART_REGION2_DSP_INFO		0x84
> +#define RK3568_SMART_REGION2_DSP_ST		0x88
> +#define RK3568_SMART_REGION2_SCL_CTRL		0x90
> +#define RK3568_SMART_REGION2_SCL_FACTOR_YRGB	0x94
> +#define RK3568_SMART_REGION2_SCL_FACTOR_CBR	0x98
> +#define RK3568_SMART_REGION2_SCL_OFFSET		0x9C
> +#define RK3568_SMART_REGION3_CTRL		0xA0
> +#define RK3568_SMART_REGION3_YRGB_MST		0xA4
> +#define RK3568_SMART_REGION3_CBR_MST		0xA8
> +#define RK3568_SMART_REGION3_VIR		0xAC
> +#define RK3568_SMART_REGION3_ACT_INFO		0xB0
> +#define RK3568_SMART_REGION3_DSP_INFO		0xB4
> +#define RK3568_SMART_REGION3_DSP_ST		0xB8
> +#define RK3568_SMART_REGION3_SCL_CTRL		0xC0
> +#define RK3568_SMART_REGION3_SCL_FACTOR_YRGB	0xC4
> +#define RK3568_SMART_REGION3_SCL_FACTOR_CBR	0xC8
> +#define RK3568_SMART_REGION3_SCL_OFFSET		0xCC
> +#define RK3568_SMART_COLOR_KEY_CTRL		0xD0
> +
> +/* HDR register definition */
> +#define RK3568_HDR_LUT_CTRL			0x2000
> +#define RK3568_HDR_LUT_MST			0x2004
> +#define RK3568_SDR2HDR_CTRL			0x2010
> +#define RK3568_HDR2SDR_CTRL			0x2020
> +#define RK3568_HDR2SDR_SRC_RANGE		0x2024
> +#define RK3568_HDR2SDR_NORMFACEETF		0x2028
> +#define RK3568_HDR2SDR_DST_RANGE		0x202C
> +#define RK3568_HDR2SDR_NORMFACCGAMMA		0x2030
> +#define RK3568_HDR_EETF_OETF_Y0			0x203C
> +#define RK3568_HDR_SAT_Y0			0x20C0
> +#define RK3568_HDR_EOTF_OETF_Y0			0x20F0
> +#define RK3568_HDR_OETF_DX_POW1			0x2200
> +#define RK3568_HDR_OETF_XN1			0x2300
> +
> +#define RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN		BIT(15)
> +
> +#define RK3568_VP_DSP_CTRL__STANDBY			BIT(31)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_MODE		BIT(20)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL		GENMASK(19, 18)
> +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_EN		BIT(17)
> +#define RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN		BIT(16)
> +#define RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y		BIT(15)
> +#define RK3568_VP_DSP_CTRL__DSP_RB_SWAP			BIT(9)
> +#define RK3568_VP_DSP_CTRL__DSP_INTERLACE		BIT(7)
> +#define RK3568_VP_DSP_CTRL__DSP_FILED_POL		BIT(6)
> +#define RK3568_VP_DSP_CTRL__P2I_EN			BIT(5)
> +#define RK3568_VP_DSP_CTRL__CORE_DCLK_DIV		BIT(4)
> +#define RK3568_VP_DSP_CTRL__OUT_MODE			GENMASK(3, 0)
> +
> +#define RK3568_VP_POST_SCL_CTRL__VSCALEDOWN		BIT(1)
> +#define RK3568_VP_POST_SCL_CTRL__HSCALEDOWN		BIT(0)
> +
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX		GENMASK(26, 25)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS1			BIT(24)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX		GENMASK(22, 21)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI1			BIT(20)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX		GENMASK(19, 18)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX		GENMASK(17, 16)
> +#define RK3568_SYS_DSP_INFACE_EN_EDP_MUX		GENMASK(15, 14)
> +#define RK3568_SYS_DSP_INFACE_EN_HDMI_MUX		GENMASK(11, 10)
> +#define RK3568_SYS_DSP_INFACE_EN_RGB_MUX		GENMASK(9, 8)
> +#define RK3568_SYS_DSP_INFACE_EN_LVDS0			BIT(5)
> +#define RK3568_SYS_DSP_INFACE_EN_MIPI0			BIT(4)
> +#define RK3568_SYS_DSP_INFACE_EN_EDP			BIT(3)
> +#define RK3568_SYS_DSP_INFACE_EN_HDMI			BIT(1)
> +#define RK3568_SYS_DSP_INFACE_EN_RGB			BIT(0)
> +
> +#define RK3568_DSP_IF_POL__MIPI_PIN_POL			GENMASK(19, 16)
> +#define RK3568_DSP_IF_POL__EDP_PIN_POL			GENMASK(15, 12)
> +#define RK3568_DSP_IF_POL__HDMI_PIN_POL			GENMASK(7, 4)
> +#define RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL		GENMASK(3, 0)
> +
> +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2_PHASE_LOCK	BIT(5)
> +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2			BIT(4)
> +
> +#define RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN	BIT(31)
> +
> +#define RK3568_DSP_IF_POL__CFG_DONE_IMD			BIT(28)
> +
> +#define VOP2_SYS_AXI_BUS_NUM				2
> +
> +#define VOP2_CLUSTER_YUV444_10				0x12
> +
> +#define VOP2_COLOR_KEY_MASK				BIT(31)
> +
> +#define RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD		BIT(28)
> +
> +#define RK3568_VP_BG_MIX_CTRL__BG_DLY			GENMASK(31, 24)
> +
> +#define RK3568_OVL_PORT_SEL__SEL_PORT			GENMASK(31, 16)
> +#define RK3568_OVL_PORT_SEL__SMART1			GENMASK(31, 30)
> +#define RK3568_OVL_PORT_SEL__SMART0			GENMASK(29, 28)
> +#define RK3568_OVL_PORT_SEL__ESMART1			GENMASK(27, 26)
> +#define RK3568_OVL_PORT_SEL__ESMART0			GENMASK(25, 24)
> +#define RK3568_OVL_PORT_SEL__CLUSTER1			GENMASK(19, 18)
> +#define RK3568_OVL_PORT_SEL__CLUSTER0			GENMASK(17, 16)
> +#define RK3568_OVL_PORT_SET__PORT2_MUX			GENMASK(11, 8)
> +#define RK3568_OVL_PORT_SET__PORT1_MUX			GENMASK(7, 4)
> +#define RK3568_OVL_PORT_SET__PORT0_MUX			GENMASK(3, 0)
> +#define RK3568_OVL_LAYER_SEL__LAYER(layer, x)		((x) << ((layer) * 4))
> +
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_1		GENMASK(31, 24)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_0		GENMASK(23, 16)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_1		GENMASK(15, 8)
> +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_0		GENMASK(7, 0)
> +
> +#define RK3568_SMART_DLY_NUM__SMART1			GENMASK(31, 24)
> +#define RK3568_SMART_DLY_NUM__SMART0			GENMASK(23, 16)
> +#define RK3568_SMART_DLY_NUM__ESMART1			GENMASK(15, 8)
> +#define RK3568_SMART_DLY_NUM__ESMART0			GENMASK(7, 0)
> +
> +#define VP_INT_DSP_HOLD_VALID	BIT(6)
> +#define VP_INT_FS_FIELD		BIT(5)
> +#define VP_INT_POST_BUF_EMPTY	BIT(4)
> +#define VP_INT_LINE_FLAG1	BIT(3)
> +#define VP_INT_LINE_FLAG0	BIT(2)
> +#define VOP2_INT_BUS_ERRPR	BIT(1)
> +#define VP_INT_FS		BIT(0)
> +
> +#define POLFLAG_DCLK_INV	BIT(3)
> +
> +enum vop2_layer_phy_id {
> +	ROCKCHIP_VOP2_CLUSTER0 = 0,
> +	ROCKCHIP_VOP2_CLUSTER1,
> +	ROCKCHIP_VOP2_ESMART0,
> +	ROCKCHIP_VOP2_ESMART1,
> +	ROCKCHIP_VOP2_SMART0,
> +	ROCKCHIP_VOP2_SMART1,
> +	ROCKCHIP_VOP2_CLUSTER2,
> +	ROCKCHIP_VOP2_CLUSTER3,
> +	ROCKCHIP_VOP2_ESMART2,
> +	ROCKCHIP_VOP2_ESMART3,
> +	ROCKCHIP_VOP2_PHY_ID_INVALID = -1,
> +};
> +
> +extern const struct component_ops vop2_component_ops;
> +
> +#endif /* _ROCKCHIP_DRM_VOP2_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> new file mode 100644
> index 0000000000000..9bf0637bf8e26
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> @@ -0,0 +1,281 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) Rockchip Electronics Co.Ltd
> + * Author: Andy Yan <andy.yan@rock-chips.com>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/component.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_plane.h>
> +#include <drm/drm_print.h>
> +
> +#include "rockchip_drm_vop2.h"
> +
> +static const uint32_t formats_win_full_10bit[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV24,
> +};
> +
> +static const uint32_t formats_win_full_10bit_yuyv[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_NV12,
> +	DRM_FORMAT_NV16,
> +	DRM_FORMAT_NV24,
> +	DRM_FORMAT_YVYU,
> +	DRM_FORMAT_VYUY,
> +};
> +
> +static const uint32_t formats_win_lite[] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +};
> +
> +static const uint64_t format_modifiers[] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	DRM_FORMAT_MOD_INVALID,
> +};
> +
> +static const uint64_t format_modifiers_afbc[] = {
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_CBR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_CBR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_CBR),
> +
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_CBR |
> +				AFBC_FORMAT_MOD_SPARSE),
> +
> +	/* SPLIT mandates SPARSE, RGB modes mandates YTR */
> +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> +				AFBC_FORMAT_MOD_YTR |
> +				AFBC_FORMAT_MOD_SPARSE |
> +				AFBC_FORMAT_MOD_SPLIT),
> +	DRM_FORMAT_MOD_INVALID,
> +};
> +
> +static const struct vop2_video_port_data rk3568_vop_video_ports[] = {
> +	{
> +		.id = 0,
> +		.feature = VOP_FEATURE_OUTPUT_10BIT,
> +		.gamma_lut_len = 1024,
> +		.cubic_lut_len = 9 * 9 * 9,
> +		.max_output = { 4096, 2304 },
> +		.pre_scan_max_dly = { 69, 53, 53, 42 },
> +		.offset = 0xc00,
> +	}, {
> +		.id = 1,
> +		.gamma_lut_len = 1024,
> +		.max_output = { 2048, 1536 },
> +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> +		.offset = 0xd00,
> +	}, {
> +		.id = 2,
> +		.gamma_lut_len = 1024,
> +		.max_output = { 1920, 1080 },
> +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> +		.offset = 0xe00,
> +	},
> +};
> +
> +/*
> + * rk3568 vop with 2 cluster, 2 esmart win, 2 smart win.
> + * Every cluster can work as 4K win or split into two win.
> + * All win in cluster support AFBCD.
> + *
> + * Every esmart win and smart win support 4 Multi-region.
> + *
> + * Scale filter mode:
> + *
> + * * Cluster:  bicubic for horizontal scale up, others use bilinear
> + * * ESmart:
> + *    * nearest-neighbor/bilinear/bicubic for scale up
> + *    * nearest-neighbor/bilinear/average for scale down
> + *
> + *
> + * @TODO describe the wind like cpu-map dt nodes;
> + */
> +static const struct vop2_win_data rk3568_vop_win_data[] = {
> +	{
> +		.name = "Smart0-win0",
> +		.phys_id = ROCKCHIP_VOP2_SMART0,
> +		.base = 0x1c00,
> +		.formats = formats_win_lite,
> +		.nformats = ARRAY_SIZE(formats_win_lite),
> +		.format_modifiers = format_modifiers,
> +		.layer_sel_id = 3,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Smart1-win0",
> +		.phys_id = ROCKCHIP_VOP2_SMART1,
> +		.formats = formats_win_lite,
> +		.nformats = ARRAY_SIZE(formats_win_lite),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1e00,
> +		.layer_sel_id = 7,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Esmart1-win0",
> +		.phys_id = ROCKCHIP_VOP2_ESMART1,
> +		.formats = formats_win_full_10bit_yuyv,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1a00,
> +		.layer_sel_id = 6,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_PRIMARY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Esmart0-win0",
> +		.phys_id = ROCKCHIP_VOP2_ESMART0,
> +		.formats = formats_win_full_10bit_yuyv,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> +		.format_modifiers = format_modifiers,
> +		.base = 0x1800,
> +		.layer_sel_id = 2,
> +		.supported_rotations = DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.max_upscale_factor = 8,
> +		.max_downscale_factor = 8,
> +		.dly = { 20, 47, 41 },
> +	}, {
> +		.name = "Cluster0-win0",
> +		.phys_id = ROCKCHIP_VOP2_CLUSTER0,
> +		.base = 0x1000,
> +		.formats = formats_win_full_10bit,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> +		.format_modifiers = format_modifiers_afbc,
> +		.layer_sel_id = 0,
> +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> +		.max_upscale_factor = 4,
> +		.max_downscale_factor = 4,
> +		.dly = { 0, 27, 21 },
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> +	}, {
> +		.name = "Cluster1-win0",
> +		.phys_id = ROCKCHIP_VOP2_CLUSTER1,
> +		.base = 0x1200,
> +		.formats = formats_win_full_10bit,
> +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> +		.format_modifiers = format_modifiers_afbc,
> +		.layer_sel_id = 1,
> +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> +		.type = DRM_PLANE_TYPE_OVERLAY,
> +		.max_upscale_factor = 4,
> +		.max_downscale_factor = 4,
> +		.dly = { 0, 27, 21 },
> +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> +	},
> +};
> +
> +static const struct vop2_data rk3566_vop = {
> +	.nr_vps = 3,
> +	.max_input = { 4096, 2304 },
> +	.max_output = { 4096, 2304 },
> +	.vp = rk3568_vop_video_ports,
> +	.win = rk3568_vop_win_data,
> +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> +	.soc_id = 3566,
> +};
> +
> +static const struct vop2_data rk3568_vop = {
> +	.nr_vps = 3,
> +	.max_input = { 4096, 2304 },
> +	.max_output = { 4096, 2304 },
> +	.vp = rk3568_vop_video_ports,
> +	.win = rk3568_vop_win_data,
> +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> +	.soc_id = 3568,
> +};
> +
> +static const struct of_device_id vop2_dt_match[] = {
> +	{
> +		.compatible = "rockchip,rk3566-vop",
> +		.data = &rk3566_vop,
> +	}, {
> +		.compatible = "rockchip,rk3568-vop",
> +		.data = &rk3568_vop,
> +	}, {
> +	},
> +};
> +MODULE_DEVICE_TABLE(of, vop2_dt_match);
> +
> +static int vop2_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +
> +	return component_add(dev, &vop2_component_ops);
> +}
> +
> +static int vop2_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &vop2_component_ops);
> +
> +	return 0;
> +}
> +
> +struct platform_driver vop2_platform_driver = {
> +	.probe = vop2_probe,
> +	.remove = vop2_remove,
> +	.driver = {
> +		.name = "rockchip-vop2",
> +		.of_match_table = of_match_ptr(vop2_dt_match),
> +	},
> +};

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
  2022-02-21 11:51     ` Andy Yan
  (?)
@ 2022-02-21 18:18       ` Lucas Stach
  -1 siblings, 0 replies; 201+ messages in thread
From: Lucas Stach @ 2022-02-21 18:18 UTC (permalink / raw)
  To: Andy Yan, Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Heiko Stübner, Sandy Huang,
	Kever Yang, linux-rockchip, Michael Riesch, kernel, Peter Geis,
	tfiga, linux-arm-kernel

Hi Andy,

Am Montag, dem 21.02.2022 um 19:51 +0800 schrieb Andy Yan:
> Hi Sascha:
> 
> On 2/17/22 16:29, Sascha Hauer wrote:
> > From: Andy Yan <andy.yan@rock-chips.com>
> > 
> > The VOP2 unit is found on Rockchip SoCs beginning with rk3566/rk3568.
> > It replaces the VOP unit found in the older Rockchip SoCs.
> > 
> > This driver has been derived from the downstream Rockchip Kernel and
> > heavily modified:
> > 
> > - All nonstandard DRM properties have been removed
> > - dropped struct vop2_plane_state and pass around less data between
> >    functions
> > - Dropped all DRM_FORMAT_* not known on upstream
> > - rework register access to get rid of excessively used macros
> > - Drop all waiting for framesyncs
> 
> All the waiting sync in the downstream divers are try to fix special 
> problems,
> 
> and some of them inherited from upstream vop driver, for example: the 
> fb_unref_work,
> 
> It submitted by Tomas Figa to upstream vop driver to make the wait for 
> flip and asynchronously cursor work
> 
> right.
> 
> VOP2 share the same hardware design in vblank, so I don't think these 
> code are useless.

We've discussed this quite a bit internally before dropping this custom
frame synchronization. We believe that this is okay to do, as Sascha
also dropped a lot of code that would have required more
synchronization than what the common DRM atomic core code provides.

Fundamentally, a lot of the extra synchronization, also in the
upstream, VOP driver seems like it is working around the fact that the
vblank event might get sent out too early, so references to still
active framebuffers are dropped from the DRM core and userspace is
allowed to make further progress.

With this VOP2 submission, the vblank event is only armed at
atomic_flush time after all the HW programming is in place, which
ensures that even though we still race with the vblank IRQ, we only
loose this race in a predictable way and the only bad thing caused by
the race is a temporary hickup where a page-flip is deferred for one
more vblank cycle, but all the reference counts are still correct and
nothing gets freed prematurely.

Features not included in this submission, like dynamic assignment of
overlay planes to CRTCs, might need additional frame synchronization,
but at the current state of the driver any additional synchronization
is just cargo-cult and not actually needed.

Regards,
Lucas

> 
> [0] 
> https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-5-git-send-email-tfiga@chromium.org/
> 
> [1] 
> https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-6-git-send-email-tfiga@chromium.org/
> 
> [2] 
> https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-4-git-send-email-tfiga@chromium.org/
> 
> 
> > 
> > The driver is tested with HDMI and MIPI-DSI display on a RK3568-EVB
> > board. Overlay support is tested with the modetest utility. AFBC support
> > on the cluster windows is tested with weston-simple-dmabuf-egl on
> > weston using the (yet to be upstreamed) panfrost driver support.
> > 
> > Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> > 
> > Notes:
> >      Changes since v5:
> >      - consistently use u8/u16/u32 rather than uint8_t/uint16_t/uint32_t
> >      - Use spin_lock rather than spin_lock_irqsave
> >      - replace printk with drm_dbg
> >      - break some overlong lines
> >      
> >      Changes since v4:
> >      - Avoid stack frame overflow by not allocating big array on the stack
> >      
> >      Changes since v3:
> >      - Sort includes
> >      - fix typos
> >      - Drop spinlock
> >      - Use regmap_set_bits()/regmap_clear_bits()
> >      - simplify vop2_scale_factor()
> >      - simplify vop2_afbc_transform_offset()
> >      
> >      Changes since v4:
> >      - Sort nodes alphabetically
> >      
> >      Changes since v3:
> >      - Fix HDMI connector type
> > 
> >   drivers/gpu/drm/rockchip/Kconfig             |    6 +
> >   drivers/gpu/drm/rockchip/Makefile            |    1 +
> >   drivers/gpu/drm/rockchip/rockchip_drm_drv.c  |    1 +
> >   drivers/gpu/drm/rockchip/rockchip_drm_drv.h  |    6 +-
> >   drivers/gpu/drm/rockchip/rockchip_drm_fb.c   |    2 +
> >   drivers/gpu/drm/rockchip/rockchip_drm_vop.h  |   15 +
> >   drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2708 ++++++++++++++++++
> >   drivers/gpu/drm/rockchip/rockchip_drm_vop2.h |  477 +++
> >   drivers/gpu/drm/rockchip/rockchip_vop2_reg.c |  281 ++
> >   9 files changed, 3496 insertions(+), 1 deletion(-)
> >   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> >   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> >   create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> > 
> > diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> > index b9b156308460a..4ff0043f0ee70 100644
> > --- a/drivers/gpu/drm/rockchip/Kconfig
> > +++ b/drivers/gpu/drm/rockchip/Kconfig
> > @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
> >   	  This selects support for the VOP driver. You should enable it
> >   	  on all older SoCs up to RK3399.
> >   
> > +config ROCKCHIP_VOP2
> > +	bool "Rockchip VOP2 driver"
> > +	help
> > +	  This selects support for the VOP2 driver. You should enable it
> > +	  on all newer SoCs beginning form RK3568.
> > +
> >   config ROCKCHIP_ANALOGIX_DP
> >   	bool "Rockchip specific extensions for Analogix DP driver"
> >   	depends on ROCKCHIP_VOP
> > diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> > index dfc5512fdb9f1..3ff7b21c04149 100644
> > --- a/drivers/gpu/drm/rockchip/Makefile
> > +++ b/drivers/gpu/drm/rockchip/Makefile
> > @@ -6,6 +6,7 @@
> >   rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
> >   		rockchip_drm_gem.o
> >   
> > +rockchipdrm-$(CONFIG_ROCKCHIP_VOP2) += rockchip_drm_vop2.o rockchip_vop2_reg.o
> >   rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
> >   rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
> >   rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> > index 82c8faf1fb6b8..95f6c5985fdd7 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> > @@ -459,6 +459,7 @@ static int __init rockchip_drm_init(void)
> >   
> >   	num_rockchip_sub_drivers = 0;
> >   	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
> > +	ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2);
> >   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
> >   				CONFIG_ROCKCHIP_LVDS);
> >   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> > index d3e42410ae5da..5cb207aaaadd0 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> > @@ -18,7 +18,7 @@
> >   
> >   #define ROCKCHIP_MAX_FB_BUFFER	3
> >   #define ROCKCHIP_MAX_CONNECTOR	2
> > -#define ROCKCHIP_MAX_CRTC	2
> > +#define ROCKCHIP_MAX_CRTC	4
> >   
> >   struct drm_device;
> >   struct drm_connector;
> > @@ -31,6 +31,9 @@ struct rockchip_crtc_state {
> >   	int output_bpc;
> >   	int output_flags;
> >   	bool enable_afbc;
> > +	u32 bus_format;
> > +	u32 bus_flags;
> > +	int color_space;
> >   };
> >   #define to_rockchip_crtc_state(s) \
> >   		container_of(s, struct rockchip_crtc_state, base)
> > @@ -63,6 +66,7 @@ extern struct platform_driver rockchip_dp_driver;
> >   extern struct platform_driver rockchip_lvds_driver;
> >   extern struct platform_driver vop_platform_driver;
> >   extern struct platform_driver rk3066_hdmi_driver;
> > +extern struct platform_driver vop2_platform_driver;
> >   
> >   struct rockchip_encoder {
> >   	struct device_node *port;
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > index 3aa37e177667e..0d2cb4f3922b8 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > @@ -134,4 +134,6 @@ void rockchip_drm_mode_config_init(struct drm_device *dev)
> >   
> >   	dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
> >   	dev->mode_config.helper_private = &rockchip_mode_config_helpers;
> > +
> > +	dev->mode_config.normalize_zpos = true;
> >   }
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> > index 857d97cdc67c6..1e364d7b50e69 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> > @@ -54,9 +54,23 @@ struct vop_afbc {
> >   	struct vop_reg enable;
> >   	struct vop_reg win_sel;
> >   	struct vop_reg format;
> > +	struct vop_reg rb_swap;
> > +	struct vop_reg uv_swap;
> > +	struct vop_reg auto_gating_en;
> > +	struct vop_reg block_split_en;
> > +	struct vop_reg pic_vir_width;
> > +	struct vop_reg tile_num;
> >   	struct vop_reg hreg_block_split;
> > +	struct vop_reg pic_offset;
> >   	struct vop_reg pic_size;
> > +	struct vop_reg dsp_offset;
> > +	struct vop_reg transform_offset;
> >   	struct vop_reg hdr_ptr;
> > +	struct vop_reg half_block_en;
> > +	struct vop_reg xmirror;
> > +	struct vop_reg ymirror;
> > +	struct vop_reg rotate_270;
> > +	struct vop_reg rotate_90;
> >   	struct vop_reg rstn;
> >   };
> >   
> > @@ -410,4 +424,5 @@ static inline int scl_vop_cal_lb_mode(int width, bool is_yuv)
> >   }
> >   
> >   extern const struct component_ops vop_component_ops;
> > +
> >   #endif /* _ROCKCHIP_DRM_VOP_H */
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> > new file mode 100644
> > index 0000000000000..394dd6c583682
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> > @@ -0,0 +1,2708 @@
> > +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> > +/*
> > + * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
> > + * Author: Andy Yan <andy.yan@rock-chips.com>
> > + */
> > +#include <linux/bitfield.h>
> > +#include <linux/clk.h>
> > +#include <linux/component.h>
> > +#include <linux/delay.h>
> > +#include <linux/iopoll.h>
> > +#include <linux/kernel.h>
> > +#include <linux/mfd/syscon.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_graph.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <linux/swab.h>
> > +
> > +#include <drm/drm.h>
> > +#include <drm/drm_atomic.h>
> > +#include <drm/drm_atomic_uapi.h>
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_crtc_helper.h>
> > +#include <drm/drm_debugfs.h>
> > +#include <drm/drm_flip_work.h>
> > +#include <drm/drm_plane_helper.h>
> > +#include <drm/drm_probe_helper.h>
> > +#include <drm/drm_vblank.h>
> > +
> > +#include <uapi/linux/videodev2.h>
> > +#include <dt-bindings/soc/rockchip,vop2.h>
> > +
> > +#include "rockchip_drm_drv.h"
> > +#include "rockchip_drm_gem.h"
> > +#include "rockchip_drm_fb.h"
> > +#include "rockchip_drm_vop2.h"
> > +
> > +/*
> > + * VOP2 architecture
> > + *
> > + +----------+   +-------------+                                                        +-----------+
> > + |  Cluster |   | Sel 1 from 6|                                                        | 1 from 3  |
> > + |  window0 |   |    Layer0   |                                                        |    RGB    |
> > + +----------+   +-------------+              +---------------+    +-------------+      +-----------+
> > + +----------+   +-------------+              |N from 6 layers|    |             |
> > + |  Cluster |   | Sel 1 from 6|              |   Overlay0    +--->| Video Port0 |      +-----------+
> > + |  window1 |   |    Layer1   |              |               |    |             |      | 1 from 3  |
> > + +----------+   +-------------+              +---------------+    +-------------+      |   LVDS    |
> > + +----------+   +-------------+                                                        +-----------+
> > + |  Esmart  |   | Sel 1 from 6|
> > + |  window0 |   |   Layer2    |              +---------------+    +-------------+      +-----------+
> > + +----------+   +-------------+              |N from 6 Layers|    |             | +--> | 1 from 3  |
> > + +----------+   +-------------+   -------->  |   Overlay1    +--->| Video Port1 |      |   MIPI    |
> > + |  Esmart  |   | Sel 1 from 6|   -------->  |               |    |             |      +-----------+
> > + |  Window1 |   |   Layer3    |              +---------------+    +-------------+
> > + +----------+   +-------------+                                                        +-----------+
> > + +----------+   +-------------+                                                        | 1 from 3  |
> > + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |   HDMI    |
> > + |  Window0 |   |    Layer4   |              |N from 6 Layers|    |             |      +-----------+
> > + +----------+   +-------------+              |   Overlay2    +--->| Video Port2 |
> > + +----------+   +-------------+              |               |    |             |      +-----------+
> > + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |  1 from 3 |
> > + |  Window1 |   |    Layer5   |                                                        |    eDP    |
> > + +----------+   +-------------+                                                        +-----------+
> > + *
> > + */
> > +
> > +enum vop2_data_format {
> > +	VOP2_FMT_ARGB8888 = 0,
> > +	VOP2_FMT_RGB888,
> > +	VOP2_FMT_RGB565,
> > +	VOP2_FMT_XRGB101010,
> > +	VOP2_FMT_YUV420SP,
> > +	VOP2_FMT_YUV422SP,
> > +	VOP2_FMT_YUV444SP,
> > +	VOP2_FMT_YUYV422 = 8,
> > +	VOP2_FMT_YUYV420,
> > +	VOP2_FMT_VYUY422,
> > +	VOP2_FMT_VYUY420,
> > +	VOP2_FMT_YUV420SP_TILE_8x4 = 0x10,
> > +	VOP2_FMT_YUV420SP_TILE_16x2,
> > +	VOP2_FMT_YUV422SP_TILE_8x4,
> > +	VOP2_FMT_YUV422SP_TILE_16x2,
> > +	VOP2_FMT_YUV420SP_10,
> > +	VOP2_FMT_YUV422SP_10,
> > +	VOP2_FMT_YUV444SP_10,
> > +};
> > +
> > +enum vop2_afbc_format {
> > +	VOP2_AFBC_FMT_RGB565,
> > +	VOP2_AFBC_FMT_ARGB2101010 = 2,
> > +	VOP2_AFBC_FMT_YUV420_10BIT,
> > +	VOP2_AFBC_FMT_RGB888,
> > +	VOP2_AFBC_FMT_ARGB8888,
> > +	VOP2_AFBC_FMT_YUV420 = 9,
> > +	VOP2_AFBC_FMT_YUV422 = 0xb,
> > +	VOP2_AFBC_FMT_YUV422_10BIT = 0xe,
> > +	VOP2_AFBC_FMT_INVALID = -1,
> > +};
> > +
> > +union vop2_alpha_ctrl {
> > +	u32 val;
> > +	struct {
> > +		/* [0:1] */
> > +		u32 color_mode:1;
> > +		u32 alpha_mode:1;
> > +		/* [2:3] */
> > +		u32 blend_mode:2;
> > +		u32 alpha_cal_mode:1;
> > +		/* [5:7] */
> > +		u32 factor_mode:3;
> > +		/* [8:9] */
> > +		u32 alpha_en:1;
> > +		u32 src_dst_swap:1;
> > +		u32 reserved:6;
> > +		/* [16:23] */
> > +		u32 glb_alpha:8;
> > +	} bits;
> > +};
> > +
> > +struct vop2_alpha {
> > +	union vop2_alpha_ctrl src_color_ctrl;
> > +	union vop2_alpha_ctrl dst_color_ctrl;
> > +	union vop2_alpha_ctrl src_alpha_ctrl;
> > +	union vop2_alpha_ctrl dst_alpha_ctrl;
> > +};
> > +
> > +struct vop2_alpha_config {
> > +	bool src_premulti_en;
> > +	bool dst_premulti_en;
> > +	bool src_pixel_alpha_en;
> > +	bool dst_pixel_alpha_en;
> > +	u16 src_glb_alpha_value;
> > +	u16 dst_glb_alpha_value;
> > +};
> > +
> > +struct vop2_win {
> > +	struct vop2 *vop2;
> > +	struct drm_plane base;
> > +	const struct vop2_win_data *data;
> > +	struct regmap_field *reg[VOP2_WIN_MAX_REG];
> > +
> > +	/**
> > +	 * @win_id: graphic window id, a cluster may be split into two
> > +	 * graphics windows.
> > +	 */
> > +	u8 win_id;
> > +	u8 delay;
> > +	u32 offset;
> > +
> > +	enum drm_plane_type type;
> > +};
> > +
> > +struct vop2_video_port {
> > +	struct drm_crtc crtc;
> > +	struct vop2 *vop2;
> > +	struct clk *dclk;
> > +	unsigned int id;
> > +	const struct vop2_video_port_regs *regs;
> > +	const struct vop2_video_port_data *data;
> > +
> > +	struct completion dsp_hold_completion;
> > +
> > +	/**
> > +	 * @win_mask: Bitmask of windows attached to the video port;
> > +	 */
> > +	u32 win_mask;
> > +
> > +	struct vop2_win *primary_plane;
> > +	struct drm_pending_vblank_event *event;
> > +
> > +	unsigned int nlayers;
> > +};
> > +
> > +struct vop2 {
> > +	struct device *dev;
> > +	struct drm_device *drm;
> > +	struct vop2_video_port vps[ROCKCHIP_MAX_CRTC];
> > +
> > +	const struct vop2_data *data;
> > +	/*
> > +	 * Number of windows that are registered as plane, may be less than the
> > +	 * total number of hardware windows.
> > +	 */
> > +	u32 registered_num_wins;
> > +
> > +	void __iomem *regs;
> > +	struct regmap *map;
> > +
> > +	struct regmap *grf;
> > +
> > +	/* physical map length of vop2 register */
> > +	u32 len;
> > +
> > +	void __iomem *lut_regs;
> > +
> > +	/* protects crtc enable/disable */
> > +	struct mutex vop2_lock;
> > +
> > +	int irq;
> > +
> > +	/*
> > +	 * Some global resources are shared between all video ports(crtcs), so
> > +	 * we need a ref counter here.
> > +	 */
> > +	unsigned int enable_count;
> > +	struct clk *hclk;
> > +	struct clk *aclk;
> > +
> > +	/* must be put at the end of the struct */
> > +	struct vop2_win win[];
> > +};
> > +
> > +static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
> > +{
> > +	return container_of(crtc, struct vop2_video_port, crtc);
> > +}
> > +
> > +static struct vop2_win *to_vop2_win(struct drm_plane *p)
> > +{
> > +	return container_of(p, struct vop2_win, base);
> > +}
> > +
> > +static void vop2_lock(struct vop2 *vop2)
> > +{
> > +	mutex_lock(&vop2->vop2_lock);
> > +}
> > +
> > +static void vop2_unlock(struct vop2 *vop2)
> > +{
> > +	mutex_unlock(&vop2->vop2_lock);
> > +}
> > +
> > +static void vop2_writel(struct vop2 *vop2, u32 offset, u32 v)
> > +{
> > +	regmap_write(vop2->map, offset, v);
> > +}
> > +
> > +static void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
> > +{
> > +	regmap_write(vp->vop2->map, vp->data->offset + offset, v);
> > +}
> > +
> > +static u32 vop2_readl(struct vop2 *vop2, u32 offset)
> > +{
> > +	u32 val;
> > +
> > +	regmap_read(vop2->map, offset, &val);
> > +
> > +	return val;
> > +}
> > +
> > +static void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v)
> > +{
> > +	regmap_field_write(win->reg[reg], v);
> > +}
> > +
> > +static bool vop2_cluster_window(const struct vop2_win *win)
> > +{
> > +	return win->data->feature & WIN_FEATURE_CLUSTER;
> > +}
> > +
> > +static void vop2_cfg_done(struct vop2_video_port *vp)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +
> > +	regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE,
> > +			BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> > +}
> > +
> > +static void vop2_win_disable(struct vop2_win *win)
> > +{
> > +	vop2_win_write(win, VOP2_WIN_ENABLE, 0);
> > +
> > +	if (vop2_cluster_window(win))
> > +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0);
> > +}
> > +
> > +static enum vop2_data_format vop2_convert_format(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_XRGB8888:
> > +	case DRM_FORMAT_ARGB8888:
> > +	case DRM_FORMAT_XBGR8888:
> > +	case DRM_FORMAT_ABGR8888:
> > +		return VOP2_FMT_ARGB8888;
> > +	case DRM_FORMAT_RGB888:
> > +	case DRM_FORMAT_BGR888:
> > +		return VOP2_FMT_RGB888;
> > +	case DRM_FORMAT_RGB565:
> > +	case DRM_FORMAT_BGR565:
> > +		return VOP2_FMT_RGB565;
> > +	case DRM_FORMAT_NV12:
> > +		return VOP2_FMT_YUV420SP;
> > +	case DRM_FORMAT_NV16:
> > +		return VOP2_FMT_YUV422SP;
> > +	case DRM_FORMAT_NV24:
> > +		return VOP2_FMT_YUV444SP;
> > +	case DRM_FORMAT_YUYV:
> > +	case DRM_FORMAT_YVYU:
> > +		return VOP2_FMT_VYUY422;
> > +	case DRM_FORMAT_VYUY:
> > +	case DRM_FORMAT_UYVY:
> > +		return VOP2_FMT_YUYV422;
> > +	default:
> > +		DRM_ERROR("unsupported format[%08x]\n", format);
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static enum vop2_afbc_format vop2_convert_afbc_format(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_XRGB8888:
> > +	case DRM_FORMAT_ARGB8888:
> > +	case DRM_FORMAT_XBGR8888:
> > +	case DRM_FORMAT_ABGR8888:
> > +		return VOP2_AFBC_FMT_ARGB8888;
> > +	case DRM_FORMAT_RGB888:
> > +	case DRM_FORMAT_BGR888:
> > +		return VOP2_AFBC_FMT_RGB888;
> > +	case DRM_FORMAT_RGB565:
> > +	case DRM_FORMAT_BGR565:
> > +		return VOP2_AFBC_FMT_RGB565;
> > +	case DRM_FORMAT_NV12:
> > +		return VOP2_AFBC_FMT_YUV420;
> > +	case DRM_FORMAT_NV16:
> > +		return VOP2_AFBC_FMT_YUV422;
> > +	default:
> > +		return VOP2_AFBC_FMT_INVALID;
> > +	}
> > +
> > +	return VOP2_AFBC_FMT_INVALID;
> > +}
> > +
> > +static bool vop2_win_rb_swap(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_XBGR8888:
> > +	case DRM_FORMAT_ABGR8888:
> > +	case DRM_FORMAT_BGR888:
> > +	case DRM_FORMAT_BGR565:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool vop2_afbc_rb_swap(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_NV24:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool vop2_afbc_uv_swap(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_NV12:
> > +	case DRM_FORMAT_NV16:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool vop2_win_uv_swap(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_NV12:
> > +	case DRM_FORMAT_NV16:
> > +	case DRM_FORMAT_NV24:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool vop2_win_dither_up(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_BGR565:
> > +	case DRM_FORMAT_RGB565:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
> > +{
> > +	/*
> > +	 * FIXME:
> > +	 *
> > +	 * There is no media type for YUV444 output,
> > +	 * so when out_mode is AAAA or P888, assume output is YUV444 on
> > +	 * yuv format.
> > +	 *
> > +	 * From H/W testing, YUV444 mode need a rb swap.
> > +	 */
> > +	if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 ||
> > +	    bus_format == MEDIA_BUS_FMT_VYUY8_1X16 ||
> > +	    bus_format == MEDIA_BUS_FMT_YVYU8_2X8 ||
> > +	    bus_format == MEDIA_BUS_FMT_VYUY8_2X8 ||
> > +	    ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
> > +	      bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
> > +	     (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
> > +	      output_mode == ROCKCHIP_OUT_MODE_P888)))
> > +		return true;
> > +	else
> > +		return false;
> > +}
> > +
> > +static bool is_yuv_output(u32 bus_format)
> > +{
> > +	switch (bus_format) {
> > +	case MEDIA_BUS_FMT_YUV8_1X24:
> > +	case MEDIA_BUS_FMT_YUV10_1X30:
> > +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> > +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> > +	case MEDIA_BUS_FMT_YUYV8_2X8:
> > +	case MEDIA_BUS_FMT_YVYU8_2X8:
> > +	case MEDIA_BUS_FMT_UYVY8_2X8:
> > +	case MEDIA_BUS_FMT_VYUY8_2X8:
> > +	case MEDIA_BUS_FMT_YUYV8_1X16:
> > +	case MEDIA_BUS_FMT_YVYU8_1X16:
> > +	case MEDIA_BUS_FMT_UYVY8_1X16:
> > +	case MEDIA_BUS_FMT_VYUY8_1X16:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool rockchip_afbc(struct drm_plane *plane, u64 modifier)
> > +{
> > +	int i;
> > +
> > +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> > +		return false;
> > +
> > +	for (i = 0 ; i < plane->modifier_count; i++)
> > +		if (plane->modifiers[i] == modifier)
> > +			return true;
> > +
> > +	return false;
> > +
> > +}
> > +
> > +static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
> > +					u64 modifier)
> > +{
> > +	struct vop2_win *win = to_vop2_win(plane);
> > +	struct vop2 *vop2 = win->vop2;
> > +
> > +	if (modifier == DRM_FORMAT_MOD_INVALID)
> > +		return false;
> > +
> > +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> > +		return true;
> > +
> > +	if (!rockchip_afbc(plane, modifier)) {
> > +		drm_err(vop2->drm, "Unsupported format modifier 0x%llx\n",
> > +			modifier);
> > +
> > +		return false;
> > +	}
> > +
> > +	return vop2_convert_afbc_format(format) >= 0;
> > +}
> > +
> > +static u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate,
> > +				      bool afbc_half_block_en)
> > +{
> > +	struct drm_rect *src = &pstate->src;
> > +	struct drm_framebuffer *fb = pstate->fb;
> > +	u32 bpp = fb->format->cpp[0] * 8;
> > +	u32 vir_width = (fb->pitches[0] << 3) / bpp;
> > +	u32 width = drm_rect_width(src) >> 16;
> > +	u32 height = drm_rect_height(src) >> 16;
> > +	u32 act_xoffset = src->x1 >> 16;
> > +	u32 act_yoffset = src->y1 >> 16;
> > +	u32 align16_crop = 0;
> > +	u32 align64_crop = 0;
> > +	u32 height_tmp;
> > +	u8 tx, ty;
> > +	u8 bottom_crop_line_num = 0;
> > +
> > +	/* 16 pixel align */
> > +	if (height & 0xf)
> > +		align16_crop = 16 - (height & 0xf);
> > +
> > +	height_tmp = height + align16_crop;
> > +
> > +	/* 64 pixel align */
> > +	if (height_tmp & 0x3f)
> > +		align64_crop = 64 - (height_tmp & 0x3f);
> > +
> > +	bottom_crop_line_num = align16_crop + align64_crop;
> > +
> > +	switch (pstate->rotation &
> > +		(DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y |
> > +		 DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) {
> > +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> > +		tx = 16 - ((act_xoffset + width) & 0xf);
> > +		ty = bottom_crop_line_num - act_yoffset;
> > +		break;
> > +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90:
> > +		tx = bottom_crop_line_num - act_yoffset;
> > +		ty = vir_width - width - act_xoffset;
> > +		break;
> > +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270:
> > +		tx = act_yoffset;
> > +		ty = act_xoffset;
> > +		break;
> > +	case DRM_MODE_REFLECT_X:
> > +		tx = 16 - ((act_xoffset + width) & 0xf);
> > +		ty = act_yoffset;
> > +		break;
> > +	case DRM_MODE_REFLECT_Y:
> > +		tx = act_xoffset;
> > +		ty = bottom_crop_line_num - act_yoffset;
> > +		break;
> > +	case DRM_MODE_ROTATE_90:
> > +		tx = bottom_crop_line_num - act_yoffset;
> > +		ty = act_xoffset;
> > +		break;
> > +	case DRM_MODE_ROTATE_270:
> > +		tx = act_yoffset;
> > +		ty = vir_width - width - act_xoffset;
> > +		break;
> > +	case 0:
> > +		tx = act_xoffset;
> > +		ty = act_yoffset;
> > +		break;
> > +	}
> > +
> > +	if (afbc_half_block_en)
> > +		ty &= 0x7f;
> > +
> > +#define TRANSFORM_XOFFSET GENMASK(7, 0)
> > +#define TRANSFORM_YOFFSET GENMASK(23, 16)
> > +	return FIELD_PREP(TRANSFORM_XOFFSET, tx) |
> > +		FIELD_PREP(TRANSFORM_YOFFSET, ty);
> > +}
> > +
> > +/*
> > + * A Cluster window has 2048 x 16 line buffer, which can
> > + * works at 2048 x 16(Full) or 4096 x 8 (Half) mode.
> > + * for Cluster_lb_mode register:
> > + * 0: half mode, for plane input width range 2048 ~ 4096
> > + * 1: half mode, for cluster work at 2 * 2048 plane mode
> > + * 2: half mode, for rotate_90/270 mode
> > + *
> > + */
> > +static int vop2_get_cluster_lb_mode(struct vop2_win *win,
> > +				    struct drm_plane_state *pstate)
> > +{
> > +	if ((pstate->rotation & DRM_MODE_ROTATE_270) ||
> > +	    (pstate->rotation & DRM_MODE_ROTATE_90))
> > +		return 2;
> > +	else
> > +		return 0;
> > +}
> > +
> > +static u16 vop2_scale_factor(u32 src, u32 dst)
> > +{
> > +	u32 fac;
> > +	int shift;
> > +
> > +	if (src == dst)
> > +		return 0;
> > +
> > +	if (dst < 2)
> > +		return U16_MAX;
> > +
> > +	if (src < 2)
> > +		return 0;
> > +
> > +	if (src > dst)
> > +		shift = 12;
> > +	else
> > +		shift = 16;
> > +
> > +	src--;
> > +	dst--;
> > +
> > +	fac = DIV_ROUND_UP(src << shift, dst) - 1;
> > +
> > +	if (fac > U16_MAX)
> > +		return U16_MAX;
> > +
> > +	return fac;
> > +}
> > +
> > +static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
> > +			     u32 src_w, u32 src_h, u32 dst_w,
> > +			     u32 dst_h, u32 pixel_format)
> > +{
> > +	const struct drm_format_info *info;
> > +	u16 hor_scl_mode, ver_scl_mode;
> > +	u16 hscl_filter_mode, vscl_filter_mode;
> > +	u8 gt2 = 0;
> > +	u8 gt4 = 0;
> > +	u32 val;
> > +
> > +	info = drm_format_info(pixel_format);
> > +
> > +	if (src_h >= (4 * dst_h)) {
> > +		gt4 = 1;
> > +		src_h >>= 2;
> > +	} else if (src_h >= (2 * dst_h)) {
> > +		gt2 = 1;
> > +		src_h >>= 1;
> > +	}
> > +
> > +	hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> > +	ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> > +
> > +	if (hor_scl_mode == SCALE_UP)
> > +		hscl_filter_mode = VOP2_SCALE_UP_BIC;
> > +	else
> > +		hscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> > +
> > +	if (ver_scl_mode == SCALE_UP)
> > +		vscl_filter_mode = VOP2_SCALE_UP_BIL;
> > +	else
> > +		vscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> > +
> > +	/*
> > +	 * RK3568 VOP Esmart/Smart dsp_w should be even pixel
> > +	 * at scale down mode
> > +	 */
> > +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> > +		if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) {
> > +			drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n",
> > +				win->data->name, dst_w);
> > +			dst_w++;
> > +		}
> > +	}
> > +
> > +	val = vop2_scale_factor(src_w, dst_w);
> > +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val);
> > +	val = vop2_scale_factor(src_h, dst_h);
> > +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val);
> > +
> > +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4);
> > +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2);
> > +
> > +	vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode);
> > +	vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode);
> > +
> > +	if (vop2_cluster_window(win))
> > +		return;
> > +
> > +	vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode);
> > +	vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode);
> > +
> > +	if (info->is_yuv) {
> > +		src_w /= info->hsub;
> > +		src_h /= info->vsub;
> > +
> > +		gt4 = gt2 = 0;
> > +
> > +		if (src_h >= (4 * dst_h)) {
> > +			gt4 = 1;
> > +			src_h >>= 2;
> > +		} else if (src_h >= (2 * dst_h)) {
> > +			gt2 = 1;
> > +			src_h >>= 1;
> > +		}
> > +
> > +		hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> > +		ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> > +
> > +		val = vop2_scale_factor(src_w, dst_w);
> > +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val);
> > +
> > +		val = vop2_scale_factor(src_h, dst_h);
> > +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val);
> > +
> > +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4);
> > +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2);
> > +		vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode);
> > +		vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode);
> > +		vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode);
> > +		vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode);
> > +	}
> > +}
> > +
> > +static int vop2_convert_csc_mode(int csc_mode)
> > +{
> > +	switch (csc_mode) {
> > +	case V4L2_COLORSPACE_SMPTE170M:
> > +	case V4L2_COLORSPACE_470_SYSTEM_M:
> > +	case V4L2_COLORSPACE_470_SYSTEM_BG:
> > +		return CSC_BT601L;
> > +	case V4L2_COLORSPACE_REC709:
> > +	case V4L2_COLORSPACE_SMPTE240M:
> > +	case V4L2_COLORSPACE_DEFAULT:
> > +		return CSC_BT709L;
> > +	case V4L2_COLORSPACE_JPEG:
> > +		return CSC_BT601F;
> > +	case V4L2_COLORSPACE_BT2020:
> > +		return CSC_BT2020;
> > +	default:
> > +		return CSC_BT709L;
> > +	}
> > +}
> > +
> > +/*
> > + * colorspace path:
> > + *      Input        Win csc                     Output
> > + * 1. YUV(2020)  --> Y2R->2020To709->R2Y   --> YUV_OUTPUT(601/709)
> > + *    RGB        --> R2Y                  __/
> > + *
> > + * 2. YUV(2020)  --> bypasss               --> YUV_OUTPUT(2020)
> > + *    RGB        --> 709To2020->R2Y       __/
> > + *
> > + * 3. YUV(2020)  --> Y2R->2020To709        --> RGB_OUTPUT(709)
> > + *    RGB        --> R2Y                  __/
> > + *
> > + * 4. YUV(601/709)-> Y2R->709To2020->R2Y   --> YUV_OUTPUT(2020)
> > + *    RGB        --> 709To2020->R2Y       __/
> > + *
> > + * 5. YUV(601/709)-> bypass                --> YUV_OUTPUT(709)
> > + *    RGB        --> R2Y                  __/
> > + *
> > + * 6. YUV(601/709)-> bypass                --> YUV_OUTPUT(601)
> > + *    RGB        --> R2Y(601)             __/
> > + *
> > + * 7. YUV        --> Y2R(709)              --> RGB_OUTPUT(709)
> > + *    RGB        --> bypass               __/
> > + *
> > + * 8. RGB        --> 709To2020->R2Y        --> YUV_OUTPUT(2020)
> > + *
> > + * 9. RGB        --> R2Y(709)              --> YUV_OUTPUT(709)
> > + *
> > + * 10. RGB       --> R2Y(601)              --> YUV_OUTPUT(601)
> > + *
> > + * 11. RGB       --> bypass                --> RGB_OUTPUT(709)
> > + */
> > +
> > +static void vop2_setup_csc_mode(struct vop2_video_port *vp,
> > +				struct vop2_win *win,
> > +				struct drm_plane_state *pstate)
> > +{
> > +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
> > +	int is_input_yuv = pstate->fb->format->is_yuv;
> > +	int is_output_yuv = is_yuv_output(vcstate->bus_format);
> > +	int input_csc = V4L2_COLORSPACE_DEFAULT;
> > +	int output_csc = vcstate->color_space;
> > +	bool r2y_en, y2r_en;
> > +	int csc_mode;
> > +
> > +	if (is_input_yuv && !is_output_yuv) {
> > +		y2r_en = true;
> > +		r2y_en = false;
> > +		csc_mode = vop2_convert_csc_mode(input_csc);
> > +	} else if (!is_input_yuv && is_output_yuv) {
> > +		y2r_en = false;
> > +		r2y_en = true;
> > +		csc_mode = vop2_convert_csc_mode(output_csc);
> > +	} else {
> > +		y2r_en = false;
> > +		r2y_en = false;
> > +		csc_mode = false;
> > +	}
> > +
> > +	vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en);
> > +	vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en);
> > +	vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
> > +}
> > +
> > +static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +
> > +	vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq);
> > +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq);
> > +}
> > +
> > +static void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +
> > +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16);
> > +}
> > +
> > +static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
> > +{
> > +	int ret;
> > +
> > +	ret = clk_prepare_enable(vop2->hclk);
> > +	if (ret < 0) {
> > +		drm_err(vop2->drm, "failed to enable hclk - %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = clk_prepare_enable(vop2->aclk);
> > +	if (ret < 0) {
> > +		drm_err(vop2->drm, "failed to enable aclk - %d\n", ret);
> > +		goto err;
> > +	}
> > +
> > +	return 0;
> > +err:
> > +	clk_disable_unprepare(vop2->hclk);
> > +
> > +	return ret;
> > +}
> > +
> > +static void vop2_enable(struct vop2 *vop2)
> > +{
> > +	int ret;
> > +
> > +	ret = pm_runtime_get_sync(vop2->dev);
> > +	if (ret < 0) {
> > +		drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret);
> > +		return;
> > +	}
> > +
> > +	ret = vop2_core_clks_prepare_enable(vop2);
> > +	if (ret) {
> > +		pm_runtime_put_sync(vop2->dev);
> > +		return;
> > +	}
> > +
> > +	if (vop2->data->soc_id == 3566)
> > +		vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
> > +
> > +	vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> > +
> > +	/*
> > +	 * Disable auto gating, this is a workaround to
> > +	 * avoid display image shift when a window enabled.
> > +	 */
> > +	regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL,
> > +			  RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN);
> > +
> > +	vop2_writel(vop2, RK3568_SYS0_INT_CLR,
> > +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> > +	vop2_writel(vop2, RK3568_SYS0_INT_EN,
> > +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> > +	vop2_writel(vop2, RK3568_SYS1_INT_CLR,
> > +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> > +	vop2_writel(vop2, RK3568_SYS1_INT_EN,
> > +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> > +}
> > +
> > +static void vop2_disable(struct vop2 *vop2)
> > +{
> > +	pm_runtime_put_sync(vop2->dev);
> > +
> > +	clk_disable_unprepare(vop2->aclk);
> > +	clk_disable_unprepare(vop2->hclk);
> > +}
> > +
> > +static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
> > +				     struct drm_atomic_state *state)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct vop2 *vop2 = vp->vop2;
> > +	int ret;
> > +
> > +	vop2_lock(vop2);
> > +
> > +	drm_crtc_vblank_off(crtc);
> > +
> > +	/*
> > +	 * Vop standby will take effect at end of current frame,
> > +	 * if dsp hold valid irq happen, it means standby complete.
> > +	 *
> > +	 * we must wait standby complete when we want to disable aclk,
> > +	 * if not, memory bus maybe dead.
> > +	 */
> > +	reinit_completion(&vp->dsp_hold_completion);
> > +
> > +	vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
> > +
> > +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
> > +
> > +	ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
> > +					  msecs_to_jiffies(50));
> > +	if (!ret)
> > +		drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
> > +
> > +	vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
> > +
> > +	clk_disable_unprepare(vp->dclk);
> > +
> > +	vop2->enable_count--;
> > +
> > +	if (!vop2->enable_count)
> > +		vop2_disable(vop2);
> > +
> > +	vop2_unlock(vop2);
> > +
> > +	if (crtc->state->event && !crtc->state->active) {
> > +		spin_lock_irq(&crtc->dev->event_lock);
> > +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +		spin_unlock_irq(&crtc->dev->event_lock);
> > +
> > +		crtc->state->event = NULL;
> > +	}
> > +}
> > +
> > +static int vop2_plane_atomic_check(struct drm_plane *plane,
> > +				   struct drm_atomic_state *astate)
> > +{
> > +	struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane);
> > +	struct drm_framebuffer *fb = pstate->fb;
> > +	struct drm_crtc *crtc = pstate->crtc;
> > +	struct drm_crtc_state *cstate;
> > +	struct vop2_video_port *vp;
> > +	struct vop2 *vop2;
> > +	const struct vop2_data *vop2_data;
> > +	struct drm_rect *dest = &pstate->dst;
> > +	struct drm_rect *src = &pstate->src;
> > +	int min_scale = FRAC_16_16(1, 8);
> > +	int max_scale = FRAC_16_16(8, 1);
> > +	int format;
> > +	int ret;
> > +
> > +	if (!crtc)
> > +		return 0;
> > +
> > +	vp = to_vop2_video_port(crtc);
> > +	vop2 = vp->vop2;
> > +	vop2_data = vop2->data;
> > +
> > +	cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc);
> > +	if (WARN_ON(!cstate))
> > +		return -EINVAL;
> > +
> > +	ret = drm_atomic_helper_check_plane_state(pstate, cstate,
> > +						  min_scale, max_scale,
> > +						  true, true);
> > +	if (ret)
> > +		return ret;
> > +
> > +	if (!pstate->visible)
> > +		return 0;
> > +
> > +	format = vop2_convert_format(fb->format->format);
> > +	if (format < 0)
> > +		return format;
> > +
> > +	if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 ||
> > +	    drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) {
> > +		drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
> > +			  drm_rect_width(src) >> 16, drm_rect_height(src) >> 16,
> > +			  drm_rect_width(dest), drm_rect_height(dest));
> > +		pstate->visible = false;
> > +		return 0;
> > +	}
> > +
> > +	if (drm_rect_width(src) >> 16 > vop2_data->max_input.width ||
> > +	    drm_rect_height(src) >> 16 > vop2_data->max_input.height) {
> > +		drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
> > +			  drm_rect_width(src) >> 16,
> > +			  drm_rect_height(src) >> 16,
> > +			  vop2_data->max_input.width,
> > +			  vop2_data->max_input.height);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/*
> > +	 * Src.x1 can be odd when do clip, but yuv plane start point
> > +	 * need align with 2 pixel.
> > +	 */
> > +	if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
> > +		drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void vop2_plane_atomic_disable(struct drm_plane *plane,
> > +				      struct drm_atomic_state *state)
> > +{
> > +	struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state, plane);
> > +	struct vop2_win *win = to_vop2_win(plane);
> > +	struct vop2 *vop2 = win->vop2;
> > +
> > +	drm_dbg(vop2->drm, "%s disable\n", win->data->name);
> > +
> > +	if (!old_pstate->crtc)
> > +		return;
> > +
> > +	vop2_win_disable(win);
> > +	vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0);
> > +}
> > +
> > +/*
> > + * The color key is 10 bit, so all format should
> > + * convert to 10 bit here.
> > + */
> > +static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
> > +{
> > +	struct drm_plane_state *pstate = plane->state;
> > +	struct drm_framebuffer *fb = pstate->fb;
> > +	struct vop2_win *win = to_vop2_win(plane);
> > +	u32 color_key_en = 0;
> > +	u32 r = 0;
> > +	u32 g = 0;
> > +	u32 b = 0;
> > +
> > +	if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) {
> > +		vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0);
> > +		return;
> > +	}
> > +
> > +	switch (fb->format->format) {
> > +	case DRM_FORMAT_RGB565:
> > +	case DRM_FORMAT_BGR565:
> > +		r = (color_key & 0xf800) >> 11;
> > +		g = (color_key & 0x7e0) >> 5;
> > +		b = (color_key & 0x1f);
> > +		r <<= 5;
> > +		g <<= 4;
> > +		b <<= 5;
> > +		color_key_en = 1;
> > +		break;
> > +	case DRM_FORMAT_XRGB8888:
> > +	case DRM_FORMAT_ARGB8888:
> > +	case DRM_FORMAT_XBGR8888:
> > +	case DRM_FORMAT_ABGR8888:
> > +	case DRM_FORMAT_RGB888:
> > +	case DRM_FORMAT_BGR888:
> > +		r = (color_key & 0xff0000) >> 16;
> > +		g = (color_key & 0xff00) >> 8;
> > +		b = (color_key & 0xff);
> > +		r <<= 2;
> > +		g <<= 2;
> > +		b <<= 2;
> > +		color_key_en = 1;
> > +		break;
> > +	}
> > +
> > +	vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en);
> > +	vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b);
> > +}
> > +
> > +static void vop2_plane_atomic_update(struct drm_plane *plane,
> > +				     struct drm_atomic_state *state)
> > +{
> > +	struct drm_plane_state *pstate = plane->state;
> > +	struct drm_crtc *crtc = pstate->crtc;
> > +	struct vop2_win *win = to_vop2_win(plane);
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
> > +	struct vop2 *vop2 = win->vop2;
> > +	struct drm_framebuffer *fb = pstate->fb;
> > +	u32 bpp = fb->format->cpp[0] * 8;
> > +	u32 actual_w, actual_h, dsp_w, dsp_h;
> > +	u32 act_info, dsp_info;
> > +	u32 format;
> > +	u32 afbc_format;
> > +	u32 rb_swap;
> > +	u32 uv_swap;
> > +	struct drm_rect *src = &pstate->src;
> > +	struct drm_rect *dest = &pstate->dst;
> > +	u32 afbc_tile_num;
> > +	u32 transform_offset;
> > +	bool dither_up;
> > +	bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
> > +	bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
> > +	bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
> > +	bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
> > +	struct rockchip_gem_object *rk_obj;
> > +	unsigned long offset;
> > +	bool afbc_en;
> > +	dma_addr_t yrgb_mst;
> > +	dma_addr_t uv_mst;
> > +
> > +	/*
> > +	 * can't update plane when vop2 is disabled.
> > +	 */
> > +	if (WARN_ON(!crtc))
> > +		return;
> > +
> > +	if (!pstate->visible) {
> > +		vop2_plane_atomic_disable(plane, state);
> > +		return;
> > +	}
> > +
> > +	afbc_en = rockchip_afbc(plane, fb->modifier);
> > +
> > +	offset = (src->x1 >> 16) * fb->format->cpp[0];
> > +
> > +	/*
> > +	 * AFBC HDR_PTR must set to the zero offset of the framebuffer.
> > +	 */
> > +	if (afbc_en)
> > +		offset = 0;
> > +	else if (pstate->rotation & DRM_MODE_REFLECT_Y)
> > +		offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
> > +	else
> > +		offset += (src->y1 >> 16) * fb->pitches[0];
> > +
> > +	rk_obj = to_rockchip_obj(fb->obj[0]);
> > +
> > +	yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
> > +	if (fb->format->is_yuv) {
> > +		int hsub = fb->format->hsub;
> > +		int vsub = fb->format->vsub;
> > +
> > +		offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub;
> > +		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
> > +
> > +		if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en)
> > +			offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub;
> > +
> > +		rk_obj = to_rockchip_obj(fb->obj[0]);
> > +		uv_mst = rk_obj->dma_addr + offset + fb->offsets[1];
> > +	}
> > +
> > +	actual_w = drm_rect_width(src) >> 16;
> > +	actual_h = drm_rect_height(src) >> 16;
> > +	dsp_w = drm_rect_width(dest);
> > +
> > +	if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
> > +		drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
> > +			  vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
> > +		dsp_w = adjusted_mode->hdisplay - dest->x1;
> > +		if (dsp_w < 4)
> > +			dsp_w = 4;
> > +		actual_w = dsp_w * actual_w / drm_rect_width(dest);
> > +	}
> > +
> > +	dsp_h = drm_rect_height(dest);
> > +
> > +	if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
> > +		drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
> > +			  vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
> > +		dsp_h = adjusted_mode->vdisplay - dest->y1;
> > +		if (dsp_h < 4)
> > +			dsp_h = 4;
> > +		actual_h = dsp_h * actual_h / drm_rect_height(dest);
> > +	}
> > +
> > +	/*
> > +	 * This is workaround solution for IC design:
> > +	 * esmart can't support scale down when actual_w % 16 == 1.
> > +	 */
> > +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> > +		if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
> > +			drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
> > +				vp->id, win->data->name, actual_w);
> > +			actual_w -= 1;
> > +		}
> > +	}
> > +
> > +	if (afbc_en && actual_w % 4) {
> > +		drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
> > +			  vp->id, win->data->name, actual_w);
> > +		actual_w = ALIGN_DOWN(actual_w, 4);
> > +	}
> > +
> > +	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
> > +	dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff);
> > +
> > +	format = vop2_convert_format(fb->format->format);
> > +
> > +	drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n",
> > +		      vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h,
> > +		      dest->x1, dest->y1,
> > +		      &fb->format->format,
> > +		      afbc_en ? "AFBC" : "", &yrgb_mst);
> > +
> > +	if (afbc_en) {
> > +		u32 stride;
> > +
> > +		/* the afbc superblock is 16 x 16 */
> > +		afbc_format = vop2_convert_afbc_format(fb->format->format);
> > +
> > +		/* Enable color transform for YTR */
> > +		if (fb->modifier & AFBC_FORMAT_MOD_YTR)
> > +			afbc_format |= (1 << 4);
> > +
> > +		afbc_tile_num = ALIGN(actual_w, 16) >> 4;
> > +
> > +		/*
> > +		 * AFBC pic_vir_width is count by pixel, this is different
> > +		 * with WIN_VIR_STRIDE.
> > +		 */
> > +		stride = (fb->pitches[0] << 3) / bpp;
> > +		if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
> > +			drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligened\n",
> > +				  vp->id, win->data->name, stride);
> > +
> > +		rb_swap = vop2_afbc_rb_swap(fb->format->format);
> > +		uv_swap = vop2_afbc_uv_swap(fb->format->format);
> > +		/*
> > +		 * This is a workaround for crazy IC design, Cluster
> > +		 * and Esmart/Smart use different format configuration map:
> > +		 * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart.
> > +		 *
> > +		 * This is one thing we can make the convert simple:
> > +		 * AFBCD decode all the YUV data to YUV444. So we just
> > +		 * set all the yuv 10 bit to YUV444_10.
> > +		 */
> > +		if (fb->format->is_yuv && (bpp == 10))
> > +			format = VOP2_CLUSTER_YUV444_10;
> > +
> > +		if (vop2_cluster_window(win))
> > +			vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_RB_SWAP, rb_swap);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
> > +		if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) {
> > +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0);
> > +			transform_offset = vop2_afbc_transform_offset(pstate, false);
> > +		} else {
> > +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 1);
> > +			transform_offset = vop2_afbc_transform_offset(pstate, true);
> > +		}
> > +		vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1));
> > +		vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16)));
> > +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num);
> > +		vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90);
> > +	} else {
> > +		vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4));
> > +	}
> > +
> > +	vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror);
> > +
> > +	if (rotate_90 || rotate_270) {
> > +		act_info = swahw32(act_info);
> > +		actual_w = drm_rect_height(src) >> 16;
> > +		actual_h = drm_rect_width(src) >> 16;
> > +	}
> > +
> > +	vop2_win_write(win, VOP2_WIN_FORMAT, format);
> > +	vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst);
> > +
> > +	rb_swap = vop2_win_rb_swap(fb->format->format);
> > +	vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap);
> > +	if (!vop2_cluster_window(win)) {
> > +		uv_swap = vop2_win_uv_swap(fb->format->format);
> > +		vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
> > +	}
> > +
> > +	if (fb->format->is_yuv) {
> > +		vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4));
> > +		vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst);
> > +	}
> > +
> > +	vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format);
> > +	if (!vop2_cluster_window(win))
> > +		vop2_plane_setup_color_key(plane, 0);
> > +	vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info);
> > +	vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info);
> > +	vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff));
> > +
> > +	vop2_setup_csc_mode(vp, win, pstate);
> > +
> > +	dither_up = vop2_win_dither_up(fb->format->format);
> > +	vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up);
> > +
> > +	vop2_win_write(win, VOP2_WIN_ENABLE, 1);
> > +
> > +	if (vop2_cluster_window(win)) {
> > +		int lb_mode = vop2_get_cluster_lb_mode(win, pstate);
> > +
> > +		vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode);
> > +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1);
> > +	}
> > +}
> > +
> > +static const struct drm_plane_helper_funcs vop2_plane_helper_funcs = {
> > +	.atomic_check = vop2_plane_atomic_check,
> > +	.atomic_update = vop2_plane_atomic_update,
> > +	.atomic_disable = vop2_plane_atomic_disable,
> > +};
> > +
> > +static const struct drm_plane_funcs vop2_plane_funcs = {
> > +	.update_plane	= drm_atomic_helper_update_plane,
> > +	.disable_plane	= drm_atomic_helper_disable_plane,
> > +	.destroy = drm_plane_cleanup,
> > +	.reset = drm_atomic_helper_plane_reset,
> > +	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> > +	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> > +	.format_mod_supported = rockchip_vop2_mod_supported,
> > +};
> > +
> > +static int vop2_crtc_enable_vblank(struct drm_crtc *crtc)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +
> > +	vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD);
> > +
> > +	return 0;
> > +}
> > +
> > +static void vop2_crtc_disable_vblank(struct drm_crtc *crtc)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +
> > +	vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD);
> > +}
> > +
> > +static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
> > +				 const struct drm_display_mode *mode,
> > +				 struct drm_display_mode *adj_mode)
> > +{
> > +	drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V |
> > +					CRTC_STEREO_DOUBLE);
> > +
> > +	return true;
> > +}
> > +
> > +static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
> > +{
> > +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> > +
> > +	switch (vcstate->bus_format) {
> > +	case MEDIA_BUS_FMT_RGB565_1X16:
> > +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> > +		break;
> > +	case MEDIA_BUS_FMT_RGB666_1X18:
> > +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> > +	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
> > +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> > +		*dsp_ctrl |= RGB888_TO_RGB666;
> > +		break;
> > +	case MEDIA_BUS_FMT_YUV8_1X24:
> > +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> > +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +
> > +	if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA)
> > +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> > +
> > +	*dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL,
> > +				DITHER_DOWN_ALLEGRO);
> > +}
> > +
> > +static void vop2_post_config(struct drm_crtc *crtc)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> > +	u16 vtotal = mode->crtc_vtotal;
> > +	u16 hdisplay = mode->crtc_hdisplay;
> > +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> > +	u16 vdisplay = mode->crtc_vdisplay;
> > +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> > +	u32 left_margin = 100, right_margin = 100;
> > +	u32 top_margin = 100, bottom_margin = 100;
> > +	u16 hsize = hdisplay * (left_margin + right_margin) / 200;
> > +	u16 vsize = vdisplay * (top_margin + bottom_margin) / 200;
> > +	u16 hact_end, vact_end;
> > +	u32 val;
> > +
> > +	vsize = rounddown(vsize, 2);
> > +	hsize = rounddown(hsize, 2);
> > +	hact_st += hdisplay * (100 - left_margin) / 200;
> > +	hact_end = hact_st + hsize;
> > +	val = hact_st << 16;
> > +	val |= hact_end;
> > +	vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val);
> > +	vact_st += vdisplay * (100 - top_margin) / 200;
> > +	vact_end = vact_st + vsize;
> > +	val = vact_st << 16;
> > +	val |= vact_end;
> > +	vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val);
> > +	val = scl_cal_scale2(vdisplay, vsize) << 16;
> > +	val |= scl_cal_scale2(hdisplay, hsize);
> > +	vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val);
> > +
> > +	val = 0;
> > +	if (hdisplay != hsize)
> > +		val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN;
> > +	if (vdisplay != vsize)
> > +		val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN;
> > +	vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val);
> > +
> > +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> > +		u16 vact_st_f1 = vtotal + vact_st + 1;
> > +		u16 vact_end_f1 = vact_st_f1 + vsize;
> > +
> > +		val = vact_st_f1 << 16 | vact_end_f1;
> > +		vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
> > +	}
> > +
> > +	vop2_vp_write(vp, RK3568_VP_DSP_BG, 0);
> > +}
> > +
> > +static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
> > +				u32 polflags)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +	u32 die, dip;
> > +
> > +	die = vop2_readl(vop2, RK3568_DSP_IF_EN);
> > +	dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
> > +
> > +	switch (id) {
> > +	case RK3568_VOP2_EP_RGB:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_RGB |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
> > +		if (polflags & POLFLAG_DCLK_INV)
> > +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
> > +		else
> > +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16));
> > +		break;
> > +	case RK3568_VOP2_EP_HDMI:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_HDMI |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id);
> > +		break;
> > +	case RK3568_VOP2_EP_EDP:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_EDP |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id);
> > +		break;
> > +	case RK3568_VOP2_EP_MIPI0:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id);
> > +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> > +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> > +		break;
> > +	case RK3568_VOP2_EP_MIPI1:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
> > +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> > +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> > +		break;
> > +	case RK3568_VOP2_EP_LVDS0:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id);
> > +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> > +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> > +		break;
> > +	case RK3568_VOP2_EP_LVDS1:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id);
> > +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> > +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> > +		break;
> > +	default:
> > +		return;
> > +	};
> > +
> > +	dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
> > +
> > +	vop2_writel(vop2, RK3568_DSP_IF_EN, die);
> > +	vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
> > +}
> > +
> > +static int us_to_vertical_line(struct drm_display_mode *mode, int us)
> > +{
> > +	return us * mode->clock / mode->htotal / 1000;
> > +}
> > +
> > +static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
> > +				    struct drm_atomic_state *state)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct vop2 *vop2 = vp->vop2;
> > +	const struct vop2_data *vop2_data = vop2->data;
> > +	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
> > +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> > +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> > +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> > +	unsigned long clock = mode->crtc_clock * 1000;
> > +	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
> > +	u16 hdisplay = mode->crtc_hdisplay;
> > +	u16 htotal = mode->crtc_htotal;
> > +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> > +	u16 hact_end = hact_st + hdisplay;
> > +	u16 vdisplay = mode->crtc_vdisplay;
> > +	u16 vtotal = mode->crtc_vtotal;
> > +	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
> > +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> > +	u16 vact_end = vact_st + vdisplay;
> > +	u8 out_mode;
> > +	u32 dsp_ctrl = 0;
> > +	int act_end;
> > +	u32 val, polflags;
> > +	int ret;
> > +	struct drm_encoder *encoder;
> > +
> > +	drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n",
> > +		hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
> > +		drm_mode_vrefresh(mode), vcstate->output_type, vp->id);
> > +
> > +	vop2_lock(vop2);
> > +
> > +	ret = clk_prepare_enable(vp->dclk);
> > +	if (ret < 0) {
> > +		drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
> > +			      vp->id, ret);
> > +		return;
> > +	}
> > +
> > +	if (!vop2->enable_count)
> > +		vop2_enable(vop2);
> > +
> > +	vop2->enable_count++;
> > +
> > +	vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY);
> > +
> > +	polflags = 0;
> > +	if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
> > +		polflags |= POLFLAG_DCLK_INV;
> > +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> > +		polflags |= BIT(HSYNC_POSITIVE);
> > +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> > +		polflags |= BIT(VSYNC_POSITIVE);
> > +
> > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > +		struct device_node *node, *parent;
> > +
> > +		parent = of_get_parent(rkencoder->port);
> > +
> > +		for_each_endpoint_of_node(parent, node) {
> > +			struct device_node *crtc_port = of_graph_get_remote_port(node);
> > +			struct device_node *epn;
> > +			struct of_endpoint endpoint;
> > +
> > +			if (crtc->port != crtc_port) {
> > +				of_node_put(crtc_port);
> > +				continue;
> > +			}
> > +
> > +			of_node_put(crtc_port);
> > +
> > +			epn = of_graph_get_remote_endpoint(node);
> > +			of_graph_parse_endpoint(epn, &endpoint);
> > +			of_node_put(epn);
> > +
> > +			drm_dbg(vop2->drm, "vp%d is connected to %s, id %d\n",
> > +					   vp->id, encoder->name, endpoint.id);
> > +			rk3568_set_intf_mux(vp, endpoint.id, polflags);
> > +		}
> > +		of_node_put(parent);
> > +	}
> > +
> > +	if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
> > +	     !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT))
> > +		out_mode = ROCKCHIP_OUT_MODE_P888;
> > +	else
> > +		out_mode = vcstate->output_mode;
> > +
> > +	dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode);
> > +
> > +	if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP;
> > +
> > +	if (is_yuv_output(vcstate->bus_format))
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y;
> > +
> > +	vop2_dither_setup(crtc, &dsp_ctrl);
> > +
> > +	vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len);
> > +	val = hact_st << 16;
> > +	val |= hact_end;
> > +	vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val);
> > +
> > +	val = vact_st << 16;
> > +	val |= vact_end;
> > +	vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val);
> > +
> > +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> > +		u16 vact_st_f1 = vtotal + vact_st + 1;
> > +		u16 vact_end_f1 = vact_st_f1 + vdisplay;
> > +
> > +		val = vact_st_f1 << 16 | vact_end_f1;
> > +		vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val);
> > +
> > +		val = vtotal << 16 | (vtotal + vsync_len);
> > +		vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val);
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE;
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL;
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN;
> > +		vtotal += vtotal + 1;
> > +		act_end = vact_end_f1;
> > +	} else {
> > +		act_end = vact_end;
> > +	}
> > +
> > +	vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id),
> > +		    (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end);
> > +
> > +	vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len);
> > +
> > +	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
> > +		clock *= 2;
> > +	}
> > +
> > +	vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
> > +
> > +	clk_set_rate(vp->dclk, clock);
> > +
> > +	vop2_post_config(crtc);
> > +
> > +	vop2_cfg_done(vp);
> > +
> > +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
> > +
> > +	drm_crtc_vblank_on(crtc);
> > +
> > +	vop2_unlock(vop2);
> > +}
> > +
> > +static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
> > +				  struct drm_atomic_state *state)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct drm_plane *plane;
> > +	int nplanes = 0;
> > +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> > +
> > +	drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
> > +		nplanes++;
> > +
> > +	if (nplanes > vp->nlayers)
> > +		return -EINVAL;
> > +
> > +	return 0;
> > +}
> > +
> > +static bool is_opaque(u16 alpha)
> > +{
> > +	return (alpha >> 8) == 0xff;
> > +}
> > +
> > +static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config,
> > +			     struct vop2_alpha *alpha)
> > +{
> > +	int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1;
> > +	int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1;
> > +	int src_color_mode = alpha_config->src_premulti_en ?
> > +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> > +	int dst_color_mode = alpha_config->dst_premulti_en ?
> > +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> > +
> > +	alpha->src_color_ctrl.val = 0;
> > +	alpha->dst_color_ctrl.val = 0;
> > +	alpha->src_alpha_ctrl.val = 0;
> > +	alpha->dst_alpha_ctrl.val = 0;
> > +
> > +	if (!alpha_config->src_pixel_alpha_en)
> > +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> > +	else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en)
> > +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> > +	else
> > +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> > +
> > +	alpha->src_color_ctrl.bits.alpha_en = 1;
> > +
> > +	if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) {
> > +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> > +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> > +	} else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) {
> > +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> > +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE;
> > +	} else {
> > +		alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL;
> > +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> > +	}
> > +	alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8;
> > +	alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> > +	alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> > +
> > +	alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> > +	alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> > +	alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> > +	alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8;
> > +	alpha->dst_color_ctrl.bits.color_mode = dst_color_mode;
> > +	alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> > +
> > +	alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> > +	alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode;
> > +	alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> > +	alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE;
> > +
> > +	alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> > +	if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en)
> > +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> > +	else
> > +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> > +	alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION;
> > +	alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> > +}
> > +
> > +static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id)
> > +{
> > +	struct vop2_video_port *vp;
> > +	int used_layer = 0;
> > +	int i;
> > +
> > +	for (i = 0; i < port_id; i++) {
> > +		vp = &vop2->vps[i];
> > +		used_layer += hweight32(vp->win_mask);
> > +	}
> > +
> > +	return used_layer;
> > +}
> > +
> > +static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win)
> > +{
> > +	u32 offset = (main_win->data->phys_id * 0x10);
> > +	struct vop2_alpha_config alpha_config;
> > +	struct vop2_alpha alpha;
> > +	struct drm_plane_state *bottom_win_pstate;
> > +	bool src_pixel_alpha_en = false;
> > +	u16 src_glb_alpha_val, dst_glb_alpha_val;
> > +	bool premulti_en = false;
> > +	bool swap = false;
> > +
> > +	/* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */
> > +	bottom_win_pstate = main_win->base.state;
> > +	src_glb_alpha_val = 0;
> > +	dst_glb_alpha_val = main_win->base.state->alpha;
> > +
> > +	if (!bottom_win_pstate->fb)
> > +		return;
> > +
> > +	alpha_config.src_premulti_en = premulti_en;
> > +	alpha_config.dst_premulti_en = false;
> > +	alpha_config.src_pixel_alpha_en = src_pixel_alpha_en;
> > +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> > +	alpha_config.src_glb_alpha_value = src_glb_alpha_val;
> > +	alpha_config.dst_glb_alpha_value = dst_glb_alpha_val;
> > +	vop2_parse_alpha(&alpha_config, &alpha);
> > +
> > +	alpha.src_color_ctrl.bits.src_dst_swap = swap;
> > +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset,
> > +		    alpha.src_color_ctrl.val);
> > +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset,
> > +		    alpha.dst_color_ctrl.val);
> > +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset,
> > +		    alpha.src_alpha_ctrl.val);
> > +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset,
> > +		    alpha.dst_alpha_ctrl.val);
> > +}
> > +
> > +static void vop2_setup_alpha(struct vop2_video_port *vp)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +	struct drm_framebuffer *fb;
> > +	struct vop2_alpha_config alpha_config;
> > +	struct vop2_alpha alpha;
> > +	struct drm_plane *plane;
> > +	int pixel_alpha_en;
> > +	int premulti_en, gpremulti_en = 0;
> > +	int mixer_id;
> > +	u32 offset;
> > +	bool bottom_layer_alpha_en = false;
> > +	u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE;
> > +
> > +	mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id);
> > +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> > +
> > +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> > +		struct vop2_win *win = to_vop2_win(plane);
> > +
> > +		if (plane->state->normalized_zpos == 0 &&
> > +		    !is_opaque(plane->state->alpha) &&
> > +		    !vop2_cluster_window(win)) {
> > +			/*
> > +			 * If bottom layer have global alpha effect [except cluster layer,
> > +			 * because cluster have deal with bottom layer global alpha value
> > +			 * at cluster mix], bottom layer mix need deal with global alpha.
> > +			 */
> > +			bottom_layer_alpha_en = true;
> > +			dst_global_alpha = plane->state->alpha;
> > +		}
> > +	}
> > +
> > +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> > +		struct vop2_win *win = to_vop2_win(plane);
> > +		int zpos = plane->state->normalized_zpos;
> > +
> > +		if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
> > +			premulti_en = 1;
> > +		else
> > +			premulti_en = 0;
> > +
> > +		plane = &win->base;
> > +		fb = plane->state->fb;
> > +
> > +		pixel_alpha_en = fb->format->has_alpha;
> > +
> > +		alpha_config.src_premulti_en = premulti_en;
> > +
> > +		if (bottom_layer_alpha_en && zpos == 1) {
> > +			gpremulti_en = premulti_en;
> > +			/* Cd = Cs + (1 - As) * Cd * Agd */
> > +			alpha_config.dst_premulti_en = false;
> > +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> > +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> > +			alpha_config.dst_glb_alpha_value = dst_global_alpha;
> > +		} else if (vop2_cluster_window(win)) {
> > +			/* Mix output data only have pixel alpha */
> > +			alpha_config.dst_premulti_en = true;
> > +			alpha_config.src_pixel_alpha_en = true;
> > +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> > +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> > +		} else {
> > +			/* Cd = Cs + (1 - As) * Cd */
> > +			alpha_config.dst_premulti_en = true;
> > +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> > +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> > +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> > +		}
> > +
> > +		vop2_parse_alpha(&alpha_config, &alpha);
> > +
> > +		offset = (mixer_id + zpos - 1) * 0x10;
> > +		vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset,
> > +			    alpha.src_color_ctrl.val);
> > +		vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset,
> > +			    alpha.dst_color_ctrl.val);
> > +		vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset,
> > +			    alpha.src_alpha_ctrl.val);
> > +		vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset,
> > +			    alpha.dst_alpha_ctrl.val);
> > +	}
> > +
> > +	if (vp->id == 0) {
> > +		if (bottom_layer_alpha_en) {
> > +			/* Transfer pixel alpha to hdr mix */
> > +			alpha_config.src_premulti_en = gpremulti_en;
> > +			alpha_config.dst_premulti_en = true;
> > +			alpha_config.src_pixel_alpha_en = true;
> > +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> > +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> > +			vop2_parse_alpha(&alpha_config, &alpha);
> > +
> > +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL,
> > +				alpha.src_color_ctrl.val);
> > +			vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL,
> > +				alpha.dst_color_ctrl.val);
> > +			vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL,
> > +				alpha.src_alpha_ctrl.val);
> > +			vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL,
> > +				alpha.dst_alpha_ctrl.val);
> > +		} else {
> > +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0);
> > +		}
> > +	}
> > +}
> > +
> > +static void vop2_setup_layer_mixer(struct vop2_video_port *vp)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +	struct drm_plane *plane;
> > +	u32 layer_sel = 0;
> > +	u32 port_sel;
> > +	unsigned int nlayer, ofs;
> > +	struct drm_display_mode *adjusted_mode;
> > +	u16 hsync_len;
> > +	u16 hdisplay;
> > +	u32 bg_dly;
> > +	u32 pre_scan_dly;
> > +	int i;
> > +	struct vop2_video_port *vp0 = &vop2->vps[0];
> > +	struct vop2_video_port *vp1 = &vop2->vps[1];
> > +	struct vop2_video_port *vp2 = &vop2->vps[2];
> > +
> > +	adjusted_mode = &vp->crtc.state->adjusted_mode;
> > +	hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
> > +	hdisplay = adjusted_mode->crtc_hdisplay;
> > +
> > +	bg_dly = vp->data->pre_scan_max_dly[3];
> > +	vop2_writel(vop2, RK3568_VP_BG_MIX_CTRL(vp->id),
> > +			    FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly));
> > +
> > +	pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len;
> > +	vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly);
> > +
> > +	vop2_writel(vop2, RK3568_OVL_CTRL, 0);
> > +	port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
> > +	port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT;
> > +
> > +	if (vp0->nlayers)
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX,
> > +				     vp0->nlayers - 1);
> > +	else
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8);
> > +
> > +	if (vp1->nlayers)
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX,
> > +				     (vp0->nlayers + vp1->nlayers - 1));
> > +	else
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> > +
> > +	if (vp2->nlayers)
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX,
> > +			(vp2->nlayers + vp1->nlayers + vp0->nlayers - 1));
> > +	else
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> > +
> > +	layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
> > +
> > +	ofs = 0;
> > +	for (i = 0; i < vp->id; i++)
> > +		ofs += vop2->vps[i].nlayers;
> > +
> > +	nlayer = 0;
> > +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> > +		struct vop2_win *win = to_vop2_win(plane);
> > +
> > +		switch (win->data->phys_id) {
> > +		case ROCKCHIP_VOP2_CLUSTER0:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id);
> > +			break;
> > +		case ROCKCHIP_VOP2_CLUSTER1:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id);
> > +			break;
> > +		case ROCKCHIP_VOP2_ESMART0:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id);
> > +			break;
> > +		case ROCKCHIP_VOP2_ESMART1:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id);
> > +			break;
> > +		case ROCKCHIP_VOP2_SMART0:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART0;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id);
> > +			break;
> > +		case ROCKCHIP_VOP2_SMART1:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART1;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id);
> > +			break;
> > +		}
> > +
> > +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> > +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, win->data->layer_sel_id);
> > +		nlayer++;
> > +	}
> > +
> > +	/* configure unused layers to 0x5 (reserved) */
> > +	for (; nlayer < 3; nlayer++) {
> > +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> > +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5);
> > +	}
> > +
> > +	vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel);
> > +	vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
> > +	vop2_writel(vop2, RK3568_OVL_CTRL, RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD);
> > +}
> > +
> > +static void vop2_setup_dly_for_windows(struct vop2 *vop2)
> > +{
> > +	struct vop2_win *win;
> > +	int i = 0;
> > +	u32 cdly = 0, sdly = 0;
> > +
> > +	for (i = 0; i < vop2->data->win_size; i++) {
> > +		u32 dly;
> > +
> > +		win = &vop2->win[i];
> > +		dly = win->delay;
> > +
> > +		switch (win->data->phys_id) {
> > +		case ROCKCHIP_VOP2_CLUSTER0:
> > +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly);
> > +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly);
> > +			break;
> > +		case ROCKCHIP_VOP2_CLUSTER1:
> > +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly);
> > +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly);
> > +			break;
> > +		case ROCKCHIP_VOP2_ESMART0:
> > +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly);
> > +			break;
> > +		case ROCKCHIP_VOP2_ESMART1:
> > +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly);
> > +			break;
> > +		case ROCKCHIP_VOP2_SMART0:
> > +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly);
> > +			break;
> > +		case ROCKCHIP_VOP2_SMART1:
> > +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly);
> > +			break;
> > +		}
> > +	}
> > +
> > +	vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly);
> > +	vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly);
> > +}
> > +
> > +static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
> > +				   struct drm_atomic_state *state)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct vop2 *vop2 = vp->vop2;
> > +	struct drm_plane *plane;
> > +
> > +	vp->win_mask = 0;
> > +
> > +	drm_atomic_crtc_for_each_plane(plane, crtc) {
> > +		struct vop2_win *win = to_vop2_win(plane);
> > +
> > +		win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT];
> > +
> > +		vp->win_mask |= BIT(win->data->phys_id);
> > +
> > +		if (vop2_cluster_window(win))
> > +			vop2_setup_cluster_alpha(vop2, win);
> > +	}
> > +
> > +	if (!vp->win_mask)
> > +		return;
> > +
> > +	vop2_setup_layer_mixer(vp);
> > +	vop2_setup_alpha(vp);
> > +	vop2_setup_dly_for_windows(vop2);
> > +}
> > +
> > +static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
> > +				   struct drm_atomic_state *state)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +
> > +	vop2_post_config(crtc);
> > +
> > +	vop2_cfg_done(vp);
> > +
> > +	spin_lock_irq(&crtc->dev->event_lock);
> > +
> > +	if (crtc->state->event) {
> > +		WARN_ON(drm_crtc_vblank_get(crtc));
> > +		vp->event = crtc->state->event;
> > +		crtc->state->event = NULL;
> > +	}
> > +
> > +	spin_unlock_irq(&crtc->dev->event_lock);
> > +}
> > +
> > +static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
> > +	.mode_fixup = vop2_crtc_mode_fixup,
> > +	.atomic_check = vop2_crtc_atomic_check,
> > +	.atomic_begin = vop2_crtc_atomic_begin,
> > +	.atomic_flush = vop2_crtc_atomic_flush,
> > +	.atomic_enable = vop2_crtc_atomic_enable,
> > +	.atomic_disable = vop2_crtc_atomic_disable,
> > +};
> > +
> > +static void vop2_crtc_reset(struct drm_crtc *crtc)
> > +{
> > +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> > +
> > +	if (crtc->state) {
> > +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
> > +		kfree(vcstate);
> > +	}
> > +
> > +	vcstate = kzalloc(sizeof(*vcstate), GFP_KERNEL);
> > +	if (!vcstate)
> > +		return;
> > +
> > +	crtc->state = &vcstate->base;
> > +	crtc->state->crtc = crtc;
> > +}
> > +
> > +static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc)
> > +{
> > +	struct rockchip_crtc_state *vcstate, *old_vcstate;
> > +
> > +	old_vcstate = to_rockchip_crtc_state(crtc->state);
> > +
> > +	vcstate = kmemdup(old_vcstate, sizeof(*old_vcstate), GFP_KERNEL);
> > +	if (!vcstate)
> > +		return NULL;
> > +
> > +	__drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
> > +
> > +	return &vcstate->base;
> > +}
> > +
> > +static void vop2_crtc_destroy_state(struct drm_crtc *crtc,
> > +				    struct drm_crtc_state *state)
> > +{
> > +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state);
> > +
> > +	__drm_atomic_helper_crtc_destroy_state(&vcstate->base);
> > +	kfree(vcstate);
> > +}
> > +
> > +static const struct drm_crtc_funcs vop2_crtc_funcs = {
> > +	.set_config = drm_atomic_helper_set_config,
> > +	.page_flip = drm_atomic_helper_page_flip,
> > +	.destroy = drm_crtc_cleanup,
> > +	.reset = vop2_crtc_reset,
> > +	.atomic_duplicate_state = vop2_crtc_duplicate_state,
> > +	.atomic_destroy_state = vop2_crtc_destroy_state,
> > +	.enable_vblank = vop2_crtc_enable_vblank,
> > +	.disable_vblank = vop2_crtc_disable_vblank,
> > +};
> > +
> > +static irqreturn_t vop2_isr(int irq, void *data)
> > +{
> > +	struct vop2 *vop2 = data;
> > +	const struct vop2_data *vop2_data = vop2->data;
> > +	u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM];
> > +	int ret = IRQ_NONE;
> > +	int i;
> > +
> > +	/*
> > +	 * The irq is shared with the iommu. If the runtime-pm state of the
> > +	 * vop2-device is disabled the irq has to be targeted at the iommu.
> > +	 */
> > +	if (!pm_runtime_get_if_in_use(vop2->dev))
> > +		return IRQ_NONE;
> > +
> > +	for (i = 0; i < vop2_data->nr_vps; i++) {
> > +		struct vop2_video_port *vp = &vop2->vps[i];
> > +		struct drm_crtc *crtc = &vp->crtc;
> > +		u32 irqs;
> > +
> > +		irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
> > +		vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
> > +
> > +		if (irqs & VP_INT_DSP_HOLD_VALID) {
> > +			complete(&vp->dsp_hold_completion);
> > +			ret = IRQ_HANDLED;
> > +		}
> > +
> > +		if (irqs & VP_INT_FS_FIELD) {
> > +			drm_crtc_handle_vblank(crtc);
> > +			spin_lock(&crtc->dev->event_lock);
> > +			if (vp->event) {
> > +				u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
> > +
> > +				if (!(val & BIT(vp->id))) {
> > +					drm_crtc_send_vblank_event(crtc, vp->event);
> > +					vp->event = NULL;
> > +					drm_crtc_vblank_put(crtc);
> > +				}
> > +			}
> > +			spin_unlock(&crtc->dev->event_lock);
> > +
> > +			ret = IRQ_HANDLED;
> > +		}
> > +
> > +		if (irqs & VP_INT_POST_BUF_EMPTY) {
> > +			drm_err_ratelimited(vop2->drm,
> > +					    "POST_BUF_EMPTY irq err at vp%d\n",
> > +					    vp->id);
> > +			ret = IRQ_HANDLED;
> > +		}
> > +	}
> > +
> > +	axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS);
> > +	vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]);
> > +	axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS);
> > +	vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]);
> > +
> > +	for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) {
> > +		if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) {
> > +			drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n");
> > +			ret = IRQ_HANDLED;
> > +		}
> > +	}
> > +
> > +	pm_runtime_put(vop2->dev);
> > +
> > +	return ret;
> > +}
> > +
> > +static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win,
> > +			   unsigned long possible_crtcs)
> > +{
> > +	const struct vop2_win_data *win_data = win->data;
> > +	unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> > +				  BIT(DRM_MODE_BLEND_PREMULTI) |
> > +				  BIT(DRM_MODE_BLEND_COVERAGE);
> > +	int ret;
> > +
> > +	ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs,
> > +				       &vop2_plane_funcs, win_data->formats,
> > +				       win_data->nformats,
> > +				       win_data->format_modifiers,
> > +				       win->type, win_data->name);
> > +	if (ret) {
> > +		drm_err(vop2->drm, "failed to initialize plane %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs);
> > +
> > +	if (win->data->supported_rotations)
> > +		drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0,
> > +						   DRM_MODE_ROTATE_0 |
> > +						   win->data->supported_rotations);
> > +	drm_plane_create_alpha_property(&win->base);
> > +	drm_plane_create_blend_mode_property(&win->base, blend_caps);
> > +	drm_plane_create_zpos_property(&win->base, win->win_id, 0,
> > +				       vop2->registered_num_wins - 1);
> > +
> > +	return 0;
> > +}
> > +
> > +static struct vop2_video_port *get_activated_vp(struct vop2 *vop2, int n)
> > +{
> > +	int i, id = 0;
> > +
> > +	for (i = 0; i < vop2->data->nr_vps; i++) {
> > +		struct vop2_video_port *vp = &vop2->vps[i];
> > +
> > +		if (!vp->crtc.port)
> > +			continue;
> > +
> > +		if (n == id)
> > +			return vp;
> > +		id++;
> > +	}
> > +
> > +	return NULL;
> > +}
> > +
> > +#define NR_LAYERS 6
> > +
> > +static int vop2_create_crtc(struct vop2 *vop2)
> > +{
> > +	const struct vop2_data *vop2_data = vop2->data;
> > +	struct drm_device *drm = vop2->drm;
> > +	struct device *dev = vop2->dev;
> > +	struct drm_plane *plane;
> > +	struct device_node *port;
> > +	struct vop2_video_port *vp;
> > +	u32 possible_crtcs;
> > +	int i, nvp, nvps = 0;
> > +	int ret;
> > +
> > +	for (i = 0; i < vop2_data->nr_vps; i++) {
> > +		const struct vop2_video_port_data *vp_data;
> > +		struct device_node *np;
> > +		char dclk_name[9];
> > +
> > +		vp_data = &vop2_data->vp[i];
> > +		vp = &vop2->vps[i];
> > +		vp->vop2 = vop2;
> > +		vp->id = vp_data->id;
> > +		vp->regs = vp_data->regs;
> > +		vp->data = vp_data;
> > +
> > +		snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
> > +		vp->dclk = devm_clk_get(vop2->dev, dclk_name);
> > +		if (IS_ERR(vp->dclk)) {
> > +			drm_err(vop2->drm, "failed to get %s\n", dclk_name);
> > +			return PTR_ERR(vp->dclk);
> > +		}
> > +
> > +		np = of_graph_get_remote_node(dev->of_node, i, -1);
> > +		if (!np) {
> > +			drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
> > +			continue;
> > +		}
> > +		of_node_put(np);
> > +
> > +		port = of_graph_get_port_by_id(dev->of_node, i);
> > +		if (!port) {
> > +			drm_err(vop2->drm, "no port node found for video_port%d\n", i);
> > +			return -ENOENT;
> > +		}
> > +
> > +		vp->crtc.port = port;
> > +		nvps++;
> > +	}
> > +
> > +	nvp = 0;
> > +	for (i = 0; i < vop2->registered_num_wins; i++) {
> > +		struct vop2_win *win = &vop2->win[i];
> > +
> > +		if (win->type == DRM_PLANE_TYPE_PRIMARY) {
> > +			vp = get_activated_vp(vop2, nvp);
> > +
> > +			if (vp) {
> > +				possible_crtcs = BIT(nvp);
> > +				vp->primary_plane = win;
> > +
> > +				nvp++;
> > +			} else {
> > +				/* change the unused primary window to overlay window */
> > +				win->type = DRM_PLANE_TYPE_OVERLAY;
> > +			}
> > +		}
> > +
> > +		if (win->type == DRM_PLANE_TYPE_OVERLAY)
> > +			possible_crtcs = (1 << vop2_data->nr_vps) - 1;
> > +
> > +		ret = vop2_plane_init(vop2, win, possible_crtcs);
> > +
> > +		if (ret) {
> > +			drm_err(vop2->drm, "failed to init plane %s: %d\n",
> > +				win->data->name, ret);
> > +			return ret;
> > +		}
> > +	}
> > +
> > +	for (i = 0; i < vop2_data->nr_vps; i++) {
> > +		vp = &vop2->vps[i];
> > +
> > +		if (!vp->crtc.port)
> > +			continue;
> > +
> > +		plane = &vp->primary_plane->base;
> > +
> > +		ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL,
> > +						&vop2_crtc_funcs,
> > +						"video_port%d", vp->id);
> > +		if (ret) {
> > +			drm_err(vop2->drm, "crtc init for video_port%d failed\n", i);
> > +			return ret;
> > +		}
> > +
> > +		drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs);
> > +
> > +		init_completion(&vp->dsp_hold_completion);
> > +	}
> > +
> > +	for (i = 0; i < vop2->data->nr_vps; i++) {
> > +		struct vop2_video_port *vp = &vop2->vps[i];
> > +
> > +		if (vp->crtc.port)
> > +			vp->nlayers = NR_LAYERS / nvps;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void vop2_destroy_crtc(struct drm_crtc *crtc)
> > +{
> > +	of_node_put(crtc->port);
> > +
> > +	/*
> > +	 * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane()
> > +	 * references the CRTC.
> > +	 */
> > +	drm_crtc_cleanup(crtc);
> > +}
> > +
> > +static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = {
> > +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0),
> > +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5),
> > +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14),
> > +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18),
> > +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31),
> > +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31),
> > +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31),
> > +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31),
> > +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31),
> > +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19),
> > +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15),
> > +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31),
> > +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8),
> > +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9),
> > +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11),
> > +
> > +	/* Scale */
> > +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15),
> > +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31),
> > +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15),
> > +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13),
> > +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3),
> > +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28),
> > +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29),
> > +
> > +	/* cluster regs */
> > +	[VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1),
> > +	[VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0),
> > +	[VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7),
> > +
> > +	/* afbc regs */
> > +	[VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6),
> > +	[VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9),
> > +	[VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10),
> > +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4),
> > +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7),
> > +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8),
> > +	[VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31),
> > +	[VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31),
> > +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15),
> > +	[VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31),
> > +	[VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31),
> > +	[VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31),
> > +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31),
> > +	[VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0),
> > +	[VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1),
> > +	[VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2),
> > +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3),
> > +	[VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff },
> > +};
> > +
> > +static int vop2_cluster_init(struct vop2_win *win)
> > +{
> > +	struct vop2 *vop2 = win->vop2;
> > +	struct reg_field *cluster_regs;
> > +	int ret, i;
> > +
> > +	cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs),
> > +			       GFP_KERNEL);
> > +	if (!cluster_regs)
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++)
> > +		if (cluster_regs[i].reg != 0xffffffff)
> > +			cluster_regs[i].reg += win->offset;
> > +
> > +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> > +					    cluster_regs,
> > +					    ARRAY_SIZE(vop2_cluster_regs));
> > +
> > +	kfree(cluster_regs);
> > +
> > +	return ret;
> > +};
> > +
> > +static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = {
> > +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0),
> > +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5),
> > +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12),
> > +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14),
> > +	[VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16),
> > +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31),
> > +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31),
> > +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28),
> > +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31),
> > +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31),
> > +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17),
> > +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15),
> > +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31),
> > +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0),
> > +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1),
> > +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3),
> > +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31),
> > +	[VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29),
> > +	[VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31),
> > +
> > +	/* Scale */
> > +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15),
> > +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31),
> > +	[VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15),
> > +	[VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31),
> > +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1),
> > +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3),
> > +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5),
> > +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7),
> > +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9),
> > +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11),
> > +	[VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13),
> > +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15),
> > +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17),
> > +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8),
> > +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9),
> > +	[VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10),
> > +	[VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11),
> > +	[VOP2_WIN_XMIRROR] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff },
> > +};
> > +
> > +static int vop2_esmart_init(struct vop2_win *win)
> > +{
> > +	struct vop2 *vop2 = win->vop2;
> > +	struct reg_field *esmart_regs;
> > +	int ret, i;
> > +
> > +	esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs),
> > +			       GFP_KERNEL);
> > +	if (!esmart_regs)
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++)
> > +		if (esmart_regs[i].reg != 0xffffffff)
> > +			esmart_regs[i].reg += win->offset;
> > +
> > +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> > +					    esmart_regs,
> > +					    ARRAY_SIZE(vop2_esmart_regs));
> > +
> > +	kfree(esmart_regs);
> > +
> > +	return ret;
> > +};
> > +
> > +static int vop2_win_init(struct vop2 *vop2)
> > +{
> > +	const struct vop2_data *vop2_data = vop2->data;
> > +	struct vop2_win *win;
> > +	int i, ret;
> > +
> > +	for (i = 0; i < vop2_data->win_size; i++) {
> > +		const struct vop2_win_data *win_data = &vop2_data->win[i];
> > +
> > +		win = &vop2->win[i];
> > +		win->data = win_data;
> > +		win->type = win_data->type;
> > +		win->offset = win_data->base;
> > +		win->win_id = i;
> > +		win->vop2 = vop2;
> > +		if (vop2_cluster_window(win))
> > +			ret = vop2_cluster_init(win);
> > +		else
> > +			ret = vop2_esmart_init(win);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> > +	vop2->registered_num_wins = vop2_data->win_size;
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * The window registers are only updated when config done is written.
> > + * Until that they read back the old value. As we read-modify-write
> > + * these registers mark them as non-volatile. This makes sure we read
> > + * the new values from the regmap register cache.
> > + */
> > +static const struct regmap_range vop2_nonvolatile_range[] = {
> > +	regmap_reg_range(0x1000, 0x23ff),
> > +};
> > +
> > +static const struct regmap_access_table vop2_volatile_table = {
> > +	.no_ranges = vop2_nonvolatile_range,
> > +	.n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range),
> > +};
> > +
> > +static const struct regmap_config vop2_regmap_config = {
> > +	.reg_bits	= 32,
> > +	.val_bits	= 32,
> > +	.reg_stride	= 4,
> > +	.max_register	= 0x3000,
> > +	.name		= "vop2",
> > +	.volatile_table	= &vop2_volatile_table,
> > +	.cache_type	= REGCACHE_RBTREE,
> > +};
> > +
> > +static int vop2_bind(struct device *dev, struct device *master, void *data)
> > +{
> > +	struct platform_device *pdev = to_platform_device(dev);
> > +	const struct vop2_data *vop2_data;
> > +	struct drm_device *drm = data;
> > +	struct vop2 *vop2;
> > +	struct resource *res;
> > +	size_t alloc_size;
> > +	int ret;
> > +
> > +	vop2_data = of_device_get_match_data(dev);
> > +	if (!vop2_data)
> > +		return -ENODEV;
> > +
> > +	/* Allocate vop2 struct and its vop2_win array */
> > +	alloc_size = sizeof(*vop2) + sizeof(*vop2->win) * vop2_data->win_size;
> > +	vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
> > +	if (!vop2)
> > +		return -ENOMEM;
> > +
> > +	vop2->dev = dev;
> > +	vop2->data = vop2_data;
> > +	vop2->drm = drm;
> > +
> > +	dev_set_drvdata(dev, vop2);
> > +
> > +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
> > +	if (!res) {
> > +		drm_err(vop2->drm, "failed to get vop2 register byname\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	vop2->regs = devm_ioremap_resource(dev, res);
> > +	if (IS_ERR(vop2->regs))
> > +		return PTR_ERR(vop2->regs);
> > +	vop2->len = resource_size(res);
> > +
> > +	vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config);
> > +
> > +	ret = vop2_win_init(vop2);
> > +	if (ret)
> > +		return ret;
> > +
> > +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma_lut");
> > +	if (res) {
> > +		vop2->lut_regs = devm_ioremap_resource(dev, res);
> > +		if (IS_ERR(vop2->lut_regs))
> > +			return PTR_ERR(vop2->lut_regs);
> > +	}
> > +
> > +	vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
> > +
> > +	vop2->hclk = devm_clk_get(vop2->dev, "hclk");
> > +	if (IS_ERR(vop2->hclk)) {
> > +		drm_err(vop2->drm, "failed to get hclk source\n");
> > +		return PTR_ERR(vop2->hclk);
> > +	}
> > +
> > +	vop2->aclk = devm_clk_get(vop2->dev, "aclk");
> > +	if (IS_ERR(vop2->aclk)) {
> > +		drm_err(vop2->drm, "failed to get aclk source\n");
> > +		return PTR_ERR(vop2->aclk);
> > +	}
> > +
> > +	vop2->irq = platform_get_irq(pdev, 0);
> > +	if (vop2->irq < 0) {
> > +		drm_err(vop2->drm, "cannot find irq for vop2\n");
> > +		return vop2->irq;
> > +	}
> > +
> > +	mutex_init(&vop2->vop2_lock);
> > +
> > +	ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev);
> > +	if (ret) {
> > +		drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = vop2_create_crtc(vop2);
> > +	if (ret)
> > +		return ret;
> > +
> > +	pm_runtime_enable(&pdev->dev);
> > +
> > +	return 0;
> > +}
> > +
> > +static void vop2_unbind(struct device *dev, struct device *master, void *data)
> > +{
> > +	struct vop2 *vop2 = dev_get_drvdata(dev);
> > +	struct drm_device *drm = vop2->drm;
> > +	struct list_head *plane_list = &drm->mode_config.plane_list;
> > +	struct list_head *crtc_list = &drm->mode_config.crtc_list;
> > +	struct drm_crtc *crtc, *tmpc;
> > +	struct drm_plane *plane, *tmpp;
> > +
> > +	rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
> > +
> > +	pm_runtime_disable(dev);
> > +
> > +	list_for_each_entry_safe(plane, tmpp, plane_list, head)
> > +		drm_plane_cleanup(plane);
> > +
> > +	list_for_each_entry_safe(crtc, tmpc, crtc_list, head)
> > +		vop2_destroy_crtc(crtc);
> > +}
> > +
> > +const struct component_ops vop2_component_ops = {
> > +	.bind = vop2_bind,
> > +	.unbind = vop2_unbind,
> > +};
> > +EXPORT_SYMBOL_GPL(vop2_component_ops);
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> > new file mode 100644
> > index 0000000000000..c727093a06d68
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> > @@ -0,0 +1,477 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> > + * Author:Mark Yao <mark.yao@rock-chips.com>
> > + */
> > +
> > +#ifndef _ROCKCHIP_DRM_VOP2_H
> > +#define _ROCKCHIP_DRM_VOP2_H
> > +
> > +#include "rockchip_drm_vop.h"
> > +
> > +#include <linux/regmap.h>
> > +#include <drm/drm_modes.h>
> > +
> > +#define VOP_FEATURE_OUTPUT_10BIT        BIT(0)
> > +
> > +#define WIN_FEATURE_AFBDC		BIT(0)
> > +#define WIN_FEATURE_CLUSTER		BIT(1)
> > +
> > +/*
> > + *  the delay number of a window in different mode.
> > + */
> > +enum win_dly_mode {
> > +	VOP2_DLY_MODE_DEFAULT,   /**< default mode */
> > +	VOP2_DLY_MODE_HISO_S,    /** HDR in SDR out mode, as a SDR window */
> > +	VOP2_DLY_MODE_HIHO_H,    /** HDR in HDR out mode, as a HDR window */
> > +	VOP2_DLY_MODE_MAX,
> > +};
> > +
> > +struct vop_rect {
> > +	int width;
> > +	int height;
> > +};
> > +
> > +enum vop2_scale_up_mode {
> > +	VOP2_SCALE_UP_NRST_NBOR,
> > +	VOP2_SCALE_UP_BIL,
> > +	VOP2_SCALE_UP_BIC,
> > +};
> > +
> > +enum vop2_scale_down_mode {
> > +	VOP2_SCALE_DOWN_NRST_NBOR,
> > +	VOP2_SCALE_DOWN_BIL,
> > +	VOP2_SCALE_DOWN_AVG,
> > +};
> > +
> > +enum vop2_win_regs {
> > +	VOP2_WIN_ENABLE,
> > +	VOP2_WIN_FORMAT,
> > +	VOP2_WIN_CSC_MODE,
> > +	VOP2_WIN_XMIRROR,
> > +	VOP2_WIN_YMIRROR,
> > +	VOP2_WIN_RB_SWAP,
> > +	VOP2_WIN_UV_SWAP,
> > +	VOP2_WIN_ACT_INFO,
> > +	VOP2_WIN_DSP_INFO,
> > +	VOP2_WIN_DSP_ST,
> > +	VOP2_WIN_YRGB_MST,
> > +	VOP2_WIN_UV_MST,
> > +	VOP2_WIN_YRGB_VIR,
> > +	VOP2_WIN_UV_VIR,
> > +	VOP2_WIN_YUV_CLIP,
> > +	VOP2_WIN_Y2R_EN,
> > +	VOP2_WIN_R2Y_EN,
> > +	VOP2_WIN_COLOR_KEY,
> > +	VOP2_WIN_COLOR_KEY_EN,
> > +	VOP2_WIN_DITHER_UP,
> > +
> > +	/* scale regs */
> > +	VOP2_WIN_SCALE_YRGB_X,
> > +	VOP2_WIN_SCALE_YRGB_Y,
> > +	VOP2_WIN_SCALE_CBCR_X,
> > +	VOP2_WIN_SCALE_CBCR_Y,
> > +	VOP2_WIN_YRGB_HOR_SCL_MODE,
> > +	VOP2_WIN_YRGB_HSCL_FILTER_MODE,
> > +	VOP2_WIN_YRGB_VER_SCL_MODE,
> > +	VOP2_WIN_YRGB_VSCL_FILTER_MODE,
> > +	VOP2_WIN_CBCR_VER_SCL_MODE,
> > +	VOP2_WIN_CBCR_HSCL_FILTER_MODE,
> > +	VOP2_WIN_CBCR_HOR_SCL_MODE,
> > +	VOP2_WIN_CBCR_VSCL_FILTER_MODE,
> > +	VOP2_WIN_VSD_CBCR_GT2,
> > +	VOP2_WIN_VSD_CBCR_GT4,
> > +	VOP2_WIN_VSD_YRGB_GT2,
> > +	VOP2_WIN_VSD_YRGB_GT4,
> > +	VOP2_WIN_BIC_COE_SEL,
> > +
> > +	/* cluster regs */
> > +	VOP2_WIN_CLUSTER_ENABLE,
> > +	VOP2_WIN_AFBC_ENABLE,
> > +	VOP2_WIN_CLUSTER_LB_MODE,
> > +
> > +	/* afbc regs */
> > +	VOP2_WIN_AFBC_FORMAT,
> > +	VOP2_WIN_AFBC_RB_SWAP,
> > +	VOP2_WIN_AFBC_UV_SWAP,
> > +	VOP2_WIN_AFBC_AUTO_GATING_EN,
> > +	VOP2_WIN_AFBC_BLOCK_SPLIT_EN,
> > +	VOP2_WIN_AFBC_PIC_VIR_WIDTH,
> > +	VOP2_WIN_AFBC_TILE_NUM,
> > +	VOP2_WIN_AFBC_PIC_OFFSET,
> > +	VOP2_WIN_AFBC_PIC_SIZE,
> > +	VOP2_WIN_AFBC_DSP_OFFSET,
> > +	VOP2_WIN_AFBC_TRANSFORM_OFFSET,
> > +	VOP2_WIN_AFBC_HDR_PTR,
> > +	VOP2_WIN_AFBC_HALF_BLOCK_EN,
> > +	VOP2_WIN_AFBC_ROTATE_270,
> > +	VOP2_WIN_AFBC_ROTATE_90,
> > +	VOP2_WIN_MAX_REG,
> > +};
> > +
> > +struct vop2_win_data {
> > +	const char *name;
> > +	unsigned int phys_id;
> > +
> > +	u32 base;
> > +	enum drm_plane_type type;
> > +
> > +	u32 nformats;
> > +	const u32 *formats;
> > +	const uint64_t *format_modifiers;
> > +	const unsigned int supported_rotations;
> > +
> > +	/**
> > +	 * @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2
> > +	 */
> > +	unsigned int layer_sel_id;
> > +	uint64_t feature;
> > +
> > +	unsigned int max_upscale_factor;
> > +	unsigned int max_downscale_factor;
> > +	const u8 dly[VOP2_DLY_MODE_MAX];
> > +};
> > +
> > +struct vop2_video_port_data {
> > +	unsigned int id;
> > +	u32 feature;
> > +	u16 gamma_lut_len;
> > +	u16 cubic_lut_len;
> > +	struct vop_rect max_output;
> > +	const u8 pre_scan_max_dly[4];
> > +	const struct vop2_video_port_regs *regs;
> > +	unsigned int offset;
> > +};
> > +
> > +struct vop2_data {
> > +	u8 nr_vps;
> > +	const struct vop2_ctrl *ctrl;
> > +	const struct vop2_win_data *win;
> > +	const struct vop2_video_port_data *vp;
> > +	const struct vop_csc_table *csc_table;
> > +	struct vop_rect max_input;
> > +	struct vop_rect max_output;
> > +
> > +	unsigned int win_size;
> > +	unsigned int soc_id;
> > +};
> > +
> > +/* interrupt define */
> > +#define FS_NEW_INTR			BIT(4)
> > +#define ADDR_SAME_INTR			BIT(5)
> > +#define LINE_FLAG1_INTR			BIT(6)
> > +#define WIN0_EMPTY_INTR			BIT(7)
> > +#define WIN1_EMPTY_INTR			BIT(8)
> > +#define WIN2_EMPTY_INTR			BIT(9)
> > +#define WIN3_EMPTY_INTR			BIT(10)
> > +#define HWC_EMPTY_INTR			BIT(11)
> > +#define POST_BUF_EMPTY_INTR		BIT(12)
> > +#define PWM_GEN_INTR			BIT(13)
> > +#define DMA_FINISH_INTR			BIT(14)
> > +#define FS_FIELD_INTR			BIT(15)
> > +#define FE_INTR				BIT(16)
> > +#define WB_UV_FIFO_FULL_INTR		BIT(17)
> > +#define WB_YRGB_FIFO_FULL_INTR		BIT(18)
> > +#define WB_COMPLETE_INTR		BIT(19)
> > +
> > +/*
> > + * display output interface supported by rockchip lcdc
> > + */
> > +#define ROCKCHIP_OUT_MODE_P888		0
> > +#define ROCKCHIP_OUT_MODE_BT1120	0
> > +#define ROCKCHIP_OUT_MODE_P666		1
> > +#define ROCKCHIP_OUT_MODE_P565		2
> > +#define ROCKCHIP_OUT_MODE_BT656		5
> > +#define ROCKCHIP_OUT_MODE_S888		8
> > +#define ROCKCHIP_OUT_MODE_S888_DUMMY	12
> > +#define ROCKCHIP_OUT_MODE_YUV420	14
> > +/* for use special outface */
> > +#define ROCKCHIP_OUT_MODE_AAAA		15
> > +
> > +enum vop_csc_format {
> > +	CSC_BT601L,
> > +	CSC_BT709L,
> > +	CSC_BT601F,
> > +	CSC_BT2020,
> > +};
> > +
> > +enum src_factor_mode {
> > +	SRC_FAC_ALPHA_ZERO,
> > +	SRC_FAC_ALPHA_ONE,
> > +	SRC_FAC_ALPHA_DST,
> > +	SRC_FAC_ALPHA_DST_INVERSE,
> > +	SRC_FAC_ALPHA_SRC,
> > +	SRC_FAC_ALPHA_SRC_GLOBAL,
> > +};
> > +
> > +enum dst_factor_mode {
> > +	DST_FAC_ALPHA_ZERO,
> > +	DST_FAC_ALPHA_ONE,
> > +	DST_FAC_ALPHA_SRC,
> > +	DST_FAC_ALPHA_SRC_INVERSE,
> > +	DST_FAC_ALPHA_DST,
> > +	DST_FAC_ALPHA_DST_GLOBAL,
> > +};
> > +
> > +#define RK3568_GRF_VO_CON1			0x0364
> > +/* System registers definition */
> > +#define RK3568_REG_CFG_DONE			0x000
> > +#define RK3568_VERSION_INFO			0x004
> > +#define RK3568_SYS_AUTO_GATING_CTRL		0x008
> > +#define RK3568_SYS_AXI_LUT_CTRL			0x024
> > +#define RK3568_DSP_IF_EN			0x028
> > +#define RK3568_DSP_IF_CTRL			0x02c
> > +#define RK3568_DSP_IF_POL			0x030
> > +#define RK3568_WB_CTRL				0x40
> > +#define RK3568_WB_XSCAL_FACTOR			0x44
> > +#define RK3568_WB_YRGB_MST			0x48
> > +#define RK3568_WB_CBR_MST			0x4C
> > +#define RK3568_OTP_WIN_EN			0x050
> > +#define RK3568_LUT_PORT_SEL			0x058
> > +#define RK3568_SYS_STATUS0			0x060
> > +#define RK3568_VP_LINE_FLAG(vp)			(0x70 + (vp) * 0x4)
> > +#define RK3568_SYS0_INT_EN			0x80
> > +#define RK3568_SYS0_INT_CLR			0x84
> > +#define RK3568_SYS0_INT_STATUS			0x88
> > +#define RK3568_SYS1_INT_EN			0x90
> > +#define RK3568_SYS1_INT_CLR			0x94
> > +#define RK3568_SYS1_INT_STATUS			0x98
> > +#define RK3568_VP_INT_EN(vp)			(0xA0 + (vp) * 0x10)
> > +#define RK3568_VP_INT_CLR(vp)			(0xA4 + (vp) * 0x10)
> > +#define RK3568_VP_INT_STATUS(vp)		(0xA8 + (vp) * 0x10)
> > +#define RK3568_VP_INT_RAW_STATUS(vp)		(0xAC + (vp) * 0x10)
> > +
> > +/* Video Port registers definition */
> > +#define RK3568_VP_DSP_CTRL			0x00
> > +#define RK3568_VP_MIPI_CTRL			0x04
> > +#define RK3568_VP_COLOR_BAR_CTRL		0x08
> > +#define RK3568_VP_3D_LUT_CTRL			0x10
> > +#define RK3568_VP_3D_LUT_MST			0x20
> > +#define RK3568_VP_DSP_BG			0x2C
> > +#define RK3568_VP_PRE_SCAN_HTIMING		0x30
> > +#define RK3568_VP_POST_DSP_HACT_INFO		0x34
> > +#define RK3568_VP_POST_DSP_VACT_INFO		0x38
> > +#define RK3568_VP_POST_SCL_FACTOR_YRGB		0x3C
> > +#define RK3568_VP_POST_SCL_CTRL			0x40
> > +#define RK3568_VP_POST_DSP_VACT_INFO_F1		0x44
> > +#define RK3568_VP_DSP_HTOTAL_HS_END		0x48
> > +#define RK3568_VP_DSP_HACT_ST_END		0x4C
> > +#define RK3568_VP_DSP_VTOTAL_VS_END		0x50
> > +#define RK3568_VP_DSP_VACT_ST_END		0x54
> > +#define RK3568_VP_DSP_VS_ST_END_F1		0x58
> > +#define RK3568_VP_DSP_VACT_ST_END_F1		0x5C
> > +#define RK3568_VP_BCSH_CTRL			0x60
> > +#define RK3568_VP_BCSH_BCS			0x64
> > +#define RK3568_VP_BCSH_H			0x68
> > +#define RK3568_VP_BCSH_COLOR_BAR		0x6C
> > +
> > +/* Overlay registers definition    */
> > +#define RK3568_OVL_CTRL				0x600
> > +#define RK3568_OVL_LAYER_SEL			0x604
> > +#define RK3568_OVL_PORT_SEL			0x608
> > +#define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL	0x610
> > +#define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL	0x614
> > +#define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL	0x618
> > +#define RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL	0x61C
> > +#define RK3568_MIX0_SRC_COLOR_CTRL		0x650
> > +#define RK3568_MIX0_DST_COLOR_CTRL		0x654
> > +#define RK3568_MIX0_SRC_ALPHA_CTRL		0x658
> > +#define RK3568_MIX0_DST_ALPHA_CTRL		0x65C
> > +#define RK3568_HDR0_SRC_COLOR_CTRL		0x6C0
> > +#define RK3568_HDR0_DST_COLOR_CTRL		0x6C4
> > +#define RK3568_HDR0_SRC_ALPHA_CTRL		0x6C8
> > +#define RK3568_HDR0_DST_ALPHA_CTRL		0x6CC
> > +#define RK3568_VP_BG_MIX_CTRL(vp)		(0x6E0 + (vp) * 4)
> > +#define RK3568_CLUSTER_DLY_NUM			0x6F0
> > +#define RK3568_SMART_DLY_NUM			0x6F8
> > +
> > +/* Cluster register definition, offset relative to window base */
> > +#define RK3568_CLUSTER_WIN_CTRL0		0x00
> > +#define RK3568_CLUSTER_WIN_CTRL1		0x04
> > +#define RK3568_CLUSTER_WIN_YRGB_MST		0x10
> > +#define RK3568_CLUSTER_WIN_CBR_MST		0x14
> > +#define RK3568_CLUSTER_WIN_VIR			0x18
> > +#define RK3568_CLUSTER_WIN_ACT_INFO		0x20
> > +#define RK3568_CLUSTER_WIN_DSP_INFO		0x24
> > +#define RK3568_CLUSTER_WIN_DSP_ST		0x28
> > +#define RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB	0x30
> > +#define RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET	0x3C
> > +#define RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL	0x50
> > +#define RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE	0x54
> > +#define RK3568_CLUSTER_WIN_AFBCD_HDR_PTR	0x58
> > +#define RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH	0x5C
> > +#define RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE	0x60
> > +#define RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET	0x64
> > +#define RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET	0x68
> > +#define RK3568_CLUSTER_WIN_AFBCD_CTRL		0x6C
> > +
> > +#define RK3568_CLUSTER_CTRL			0x100
> > +
> > +/* (E)smart register definition, offset relative to window base */
> > +#define RK3568_SMART_CTRL0			0x00
> > +#define RK3568_SMART_CTRL1			0x04
> > +#define RK3568_SMART_REGION0_CTRL		0x10
> > +#define RK3568_SMART_REGION0_YRGB_MST		0x14
> > +#define RK3568_SMART_REGION0_CBR_MST		0x18
> > +#define RK3568_SMART_REGION0_VIR		0x1C
> > +#define RK3568_SMART_REGION0_ACT_INFO		0x20
> > +#define RK3568_SMART_REGION0_DSP_INFO		0x24
> > +#define RK3568_SMART_REGION0_DSP_ST		0x28
> > +#define RK3568_SMART_REGION0_SCL_CTRL		0x30
> > +#define RK3568_SMART_REGION0_SCL_FACTOR_YRGB	0x34
> > +#define RK3568_SMART_REGION0_SCL_FACTOR_CBR	0x38
> > +#define RK3568_SMART_REGION0_SCL_OFFSET		0x3C
> > +#define RK3568_SMART_REGION1_CTRL		0x40
> > +#define RK3568_SMART_REGION1_YRGB_MST		0x44
> > +#define RK3568_SMART_REGION1_CBR_MST		0x48
> > +#define RK3568_SMART_REGION1_VIR		0x4C
> > +#define RK3568_SMART_REGION1_ACT_INFO		0x50
> > +#define RK3568_SMART_REGION1_DSP_INFO		0x54
> > +#define RK3568_SMART_REGION1_DSP_ST		0x58
> > +#define RK3568_SMART_REGION1_SCL_CTRL		0x60
> > +#define RK3568_SMART_REGION1_SCL_FACTOR_YRGB	0x64
> > +#define RK3568_SMART_REGION1_SCL_FACTOR_CBR	0x68
> > +#define RK3568_SMART_REGION1_SCL_OFFSET		0x6C
> > +#define RK3568_SMART_REGION2_CTRL		0x70
> > +#define RK3568_SMART_REGION2_YRGB_MST		0x74
> > +#define RK3568_SMART_REGION2_CBR_MST		0x78
> > +#define RK3568_SMART_REGION2_VIR		0x7C
> > +#define RK3568_SMART_REGION2_ACT_INFO		0x80
> > +#define RK3568_SMART_REGION2_DSP_INFO		0x84
> > +#define RK3568_SMART_REGION2_DSP_ST		0x88
> > +#define RK3568_SMART_REGION2_SCL_CTRL		0x90
> > +#define RK3568_SMART_REGION2_SCL_FACTOR_YRGB	0x94
> > +#define RK3568_SMART_REGION2_SCL_FACTOR_CBR	0x98
> > +#define RK3568_SMART_REGION2_SCL_OFFSET		0x9C
> > +#define RK3568_SMART_REGION3_CTRL		0xA0
> > +#define RK3568_SMART_REGION3_YRGB_MST		0xA4
> > +#define RK3568_SMART_REGION3_CBR_MST		0xA8
> > +#define RK3568_SMART_REGION3_VIR		0xAC
> > +#define RK3568_SMART_REGION3_ACT_INFO		0xB0
> > +#define RK3568_SMART_REGION3_DSP_INFO		0xB4
> > +#define RK3568_SMART_REGION3_DSP_ST		0xB8
> > +#define RK3568_SMART_REGION3_SCL_CTRL		0xC0
> > +#define RK3568_SMART_REGION3_SCL_FACTOR_YRGB	0xC4
> > +#define RK3568_SMART_REGION3_SCL_FACTOR_CBR	0xC8
> > +#define RK3568_SMART_REGION3_SCL_OFFSET		0xCC
> > +#define RK3568_SMART_COLOR_KEY_CTRL		0xD0
> > +
> > +/* HDR register definition */
> > +#define RK3568_HDR_LUT_CTRL			0x2000
> > +#define RK3568_HDR_LUT_MST			0x2004
> > +#define RK3568_SDR2HDR_CTRL			0x2010
> > +#define RK3568_HDR2SDR_CTRL			0x2020
> > +#define RK3568_HDR2SDR_SRC_RANGE		0x2024
> > +#define RK3568_HDR2SDR_NORMFACEETF		0x2028
> > +#define RK3568_HDR2SDR_DST_RANGE		0x202C
> > +#define RK3568_HDR2SDR_NORMFACCGAMMA		0x2030
> > +#define RK3568_HDR_EETF_OETF_Y0			0x203C
> > +#define RK3568_HDR_SAT_Y0			0x20C0
> > +#define RK3568_HDR_EOTF_OETF_Y0			0x20F0
> > +#define RK3568_HDR_OETF_DX_POW1			0x2200
> > +#define RK3568_HDR_OETF_XN1			0x2300
> > +
> > +#define RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN		BIT(15)
> > +
> > +#define RK3568_VP_DSP_CTRL__STANDBY			BIT(31)
> > +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_MODE		BIT(20)
> > +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL		GENMASK(19, 18)
> > +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_EN		BIT(17)
> > +#define RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN		BIT(16)
> > +#define RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y		BIT(15)
> > +#define RK3568_VP_DSP_CTRL__DSP_RB_SWAP			BIT(9)
> > +#define RK3568_VP_DSP_CTRL__DSP_INTERLACE		BIT(7)
> > +#define RK3568_VP_DSP_CTRL__DSP_FILED_POL		BIT(6)
> > +#define RK3568_VP_DSP_CTRL__P2I_EN			BIT(5)
> > +#define RK3568_VP_DSP_CTRL__CORE_DCLK_DIV		BIT(4)
> > +#define RK3568_VP_DSP_CTRL__OUT_MODE			GENMASK(3, 0)
> > +
> > +#define RK3568_VP_POST_SCL_CTRL__VSCALEDOWN		BIT(1)
> > +#define RK3568_VP_POST_SCL_CTRL__HSCALEDOWN		BIT(0)
> > +
> > +#define RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX		GENMASK(26, 25)
> > +#define RK3568_SYS_DSP_INFACE_EN_LVDS1			BIT(24)
> > +#define RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX		GENMASK(22, 21)
> > +#define RK3568_SYS_DSP_INFACE_EN_MIPI1			BIT(20)
> > +#define RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX		GENMASK(19, 18)
> > +#define RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX		GENMASK(17, 16)
> > +#define RK3568_SYS_DSP_INFACE_EN_EDP_MUX		GENMASK(15, 14)
> > +#define RK3568_SYS_DSP_INFACE_EN_HDMI_MUX		GENMASK(11, 10)
> > +#define RK3568_SYS_DSP_INFACE_EN_RGB_MUX		GENMASK(9, 8)
> > +#define RK3568_SYS_DSP_INFACE_EN_LVDS0			BIT(5)
> > +#define RK3568_SYS_DSP_INFACE_EN_MIPI0			BIT(4)
> > +#define RK3568_SYS_DSP_INFACE_EN_EDP			BIT(3)
> > +#define RK3568_SYS_DSP_INFACE_EN_HDMI			BIT(1)
> > +#define RK3568_SYS_DSP_INFACE_EN_RGB			BIT(0)
> > +
> > +#define RK3568_DSP_IF_POL__MIPI_PIN_POL			GENMASK(19, 16)
> > +#define RK3568_DSP_IF_POL__EDP_PIN_POL			GENMASK(15, 12)
> > +#define RK3568_DSP_IF_POL__HDMI_PIN_POL			GENMASK(7, 4)
> > +#define RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL		GENMASK(3, 0)
> > +
> > +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2_PHASE_LOCK	BIT(5)
> > +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2			BIT(4)
> > +
> > +#define RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN	BIT(31)
> > +
> > +#define RK3568_DSP_IF_POL__CFG_DONE_IMD			BIT(28)
> > +
> > +#define VOP2_SYS_AXI_BUS_NUM				2
> > +
> > +#define VOP2_CLUSTER_YUV444_10				0x12
> > +
> > +#define VOP2_COLOR_KEY_MASK				BIT(31)
> > +
> > +#define RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD		BIT(28)
> > +
> > +#define RK3568_VP_BG_MIX_CTRL__BG_DLY			GENMASK(31, 24)
> > +
> > +#define RK3568_OVL_PORT_SEL__SEL_PORT			GENMASK(31, 16)
> > +#define RK3568_OVL_PORT_SEL__SMART1			GENMASK(31, 30)
> > +#define RK3568_OVL_PORT_SEL__SMART0			GENMASK(29, 28)
> > +#define RK3568_OVL_PORT_SEL__ESMART1			GENMASK(27, 26)
> > +#define RK3568_OVL_PORT_SEL__ESMART0			GENMASK(25, 24)
> > +#define RK3568_OVL_PORT_SEL__CLUSTER1			GENMASK(19, 18)
> > +#define RK3568_OVL_PORT_SEL__CLUSTER0			GENMASK(17, 16)
> > +#define RK3568_OVL_PORT_SET__PORT2_MUX			GENMASK(11, 8)
> > +#define RK3568_OVL_PORT_SET__PORT1_MUX			GENMASK(7, 4)
> > +#define RK3568_OVL_PORT_SET__PORT0_MUX			GENMASK(3, 0)
> > +#define RK3568_OVL_LAYER_SEL__LAYER(layer, x)		((x) << ((layer) * 4))
> > +
> > +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_1		GENMASK(31, 24)
> > +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_0		GENMASK(23, 16)
> > +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_1		GENMASK(15, 8)
> > +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_0		GENMASK(7, 0)
> > +
> > +#define RK3568_SMART_DLY_NUM__SMART1			GENMASK(31, 24)
> > +#define RK3568_SMART_DLY_NUM__SMART0			GENMASK(23, 16)
> > +#define RK3568_SMART_DLY_NUM__ESMART1			GENMASK(15, 8)
> > +#define RK3568_SMART_DLY_NUM__ESMART0			GENMASK(7, 0)
> > +
> > +#define VP_INT_DSP_HOLD_VALID	BIT(6)
> > +#define VP_INT_FS_FIELD		BIT(5)
> > +#define VP_INT_POST_BUF_EMPTY	BIT(4)
> > +#define VP_INT_LINE_FLAG1	BIT(3)
> > +#define VP_INT_LINE_FLAG0	BIT(2)
> > +#define VOP2_INT_BUS_ERRPR	BIT(1)
> > +#define VP_INT_FS		BIT(0)
> > +
> > +#define POLFLAG_DCLK_INV	BIT(3)
> > +
> > +enum vop2_layer_phy_id {
> > +	ROCKCHIP_VOP2_CLUSTER0 = 0,
> > +	ROCKCHIP_VOP2_CLUSTER1,
> > +	ROCKCHIP_VOP2_ESMART0,
> > +	ROCKCHIP_VOP2_ESMART1,
> > +	ROCKCHIP_VOP2_SMART0,
> > +	ROCKCHIP_VOP2_SMART1,
> > +	ROCKCHIP_VOP2_CLUSTER2,
> > +	ROCKCHIP_VOP2_CLUSTER3,
> > +	ROCKCHIP_VOP2_ESMART2,
> > +	ROCKCHIP_VOP2_ESMART3,
> > +	ROCKCHIP_VOP2_PHY_ID_INVALID = -1,
> > +};
> > +
> > +extern const struct component_ops vop2_component_ops;
> > +
> > +#endif /* _ROCKCHIP_DRM_VOP2_H */
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> > new file mode 100644
> > index 0000000000000..9bf0637bf8e26
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> > @@ -0,0 +1,281 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Copyright (C) Rockchip Electronics Co.Ltd
> > + * Author: Andy Yan <andy.yan@rock-chips.com>
> > + */
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/component.h>
> > +#include <linux/mod_devicetable.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/of.h>
> > +#include <drm/drm_fourcc.h>
> > +#include <drm/drm_plane.h>
> > +#include <drm/drm_print.h>
> > +
> > +#include "rockchip_drm_vop2.h"
> > +
> > +static const uint32_t formats_win_full_10bit[] = {
> > +	DRM_FORMAT_XRGB8888,
> > +	DRM_FORMAT_ARGB8888,
> > +	DRM_FORMAT_XBGR8888,
> > +	DRM_FORMAT_ABGR8888,
> > +	DRM_FORMAT_RGB888,
> > +	DRM_FORMAT_BGR888,
> > +	DRM_FORMAT_RGB565,
> > +	DRM_FORMAT_BGR565,
> > +	DRM_FORMAT_NV12,
> > +	DRM_FORMAT_NV16,
> > +	DRM_FORMAT_NV24,
> > +};
> > +
> > +static const uint32_t formats_win_full_10bit_yuyv[] = {
> > +	DRM_FORMAT_XRGB8888,
> > +	DRM_FORMAT_ARGB8888,
> > +	DRM_FORMAT_XBGR8888,
> > +	DRM_FORMAT_ABGR8888,
> > +	DRM_FORMAT_RGB888,
> > +	DRM_FORMAT_BGR888,
> > +	DRM_FORMAT_RGB565,
> > +	DRM_FORMAT_BGR565,
> > +	DRM_FORMAT_NV12,
> > +	DRM_FORMAT_NV16,
> > +	DRM_FORMAT_NV24,
> > +	DRM_FORMAT_YVYU,
> > +	DRM_FORMAT_VYUY,
> > +};
> > +
> > +static const uint32_t formats_win_lite[] = {
> > +	DRM_FORMAT_XRGB8888,
> > +	DRM_FORMAT_ARGB8888,
> > +	DRM_FORMAT_XBGR8888,
> > +	DRM_FORMAT_ABGR8888,
> > +	DRM_FORMAT_RGB888,
> > +	DRM_FORMAT_BGR888,
> > +	DRM_FORMAT_RGB565,
> > +	DRM_FORMAT_BGR565,
> > +};
> > +
> > +static const uint64_t format_modifiers[] = {
> > +	DRM_FORMAT_MOD_LINEAR,
> > +	DRM_FORMAT_MOD_INVALID,
> > +};
> > +
> > +static const uint64_t format_modifiers_afbc[] = {
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_SPARSE),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_YTR),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_CBR),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_YTR |
> > +				AFBC_FORMAT_MOD_SPARSE),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_CBR |
> > +				AFBC_FORMAT_MOD_SPARSE),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_YTR |
> > +				AFBC_FORMAT_MOD_CBR),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_YTR |
> > +				AFBC_FORMAT_MOD_CBR |
> > +				AFBC_FORMAT_MOD_SPARSE),
> > +
> > +	/* SPLIT mandates SPARSE, RGB modes mandates YTR */
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_YTR |
> > +				AFBC_FORMAT_MOD_SPARSE |
> > +				AFBC_FORMAT_MOD_SPLIT),
> > +	DRM_FORMAT_MOD_INVALID,
> > +};
> > +
> > +static const struct vop2_video_port_data rk3568_vop_video_ports[] = {
> > +	{
> > +		.id = 0,
> > +		.feature = VOP_FEATURE_OUTPUT_10BIT,
> > +		.gamma_lut_len = 1024,
> > +		.cubic_lut_len = 9 * 9 * 9,
> > +		.max_output = { 4096, 2304 },
> > +		.pre_scan_max_dly = { 69, 53, 53, 42 },
> > +		.offset = 0xc00,
> > +	}, {
> > +		.id = 1,
> > +		.gamma_lut_len = 1024,
> > +		.max_output = { 2048, 1536 },
> > +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> > +		.offset = 0xd00,
> > +	}, {
> > +		.id = 2,
> > +		.gamma_lut_len = 1024,
> > +		.max_output = { 1920, 1080 },
> > +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> > +		.offset = 0xe00,
> > +	},
> > +};
> > +
> > +/*
> > + * rk3568 vop with 2 cluster, 2 esmart win, 2 smart win.
> > + * Every cluster can work as 4K win or split into two win.
> > + * All win in cluster support AFBCD.
> > + *
> > + * Every esmart win and smart win support 4 Multi-region.
> > + *
> > + * Scale filter mode:
> > + *
> > + * * Cluster:  bicubic for horizontal scale up, others use bilinear
> > + * * ESmart:
> > + *    * nearest-neighbor/bilinear/bicubic for scale up
> > + *    * nearest-neighbor/bilinear/average for scale down
> > + *
> > + *
> > + * @TODO describe the wind like cpu-map dt nodes;
> > + */
> > +static const struct vop2_win_data rk3568_vop_win_data[] = {
> > +	{
> > +		.name = "Smart0-win0",
> > +		.phys_id = ROCKCHIP_VOP2_SMART0,
> > +		.base = 0x1c00,
> > +		.formats = formats_win_lite,
> > +		.nformats = ARRAY_SIZE(formats_win_lite),
> > +		.format_modifiers = format_modifiers,
> > +		.layer_sel_id = 3,
> > +		.supported_rotations = DRM_MODE_REFLECT_Y,
> > +		.type = DRM_PLANE_TYPE_PRIMARY,
> > +		.max_upscale_factor = 8,
> > +		.max_downscale_factor = 8,
> > +		.dly = { 20, 47, 41 },
> > +	}, {
> > +		.name = "Smart1-win0",
> > +		.phys_id = ROCKCHIP_VOP2_SMART1,
> > +		.formats = formats_win_lite,
> > +		.nformats = ARRAY_SIZE(formats_win_lite),
> > +		.format_modifiers = format_modifiers,
> > +		.base = 0x1e00,
> > +		.layer_sel_id = 7,
> > +		.supported_rotations = DRM_MODE_REFLECT_Y,
> > +		.type = DRM_PLANE_TYPE_PRIMARY,
> > +		.max_upscale_factor = 8,
> > +		.max_downscale_factor = 8,
> > +		.dly = { 20, 47, 41 },
> > +	}, {
> > +		.name = "Esmart1-win0",
> > +		.phys_id = ROCKCHIP_VOP2_ESMART1,
> > +		.formats = formats_win_full_10bit_yuyv,
> > +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> > +		.format_modifiers = format_modifiers,
> > +		.base = 0x1a00,
> > +		.layer_sel_id = 6,
> > +		.supported_rotations = DRM_MODE_REFLECT_Y,
> > +		.type = DRM_PLANE_TYPE_PRIMARY,
> > +		.max_upscale_factor = 8,
> > +		.max_downscale_factor = 8,
> > +		.dly = { 20, 47, 41 },
> > +	}, {
> > +		.name = "Esmart0-win0",
> > +		.phys_id = ROCKCHIP_VOP2_ESMART0,
> > +		.formats = formats_win_full_10bit_yuyv,
> > +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> > +		.format_modifiers = format_modifiers,
> > +		.base = 0x1800,
> > +		.layer_sel_id = 2,
> > +		.supported_rotations = DRM_MODE_REFLECT_Y,
> > +		.type = DRM_PLANE_TYPE_OVERLAY,
> > +		.max_upscale_factor = 8,
> > +		.max_downscale_factor = 8,
> > +		.dly = { 20, 47, 41 },
> > +	}, {
> > +		.name = "Cluster0-win0",
> > +		.phys_id = ROCKCHIP_VOP2_CLUSTER0,
> > +		.base = 0x1000,
> > +		.formats = formats_win_full_10bit,
> > +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> > +		.format_modifiers = format_modifiers_afbc,
> > +		.layer_sel_id = 0,
> > +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> > +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> > +		.max_upscale_factor = 4,
> > +		.max_downscale_factor = 4,
> > +		.dly = { 0, 27, 21 },
> > +		.type = DRM_PLANE_TYPE_OVERLAY,
> > +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> > +	}, {
> > +		.name = "Cluster1-win0",
> > +		.phys_id = ROCKCHIP_VOP2_CLUSTER1,
> > +		.base = 0x1200,
> > +		.formats = formats_win_full_10bit,
> > +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> > +		.format_modifiers = format_modifiers_afbc,
> > +		.layer_sel_id = 1,
> > +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> > +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> > +		.type = DRM_PLANE_TYPE_OVERLAY,
> > +		.max_upscale_factor = 4,
> > +		.max_downscale_factor = 4,
> > +		.dly = { 0, 27, 21 },
> > +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> > +	},
> > +};
> > +
> > +static const struct vop2_data rk3566_vop = {
> > +	.nr_vps = 3,
> > +	.max_input = { 4096, 2304 },
> > +	.max_output = { 4096, 2304 },
> > +	.vp = rk3568_vop_video_ports,
> > +	.win = rk3568_vop_win_data,
> > +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> > +	.soc_id = 3566,
> > +};
> > +
> > +static const struct vop2_data rk3568_vop = {
> > +	.nr_vps = 3,
> > +	.max_input = { 4096, 2304 },
> > +	.max_output = { 4096, 2304 },
> > +	.vp = rk3568_vop_video_ports,
> > +	.win = rk3568_vop_win_data,
> > +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> > +	.soc_id = 3568,
> > +};
> > +
> > +static const struct of_device_id vop2_dt_match[] = {
> > +	{
> > +		.compatible = "rockchip,rk3566-vop",
> > +		.data = &rk3566_vop,
> > +	}, {
> > +		.compatible = "rockchip,rk3568-vop",
> > +		.data = &rk3568_vop,
> > +	}, {
> > +	},
> > +};
> > +MODULE_DEVICE_TABLE(of, vop2_dt_match);
> > +
> > +static int vop2_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +
> > +	return component_add(dev, &vop2_component_ops);
> > +}
> > +
> > +static int vop2_remove(struct platform_device *pdev)
> > +{
> > +	component_del(&pdev->dev, &vop2_component_ops);
> > +
> > +	return 0;
> > +}
> > +
> > +struct platform_driver vop2_platform_driver = {
> > +	.probe = vop2_probe,
> > +	.remove = vop2_remove,
> > +	.driver = {
> > +		.name = "rockchip-vop2",
> > +		.of_match_table = of_match_ptr(vop2_dt_match),
> > +	},
> > +};
> 



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

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-21 18:18       ` Lucas Stach
  0 siblings, 0 replies; 201+ messages in thread
From: Lucas Stach @ 2022-02-21 18:18 UTC (permalink / raw)
  To: Andy Yan, Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Kever Yang, Sandy Huang,
	linux-rockchip, Michael Riesch, kernel, Peter Geis, tfiga,
	linux-arm-kernel

Hi Andy,

Am Montag, dem 21.02.2022 um 19:51 +0800 schrieb Andy Yan:
> Hi Sascha:
> 
> On 2/17/22 16:29, Sascha Hauer wrote:
> > From: Andy Yan <andy.yan@rock-chips.com>
> > 
> > The VOP2 unit is found on Rockchip SoCs beginning with rk3566/rk3568.
> > It replaces the VOP unit found in the older Rockchip SoCs.
> > 
> > This driver has been derived from the downstream Rockchip Kernel and
> > heavily modified:
> > 
> > - All nonstandard DRM properties have been removed
> > - dropped struct vop2_plane_state and pass around less data between
> >    functions
> > - Dropped all DRM_FORMAT_* not known on upstream
> > - rework register access to get rid of excessively used macros
> > - Drop all waiting for framesyncs
> 
> All the waiting sync in the downstream divers are try to fix special 
> problems,
> 
> and some of them inherited from upstream vop driver, for example: the 
> fb_unref_work,
> 
> It submitted by Tomas Figa to upstream vop driver to make the wait for 
> flip and asynchronously cursor work
> 
> right.
> 
> VOP2 share the same hardware design in vblank, so I don't think these 
> code are useless.

We've discussed this quite a bit internally before dropping this custom
frame synchronization. We believe that this is okay to do, as Sascha
also dropped a lot of code that would have required more
synchronization than what the common DRM atomic core code provides.

Fundamentally, a lot of the extra synchronization, also in the
upstream, VOP driver seems like it is working around the fact that the
vblank event might get sent out too early, so references to still
active framebuffers are dropped from the DRM core and userspace is
allowed to make further progress.

With this VOP2 submission, the vblank event is only armed at
atomic_flush time after all the HW programming is in place, which
ensures that even though we still race with the vblank IRQ, we only
loose this race in a predictable way and the only bad thing caused by
the race is a temporary hickup where a page-flip is deferred for one
more vblank cycle, but all the reference counts are still correct and
nothing gets freed prematurely.

Features not included in this submission, like dynamic assignment of
overlay planes to CRTCs, might need additional frame synchronization,
but at the current state of the driver any additional synchronization
is just cargo-cult and not actually needed.

Regards,
Lucas

> 
> [0] 
> https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-5-git-send-email-tfiga@chromium.org/
> 
> [1] 
> https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-6-git-send-email-tfiga@chromium.org/
> 
> [2] 
> https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-4-git-send-email-tfiga@chromium.org/
> 
> 
> > 
> > The driver is tested with HDMI and MIPI-DSI display on a RK3568-EVB
> > board. Overlay support is tested with the modetest utility. AFBC support
> > on the cluster windows is tested with weston-simple-dmabuf-egl on
> > weston using the (yet to be upstreamed) panfrost driver support.
> > 
> > Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> > 
> > Notes:
> >      Changes since v5:
> >      - consistently use u8/u16/u32 rather than uint8_t/uint16_t/uint32_t
> >      - Use spin_lock rather than spin_lock_irqsave
> >      - replace printk with drm_dbg
> >      - break some overlong lines
> >      
> >      Changes since v4:
> >      - Avoid stack frame overflow by not allocating big array on the stack
> >      
> >      Changes since v3:
> >      - Sort includes
> >      - fix typos
> >      - Drop spinlock
> >      - Use regmap_set_bits()/regmap_clear_bits()
> >      - simplify vop2_scale_factor()
> >      - simplify vop2_afbc_transform_offset()
> >      
> >      Changes since v4:
> >      - Sort nodes alphabetically
> >      
> >      Changes since v3:
> >      - Fix HDMI connector type
> > 
> >   drivers/gpu/drm/rockchip/Kconfig             |    6 +
> >   drivers/gpu/drm/rockchip/Makefile            |    1 +
> >   drivers/gpu/drm/rockchip/rockchip_drm_drv.c  |    1 +
> >   drivers/gpu/drm/rockchip/rockchip_drm_drv.h  |    6 +-
> >   drivers/gpu/drm/rockchip/rockchip_drm_fb.c   |    2 +
> >   drivers/gpu/drm/rockchip/rockchip_drm_vop.h  |   15 +
> >   drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2708 ++++++++++++++++++
> >   drivers/gpu/drm/rockchip/rockchip_drm_vop2.h |  477 +++
> >   drivers/gpu/drm/rockchip/rockchip_vop2_reg.c |  281 ++
> >   9 files changed, 3496 insertions(+), 1 deletion(-)
> >   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> >   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> >   create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> > 
> > diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> > index b9b156308460a..4ff0043f0ee70 100644
> > --- a/drivers/gpu/drm/rockchip/Kconfig
> > +++ b/drivers/gpu/drm/rockchip/Kconfig
> > @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
> >   	  This selects support for the VOP driver. You should enable it
> >   	  on all older SoCs up to RK3399.
> >   
> > +config ROCKCHIP_VOP2
> > +	bool "Rockchip VOP2 driver"
> > +	help
> > +	  This selects support for the VOP2 driver. You should enable it
> > +	  on all newer SoCs beginning form RK3568.
> > +
> >   config ROCKCHIP_ANALOGIX_DP
> >   	bool "Rockchip specific extensions for Analogix DP driver"
> >   	depends on ROCKCHIP_VOP
> > diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> > index dfc5512fdb9f1..3ff7b21c04149 100644
> > --- a/drivers/gpu/drm/rockchip/Makefile
> > +++ b/drivers/gpu/drm/rockchip/Makefile
> > @@ -6,6 +6,7 @@
> >   rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
> >   		rockchip_drm_gem.o
> >   
> > +rockchipdrm-$(CONFIG_ROCKCHIP_VOP2) += rockchip_drm_vop2.o rockchip_vop2_reg.o
> >   rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
> >   rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
> >   rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> > index 82c8faf1fb6b8..95f6c5985fdd7 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> > @@ -459,6 +459,7 @@ static int __init rockchip_drm_init(void)
> >   
> >   	num_rockchip_sub_drivers = 0;
> >   	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
> > +	ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2);
> >   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
> >   				CONFIG_ROCKCHIP_LVDS);
> >   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> > index d3e42410ae5da..5cb207aaaadd0 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> > @@ -18,7 +18,7 @@
> >   
> >   #define ROCKCHIP_MAX_FB_BUFFER	3
> >   #define ROCKCHIP_MAX_CONNECTOR	2
> > -#define ROCKCHIP_MAX_CRTC	2
> > +#define ROCKCHIP_MAX_CRTC	4
> >   
> >   struct drm_device;
> >   struct drm_connector;
> > @@ -31,6 +31,9 @@ struct rockchip_crtc_state {
> >   	int output_bpc;
> >   	int output_flags;
> >   	bool enable_afbc;
> > +	u32 bus_format;
> > +	u32 bus_flags;
> > +	int color_space;
> >   };
> >   #define to_rockchip_crtc_state(s) \
> >   		container_of(s, struct rockchip_crtc_state, base)
> > @@ -63,6 +66,7 @@ extern struct platform_driver rockchip_dp_driver;
> >   extern struct platform_driver rockchip_lvds_driver;
> >   extern struct platform_driver vop_platform_driver;
> >   extern struct platform_driver rk3066_hdmi_driver;
> > +extern struct platform_driver vop2_platform_driver;
> >   
> >   struct rockchip_encoder {
> >   	struct device_node *port;
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > index 3aa37e177667e..0d2cb4f3922b8 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > @@ -134,4 +134,6 @@ void rockchip_drm_mode_config_init(struct drm_device *dev)
> >   
> >   	dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
> >   	dev->mode_config.helper_private = &rockchip_mode_config_helpers;
> > +
> > +	dev->mode_config.normalize_zpos = true;
> >   }
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> > index 857d97cdc67c6..1e364d7b50e69 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> > @@ -54,9 +54,23 @@ struct vop_afbc {
> >   	struct vop_reg enable;
> >   	struct vop_reg win_sel;
> >   	struct vop_reg format;
> > +	struct vop_reg rb_swap;
> > +	struct vop_reg uv_swap;
> > +	struct vop_reg auto_gating_en;
> > +	struct vop_reg block_split_en;
> > +	struct vop_reg pic_vir_width;
> > +	struct vop_reg tile_num;
> >   	struct vop_reg hreg_block_split;
> > +	struct vop_reg pic_offset;
> >   	struct vop_reg pic_size;
> > +	struct vop_reg dsp_offset;
> > +	struct vop_reg transform_offset;
> >   	struct vop_reg hdr_ptr;
> > +	struct vop_reg half_block_en;
> > +	struct vop_reg xmirror;
> > +	struct vop_reg ymirror;
> > +	struct vop_reg rotate_270;
> > +	struct vop_reg rotate_90;
> >   	struct vop_reg rstn;
> >   };
> >   
> > @@ -410,4 +424,5 @@ static inline int scl_vop_cal_lb_mode(int width, bool is_yuv)
> >   }
> >   
> >   extern const struct component_ops vop_component_ops;
> > +
> >   #endif /* _ROCKCHIP_DRM_VOP_H */
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> > new file mode 100644
> > index 0000000000000..394dd6c583682
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> > @@ -0,0 +1,2708 @@
> > +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> > +/*
> > + * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
> > + * Author: Andy Yan <andy.yan@rock-chips.com>
> > + */
> > +#include <linux/bitfield.h>
> > +#include <linux/clk.h>
> > +#include <linux/component.h>
> > +#include <linux/delay.h>
> > +#include <linux/iopoll.h>
> > +#include <linux/kernel.h>
> > +#include <linux/mfd/syscon.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_graph.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <linux/swab.h>
> > +
> > +#include <drm/drm.h>
> > +#include <drm/drm_atomic.h>
> > +#include <drm/drm_atomic_uapi.h>
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_crtc_helper.h>
> > +#include <drm/drm_debugfs.h>
> > +#include <drm/drm_flip_work.h>
> > +#include <drm/drm_plane_helper.h>
> > +#include <drm/drm_probe_helper.h>
> > +#include <drm/drm_vblank.h>
> > +
> > +#include <uapi/linux/videodev2.h>
> > +#include <dt-bindings/soc/rockchip,vop2.h>
> > +
> > +#include "rockchip_drm_drv.h"
> > +#include "rockchip_drm_gem.h"
> > +#include "rockchip_drm_fb.h"
> > +#include "rockchip_drm_vop2.h"
> > +
> > +/*
> > + * VOP2 architecture
> > + *
> > + +----------+   +-------------+                                                        +-----------+
> > + |  Cluster |   | Sel 1 from 6|                                                        | 1 from 3  |
> > + |  window0 |   |    Layer0   |                                                        |    RGB    |
> > + +----------+   +-------------+              +---------------+    +-------------+      +-----------+
> > + +----------+   +-------------+              |N from 6 layers|    |             |
> > + |  Cluster |   | Sel 1 from 6|              |   Overlay0    +--->| Video Port0 |      +-----------+
> > + |  window1 |   |    Layer1   |              |               |    |             |      | 1 from 3  |
> > + +----------+   +-------------+              +---------------+    +-------------+      |   LVDS    |
> > + +----------+   +-------------+                                                        +-----------+
> > + |  Esmart  |   | Sel 1 from 6|
> > + |  window0 |   |   Layer2    |              +---------------+    +-------------+      +-----------+
> > + +----------+   +-------------+              |N from 6 Layers|    |             | +--> | 1 from 3  |
> > + +----------+   +-------------+   -------->  |   Overlay1    +--->| Video Port1 |      |   MIPI    |
> > + |  Esmart  |   | Sel 1 from 6|   -------->  |               |    |             |      +-----------+
> > + |  Window1 |   |   Layer3    |              +---------------+    +-------------+
> > + +----------+   +-------------+                                                        +-----------+
> > + +----------+   +-------------+                                                        | 1 from 3  |
> > + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |   HDMI    |
> > + |  Window0 |   |    Layer4   |              |N from 6 Layers|    |             |      +-----------+
> > + +----------+   +-------------+              |   Overlay2    +--->| Video Port2 |
> > + +----------+   +-------------+              |               |    |             |      +-----------+
> > + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |  1 from 3 |
> > + |  Window1 |   |    Layer5   |                                                        |    eDP    |
> > + +----------+   +-------------+                                                        +-----------+
> > + *
> > + */
> > +
> > +enum vop2_data_format {
> > +	VOP2_FMT_ARGB8888 = 0,
> > +	VOP2_FMT_RGB888,
> > +	VOP2_FMT_RGB565,
> > +	VOP2_FMT_XRGB101010,
> > +	VOP2_FMT_YUV420SP,
> > +	VOP2_FMT_YUV422SP,
> > +	VOP2_FMT_YUV444SP,
> > +	VOP2_FMT_YUYV422 = 8,
> > +	VOP2_FMT_YUYV420,
> > +	VOP2_FMT_VYUY422,
> > +	VOP2_FMT_VYUY420,
> > +	VOP2_FMT_YUV420SP_TILE_8x4 = 0x10,
> > +	VOP2_FMT_YUV420SP_TILE_16x2,
> > +	VOP2_FMT_YUV422SP_TILE_8x4,
> > +	VOP2_FMT_YUV422SP_TILE_16x2,
> > +	VOP2_FMT_YUV420SP_10,
> > +	VOP2_FMT_YUV422SP_10,
> > +	VOP2_FMT_YUV444SP_10,
> > +};
> > +
> > +enum vop2_afbc_format {
> > +	VOP2_AFBC_FMT_RGB565,
> > +	VOP2_AFBC_FMT_ARGB2101010 = 2,
> > +	VOP2_AFBC_FMT_YUV420_10BIT,
> > +	VOP2_AFBC_FMT_RGB888,
> > +	VOP2_AFBC_FMT_ARGB8888,
> > +	VOP2_AFBC_FMT_YUV420 = 9,
> > +	VOP2_AFBC_FMT_YUV422 = 0xb,
> > +	VOP2_AFBC_FMT_YUV422_10BIT = 0xe,
> > +	VOP2_AFBC_FMT_INVALID = -1,
> > +};
> > +
> > +union vop2_alpha_ctrl {
> > +	u32 val;
> > +	struct {
> > +		/* [0:1] */
> > +		u32 color_mode:1;
> > +		u32 alpha_mode:1;
> > +		/* [2:3] */
> > +		u32 blend_mode:2;
> > +		u32 alpha_cal_mode:1;
> > +		/* [5:7] */
> > +		u32 factor_mode:3;
> > +		/* [8:9] */
> > +		u32 alpha_en:1;
> > +		u32 src_dst_swap:1;
> > +		u32 reserved:6;
> > +		/* [16:23] */
> > +		u32 glb_alpha:8;
> > +	} bits;
> > +};
> > +
> > +struct vop2_alpha {
> > +	union vop2_alpha_ctrl src_color_ctrl;
> > +	union vop2_alpha_ctrl dst_color_ctrl;
> > +	union vop2_alpha_ctrl src_alpha_ctrl;
> > +	union vop2_alpha_ctrl dst_alpha_ctrl;
> > +};
> > +
> > +struct vop2_alpha_config {
> > +	bool src_premulti_en;
> > +	bool dst_premulti_en;
> > +	bool src_pixel_alpha_en;
> > +	bool dst_pixel_alpha_en;
> > +	u16 src_glb_alpha_value;
> > +	u16 dst_glb_alpha_value;
> > +};
> > +
> > +struct vop2_win {
> > +	struct vop2 *vop2;
> > +	struct drm_plane base;
> > +	const struct vop2_win_data *data;
> > +	struct regmap_field *reg[VOP2_WIN_MAX_REG];
> > +
> > +	/**
> > +	 * @win_id: graphic window id, a cluster may be split into two
> > +	 * graphics windows.
> > +	 */
> > +	u8 win_id;
> > +	u8 delay;
> > +	u32 offset;
> > +
> > +	enum drm_plane_type type;
> > +};
> > +
> > +struct vop2_video_port {
> > +	struct drm_crtc crtc;
> > +	struct vop2 *vop2;
> > +	struct clk *dclk;
> > +	unsigned int id;
> > +	const struct vop2_video_port_regs *regs;
> > +	const struct vop2_video_port_data *data;
> > +
> > +	struct completion dsp_hold_completion;
> > +
> > +	/**
> > +	 * @win_mask: Bitmask of windows attached to the video port;
> > +	 */
> > +	u32 win_mask;
> > +
> > +	struct vop2_win *primary_plane;
> > +	struct drm_pending_vblank_event *event;
> > +
> > +	unsigned int nlayers;
> > +};
> > +
> > +struct vop2 {
> > +	struct device *dev;
> > +	struct drm_device *drm;
> > +	struct vop2_video_port vps[ROCKCHIP_MAX_CRTC];
> > +
> > +	const struct vop2_data *data;
> > +	/*
> > +	 * Number of windows that are registered as plane, may be less than the
> > +	 * total number of hardware windows.
> > +	 */
> > +	u32 registered_num_wins;
> > +
> > +	void __iomem *regs;
> > +	struct regmap *map;
> > +
> > +	struct regmap *grf;
> > +
> > +	/* physical map length of vop2 register */
> > +	u32 len;
> > +
> > +	void __iomem *lut_regs;
> > +
> > +	/* protects crtc enable/disable */
> > +	struct mutex vop2_lock;
> > +
> > +	int irq;
> > +
> > +	/*
> > +	 * Some global resources are shared between all video ports(crtcs), so
> > +	 * we need a ref counter here.
> > +	 */
> > +	unsigned int enable_count;
> > +	struct clk *hclk;
> > +	struct clk *aclk;
> > +
> > +	/* must be put at the end of the struct */
> > +	struct vop2_win win[];
> > +};
> > +
> > +static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
> > +{
> > +	return container_of(crtc, struct vop2_video_port, crtc);
> > +}
> > +
> > +static struct vop2_win *to_vop2_win(struct drm_plane *p)
> > +{
> > +	return container_of(p, struct vop2_win, base);
> > +}
> > +
> > +static void vop2_lock(struct vop2 *vop2)
> > +{
> > +	mutex_lock(&vop2->vop2_lock);
> > +}
> > +
> > +static void vop2_unlock(struct vop2 *vop2)
> > +{
> > +	mutex_unlock(&vop2->vop2_lock);
> > +}
> > +
> > +static void vop2_writel(struct vop2 *vop2, u32 offset, u32 v)
> > +{
> > +	regmap_write(vop2->map, offset, v);
> > +}
> > +
> > +static void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
> > +{
> > +	regmap_write(vp->vop2->map, vp->data->offset + offset, v);
> > +}
> > +
> > +static u32 vop2_readl(struct vop2 *vop2, u32 offset)
> > +{
> > +	u32 val;
> > +
> > +	regmap_read(vop2->map, offset, &val);
> > +
> > +	return val;
> > +}
> > +
> > +static void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v)
> > +{
> > +	regmap_field_write(win->reg[reg], v);
> > +}
> > +
> > +static bool vop2_cluster_window(const struct vop2_win *win)
> > +{
> > +	return win->data->feature & WIN_FEATURE_CLUSTER;
> > +}
> > +
> > +static void vop2_cfg_done(struct vop2_video_port *vp)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +
> > +	regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE,
> > +			BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> > +}
> > +
> > +static void vop2_win_disable(struct vop2_win *win)
> > +{
> > +	vop2_win_write(win, VOP2_WIN_ENABLE, 0);
> > +
> > +	if (vop2_cluster_window(win))
> > +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0);
> > +}
> > +
> > +static enum vop2_data_format vop2_convert_format(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_XRGB8888:
> > +	case DRM_FORMAT_ARGB8888:
> > +	case DRM_FORMAT_XBGR8888:
> > +	case DRM_FORMAT_ABGR8888:
> > +		return VOP2_FMT_ARGB8888;
> > +	case DRM_FORMAT_RGB888:
> > +	case DRM_FORMAT_BGR888:
> > +		return VOP2_FMT_RGB888;
> > +	case DRM_FORMAT_RGB565:
> > +	case DRM_FORMAT_BGR565:
> > +		return VOP2_FMT_RGB565;
> > +	case DRM_FORMAT_NV12:
> > +		return VOP2_FMT_YUV420SP;
> > +	case DRM_FORMAT_NV16:
> > +		return VOP2_FMT_YUV422SP;
> > +	case DRM_FORMAT_NV24:
> > +		return VOP2_FMT_YUV444SP;
> > +	case DRM_FORMAT_YUYV:
> > +	case DRM_FORMAT_YVYU:
> > +		return VOP2_FMT_VYUY422;
> > +	case DRM_FORMAT_VYUY:
> > +	case DRM_FORMAT_UYVY:
> > +		return VOP2_FMT_YUYV422;
> > +	default:
> > +		DRM_ERROR("unsupported format[%08x]\n", format);
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static enum vop2_afbc_format vop2_convert_afbc_format(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_XRGB8888:
> > +	case DRM_FORMAT_ARGB8888:
> > +	case DRM_FORMAT_XBGR8888:
> > +	case DRM_FORMAT_ABGR8888:
> > +		return VOP2_AFBC_FMT_ARGB8888;
> > +	case DRM_FORMAT_RGB888:
> > +	case DRM_FORMAT_BGR888:
> > +		return VOP2_AFBC_FMT_RGB888;
> > +	case DRM_FORMAT_RGB565:
> > +	case DRM_FORMAT_BGR565:
> > +		return VOP2_AFBC_FMT_RGB565;
> > +	case DRM_FORMAT_NV12:
> > +		return VOP2_AFBC_FMT_YUV420;
> > +	case DRM_FORMAT_NV16:
> > +		return VOP2_AFBC_FMT_YUV422;
> > +	default:
> > +		return VOP2_AFBC_FMT_INVALID;
> > +	}
> > +
> > +	return VOP2_AFBC_FMT_INVALID;
> > +}
> > +
> > +static bool vop2_win_rb_swap(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_XBGR8888:
> > +	case DRM_FORMAT_ABGR8888:
> > +	case DRM_FORMAT_BGR888:
> > +	case DRM_FORMAT_BGR565:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool vop2_afbc_rb_swap(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_NV24:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool vop2_afbc_uv_swap(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_NV12:
> > +	case DRM_FORMAT_NV16:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool vop2_win_uv_swap(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_NV12:
> > +	case DRM_FORMAT_NV16:
> > +	case DRM_FORMAT_NV24:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool vop2_win_dither_up(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_BGR565:
> > +	case DRM_FORMAT_RGB565:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
> > +{
> > +	/*
> > +	 * FIXME:
> > +	 *
> > +	 * There is no media type for YUV444 output,
> > +	 * so when out_mode is AAAA or P888, assume output is YUV444 on
> > +	 * yuv format.
> > +	 *
> > +	 * From H/W testing, YUV444 mode need a rb swap.
> > +	 */
> > +	if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 ||
> > +	    bus_format == MEDIA_BUS_FMT_VYUY8_1X16 ||
> > +	    bus_format == MEDIA_BUS_FMT_YVYU8_2X8 ||
> > +	    bus_format == MEDIA_BUS_FMT_VYUY8_2X8 ||
> > +	    ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
> > +	      bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
> > +	     (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
> > +	      output_mode == ROCKCHIP_OUT_MODE_P888)))
> > +		return true;
> > +	else
> > +		return false;
> > +}
> > +
> > +static bool is_yuv_output(u32 bus_format)
> > +{
> > +	switch (bus_format) {
> > +	case MEDIA_BUS_FMT_YUV8_1X24:
> > +	case MEDIA_BUS_FMT_YUV10_1X30:
> > +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> > +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> > +	case MEDIA_BUS_FMT_YUYV8_2X8:
> > +	case MEDIA_BUS_FMT_YVYU8_2X8:
> > +	case MEDIA_BUS_FMT_UYVY8_2X8:
> > +	case MEDIA_BUS_FMT_VYUY8_2X8:
> > +	case MEDIA_BUS_FMT_YUYV8_1X16:
> > +	case MEDIA_BUS_FMT_YVYU8_1X16:
> > +	case MEDIA_BUS_FMT_UYVY8_1X16:
> > +	case MEDIA_BUS_FMT_VYUY8_1X16:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool rockchip_afbc(struct drm_plane *plane, u64 modifier)
> > +{
> > +	int i;
> > +
> > +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> > +		return false;
> > +
> > +	for (i = 0 ; i < plane->modifier_count; i++)
> > +		if (plane->modifiers[i] == modifier)
> > +			return true;
> > +
> > +	return false;
> > +
> > +}
> > +
> > +static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
> > +					u64 modifier)
> > +{
> > +	struct vop2_win *win = to_vop2_win(plane);
> > +	struct vop2 *vop2 = win->vop2;
> > +
> > +	if (modifier == DRM_FORMAT_MOD_INVALID)
> > +		return false;
> > +
> > +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> > +		return true;
> > +
> > +	if (!rockchip_afbc(plane, modifier)) {
> > +		drm_err(vop2->drm, "Unsupported format modifier 0x%llx\n",
> > +			modifier);
> > +
> > +		return false;
> > +	}
> > +
> > +	return vop2_convert_afbc_format(format) >= 0;
> > +}
> > +
> > +static u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate,
> > +				      bool afbc_half_block_en)
> > +{
> > +	struct drm_rect *src = &pstate->src;
> > +	struct drm_framebuffer *fb = pstate->fb;
> > +	u32 bpp = fb->format->cpp[0] * 8;
> > +	u32 vir_width = (fb->pitches[0] << 3) / bpp;
> > +	u32 width = drm_rect_width(src) >> 16;
> > +	u32 height = drm_rect_height(src) >> 16;
> > +	u32 act_xoffset = src->x1 >> 16;
> > +	u32 act_yoffset = src->y1 >> 16;
> > +	u32 align16_crop = 0;
> > +	u32 align64_crop = 0;
> > +	u32 height_tmp;
> > +	u8 tx, ty;
> > +	u8 bottom_crop_line_num = 0;
> > +
> > +	/* 16 pixel align */
> > +	if (height & 0xf)
> > +		align16_crop = 16 - (height & 0xf);
> > +
> > +	height_tmp = height + align16_crop;
> > +
> > +	/* 64 pixel align */
> > +	if (height_tmp & 0x3f)
> > +		align64_crop = 64 - (height_tmp & 0x3f);
> > +
> > +	bottom_crop_line_num = align16_crop + align64_crop;
> > +
> > +	switch (pstate->rotation &
> > +		(DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y |
> > +		 DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) {
> > +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> > +		tx = 16 - ((act_xoffset + width) & 0xf);
> > +		ty = bottom_crop_line_num - act_yoffset;
> > +		break;
> > +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90:
> > +		tx = bottom_crop_line_num - act_yoffset;
> > +		ty = vir_width - width - act_xoffset;
> > +		break;
> > +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270:
> > +		tx = act_yoffset;
> > +		ty = act_xoffset;
> > +		break;
> > +	case DRM_MODE_REFLECT_X:
> > +		tx = 16 - ((act_xoffset + width) & 0xf);
> > +		ty = act_yoffset;
> > +		break;
> > +	case DRM_MODE_REFLECT_Y:
> > +		tx = act_xoffset;
> > +		ty = bottom_crop_line_num - act_yoffset;
> > +		break;
> > +	case DRM_MODE_ROTATE_90:
> > +		tx = bottom_crop_line_num - act_yoffset;
> > +		ty = act_xoffset;
> > +		break;
> > +	case DRM_MODE_ROTATE_270:
> > +		tx = act_yoffset;
> > +		ty = vir_width - width - act_xoffset;
> > +		break;
> > +	case 0:
> > +		tx = act_xoffset;
> > +		ty = act_yoffset;
> > +		break;
> > +	}
> > +
> > +	if (afbc_half_block_en)
> > +		ty &= 0x7f;
> > +
> > +#define TRANSFORM_XOFFSET GENMASK(7, 0)
> > +#define TRANSFORM_YOFFSET GENMASK(23, 16)
> > +	return FIELD_PREP(TRANSFORM_XOFFSET, tx) |
> > +		FIELD_PREP(TRANSFORM_YOFFSET, ty);
> > +}
> > +
> > +/*
> > + * A Cluster window has 2048 x 16 line buffer, which can
> > + * works at 2048 x 16(Full) or 4096 x 8 (Half) mode.
> > + * for Cluster_lb_mode register:
> > + * 0: half mode, for plane input width range 2048 ~ 4096
> > + * 1: half mode, for cluster work at 2 * 2048 plane mode
> > + * 2: half mode, for rotate_90/270 mode
> > + *
> > + */
> > +static int vop2_get_cluster_lb_mode(struct vop2_win *win,
> > +				    struct drm_plane_state *pstate)
> > +{
> > +	if ((pstate->rotation & DRM_MODE_ROTATE_270) ||
> > +	    (pstate->rotation & DRM_MODE_ROTATE_90))
> > +		return 2;
> > +	else
> > +		return 0;
> > +}
> > +
> > +static u16 vop2_scale_factor(u32 src, u32 dst)
> > +{
> > +	u32 fac;
> > +	int shift;
> > +
> > +	if (src == dst)
> > +		return 0;
> > +
> > +	if (dst < 2)
> > +		return U16_MAX;
> > +
> > +	if (src < 2)
> > +		return 0;
> > +
> > +	if (src > dst)
> > +		shift = 12;
> > +	else
> > +		shift = 16;
> > +
> > +	src--;
> > +	dst--;
> > +
> > +	fac = DIV_ROUND_UP(src << shift, dst) - 1;
> > +
> > +	if (fac > U16_MAX)
> > +		return U16_MAX;
> > +
> > +	return fac;
> > +}
> > +
> > +static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
> > +			     u32 src_w, u32 src_h, u32 dst_w,
> > +			     u32 dst_h, u32 pixel_format)
> > +{
> > +	const struct drm_format_info *info;
> > +	u16 hor_scl_mode, ver_scl_mode;
> > +	u16 hscl_filter_mode, vscl_filter_mode;
> > +	u8 gt2 = 0;
> > +	u8 gt4 = 0;
> > +	u32 val;
> > +
> > +	info = drm_format_info(pixel_format);
> > +
> > +	if (src_h >= (4 * dst_h)) {
> > +		gt4 = 1;
> > +		src_h >>= 2;
> > +	} else if (src_h >= (2 * dst_h)) {
> > +		gt2 = 1;
> > +		src_h >>= 1;
> > +	}
> > +
> > +	hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> > +	ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> > +
> > +	if (hor_scl_mode == SCALE_UP)
> > +		hscl_filter_mode = VOP2_SCALE_UP_BIC;
> > +	else
> > +		hscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> > +
> > +	if (ver_scl_mode == SCALE_UP)
> > +		vscl_filter_mode = VOP2_SCALE_UP_BIL;
> > +	else
> > +		vscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> > +
> > +	/*
> > +	 * RK3568 VOP Esmart/Smart dsp_w should be even pixel
> > +	 * at scale down mode
> > +	 */
> > +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> > +		if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) {
> > +			drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n",
> > +				win->data->name, dst_w);
> > +			dst_w++;
> > +		}
> > +	}
> > +
> > +	val = vop2_scale_factor(src_w, dst_w);
> > +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val);
> > +	val = vop2_scale_factor(src_h, dst_h);
> > +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val);
> > +
> > +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4);
> > +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2);
> > +
> > +	vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode);
> > +	vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode);
> > +
> > +	if (vop2_cluster_window(win))
> > +		return;
> > +
> > +	vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode);
> > +	vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode);
> > +
> > +	if (info->is_yuv) {
> > +		src_w /= info->hsub;
> > +		src_h /= info->vsub;
> > +
> > +		gt4 = gt2 = 0;
> > +
> > +		if (src_h >= (4 * dst_h)) {
> > +			gt4 = 1;
> > +			src_h >>= 2;
> > +		} else if (src_h >= (2 * dst_h)) {
> > +			gt2 = 1;
> > +			src_h >>= 1;
> > +		}
> > +
> > +		hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> > +		ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> > +
> > +		val = vop2_scale_factor(src_w, dst_w);
> > +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val);
> > +
> > +		val = vop2_scale_factor(src_h, dst_h);
> > +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val);
> > +
> > +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4);
> > +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2);
> > +		vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode);
> > +		vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode);
> > +		vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode);
> > +		vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode);
> > +	}
> > +}
> > +
> > +static int vop2_convert_csc_mode(int csc_mode)
> > +{
> > +	switch (csc_mode) {
> > +	case V4L2_COLORSPACE_SMPTE170M:
> > +	case V4L2_COLORSPACE_470_SYSTEM_M:
> > +	case V4L2_COLORSPACE_470_SYSTEM_BG:
> > +		return CSC_BT601L;
> > +	case V4L2_COLORSPACE_REC709:
> > +	case V4L2_COLORSPACE_SMPTE240M:
> > +	case V4L2_COLORSPACE_DEFAULT:
> > +		return CSC_BT709L;
> > +	case V4L2_COLORSPACE_JPEG:
> > +		return CSC_BT601F;
> > +	case V4L2_COLORSPACE_BT2020:
> > +		return CSC_BT2020;
> > +	default:
> > +		return CSC_BT709L;
> > +	}
> > +}
> > +
> > +/*
> > + * colorspace path:
> > + *      Input        Win csc                     Output
> > + * 1. YUV(2020)  --> Y2R->2020To709->R2Y   --> YUV_OUTPUT(601/709)
> > + *    RGB        --> R2Y                  __/
> > + *
> > + * 2. YUV(2020)  --> bypasss               --> YUV_OUTPUT(2020)
> > + *    RGB        --> 709To2020->R2Y       __/
> > + *
> > + * 3. YUV(2020)  --> Y2R->2020To709        --> RGB_OUTPUT(709)
> > + *    RGB        --> R2Y                  __/
> > + *
> > + * 4. YUV(601/709)-> Y2R->709To2020->R2Y   --> YUV_OUTPUT(2020)
> > + *    RGB        --> 709To2020->R2Y       __/
> > + *
> > + * 5. YUV(601/709)-> bypass                --> YUV_OUTPUT(709)
> > + *    RGB        --> R2Y                  __/
> > + *
> > + * 6. YUV(601/709)-> bypass                --> YUV_OUTPUT(601)
> > + *    RGB        --> R2Y(601)             __/
> > + *
> > + * 7. YUV        --> Y2R(709)              --> RGB_OUTPUT(709)
> > + *    RGB        --> bypass               __/
> > + *
> > + * 8. RGB        --> 709To2020->R2Y        --> YUV_OUTPUT(2020)
> > + *
> > + * 9. RGB        --> R2Y(709)              --> YUV_OUTPUT(709)
> > + *
> > + * 10. RGB       --> R2Y(601)              --> YUV_OUTPUT(601)
> > + *
> > + * 11. RGB       --> bypass                --> RGB_OUTPUT(709)
> > + */
> > +
> > +static void vop2_setup_csc_mode(struct vop2_video_port *vp,
> > +				struct vop2_win *win,
> > +				struct drm_plane_state *pstate)
> > +{
> > +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
> > +	int is_input_yuv = pstate->fb->format->is_yuv;
> > +	int is_output_yuv = is_yuv_output(vcstate->bus_format);
> > +	int input_csc = V4L2_COLORSPACE_DEFAULT;
> > +	int output_csc = vcstate->color_space;
> > +	bool r2y_en, y2r_en;
> > +	int csc_mode;
> > +
> > +	if (is_input_yuv && !is_output_yuv) {
> > +		y2r_en = true;
> > +		r2y_en = false;
> > +		csc_mode = vop2_convert_csc_mode(input_csc);
> > +	} else if (!is_input_yuv && is_output_yuv) {
> > +		y2r_en = false;
> > +		r2y_en = true;
> > +		csc_mode = vop2_convert_csc_mode(output_csc);
> > +	} else {
> > +		y2r_en = false;
> > +		r2y_en = false;
> > +		csc_mode = false;
> > +	}
> > +
> > +	vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en);
> > +	vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en);
> > +	vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
> > +}
> > +
> > +static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +
> > +	vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq);
> > +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq);
> > +}
> > +
> > +static void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +
> > +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16);
> > +}
> > +
> > +static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
> > +{
> > +	int ret;
> > +
> > +	ret = clk_prepare_enable(vop2->hclk);
> > +	if (ret < 0) {
> > +		drm_err(vop2->drm, "failed to enable hclk - %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = clk_prepare_enable(vop2->aclk);
> > +	if (ret < 0) {
> > +		drm_err(vop2->drm, "failed to enable aclk - %d\n", ret);
> > +		goto err;
> > +	}
> > +
> > +	return 0;
> > +err:
> > +	clk_disable_unprepare(vop2->hclk);
> > +
> > +	return ret;
> > +}
> > +
> > +static void vop2_enable(struct vop2 *vop2)
> > +{
> > +	int ret;
> > +
> > +	ret = pm_runtime_get_sync(vop2->dev);
> > +	if (ret < 0) {
> > +		drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret);
> > +		return;
> > +	}
> > +
> > +	ret = vop2_core_clks_prepare_enable(vop2);
> > +	if (ret) {
> > +		pm_runtime_put_sync(vop2->dev);
> > +		return;
> > +	}
> > +
> > +	if (vop2->data->soc_id == 3566)
> > +		vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
> > +
> > +	vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> > +
> > +	/*
> > +	 * Disable auto gating, this is a workaround to
> > +	 * avoid display image shift when a window enabled.
> > +	 */
> > +	regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL,
> > +			  RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN);
> > +
> > +	vop2_writel(vop2, RK3568_SYS0_INT_CLR,
> > +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> > +	vop2_writel(vop2, RK3568_SYS0_INT_EN,
> > +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> > +	vop2_writel(vop2, RK3568_SYS1_INT_CLR,
> > +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> > +	vop2_writel(vop2, RK3568_SYS1_INT_EN,
> > +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> > +}
> > +
> > +static void vop2_disable(struct vop2 *vop2)
> > +{
> > +	pm_runtime_put_sync(vop2->dev);
> > +
> > +	clk_disable_unprepare(vop2->aclk);
> > +	clk_disable_unprepare(vop2->hclk);
> > +}
> > +
> > +static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
> > +				     struct drm_atomic_state *state)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct vop2 *vop2 = vp->vop2;
> > +	int ret;
> > +
> > +	vop2_lock(vop2);
> > +
> > +	drm_crtc_vblank_off(crtc);
> > +
> > +	/*
> > +	 * Vop standby will take effect at end of current frame,
> > +	 * if dsp hold valid irq happen, it means standby complete.
> > +	 *
> > +	 * we must wait standby complete when we want to disable aclk,
> > +	 * if not, memory bus maybe dead.
> > +	 */
> > +	reinit_completion(&vp->dsp_hold_completion);
> > +
> > +	vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
> > +
> > +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
> > +
> > +	ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
> > +					  msecs_to_jiffies(50));
> > +	if (!ret)
> > +		drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
> > +
> > +	vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
> > +
> > +	clk_disable_unprepare(vp->dclk);
> > +
> > +	vop2->enable_count--;
> > +
> > +	if (!vop2->enable_count)
> > +		vop2_disable(vop2);
> > +
> > +	vop2_unlock(vop2);
> > +
> > +	if (crtc->state->event && !crtc->state->active) {
> > +		spin_lock_irq(&crtc->dev->event_lock);
> > +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +		spin_unlock_irq(&crtc->dev->event_lock);
> > +
> > +		crtc->state->event = NULL;
> > +	}
> > +}
> > +
> > +static int vop2_plane_atomic_check(struct drm_plane *plane,
> > +				   struct drm_atomic_state *astate)
> > +{
> > +	struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane);
> > +	struct drm_framebuffer *fb = pstate->fb;
> > +	struct drm_crtc *crtc = pstate->crtc;
> > +	struct drm_crtc_state *cstate;
> > +	struct vop2_video_port *vp;
> > +	struct vop2 *vop2;
> > +	const struct vop2_data *vop2_data;
> > +	struct drm_rect *dest = &pstate->dst;
> > +	struct drm_rect *src = &pstate->src;
> > +	int min_scale = FRAC_16_16(1, 8);
> > +	int max_scale = FRAC_16_16(8, 1);
> > +	int format;
> > +	int ret;
> > +
> > +	if (!crtc)
> > +		return 0;
> > +
> > +	vp = to_vop2_video_port(crtc);
> > +	vop2 = vp->vop2;
> > +	vop2_data = vop2->data;
> > +
> > +	cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc);
> > +	if (WARN_ON(!cstate))
> > +		return -EINVAL;
> > +
> > +	ret = drm_atomic_helper_check_plane_state(pstate, cstate,
> > +						  min_scale, max_scale,
> > +						  true, true);
> > +	if (ret)
> > +		return ret;
> > +
> > +	if (!pstate->visible)
> > +		return 0;
> > +
> > +	format = vop2_convert_format(fb->format->format);
> > +	if (format < 0)
> > +		return format;
> > +
> > +	if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 ||
> > +	    drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) {
> > +		drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
> > +			  drm_rect_width(src) >> 16, drm_rect_height(src) >> 16,
> > +			  drm_rect_width(dest), drm_rect_height(dest));
> > +		pstate->visible = false;
> > +		return 0;
> > +	}
> > +
> > +	if (drm_rect_width(src) >> 16 > vop2_data->max_input.width ||
> > +	    drm_rect_height(src) >> 16 > vop2_data->max_input.height) {
> > +		drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
> > +			  drm_rect_width(src) >> 16,
> > +			  drm_rect_height(src) >> 16,
> > +			  vop2_data->max_input.width,
> > +			  vop2_data->max_input.height);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/*
> > +	 * Src.x1 can be odd when do clip, but yuv plane start point
> > +	 * need align with 2 pixel.
> > +	 */
> > +	if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
> > +		drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void vop2_plane_atomic_disable(struct drm_plane *plane,
> > +				      struct drm_atomic_state *state)
> > +{
> > +	struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state, plane);
> > +	struct vop2_win *win = to_vop2_win(plane);
> > +	struct vop2 *vop2 = win->vop2;
> > +
> > +	drm_dbg(vop2->drm, "%s disable\n", win->data->name);
> > +
> > +	if (!old_pstate->crtc)
> > +		return;
> > +
> > +	vop2_win_disable(win);
> > +	vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0);
> > +}
> > +
> > +/*
> > + * The color key is 10 bit, so all format should
> > + * convert to 10 bit here.
> > + */
> > +static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
> > +{
> > +	struct drm_plane_state *pstate = plane->state;
> > +	struct drm_framebuffer *fb = pstate->fb;
> > +	struct vop2_win *win = to_vop2_win(plane);
> > +	u32 color_key_en = 0;
> > +	u32 r = 0;
> > +	u32 g = 0;
> > +	u32 b = 0;
> > +
> > +	if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) {
> > +		vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0);
> > +		return;
> > +	}
> > +
> > +	switch (fb->format->format) {
> > +	case DRM_FORMAT_RGB565:
> > +	case DRM_FORMAT_BGR565:
> > +		r = (color_key & 0xf800) >> 11;
> > +		g = (color_key & 0x7e0) >> 5;
> > +		b = (color_key & 0x1f);
> > +		r <<= 5;
> > +		g <<= 4;
> > +		b <<= 5;
> > +		color_key_en = 1;
> > +		break;
> > +	case DRM_FORMAT_XRGB8888:
> > +	case DRM_FORMAT_ARGB8888:
> > +	case DRM_FORMAT_XBGR8888:
> > +	case DRM_FORMAT_ABGR8888:
> > +	case DRM_FORMAT_RGB888:
> > +	case DRM_FORMAT_BGR888:
> > +		r = (color_key & 0xff0000) >> 16;
> > +		g = (color_key & 0xff00) >> 8;
> > +		b = (color_key & 0xff);
> > +		r <<= 2;
> > +		g <<= 2;
> > +		b <<= 2;
> > +		color_key_en = 1;
> > +		break;
> > +	}
> > +
> > +	vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en);
> > +	vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b);
> > +}
> > +
> > +static void vop2_plane_atomic_update(struct drm_plane *plane,
> > +				     struct drm_atomic_state *state)
> > +{
> > +	struct drm_plane_state *pstate = plane->state;
> > +	struct drm_crtc *crtc = pstate->crtc;
> > +	struct vop2_win *win = to_vop2_win(plane);
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
> > +	struct vop2 *vop2 = win->vop2;
> > +	struct drm_framebuffer *fb = pstate->fb;
> > +	u32 bpp = fb->format->cpp[0] * 8;
> > +	u32 actual_w, actual_h, dsp_w, dsp_h;
> > +	u32 act_info, dsp_info;
> > +	u32 format;
> > +	u32 afbc_format;
> > +	u32 rb_swap;
> > +	u32 uv_swap;
> > +	struct drm_rect *src = &pstate->src;
> > +	struct drm_rect *dest = &pstate->dst;
> > +	u32 afbc_tile_num;
> > +	u32 transform_offset;
> > +	bool dither_up;
> > +	bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
> > +	bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
> > +	bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
> > +	bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
> > +	struct rockchip_gem_object *rk_obj;
> > +	unsigned long offset;
> > +	bool afbc_en;
> > +	dma_addr_t yrgb_mst;
> > +	dma_addr_t uv_mst;
> > +
> > +	/*
> > +	 * can't update plane when vop2 is disabled.
> > +	 */
> > +	if (WARN_ON(!crtc))
> > +		return;
> > +
> > +	if (!pstate->visible) {
> > +		vop2_plane_atomic_disable(plane, state);
> > +		return;
> > +	}
> > +
> > +	afbc_en = rockchip_afbc(plane, fb->modifier);
> > +
> > +	offset = (src->x1 >> 16) * fb->format->cpp[0];
> > +
> > +	/*
> > +	 * AFBC HDR_PTR must set to the zero offset of the framebuffer.
> > +	 */
> > +	if (afbc_en)
> > +		offset = 0;
> > +	else if (pstate->rotation & DRM_MODE_REFLECT_Y)
> > +		offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
> > +	else
> > +		offset += (src->y1 >> 16) * fb->pitches[0];
> > +
> > +	rk_obj = to_rockchip_obj(fb->obj[0]);
> > +
> > +	yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
> > +	if (fb->format->is_yuv) {
> > +		int hsub = fb->format->hsub;
> > +		int vsub = fb->format->vsub;
> > +
> > +		offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub;
> > +		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
> > +
> > +		if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en)
> > +			offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub;
> > +
> > +		rk_obj = to_rockchip_obj(fb->obj[0]);
> > +		uv_mst = rk_obj->dma_addr + offset + fb->offsets[1];
> > +	}
> > +
> > +	actual_w = drm_rect_width(src) >> 16;
> > +	actual_h = drm_rect_height(src) >> 16;
> > +	dsp_w = drm_rect_width(dest);
> > +
> > +	if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
> > +		drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
> > +			  vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
> > +		dsp_w = adjusted_mode->hdisplay - dest->x1;
> > +		if (dsp_w < 4)
> > +			dsp_w = 4;
> > +		actual_w = dsp_w * actual_w / drm_rect_width(dest);
> > +	}
> > +
> > +	dsp_h = drm_rect_height(dest);
> > +
> > +	if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
> > +		drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
> > +			  vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
> > +		dsp_h = adjusted_mode->vdisplay - dest->y1;
> > +		if (dsp_h < 4)
> > +			dsp_h = 4;
> > +		actual_h = dsp_h * actual_h / drm_rect_height(dest);
> > +	}
> > +
> > +	/*
> > +	 * This is workaround solution for IC design:
> > +	 * esmart can't support scale down when actual_w % 16 == 1.
> > +	 */
> > +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> > +		if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
> > +			drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
> > +				vp->id, win->data->name, actual_w);
> > +			actual_w -= 1;
> > +		}
> > +	}
> > +
> > +	if (afbc_en && actual_w % 4) {
> > +		drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
> > +			  vp->id, win->data->name, actual_w);
> > +		actual_w = ALIGN_DOWN(actual_w, 4);
> > +	}
> > +
> > +	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
> > +	dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff);
> > +
> > +	format = vop2_convert_format(fb->format->format);
> > +
> > +	drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n",
> > +		      vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h,
> > +		      dest->x1, dest->y1,
> > +		      &fb->format->format,
> > +		      afbc_en ? "AFBC" : "", &yrgb_mst);
> > +
> > +	if (afbc_en) {
> > +		u32 stride;
> > +
> > +		/* the afbc superblock is 16 x 16 */
> > +		afbc_format = vop2_convert_afbc_format(fb->format->format);
> > +
> > +		/* Enable color transform for YTR */
> > +		if (fb->modifier & AFBC_FORMAT_MOD_YTR)
> > +			afbc_format |= (1 << 4);
> > +
> > +		afbc_tile_num = ALIGN(actual_w, 16) >> 4;
> > +
> > +		/*
> > +		 * AFBC pic_vir_width is count by pixel, this is different
> > +		 * with WIN_VIR_STRIDE.
> > +		 */
> > +		stride = (fb->pitches[0] << 3) / bpp;
> > +		if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
> > +			drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligened\n",
> > +				  vp->id, win->data->name, stride);
> > +
> > +		rb_swap = vop2_afbc_rb_swap(fb->format->format);
> > +		uv_swap = vop2_afbc_uv_swap(fb->format->format);
> > +		/*
> > +		 * This is a workaround for crazy IC design, Cluster
> > +		 * and Esmart/Smart use different format configuration map:
> > +		 * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart.
> > +		 *
> > +		 * This is one thing we can make the convert simple:
> > +		 * AFBCD decode all the YUV data to YUV444. So we just
> > +		 * set all the yuv 10 bit to YUV444_10.
> > +		 */
> > +		if (fb->format->is_yuv && (bpp == 10))
> > +			format = VOP2_CLUSTER_YUV444_10;
> > +
> > +		if (vop2_cluster_window(win))
> > +			vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_RB_SWAP, rb_swap);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
> > +		if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) {
> > +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0);
> > +			transform_offset = vop2_afbc_transform_offset(pstate, false);
> > +		} else {
> > +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 1);
> > +			transform_offset = vop2_afbc_transform_offset(pstate, true);
> > +		}
> > +		vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1));
> > +		vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16)));
> > +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num);
> > +		vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90);
> > +	} else {
> > +		vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4));
> > +	}
> > +
> > +	vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror);
> > +
> > +	if (rotate_90 || rotate_270) {
> > +		act_info = swahw32(act_info);
> > +		actual_w = drm_rect_height(src) >> 16;
> > +		actual_h = drm_rect_width(src) >> 16;
> > +	}
> > +
> > +	vop2_win_write(win, VOP2_WIN_FORMAT, format);
> > +	vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst);
> > +
> > +	rb_swap = vop2_win_rb_swap(fb->format->format);
> > +	vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap);
> > +	if (!vop2_cluster_window(win)) {
> > +		uv_swap = vop2_win_uv_swap(fb->format->format);
> > +		vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
> > +	}
> > +
> > +	if (fb->format->is_yuv) {
> > +		vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4));
> > +		vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst);
> > +	}
> > +
> > +	vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format);
> > +	if (!vop2_cluster_window(win))
> > +		vop2_plane_setup_color_key(plane, 0);
> > +	vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info);
> > +	vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info);
> > +	vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff));
> > +
> > +	vop2_setup_csc_mode(vp, win, pstate);
> > +
> > +	dither_up = vop2_win_dither_up(fb->format->format);
> > +	vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up);
> > +
> > +	vop2_win_write(win, VOP2_WIN_ENABLE, 1);
> > +
> > +	if (vop2_cluster_window(win)) {
> > +		int lb_mode = vop2_get_cluster_lb_mode(win, pstate);
> > +
> > +		vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode);
> > +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1);
> > +	}
> > +}
> > +
> > +static const struct drm_plane_helper_funcs vop2_plane_helper_funcs = {
> > +	.atomic_check = vop2_plane_atomic_check,
> > +	.atomic_update = vop2_plane_atomic_update,
> > +	.atomic_disable = vop2_plane_atomic_disable,
> > +};
> > +
> > +static const struct drm_plane_funcs vop2_plane_funcs = {
> > +	.update_plane	= drm_atomic_helper_update_plane,
> > +	.disable_plane	= drm_atomic_helper_disable_plane,
> > +	.destroy = drm_plane_cleanup,
> > +	.reset = drm_atomic_helper_plane_reset,
> > +	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> > +	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> > +	.format_mod_supported = rockchip_vop2_mod_supported,
> > +};
> > +
> > +static int vop2_crtc_enable_vblank(struct drm_crtc *crtc)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +
> > +	vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD);
> > +
> > +	return 0;
> > +}
> > +
> > +static void vop2_crtc_disable_vblank(struct drm_crtc *crtc)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +
> > +	vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD);
> > +}
> > +
> > +static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
> > +				 const struct drm_display_mode *mode,
> > +				 struct drm_display_mode *adj_mode)
> > +{
> > +	drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V |
> > +					CRTC_STEREO_DOUBLE);
> > +
> > +	return true;
> > +}
> > +
> > +static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
> > +{
> > +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> > +
> > +	switch (vcstate->bus_format) {
> > +	case MEDIA_BUS_FMT_RGB565_1X16:
> > +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> > +		break;
> > +	case MEDIA_BUS_FMT_RGB666_1X18:
> > +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> > +	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
> > +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> > +		*dsp_ctrl |= RGB888_TO_RGB666;
> > +		break;
> > +	case MEDIA_BUS_FMT_YUV8_1X24:
> > +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> > +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +
> > +	if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA)
> > +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> > +
> > +	*dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL,
> > +				DITHER_DOWN_ALLEGRO);
> > +}
> > +
> > +static void vop2_post_config(struct drm_crtc *crtc)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> > +	u16 vtotal = mode->crtc_vtotal;
> > +	u16 hdisplay = mode->crtc_hdisplay;
> > +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> > +	u16 vdisplay = mode->crtc_vdisplay;
> > +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> > +	u32 left_margin = 100, right_margin = 100;
> > +	u32 top_margin = 100, bottom_margin = 100;
> > +	u16 hsize = hdisplay * (left_margin + right_margin) / 200;
> > +	u16 vsize = vdisplay * (top_margin + bottom_margin) / 200;
> > +	u16 hact_end, vact_end;
> > +	u32 val;
> > +
> > +	vsize = rounddown(vsize, 2);
> > +	hsize = rounddown(hsize, 2);
> > +	hact_st += hdisplay * (100 - left_margin) / 200;
> > +	hact_end = hact_st + hsize;
> > +	val = hact_st << 16;
> > +	val |= hact_end;
> > +	vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val);
> > +	vact_st += vdisplay * (100 - top_margin) / 200;
> > +	vact_end = vact_st + vsize;
> > +	val = vact_st << 16;
> > +	val |= vact_end;
> > +	vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val);
> > +	val = scl_cal_scale2(vdisplay, vsize) << 16;
> > +	val |= scl_cal_scale2(hdisplay, hsize);
> > +	vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val);
> > +
> > +	val = 0;
> > +	if (hdisplay != hsize)
> > +		val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN;
> > +	if (vdisplay != vsize)
> > +		val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN;
> > +	vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val);
> > +
> > +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> > +		u16 vact_st_f1 = vtotal + vact_st + 1;
> > +		u16 vact_end_f1 = vact_st_f1 + vsize;
> > +
> > +		val = vact_st_f1 << 16 | vact_end_f1;
> > +		vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
> > +	}
> > +
> > +	vop2_vp_write(vp, RK3568_VP_DSP_BG, 0);
> > +}
> > +
> > +static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
> > +				u32 polflags)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +	u32 die, dip;
> > +
> > +	die = vop2_readl(vop2, RK3568_DSP_IF_EN);
> > +	dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
> > +
> > +	switch (id) {
> > +	case RK3568_VOP2_EP_RGB:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_RGB |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
> > +		if (polflags & POLFLAG_DCLK_INV)
> > +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
> > +		else
> > +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16));
> > +		break;
> > +	case RK3568_VOP2_EP_HDMI:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_HDMI |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id);
> > +		break;
> > +	case RK3568_VOP2_EP_EDP:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_EDP |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id);
> > +		break;
> > +	case RK3568_VOP2_EP_MIPI0:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id);
> > +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> > +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> > +		break;
> > +	case RK3568_VOP2_EP_MIPI1:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
> > +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> > +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> > +		break;
> > +	case RK3568_VOP2_EP_LVDS0:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id);
> > +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> > +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> > +		break;
> > +	case RK3568_VOP2_EP_LVDS1:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id);
> > +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> > +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> > +		break;
> > +	default:
> > +		return;
> > +	};
> > +
> > +	dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
> > +
> > +	vop2_writel(vop2, RK3568_DSP_IF_EN, die);
> > +	vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
> > +}
> > +
> > +static int us_to_vertical_line(struct drm_display_mode *mode, int us)
> > +{
> > +	return us * mode->clock / mode->htotal / 1000;
> > +}
> > +
> > +static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
> > +				    struct drm_atomic_state *state)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct vop2 *vop2 = vp->vop2;
> > +	const struct vop2_data *vop2_data = vop2->data;
> > +	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
> > +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> > +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> > +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> > +	unsigned long clock = mode->crtc_clock * 1000;
> > +	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
> > +	u16 hdisplay = mode->crtc_hdisplay;
> > +	u16 htotal = mode->crtc_htotal;
> > +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> > +	u16 hact_end = hact_st + hdisplay;
> > +	u16 vdisplay = mode->crtc_vdisplay;
> > +	u16 vtotal = mode->crtc_vtotal;
> > +	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
> > +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> > +	u16 vact_end = vact_st + vdisplay;
> > +	u8 out_mode;
> > +	u32 dsp_ctrl = 0;
> > +	int act_end;
> > +	u32 val, polflags;
> > +	int ret;
> > +	struct drm_encoder *encoder;
> > +
> > +	drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n",
> > +		hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
> > +		drm_mode_vrefresh(mode), vcstate->output_type, vp->id);
> > +
> > +	vop2_lock(vop2);
> > +
> > +	ret = clk_prepare_enable(vp->dclk);
> > +	if (ret < 0) {
> > +		drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
> > +			      vp->id, ret);
> > +		return;
> > +	}
> > +
> > +	if (!vop2->enable_count)
> > +		vop2_enable(vop2);
> > +
> > +	vop2->enable_count++;
> > +
> > +	vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY);
> > +
> > +	polflags = 0;
> > +	if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
> > +		polflags |= POLFLAG_DCLK_INV;
> > +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> > +		polflags |= BIT(HSYNC_POSITIVE);
> > +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> > +		polflags |= BIT(VSYNC_POSITIVE);
> > +
> > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > +		struct device_node *node, *parent;
> > +
> > +		parent = of_get_parent(rkencoder->port);
> > +
> > +		for_each_endpoint_of_node(parent, node) {
> > +			struct device_node *crtc_port = of_graph_get_remote_port(node);
> > +			struct device_node *epn;
> > +			struct of_endpoint endpoint;
> > +
> > +			if (crtc->port != crtc_port) {
> > +				of_node_put(crtc_port);
> > +				continue;
> > +			}
> > +
> > +			of_node_put(crtc_port);
> > +
> > +			epn = of_graph_get_remote_endpoint(node);
> > +			of_graph_parse_endpoint(epn, &endpoint);
> > +			of_node_put(epn);
> > +
> > +			drm_dbg(vop2->drm, "vp%d is connected to %s, id %d\n",
> > +					   vp->id, encoder->name, endpoint.id);
> > +			rk3568_set_intf_mux(vp, endpoint.id, polflags);
> > +		}
> > +		of_node_put(parent);
> > +	}
> > +
> > +	if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
> > +	     !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT))
> > +		out_mode = ROCKCHIP_OUT_MODE_P888;
> > +	else
> > +		out_mode = vcstate->output_mode;
> > +
> > +	dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode);
> > +
> > +	if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP;
> > +
> > +	if (is_yuv_output(vcstate->bus_format))
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y;
> > +
> > +	vop2_dither_setup(crtc, &dsp_ctrl);
> > +
> > +	vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len);
> > +	val = hact_st << 16;
> > +	val |= hact_end;
> > +	vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val);
> > +
> > +	val = vact_st << 16;
> > +	val |= vact_end;
> > +	vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val);
> > +
> > +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> > +		u16 vact_st_f1 = vtotal + vact_st + 1;
> > +		u16 vact_end_f1 = vact_st_f1 + vdisplay;
> > +
> > +		val = vact_st_f1 << 16 | vact_end_f1;
> > +		vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val);
> > +
> > +		val = vtotal << 16 | (vtotal + vsync_len);
> > +		vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val);
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE;
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL;
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN;
> > +		vtotal += vtotal + 1;
> > +		act_end = vact_end_f1;
> > +	} else {
> > +		act_end = vact_end;
> > +	}
> > +
> > +	vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id),
> > +		    (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end);
> > +
> > +	vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len);
> > +
> > +	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
> > +		clock *= 2;
> > +	}
> > +
> > +	vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
> > +
> > +	clk_set_rate(vp->dclk, clock);
> > +
> > +	vop2_post_config(crtc);
> > +
> > +	vop2_cfg_done(vp);
> > +
> > +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
> > +
> > +	drm_crtc_vblank_on(crtc);
> > +
> > +	vop2_unlock(vop2);
> > +}
> > +
> > +static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
> > +				  struct drm_atomic_state *state)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct drm_plane *plane;
> > +	int nplanes = 0;
> > +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> > +
> > +	drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
> > +		nplanes++;
> > +
> > +	if (nplanes > vp->nlayers)
> > +		return -EINVAL;
> > +
> > +	return 0;
> > +}
> > +
> > +static bool is_opaque(u16 alpha)
> > +{
> > +	return (alpha >> 8) == 0xff;
> > +}
> > +
> > +static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config,
> > +			     struct vop2_alpha *alpha)
> > +{
> > +	int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1;
> > +	int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1;
> > +	int src_color_mode = alpha_config->src_premulti_en ?
> > +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> > +	int dst_color_mode = alpha_config->dst_premulti_en ?
> > +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> > +
> > +	alpha->src_color_ctrl.val = 0;
> > +	alpha->dst_color_ctrl.val = 0;
> > +	alpha->src_alpha_ctrl.val = 0;
> > +	alpha->dst_alpha_ctrl.val = 0;
> > +
> > +	if (!alpha_config->src_pixel_alpha_en)
> > +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> > +	else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en)
> > +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> > +	else
> > +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> > +
> > +	alpha->src_color_ctrl.bits.alpha_en = 1;
> > +
> > +	if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) {
> > +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> > +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> > +	} else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) {
> > +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> > +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE;
> > +	} else {
> > +		alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL;
> > +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> > +	}
> > +	alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8;
> > +	alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> > +	alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> > +
> > +	alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> > +	alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> > +	alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> > +	alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8;
> > +	alpha->dst_color_ctrl.bits.color_mode = dst_color_mode;
> > +	alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> > +
> > +	alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> > +	alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode;
> > +	alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> > +	alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE;
> > +
> > +	alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> > +	if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en)
> > +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> > +	else
> > +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> > +	alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION;
> > +	alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> > +}
> > +
> > +static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id)
> > +{
> > +	struct vop2_video_port *vp;
> > +	int used_layer = 0;
> > +	int i;
> > +
> > +	for (i = 0; i < port_id; i++) {
> > +		vp = &vop2->vps[i];
> > +		used_layer += hweight32(vp->win_mask);
> > +	}
> > +
> > +	return used_layer;
> > +}
> > +
> > +static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win)
> > +{
> > +	u32 offset = (main_win->data->phys_id * 0x10);
> > +	struct vop2_alpha_config alpha_config;
> > +	struct vop2_alpha alpha;
> > +	struct drm_plane_state *bottom_win_pstate;
> > +	bool src_pixel_alpha_en = false;
> > +	u16 src_glb_alpha_val, dst_glb_alpha_val;
> > +	bool premulti_en = false;
> > +	bool swap = false;
> > +
> > +	/* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */
> > +	bottom_win_pstate = main_win->base.state;
> > +	src_glb_alpha_val = 0;
> > +	dst_glb_alpha_val = main_win->base.state->alpha;
> > +
> > +	if (!bottom_win_pstate->fb)
> > +		return;
> > +
> > +	alpha_config.src_premulti_en = premulti_en;
> > +	alpha_config.dst_premulti_en = false;
> > +	alpha_config.src_pixel_alpha_en = src_pixel_alpha_en;
> > +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> > +	alpha_config.src_glb_alpha_value = src_glb_alpha_val;
> > +	alpha_config.dst_glb_alpha_value = dst_glb_alpha_val;
> > +	vop2_parse_alpha(&alpha_config, &alpha);
> > +
> > +	alpha.src_color_ctrl.bits.src_dst_swap = swap;
> > +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset,
> > +		    alpha.src_color_ctrl.val);
> > +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset,
> > +		    alpha.dst_color_ctrl.val);
> > +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset,
> > +		    alpha.src_alpha_ctrl.val);
> > +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset,
> > +		    alpha.dst_alpha_ctrl.val);
> > +}
> > +
> > +static void vop2_setup_alpha(struct vop2_video_port *vp)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +	struct drm_framebuffer *fb;
> > +	struct vop2_alpha_config alpha_config;
> > +	struct vop2_alpha alpha;
> > +	struct drm_plane *plane;
> > +	int pixel_alpha_en;
> > +	int premulti_en, gpremulti_en = 0;
> > +	int mixer_id;
> > +	u32 offset;
> > +	bool bottom_layer_alpha_en = false;
> > +	u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE;
> > +
> > +	mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id);
> > +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> > +
> > +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> > +		struct vop2_win *win = to_vop2_win(plane);
> > +
> > +		if (plane->state->normalized_zpos == 0 &&
> > +		    !is_opaque(plane->state->alpha) &&
> > +		    !vop2_cluster_window(win)) {
> > +			/*
> > +			 * If bottom layer have global alpha effect [except cluster layer,
> > +			 * because cluster have deal with bottom layer global alpha value
> > +			 * at cluster mix], bottom layer mix need deal with global alpha.
> > +			 */
> > +			bottom_layer_alpha_en = true;
> > +			dst_global_alpha = plane->state->alpha;
> > +		}
> > +	}
> > +
> > +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> > +		struct vop2_win *win = to_vop2_win(plane);
> > +		int zpos = plane->state->normalized_zpos;
> > +
> > +		if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
> > +			premulti_en = 1;
> > +		else
> > +			premulti_en = 0;
> > +
> > +		plane = &win->base;
> > +		fb = plane->state->fb;
> > +
> > +		pixel_alpha_en = fb->format->has_alpha;
> > +
> > +		alpha_config.src_premulti_en = premulti_en;
> > +
> > +		if (bottom_layer_alpha_en && zpos == 1) {
> > +			gpremulti_en = premulti_en;
> > +			/* Cd = Cs + (1 - As) * Cd * Agd */
> > +			alpha_config.dst_premulti_en = false;
> > +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> > +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> > +			alpha_config.dst_glb_alpha_value = dst_global_alpha;
> > +		} else if (vop2_cluster_window(win)) {
> > +			/* Mix output data only have pixel alpha */
> > +			alpha_config.dst_premulti_en = true;
> > +			alpha_config.src_pixel_alpha_en = true;
> > +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> > +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> > +		} else {
> > +			/* Cd = Cs + (1 - As) * Cd */
> > +			alpha_config.dst_premulti_en = true;
> > +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> > +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> > +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> > +		}
> > +
> > +		vop2_parse_alpha(&alpha_config, &alpha);
> > +
> > +		offset = (mixer_id + zpos - 1) * 0x10;
> > +		vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset,
> > +			    alpha.src_color_ctrl.val);
> > +		vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset,
> > +			    alpha.dst_color_ctrl.val);
> > +		vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset,
> > +			    alpha.src_alpha_ctrl.val);
> > +		vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset,
> > +			    alpha.dst_alpha_ctrl.val);
> > +	}
> > +
> > +	if (vp->id == 0) {
> > +		if (bottom_layer_alpha_en) {
> > +			/* Transfer pixel alpha to hdr mix */
> > +			alpha_config.src_premulti_en = gpremulti_en;
> > +			alpha_config.dst_premulti_en = true;
> > +			alpha_config.src_pixel_alpha_en = true;
> > +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> > +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> > +			vop2_parse_alpha(&alpha_config, &alpha);
> > +
> > +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL,
> > +				alpha.src_color_ctrl.val);
> > +			vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL,
> > +				alpha.dst_color_ctrl.val);
> > +			vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL,
> > +				alpha.src_alpha_ctrl.val);
> > +			vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL,
> > +				alpha.dst_alpha_ctrl.val);
> > +		} else {
> > +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0);
> > +		}
> > +	}
> > +}
> > +
> > +static void vop2_setup_layer_mixer(struct vop2_video_port *vp)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +	struct drm_plane *plane;
> > +	u32 layer_sel = 0;
> > +	u32 port_sel;
> > +	unsigned int nlayer, ofs;
> > +	struct drm_display_mode *adjusted_mode;
> > +	u16 hsync_len;
> > +	u16 hdisplay;
> > +	u32 bg_dly;
> > +	u32 pre_scan_dly;
> > +	int i;
> > +	struct vop2_video_port *vp0 = &vop2->vps[0];
> > +	struct vop2_video_port *vp1 = &vop2->vps[1];
> > +	struct vop2_video_port *vp2 = &vop2->vps[2];
> > +
> > +	adjusted_mode = &vp->crtc.state->adjusted_mode;
> > +	hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
> > +	hdisplay = adjusted_mode->crtc_hdisplay;
> > +
> > +	bg_dly = vp->data->pre_scan_max_dly[3];
> > +	vop2_writel(vop2, RK3568_VP_BG_MIX_CTRL(vp->id),
> > +			    FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly));
> > +
> > +	pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len;
> > +	vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly);
> > +
> > +	vop2_writel(vop2, RK3568_OVL_CTRL, 0);
> > +	port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
> > +	port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT;
> > +
> > +	if (vp0->nlayers)
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX,
> > +				     vp0->nlayers - 1);
> > +	else
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8);
> > +
> > +	if (vp1->nlayers)
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX,
> > +				     (vp0->nlayers + vp1->nlayers - 1));
> > +	else
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> > +
> > +	if (vp2->nlayers)
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX,
> > +			(vp2->nlayers + vp1->nlayers + vp0->nlayers - 1));
> > +	else
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> > +
> > +	layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
> > +
> > +	ofs = 0;
> > +	for (i = 0; i < vp->id; i++)
> > +		ofs += vop2->vps[i].nlayers;
> > +
> > +	nlayer = 0;
> > +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> > +		struct vop2_win *win = to_vop2_win(plane);
> > +
> > +		switch (win->data->phys_id) {
> > +		case ROCKCHIP_VOP2_CLUSTER0:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id);
> > +			break;
> > +		case ROCKCHIP_VOP2_CLUSTER1:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id);
> > +			break;
> > +		case ROCKCHIP_VOP2_ESMART0:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id);
> > +			break;
> > +		case ROCKCHIP_VOP2_ESMART1:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id);
> > +			break;
> > +		case ROCKCHIP_VOP2_SMART0:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART0;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id);
> > +			break;
> > +		case ROCKCHIP_VOP2_SMART1:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART1;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id);
> > +			break;
> > +		}
> > +
> > +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> > +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, win->data->layer_sel_id);
> > +		nlayer++;
> > +	}
> > +
> > +	/* configure unused layers to 0x5 (reserved) */
> > +	for (; nlayer < 3; nlayer++) {
> > +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> > +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5);
> > +	}
> > +
> > +	vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel);
> > +	vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
> > +	vop2_writel(vop2, RK3568_OVL_CTRL, RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD);
> > +}
> > +
> > +static void vop2_setup_dly_for_windows(struct vop2 *vop2)
> > +{
> > +	struct vop2_win *win;
> > +	int i = 0;
> > +	u32 cdly = 0, sdly = 0;
> > +
> > +	for (i = 0; i < vop2->data->win_size; i++) {
> > +		u32 dly;
> > +
> > +		win = &vop2->win[i];
> > +		dly = win->delay;
> > +
> > +		switch (win->data->phys_id) {
> > +		case ROCKCHIP_VOP2_CLUSTER0:
> > +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly);
> > +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly);
> > +			break;
> > +		case ROCKCHIP_VOP2_CLUSTER1:
> > +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly);
> > +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly);
> > +			break;
> > +		case ROCKCHIP_VOP2_ESMART0:
> > +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly);
> > +			break;
> > +		case ROCKCHIP_VOP2_ESMART1:
> > +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly);
> > +			break;
> > +		case ROCKCHIP_VOP2_SMART0:
> > +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly);
> > +			break;
> > +		case ROCKCHIP_VOP2_SMART1:
> > +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly);
> > +			break;
> > +		}
> > +	}
> > +
> > +	vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly);
> > +	vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly);
> > +}
> > +
> > +static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
> > +				   struct drm_atomic_state *state)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct vop2 *vop2 = vp->vop2;
> > +	struct drm_plane *plane;
> > +
> > +	vp->win_mask = 0;
> > +
> > +	drm_atomic_crtc_for_each_plane(plane, crtc) {
> > +		struct vop2_win *win = to_vop2_win(plane);
> > +
> > +		win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT];
> > +
> > +		vp->win_mask |= BIT(win->data->phys_id);
> > +
> > +		if (vop2_cluster_window(win))
> > +			vop2_setup_cluster_alpha(vop2, win);
> > +	}
> > +
> > +	if (!vp->win_mask)
> > +		return;
> > +
> > +	vop2_setup_layer_mixer(vp);
> > +	vop2_setup_alpha(vp);
> > +	vop2_setup_dly_for_windows(vop2);
> > +}
> > +
> > +static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
> > +				   struct drm_atomic_state *state)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +
> > +	vop2_post_config(crtc);
> > +
> > +	vop2_cfg_done(vp);
> > +
> > +	spin_lock_irq(&crtc->dev->event_lock);
> > +
> > +	if (crtc->state->event) {
> > +		WARN_ON(drm_crtc_vblank_get(crtc));
> > +		vp->event = crtc->state->event;
> > +		crtc->state->event = NULL;
> > +	}
> > +
> > +	spin_unlock_irq(&crtc->dev->event_lock);
> > +}
> > +
> > +static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
> > +	.mode_fixup = vop2_crtc_mode_fixup,
> > +	.atomic_check = vop2_crtc_atomic_check,
> > +	.atomic_begin = vop2_crtc_atomic_begin,
> > +	.atomic_flush = vop2_crtc_atomic_flush,
> > +	.atomic_enable = vop2_crtc_atomic_enable,
> > +	.atomic_disable = vop2_crtc_atomic_disable,
> > +};
> > +
> > +static void vop2_crtc_reset(struct drm_crtc *crtc)
> > +{
> > +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> > +
> > +	if (crtc->state) {
> > +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
> > +		kfree(vcstate);
> > +	}
> > +
> > +	vcstate = kzalloc(sizeof(*vcstate), GFP_KERNEL);
> > +	if (!vcstate)
> > +		return;
> > +
> > +	crtc->state = &vcstate->base;
> > +	crtc->state->crtc = crtc;
> > +}
> > +
> > +static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc)
> > +{
> > +	struct rockchip_crtc_state *vcstate, *old_vcstate;
> > +
> > +	old_vcstate = to_rockchip_crtc_state(crtc->state);
> > +
> > +	vcstate = kmemdup(old_vcstate, sizeof(*old_vcstate), GFP_KERNEL);
> > +	if (!vcstate)
> > +		return NULL;
> > +
> > +	__drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
> > +
> > +	return &vcstate->base;
> > +}
> > +
> > +static void vop2_crtc_destroy_state(struct drm_crtc *crtc,
> > +				    struct drm_crtc_state *state)
> > +{
> > +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state);
> > +
> > +	__drm_atomic_helper_crtc_destroy_state(&vcstate->base);
> > +	kfree(vcstate);
> > +}
> > +
> > +static const struct drm_crtc_funcs vop2_crtc_funcs = {
> > +	.set_config = drm_atomic_helper_set_config,
> > +	.page_flip = drm_atomic_helper_page_flip,
> > +	.destroy = drm_crtc_cleanup,
> > +	.reset = vop2_crtc_reset,
> > +	.atomic_duplicate_state = vop2_crtc_duplicate_state,
> > +	.atomic_destroy_state = vop2_crtc_destroy_state,
> > +	.enable_vblank = vop2_crtc_enable_vblank,
> > +	.disable_vblank = vop2_crtc_disable_vblank,
> > +};
> > +
> > +static irqreturn_t vop2_isr(int irq, void *data)
> > +{
> > +	struct vop2 *vop2 = data;
> > +	const struct vop2_data *vop2_data = vop2->data;
> > +	u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM];
> > +	int ret = IRQ_NONE;
> > +	int i;
> > +
> > +	/*
> > +	 * The irq is shared with the iommu. If the runtime-pm state of the
> > +	 * vop2-device is disabled the irq has to be targeted at the iommu.
> > +	 */
> > +	if (!pm_runtime_get_if_in_use(vop2->dev))
> > +		return IRQ_NONE;
> > +
> > +	for (i = 0; i < vop2_data->nr_vps; i++) {
> > +		struct vop2_video_port *vp = &vop2->vps[i];
> > +		struct drm_crtc *crtc = &vp->crtc;
> > +		u32 irqs;
> > +
> > +		irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
> > +		vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
> > +
> > +		if (irqs & VP_INT_DSP_HOLD_VALID) {
> > +			complete(&vp->dsp_hold_completion);
> > +			ret = IRQ_HANDLED;
> > +		}
> > +
> > +		if (irqs & VP_INT_FS_FIELD) {
> > +			drm_crtc_handle_vblank(crtc);
> > +			spin_lock(&crtc->dev->event_lock);
> > +			if (vp->event) {
> > +				u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
> > +
> > +				if (!(val & BIT(vp->id))) {
> > +					drm_crtc_send_vblank_event(crtc, vp->event);
> > +					vp->event = NULL;
> > +					drm_crtc_vblank_put(crtc);
> > +				}
> > +			}
> > +			spin_unlock(&crtc->dev->event_lock);
> > +
> > +			ret = IRQ_HANDLED;
> > +		}
> > +
> > +		if (irqs & VP_INT_POST_BUF_EMPTY) {
> > +			drm_err_ratelimited(vop2->drm,
> > +					    "POST_BUF_EMPTY irq err at vp%d\n",
> > +					    vp->id);
> > +			ret = IRQ_HANDLED;
> > +		}
> > +	}
> > +
> > +	axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS);
> > +	vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]);
> > +	axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS);
> > +	vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]);
> > +
> > +	for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) {
> > +		if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) {
> > +			drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n");
> > +			ret = IRQ_HANDLED;
> > +		}
> > +	}
> > +
> > +	pm_runtime_put(vop2->dev);
> > +
> > +	return ret;
> > +}
> > +
> > +static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win,
> > +			   unsigned long possible_crtcs)
> > +{
> > +	const struct vop2_win_data *win_data = win->data;
> > +	unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> > +				  BIT(DRM_MODE_BLEND_PREMULTI) |
> > +				  BIT(DRM_MODE_BLEND_COVERAGE);
> > +	int ret;
> > +
> > +	ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs,
> > +				       &vop2_plane_funcs, win_data->formats,
> > +				       win_data->nformats,
> > +				       win_data->format_modifiers,
> > +				       win->type, win_data->name);
> > +	if (ret) {
> > +		drm_err(vop2->drm, "failed to initialize plane %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs);
> > +
> > +	if (win->data->supported_rotations)
> > +		drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0,
> > +						   DRM_MODE_ROTATE_0 |
> > +						   win->data->supported_rotations);
> > +	drm_plane_create_alpha_property(&win->base);
> > +	drm_plane_create_blend_mode_property(&win->base, blend_caps);
> > +	drm_plane_create_zpos_property(&win->base, win->win_id, 0,
> > +				       vop2->registered_num_wins - 1);
> > +
> > +	return 0;
> > +}
> > +
> > +static struct vop2_video_port *get_activated_vp(struct vop2 *vop2, int n)
> > +{
> > +	int i, id = 0;
> > +
> > +	for (i = 0; i < vop2->data->nr_vps; i++) {
> > +		struct vop2_video_port *vp = &vop2->vps[i];
> > +
> > +		if (!vp->crtc.port)
> > +			continue;
> > +
> > +		if (n == id)
> > +			return vp;
> > +		id++;
> > +	}
> > +
> > +	return NULL;
> > +}
> > +
> > +#define NR_LAYERS 6
> > +
> > +static int vop2_create_crtc(struct vop2 *vop2)
> > +{
> > +	const struct vop2_data *vop2_data = vop2->data;
> > +	struct drm_device *drm = vop2->drm;
> > +	struct device *dev = vop2->dev;
> > +	struct drm_plane *plane;
> > +	struct device_node *port;
> > +	struct vop2_video_port *vp;
> > +	u32 possible_crtcs;
> > +	int i, nvp, nvps = 0;
> > +	int ret;
> > +
> > +	for (i = 0; i < vop2_data->nr_vps; i++) {
> > +		const struct vop2_video_port_data *vp_data;
> > +		struct device_node *np;
> > +		char dclk_name[9];
> > +
> > +		vp_data = &vop2_data->vp[i];
> > +		vp = &vop2->vps[i];
> > +		vp->vop2 = vop2;
> > +		vp->id = vp_data->id;
> > +		vp->regs = vp_data->regs;
> > +		vp->data = vp_data;
> > +
> > +		snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
> > +		vp->dclk = devm_clk_get(vop2->dev, dclk_name);
> > +		if (IS_ERR(vp->dclk)) {
> > +			drm_err(vop2->drm, "failed to get %s\n", dclk_name);
> > +			return PTR_ERR(vp->dclk);
> > +		}
> > +
> > +		np = of_graph_get_remote_node(dev->of_node, i, -1);
> > +		if (!np) {
> > +			drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
> > +			continue;
> > +		}
> > +		of_node_put(np);
> > +
> > +		port = of_graph_get_port_by_id(dev->of_node, i);
> > +		if (!port) {
> > +			drm_err(vop2->drm, "no port node found for video_port%d\n", i);
> > +			return -ENOENT;
> > +		}
> > +
> > +		vp->crtc.port = port;
> > +		nvps++;
> > +	}
> > +
> > +	nvp = 0;
> > +	for (i = 0; i < vop2->registered_num_wins; i++) {
> > +		struct vop2_win *win = &vop2->win[i];
> > +
> > +		if (win->type == DRM_PLANE_TYPE_PRIMARY) {
> > +			vp = get_activated_vp(vop2, nvp);
> > +
> > +			if (vp) {
> > +				possible_crtcs = BIT(nvp);
> > +				vp->primary_plane = win;
> > +
> > +				nvp++;
> > +			} else {
> > +				/* change the unused primary window to overlay window */
> > +				win->type = DRM_PLANE_TYPE_OVERLAY;
> > +			}
> > +		}
> > +
> > +		if (win->type == DRM_PLANE_TYPE_OVERLAY)
> > +			possible_crtcs = (1 << vop2_data->nr_vps) - 1;
> > +
> > +		ret = vop2_plane_init(vop2, win, possible_crtcs);
> > +
> > +		if (ret) {
> > +			drm_err(vop2->drm, "failed to init plane %s: %d\n",
> > +				win->data->name, ret);
> > +			return ret;
> > +		}
> > +	}
> > +
> > +	for (i = 0; i < vop2_data->nr_vps; i++) {
> > +		vp = &vop2->vps[i];
> > +
> > +		if (!vp->crtc.port)
> > +			continue;
> > +
> > +		plane = &vp->primary_plane->base;
> > +
> > +		ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL,
> > +						&vop2_crtc_funcs,
> > +						"video_port%d", vp->id);
> > +		if (ret) {
> > +			drm_err(vop2->drm, "crtc init for video_port%d failed\n", i);
> > +			return ret;
> > +		}
> > +
> > +		drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs);
> > +
> > +		init_completion(&vp->dsp_hold_completion);
> > +	}
> > +
> > +	for (i = 0; i < vop2->data->nr_vps; i++) {
> > +		struct vop2_video_port *vp = &vop2->vps[i];
> > +
> > +		if (vp->crtc.port)
> > +			vp->nlayers = NR_LAYERS / nvps;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void vop2_destroy_crtc(struct drm_crtc *crtc)
> > +{
> > +	of_node_put(crtc->port);
> > +
> > +	/*
> > +	 * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane()
> > +	 * references the CRTC.
> > +	 */
> > +	drm_crtc_cleanup(crtc);
> > +}
> > +
> > +static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = {
> > +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0),
> > +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5),
> > +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14),
> > +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18),
> > +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31),
> > +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31),
> > +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31),
> > +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31),
> > +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31),
> > +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19),
> > +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15),
> > +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31),
> > +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8),
> > +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9),
> > +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11),
> > +
> > +	/* Scale */
> > +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15),
> > +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31),
> > +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15),
> > +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13),
> > +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3),
> > +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28),
> > +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29),
> > +
> > +	/* cluster regs */
> > +	[VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1),
> > +	[VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0),
> > +	[VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7),
> > +
> > +	/* afbc regs */
> > +	[VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6),
> > +	[VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9),
> > +	[VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10),
> > +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4),
> > +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7),
> > +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8),
> > +	[VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31),
> > +	[VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31),
> > +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15),
> > +	[VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31),
> > +	[VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31),
> > +	[VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31),
> > +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31),
> > +	[VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0),
> > +	[VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1),
> > +	[VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2),
> > +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3),
> > +	[VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff },
> > +};
> > +
> > +static int vop2_cluster_init(struct vop2_win *win)
> > +{
> > +	struct vop2 *vop2 = win->vop2;
> > +	struct reg_field *cluster_regs;
> > +	int ret, i;
> > +
> > +	cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs),
> > +			       GFP_KERNEL);
> > +	if (!cluster_regs)
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++)
> > +		if (cluster_regs[i].reg != 0xffffffff)
> > +			cluster_regs[i].reg += win->offset;
> > +
> > +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> > +					    cluster_regs,
> > +					    ARRAY_SIZE(vop2_cluster_regs));
> > +
> > +	kfree(cluster_regs);
> > +
> > +	return ret;
> > +};
> > +
> > +static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = {
> > +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0),
> > +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5),
> > +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12),
> > +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14),
> > +	[VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16),
> > +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31),
> > +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31),
> > +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28),
> > +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31),
> > +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31),
> > +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17),
> > +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15),
> > +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31),
> > +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0),
> > +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1),
> > +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3),
> > +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31),
> > +	[VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29),
> > +	[VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31),
> > +
> > +	/* Scale */
> > +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15),
> > +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31),
> > +	[VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15),
> > +	[VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31),
> > +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1),
> > +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3),
> > +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5),
> > +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7),
> > +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9),
> > +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11),
> > +	[VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13),
> > +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15),
> > +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17),
> > +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8),
> > +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9),
> > +	[VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10),
> > +	[VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11),
> > +	[VOP2_WIN_XMIRROR] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff },
> > +};
> > +
> > +static int vop2_esmart_init(struct vop2_win *win)
> > +{
> > +	struct vop2 *vop2 = win->vop2;
> > +	struct reg_field *esmart_regs;
> > +	int ret, i;
> > +
> > +	esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs),
> > +			       GFP_KERNEL);
> > +	if (!esmart_regs)
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++)
> > +		if (esmart_regs[i].reg != 0xffffffff)
> > +			esmart_regs[i].reg += win->offset;
> > +
> > +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> > +					    esmart_regs,
> > +					    ARRAY_SIZE(vop2_esmart_regs));
> > +
> > +	kfree(esmart_regs);
> > +
> > +	return ret;
> > +};
> > +
> > +static int vop2_win_init(struct vop2 *vop2)
> > +{
> > +	const struct vop2_data *vop2_data = vop2->data;
> > +	struct vop2_win *win;
> > +	int i, ret;
> > +
> > +	for (i = 0; i < vop2_data->win_size; i++) {
> > +		const struct vop2_win_data *win_data = &vop2_data->win[i];
> > +
> > +		win = &vop2->win[i];
> > +		win->data = win_data;
> > +		win->type = win_data->type;
> > +		win->offset = win_data->base;
> > +		win->win_id = i;
> > +		win->vop2 = vop2;
> > +		if (vop2_cluster_window(win))
> > +			ret = vop2_cluster_init(win);
> > +		else
> > +			ret = vop2_esmart_init(win);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> > +	vop2->registered_num_wins = vop2_data->win_size;
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * The window registers are only updated when config done is written.
> > + * Until that they read back the old value. As we read-modify-write
> > + * these registers mark them as non-volatile. This makes sure we read
> > + * the new values from the regmap register cache.
> > + */
> > +static const struct regmap_range vop2_nonvolatile_range[] = {
> > +	regmap_reg_range(0x1000, 0x23ff),
> > +};
> > +
> > +static const struct regmap_access_table vop2_volatile_table = {
> > +	.no_ranges = vop2_nonvolatile_range,
> > +	.n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range),
> > +};
> > +
> > +static const struct regmap_config vop2_regmap_config = {
> > +	.reg_bits	= 32,
> > +	.val_bits	= 32,
> > +	.reg_stride	= 4,
> > +	.max_register	= 0x3000,
> > +	.name		= "vop2",
> > +	.volatile_table	= &vop2_volatile_table,
> > +	.cache_type	= REGCACHE_RBTREE,
> > +};
> > +
> > +static int vop2_bind(struct device *dev, struct device *master, void *data)
> > +{
> > +	struct platform_device *pdev = to_platform_device(dev);
> > +	const struct vop2_data *vop2_data;
> > +	struct drm_device *drm = data;
> > +	struct vop2 *vop2;
> > +	struct resource *res;
> > +	size_t alloc_size;
> > +	int ret;
> > +
> > +	vop2_data = of_device_get_match_data(dev);
> > +	if (!vop2_data)
> > +		return -ENODEV;
> > +
> > +	/* Allocate vop2 struct and its vop2_win array */
> > +	alloc_size = sizeof(*vop2) + sizeof(*vop2->win) * vop2_data->win_size;
> > +	vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
> > +	if (!vop2)
> > +		return -ENOMEM;
> > +
> > +	vop2->dev = dev;
> > +	vop2->data = vop2_data;
> > +	vop2->drm = drm;
> > +
> > +	dev_set_drvdata(dev, vop2);
> > +
> > +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
> > +	if (!res) {
> > +		drm_err(vop2->drm, "failed to get vop2 register byname\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	vop2->regs = devm_ioremap_resource(dev, res);
> > +	if (IS_ERR(vop2->regs))
> > +		return PTR_ERR(vop2->regs);
> > +	vop2->len = resource_size(res);
> > +
> > +	vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config);
> > +
> > +	ret = vop2_win_init(vop2);
> > +	if (ret)
> > +		return ret;
> > +
> > +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma_lut");
> > +	if (res) {
> > +		vop2->lut_regs = devm_ioremap_resource(dev, res);
> > +		if (IS_ERR(vop2->lut_regs))
> > +			return PTR_ERR(vop2->lut_regs);
> > +	}
> > +
> > +	vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
> > +
> > +	vop2->hclk = devm_clk_get(vop2->dev, "hclk");
> > +	if (IS_ERR(vop2->hclk)) {
> > +		drm_err(vop2->drm, "failed to get hclk source\n");
> > +		return PTR_ERR(vop2->hclk);
> > +	}
> > +
> > +	vop2->aclk = devm_clk_get(vop2->dev, "aclk");
> > +	if (IS_ERR(vop2->aclk)) {
> > +		drm_err(vop2->drm, "failed to get aclk source\n");
> > +		return PTR_ERR(vop2->aclk);
> > +	}
> > +
> > +	vop2->irq = platform_get_irq(pdev, 0);
> > +	if (vop2->irq < 0) {
> > +		drm_err(vop2->drm, "cannot find irq for vop2\n");
> > +		return vop2->irq;
> > +	}
> > +
> > +	mutex_init(&vop2->vop2_lock);
> > +
> > +	ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev);
> > +	if (ret) {
> > +		drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = vop2_create_crtc(vop2);
> > +	if (ret)
> > +		return ret;
> > +
> > +	pm_runtime_enable(&pdev->dev);
> > +
> > +	return 0;
> > +}
> > +
> > +static void vop2_unbind(struct device *dev, struct device *master, void *data)
> > +{
> > +	struct vop2 *vop2 = dev_get_drvdata(dev);
> > +	struct drm_device *drm = vop2->drm;
> > +	struct list_head *plane_list = &drm->mode_config.plane_list;
> > +	struct list_head *crtc_list = &drm->mode_config.crtc_list;
> > +	struct drm_crtc *crtc, *tmpc;
> > +	struct drm_plane *plane, *tmpp;
> > +
> > +	rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
> > +
> > +	pm_runtime_disable(dev);
> > +
> > +	list_for_each_entry_safe(plane, tmpp, plane_list, head)
> > +		drm_plane_cleanup(plane);
> > +
> > +	list_for_each_entry_safe(crtc, tmpc, crtc_list, head)
> > +		vop2_destroy_crtc(crtc);
> > +}
> > +
> > +const struct component_ops vop2_component_ops = {
> > +	.bind = vop2_bind,
> > +	.unbind = vop2_unbind,
> > +};
> > +EXPORT_SYMBOL_GPL(vop2_component_ops);
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> > new file mode 100644
> > index 0000000000000..c727093a06d68
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> > @@ -0,0 +1,477 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> > + * Author:Mark Yao <mark.yao@rock-chips.com>
> > + */
> > +
> > +#ifndef _ROCKCHIP_DRM_VOP2_H
> > +#define _ROCKCHIP_DRM_VOP2_H
> > +
> > +#include "rockchip_drm_vop.h"
> > +
> > +#include <linux/regmap.h>
> > +#include <drm/drm_modes.h>
> > +
> > +#define VOP_FEATURE_OUTPUT_10BIT        BIT(0)
> > +
> > +#define WIN_FEATURE_AFBDC		BIT(0)
> > +#define WIN_FEATURE_CLUSTER		BIT(1)
> > +
> > +/*
> > + *  the delay number of a window in different mode.
> > + */
> > +enum win_dly_mode {
> > +	VOP2_DLY_MODE_DEFAULT,   /**< default mode */
> > +	VOP2_DLY_MODE_HISO_S,    /** HDR in SDR out mode, as a SDR window */
> > +	VOP2_DLY_MODE_HIHO_H,    /** HDR in HDR out mode, as a HDR window */
> > +	VOP2_DLY_MODE_MAX,
> > +};
> > +
> > +struct vop_rect {
> > +	int width;
> > +	int height;
> > +};
> > +
> > +enum vop2_scale_up_mode {
> > +	VOP2_SCALE_UP_NRST_NBOR,
> > +	VOP2_SCALE_UP_BIL,
> > +	VOP2_SCALE_UP_BIC,
> > +};
> > +
> > +enum vop2_scale_down_mode {
> > +	VOP2_SCALE_DOWN_NRST_NBOR,
> > +	VOP2_SCALE_DOWN_BIL,
> > +	VOP2_SCALE_DOWN_AVG,
> > +};
> > +
> > +enum vop2_win_regs {
> > +	VOP2_WIN_ENABLE,
> > +	VOP2_WIN_FORMAT,
> > +	VOP2_WIN_CSC_MODE,
> > +	VOP2_WIN_XMIRROR,
> > +	VOP2_WIN_YMIRROR,
> > +	VOP2_WIN_RB_SWAP,
> > +	VOP2_WIN_UV_SWAP,
> > +	VOP2_WIN_ACT_INFO,
> > +	VOP2_WIN_DSP_INFO,
> > +	VOP2_WIN_DSP_ST,
> > +	VOP2_WIN_YRGB_MST,
> > +	VOP2_WIN_UV_MST,
> > +	VOP2_WIN_YRGB_VIR,
> > +	VOP2_WIN_UV_VIR,
> > +	VOP2_WIN_YUV_CLIP,
> > +	VOP2_WIN_Y2R_EN,
> > +	VOP2_WIN_R2Y_EN,
> > +	VOP2_WIN_COLOR_KEY,
> > +	VOP2_WIN_COLOR_KEY_EN,
> > +	VOP2_WIN_DITHER_UP,
> > +
> > +	/* scale regs */
> > +	VOP2_WIN_SCALE_YRGB_X,
> > +	VOP2_WIN_SCALE_YRGB_Y,
> > +	VOP2_WIN_SCALE_CBCR_X,
> > +	VOP2_WIN_SCALE_CBCR_Y,
> > +	VOP2_WIN_YRGB_HOR_SCL_MODE,
> > +	VOP2_WIN_YRGB_HSCL_FILTER_MODE,
> > +	VOP2_WIN_YRGB_VER_SCL_MODE,
> > +	VOP2_WIN_YRGB_VSCL_FILTER_MODE,
> > +	VOP2_WIN_CBCR_VER_SCL_MODE,
> > +	VOP2_WIN_CBCR_HSCL_FILTER_MODE,
> > +	VOP2_WIN_CBCR_HOR_SCL_MODE,
> > +	VOP2_WIN_CBCR_VSCL_FILTER_MODE,
> > +	VOP2_WIN_VSD_CBCR_GT2,
> > +	VOP2_WIN_VSD_CBCR_GT4,
> > +	VOP2_WIN_VSD_YRGB_GT2,
> > +	VOP2_WIN_VSD_YRGB_GT4,
> > +	VOP2_WIN_BIC_COE_SEL,
> > +
> > +	/* cluster regs */
> > +	VOP2_WIN_CLUSTER_ENABLE,
> > +	VOP2_WIN_AFBC_ENABLE,
> > +	VOP2_WIN_CLUSTER_LB_MODE,
> > +
> > +	/* afbc regs */
> > +	VOP2_WIN_AFBC_FORMAT,
> > +	VOP2_WIN_AFBC_RB_SWAP,
> > +	VOP2_WIN_AFBC_UV_SWAP,
> > +	VOP2_WIN_AFBC_AUTO_GATING_EN,
> > +	VOP2_WIN_AFBC_BLOCK_SPLIT_EN,
> > +	VOP2_WIN_AFBC_PIC_VIR_WIDTH,
> > +	VOP2_WIN_AFBC_TILE_NUM,
> > +	VOP2_WIN_AFBC_PIC_OFFSET,
> > +	VOP2_WIN_AFBC_PIC_SIZE,
> > +	VOP2_WIN_AFBC_DSP_OFFSET,
> > +	VOP2_WIN_AFBC_TRANSFORM_OFFSET,
> > +	VOP2_WIN_AFBC_HDR_PTR,
> > +	VOP2_WIN_AFBC_HALF_BLOCK_EN,
> > +	VOP2_WIN_AFBC_ROTATE_270,
> > +	VOP2_WIN_AFBC_ROTATE_90,
> > +	VOP2_WIN_MAX_REG,
> > +};
> > +
> > +struct vop2_win_data {
> > +	const char *name;
> > +	unsigned int phys_id;
> > +
> > +	u32 base;
> > +	enum drm_plane_type type;
> > +
> > +	u32 nformats;
> > +	const u32 *formats;
> > +	const uint64_t *format_modifiers;
> > +	const unsigned int supported_rotations;
> > +
> > +	/**
> > +	 * @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2
> > +	 */
> > +	unsigned int layer_sel_id;
> > +	uint64_t feature;
> > +
> > +	unsigned int max_upscale_factor;
> > +	unsigned int max_downscale_factor;
> > +	const u8 dly[VOP2_DLY_MODE_MAX];
> > +};
> > +
> > +struct vop2_video_port_data {
> > +	unsigned int id;
> > +	u32 feature;
> > +	u16 gamma_lut_len;
> > +	u16 cubic_lut_len;
> > +	struct vop_rect max_output;
> > +	const u8 pre_scan_max_dly[4];
> > +	const struct vop2_video_port_regs *regs;
> > +	unsigned int offset;
> > +};
> > +
> > +struct vop2_data {
> > +	u8 nr_vps;
> > +	const struct vop2_ctrl *ctrl;
> > +	const struct vop2_win_data *win;
> > +	const struct vop2_video_port_data *vp;
> > +	const struct vop_csc_table *csc_table;
> > +	struct vop_rect max_input;
> > +	struct vop_rect max_output;
> > +
> > +	unsigned int win_size;
> > +	unsigned int soc_id;
> > +};
> > +
> > +/* interrupt define */
> > +#define FS_NEW_INTR			BIT(4)
> > +#define ADDR_SAME_INTR			BIT(5)
> > +#define LINE_FLAG1_INTR			BIT(6)
> > +#define WIN0_EMPTY_INTR			BIT(7)
> > +#define WIN1_EMPTY_INTR			BIT(8)
> > +#define WIN2_EMPTY_INTR			BIT(9)
> > +#define WIN3_EMPTY_INTR			BIT(10)
> > +#define HWC_EMPTY_INTR			BIT(11)
> > +#define POST_BUF_EMPTY_INTR		BIT(12)
> > +#define PWM_GEN_INTR			BIT(13)
> > +#define DMA_FINISH_INTR			BIT(14)
> > +#define FS_FIELD_INTR			BIT(15)
> > +#define FE_INTR				BIT(16)
> > +#define WB_UV_FIFO_FULL_INTR		BIT(17)
> > +#define WB_YRGB_FIFO_FULL_INTR		BIT(18)
> > +#define WB_COMPLETE_INTR		BIT(19)
> > +
> > +/*
> > + * display output interface supported by rockchip lcdc
> > + */
> > +#define ROCKCHIP_OUT_MODE_P888		0
> > +#define ROCKCHIP_OUT_MODE_BT1120	0
> > +#define ROCKCHIP_OUT_MODE_P666		1
> > +#define ROCKCHIP_OUT_MODE_P565		2
> > +#define ROCKCHIP_OUT_MODE_BT656		5
> > +#define ROCKCHIP_OUT_MODE_S888		8
> > +#define ROCKCHIP_OUT_MODE_S888_DUMMY	12
> > +#define ROCKCHIP_OUT_MODE_YUV420	14
> > +/* for use special outface */
> > +#define ROCKCHIP_OUT_MODE_AAAA		15
> > +
> > +enum vop_csc_format {
> > +	CSC_BT601L,
> > +	CSC_BT709L,
> > +	CSC_BT601F,
> > +	CSC_BT2020,
> > +};
> > +
> > +enum src_factor_mode {
> > +	SRC_FAC_ALPHA_ZERO,
> > +	SRC_FAC_ALPHA_ONE,
> > +	SRC_FAC_ALPHA_DST,
> > +	SRC_FAC_ALPHA_DST_INVERSE,
> > +	SRC_FAC_ALPHA_SRC,
> > +	SRC_FAC_ALPHA_SRC_GLOBAL,
> > +};
> > +
> > +enum dst_factor_mode {
> > +	DST_FAC_ALPHA_ZERO,
> > +	DST_FAC_ALPHA_ONE,
> > +	DST_FAC_ALPHA_SRC,
> > +	DST_FAC_ALPHA_SRC_INVERSE,
> > +	DST_FAC_ALPHA_DST,
> > +	DST_FAC_ALPHA_DST_GLOBAL,
> > +};
> > +
> > +#define RK3568_GRF_VO_CON1			0x0364
> > +/* System registers definition */
> > +#define RK3568_REG_CFG_DONE			0x000
> > +#define RK3568_VERSION_INFO			0x004
> > +#define RK3568_SYS_AUTO_GATING_CTRL		0x008
> > +#define RK3568_SYS_AXI_LUT_CTRL			0x024
> > +#define RK3568_DSP_IF_EN			0x028
> > +#define RK3568_DSP_IF_CTRL			0x02c
> > +#define RK3568_DSP_IF_POL			0x030
> > +#define RK3568_WB_CTRL				0x40
> > +#define RK3568_WB_XSCAL_FACTOR			0x44
> > +#define RK3568_WB_YRGB_MST			0x48
> > +#define RK3568_WB_CBR_MST			0x4C
> > +#define RK3568_OTP_WIN_EN			0x050
> > +#define RK3568_LUT_PORT_SEL			0x058
> > +#define RK3568_SYS_STATUS0			0x060
> > +#define RK3568_VP_LINE_FLAG(vp)			(0x70 + (vp) * 0x4)
> > +#define RK3568_SYS0_INT_EN			0x80
> > +#define RK3568_SYS0_INT_CLR			0x84
> > +#define RK3568_SYS0_INT_STATUS			0x88
> > +#define RK3568_SYS1_INT_EN			0x90
> > +#define RK3568_SYS1_INT_CLR			0x94
> > +#define RK3568_SYS1_INT_STATUS			0x98
> > +#define RK3568_VP_INT_EN(vp)			(0xA0 + (vp) * 0x10)
> > +#define RK3568_VP_INT_CLR(vp)			(0xA4 + (vp) * 0x10)
> > +#define RK3568_VP_INT_STATUS(vp)		(0xA8 + (vp) * 0x10)
> > +#define RK3568_VP_INT_RAW_STATUS(vp)		(0xAC + (vp) * 0x10)
> > +
> > +/* Video Port registers definition */
> > +#define RK3568_VP_DSP_CTRL			0x00
> > +#define RK3568_VP_MIPI_CTRL			0x04
> > +#define RK3568_VP_COLOR_BAR_CTRL		0x08
> > +#define RK3568_VP_3D_LUT_CTRL			0x10
> > +#define RK3568_VP_3D_LUT_MST			0x20
> > +#define RK3568_VP_DSP_BG			0x2C
> > +#define RK3568_VP_PRE_SCAN_HTIMING		0x30
> > +#define RK3568_VP_POST_DSP_HACT_INFO		0x34
> > +#define RK3568_VP_POST_DSP_VACT_INFO		0x38
> > +#define RK3568_VP_POST_SCL_FACTOR_YRGB		0x3C
> > +#define RK3568_VP_POST_SCL_CTRL			0x40
> > +#define RK3568_VP_POST_DSP_VACT_INFO_F1		0x44
> > +#define RK3568_VP_DSP_HTOTAL_HS_END		0x48
> > +#define RK3568_VP_DSP_HACT_ST_END		0x4C
> > +#define RK3568_VP_DSP_VTOTAL_VS_END		0x50
> > +#define RK3568_VP_DSP_VACT_ST_END		0x54
> > +#define RK3568_VP_DSP_VS_ST_END_F1		0x58
> > +#define RK3568_VP_DSP_VACT_ST_END_F1		0x5C
> > +#define RK3568_VP_BCSH_CTRL			0x60
> > +#define RK3568_VP_BCSH_BCS			0x64
> > +#define RK3568_VP_BCSH_H			0x68
> > +#define RK3568_VP_BCSH_COLOR_BAR		0x6C
> > +
> > +/* Overlay registers definition    */
> > +#define RK3568_OVL_CTRL				0x600
> > +#define RK3568_OVL_LAYER_SEL			0x604
> > +#define RK3568_OVL_PORT_SEL			0x608
> > +#define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL	0x610
> > +#define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL	0x614
> > +#define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL	0x618
> > +#define RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL	0x61C
> > +#define RK3568_MIX0_SRC_COLOR_CTRL		0x650
> > +#define RK3568_MIX0_DST_COLOR_CTRL		0x654
> > +#define RK3568_MIX0_SRC_ALPHA_CTRL		0x658
> > +#define RK3568_MIX0_DST_ALPHA_CTRL		0x65C
> > +#define RK3568_HDR0_SRC_COLOR_CTRL		0x6C0
> > +#define RK3568_HDR0_DST_COLOR_CTRL		0x6C4
> > +#define RK3568_HDR0_SRC_ALPHA_CTRL		0x6C8
> > +#define RK3568_HDR0_DST_ALPHA_CTRL		0x6CC
> > +#define RK3568_VP_BG_MIX_CTRL(vp)		(0x6E0 + (vp) * 4)
> > +#define RK3568_CLUSTER_DLY_NUM			0x6F0
> > +#define RK3568_SMART_DLY_NUM			0x6F8
> > +
> > +/* Cluster register definition, offset relative to window base */
> > +#define RK3568_CLUSTER_WIN_CTRL0		0x00
> > +#define RK3568_CLUSTER_WIN_CTRL1		0x04
> > +#define RK3568_CLUSTER_WIN_YRGB_MST		0x10
> > +#define RK3568_CLUSTER_WIN_CBR_MST		0x14
> > +#define RK3568_CLUSTER_WIN_VIR			0x18
> > +#define RK3568_CLUSTER_WIN_ACT_INFO		0x20
> > +#define RK3568_CLUSTER_WIN_DSP_INFO		0x24
> > +#define RK3568_CLUSTER_WIN_DSP_ST		0x28
> > +#define RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB	0x30
> > +#define RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET	0x3C
> > +#define RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL	0x50
> > +#define RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE	0x54
> > +#define RK3568_CLUSTER_WIN_AFBCD_HDR_PTR	0x58
> > +#define RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH	0x5C
> > +#define RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE	0x60
> > +#define RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET	0x64
> > +#define RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET	0x68
> > +#define RK3568_CLUSTER_WIN_AFBCD_CTRL		0x6C
> > +
> > +#define RK3568_CLUSTER_CTRL			0x100
> > +
> > +/* (E)smart register definition, offset relative to window base */
> > +#define RK3568_SMART_CTRL0			0x00
> > +#define RK3568_SMART_CTRL1			0x04
> > +#define RK3568_SMART_REGION0_CTRL		0x10
> > +#define RK3568_SMART_REGION0_YRGB_MST		0x14
> > +#define RK3568_SMART_REGION0_CBR_MST		0x18
> > +#define RK3568_SMART_REGION0_VIR		0x1C
> > +#define RK3568_SMART_REGION0_ACT_INFO		0x20
> > +#define RK3568_SMART_REGION0_DSP_INFO		0x24
> > +#define RK3568_SMART_REGION0_DSP_ST		0x28
> > +#define RK3568_SMART_REGION0_SCL_CTRL		0x30
> > +#define RK3568_SMART_REGION0_SCL_FACTOR_YRGB	0x34
> > +#define RK3568_SMART_REGION0_SCL_FACTOR_CBR	0x38
> > +#define RK3568_SMART_REGION0_SCL_OFFSET		0x3C
> > +#define RK3568_SMART_REGION1_CTRL		0x40
> > +#define RK3568_SMART_REGION1_YRGB_MST		0x44
> > +#define RK3568_SMART_REGION1_CBR_MST		0x48
> > +#define RK3568_SMART_REGION1_VIR		0x4C
> > +#define RK3568_SMART_REGION1_ACT_INFO		0x50
> > +#define RK3568_SMART_REGION1_DSP_INFO		0x54
> > +#define RK3568_SMART_REGION1_DSP_ST		0x58
> > +#define RK3568_SMART_REGION1_SCL_CTRL		0x60
> > +#define RK3568_SMART_REGION1_SCL_FACTOR_YRGB	0x64
> > +#define RK3568_SMART_REGION1_SCL_FACTOR_CBR	0x68
> > +#define RK3568_SMART_REGION1_SCL_OFFSET		0x6C
> > +#define RK3568_SMART_REGION2_CTRL		0x70
> > +#define RK3568_SMART_REGION2_YRGB_MST		0x74
> > +#define RK3568_SMART_REGION2_CBR_MST		0x78
> > +#define RK3568_SMART_REGION2_VIR		0x7C
> > +#define RK3568_SMART_REGION2_ACT_INFO		0x80
> > +#define RK3568_SMART_REGION2_DSP_INFO		0x84
> > +#define RK3568_SMART_REGION2_DSP_ST		0x88
> > +#define RK3568_SMART_REGION2_SCL_CTRL		0x90
> > +#define RK3568_SMART_REGION2_SCL_FACTOR_YRGB	0x94
> > +#define RK3568_SMART_REGION2_SCL_FACTOR_CBR	0x98
> > +#define RK3568_SMART_REGION2_SCL_OFFSET		0x9C
> > +#define RK3568_SMART_REGION3_CTRL		0xA0
> > +#define RK3568_SMART_REGION3_YRGB_MST		0xA4
> > +#define RK3568_SMART_REGION3_CBR_MST		0xA8
> > +#define RK3568_SMART_REGION3_VIR		0xAC
> > +#define RK3568_SMART_REGION3_ACT_INFO		0xB0
> > +#define RK3568_SMART_REGION3_DSP_INFO		0xB4
> > +#define RK3568_SMART_REGION3_DSP_ST		0xB8
> > +#define RK3568_SMART_REGION3_SCL_CTRL		0xC0
> > +#define RK3568_SMART_REGION3_SCL_FACTOR_YRGB	0xC4
> > +#define RK3568_SMART_REGION3_SCL_FACTOR_CBR	0xC8
> > +#define RK3568_SMART_REGION3_SCL_OFFSET		0xCC
> > +#define RK3568_SMART_COLOR_KEY_CTRL		0xD0
> > +
> > +/* HDR register definition */
> > +#define RK3568_HDR_LUT_CTRL			0x2000
> > +#define RK3568_HDR_LUT_MST			0x2004
> > +#define RK3568_SDR2HDR_CTRL			0x2010
> > +#define RK3568_HDR2SDR_CTRL			0x2020
> > +#define RK3568_HDR2SDR_SRC_RANGE		0x2024
> > +#define RK3568_HDR2SDR_NORMFACEETF		0x2028
> > +#define RK3568_HDR2SDR_DST_RANGE		0x202C
> > +#define RK3568_HDR2SDR_NORMFACCGAMMA		0x2030
> > +#define RK3568_HDR_EETF_OETF_Y0			0x203C
> > +#define RK3568_HDR_SAT_Y0			0x20C0
> > +#define RK3568_HDR_EOTF_OETF_Y0			0x20F0
> > +#define RK3568_HDR_OETF_DX_POW1			0x2200
> > +#define RK3568_HDR_OETF_XN1			0x2300
> > +
> > +#define RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN		BIT(15)
> > +
> > +#define RK3568_VP_DSP_CTRL__STANDBY			BIT(31)
> > +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_MODE		BIT(20)
> > +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL		GENMASK(19, 18)
> > +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_EN		BIT(17)
> > +#define RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN		BIT(16)
> > +#define RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y		BIT(15)
> > +#define RK3568_VP_DSP_CTRL__DSP_RB_SWAP			BIT(9)
> > +#define RK3568_VP_DSP_CTRL__DSP_INTERLACE		BIT(7)
> > +#define RK3568_VP_DSP_CTRL__DSP_FILED_POL		BIT(6)
> > +#define RK3568_VP_DSP_CTRL__P2I_EN			BIT(5)
> > +#define RK3568_VP_DSP_CTRL__CORE_DCLK_DIV		BIT(4)
> > +#define RK3568_VP_DSP_CTRL__OUT_MODE			GENMASK(3, 0)
> > +
> > +#define RK3568_VP_POST_SCL_CTRL__VSCALEDOWN		BIT(1)
> > +#define RK3568_VP_POST_SCL_CTRL__HSCALEDOWN		BIT(0)
> > +
> > +#define RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX		GENMASK(26, 25)
> > +#define RK3568_SYS_DSP_INFACE_EN_LVDS1			BIT(24)
> > +#define RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX		GENMASK(22, 21)
> > +#define RK3568_SYS_DSP_INFACE_EN_MIPI1			BIT(20)
> > +#define RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX		GENMASK(19, 18)
> > +#define RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX		GENMASK(17, 16)
> > +#define RK3568_SYS_DSP_INFACE_EN_EDP_MUX		GENMASK(15, 14)
> > +#define RK3568_SYS_DSP_INFACE_EN_HDMI_MUX		GENMASK(11, 10)
> > +#define RK3568_SYS_DSP_INFACE_EN_RGB_MUX		GENMASK(9, 8)
> > +#define RK3568_SYS_DSP_INFACE_EN_LVDS0			BIT(5)
> > +#define RK3568_SYS_DSP_INFACE_EN_MIPI0			BIT(4)
> > +#define RK3568_SYS_DSP_INFACE_EN_EDP			BIT(3)
> > +#define RK3568_SYS_DSP_INFACE_EN_HDMI			BIT(1)
> > +#define RK3568_SYS_DSP_INFACE_EN_RGB			BIT(0)
> > +
> > +#define RK3568_DSP_IF_POL__MIPI_PIN_POL			GENMASK(19, 16)
> > +#define RK3568_DSP_IF_POL__EDP_PIN_POL			GENMASK(15, 12)
> > +#define RK3568_DSP_IF_POL__HDMI_PIN_POL			GENMASK(7, 4)
> > +#define RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL		GENMASK(3, 0)
> > +
> > +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2_PHASE_LOCK	BIT(5)
> > +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2			BIT(4)
> > +
> > +#define RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN	BIT(31)
> > +
> > +#define RK3568_DSP_IF_POL__CFG_DONE_IMD			BIT(28)
> > +
> > +#define VOP2_SYS_AXI_BUS_NUM				2
> > +
> > +#define VOP2_CLUSTER_YUV444_10				0x12
> > +
> > +#define VOP2_COLOR_KEY_MASK				BIT(31)
> > +
> > +#define RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD		BIT(28)
> > +
> > +#define RK3568_VP_BG_MIX_CTRL__BG_DLY			GENMASK(31, 24)
> > +
> > +#define RK3568_OVL_PORT_SEL__SEL_PORT			GENMASK(31, 16)
> > +#define RK3568_OVL_PORT_SEL__SMART1			GENMASK(31, 30)
> > +#define RK3568_OVL_PORT_SEL__SMART0			GENMASK(29, 28)
> > +#define RK3568_OVL_PORT_SEL__ESMART1			GENMASK(27, 26)
> > +#define RK3568_OVL_PORT_SEL__ESMART0			GENMASK(25, 24)
> > +#define RK3568_OVL_PORT_SEL__CLUSTER1			GENMASK(19, 18)
> > +#define RK3568_OVL_PORT_SEL__CLUSTER0			GENMASK(17, 16)
> > +#define RK3568_OVL_PORT_SET__PORT2_MUX			GENMASK(11, 8)
> > +#define RK3568_OVL_PORT_SET__PORT1_MUX			GENMASK(7, 4)
> > +#define RK3568_OVL_PORT_SET__PORT0_MUX			GENMASK(3, 0)
> > +#define RK3568_OVL_LAYER_SEL__LAYER(layer, x)		((x) << ((layer) * 4))
> > +
> > +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_1		GENMASK(31, 24)
> > +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_0		GENMASK(23, 16)
> > +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_1		GENMASK(15, 8)
> > +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_0		GENMASK(7, 0)
> > +
> > +#define RK3568_SMART_DLY_NUM__SMART1			GENMASK(31, 24)
> > +#define RK3568_SMART_DLY_NUM__SMART0			GENMASK(23, 16)
> > +#define RK3568_SMART_DLY_NUM__ESMART1			GENMASK(15, 8)
> > +#define RK3568_SMART_DLY_NUM__ESMART0			GENMASK(7, 0)
> > +
> > +#define VP_INT_DSP_HOLD_VALID	BIT(6)
> > +#define VP_INT_FS_FIELD		BIT(5)
> > +#define VP_INT_POST_BUF_EMPTY	BIT(4)
> > +#define VP_INT_LINE_FLAG1	BIT(3)
> > +#define VP_INT_LINE_FLAG0	BIT(2)
> > +#define VOP2_INT_BUS_ERRPR	BIT(1)
> > +#define VP_INT_FS		BIT(0)
> > +
> > +#define POLFLAG_DCLK_INV	BIT(3)
> > +
> > +enum vop2_layer_phy_id {
> > +	ROCKCHIP_VOP2_CLUSTER0 = 0,
> > +	ROCKCHIP_VOP2_CLUSTER1,
> > +	ROCKCHIP_VOP2_ESMART0,
> > +	ROCKCHIP_VOP2_ESMART1,
> > +	ROCKCHIP_VOP2_SMART0,
> > +	ROCKCHIP_VOP2_SMART1,
> > +	ROCKCHIP_VOP2_CLUSTER2,
> > +	ROCKCHIP_VOP2_CLUSTER3,
> > +	ROCKCHIP_VOP2_ESMART2,
> > +	ROCKCHIP_VOP2_ESMART3,
> > +	ROCKCHIP_VOP2_PHY_ID_INVALID = -1,
> > +};
> > +
> > +extern const struct component_ops vop2_component_ops;
> > +
> > +#endif /* _ROCKCHIP_DRM_VOP2_H */
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> > new file mode 100644
> > index 0000000000000..9bf0637bf8e26
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> > @@ -0,0 +1,281 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Copyright (C) Rockchip Electronics Co.Ltd
> > + * Author: Andy Yan <andy.yan@rock-chips.com>
> > + */
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/component.h>
> > +#include <linux/mod_devicetable.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/of.h>
> > +#include <drm/drm_fourcc.h>
> > +#include <drm/drm_plane.h>
> > +#include <drm/drm_print.h>
> > +
> > +#include "rockchip_drm_vop2.h"
> > +
> > +static const uint32_t formats_win_full_10bit[] = {
> > +	DRM_FORMAT_XRGB8888,
> > +	DRM_FORMAT_ARGB8888,
> > +	DRM_FORMAT_XBGR8888,
> > +	DRM_FORMAT_ABGR8888,
> > +	DRM_FORMAT_RGB888,
> > +	DRM_FORMAT_BGR888,
> > +	DRM_FORMAT_RGB565,
> > +	DRM_FORMAT_BGR565,
> > +	DRM_FORMAT_NV12,
> > +	DRM_FORMAT_NV16,
> > +	DRM_FORMAT_NV24,
> > +};
> > +
> > +static const uint32_t formats_win_full_10bit_yuyv[] = {
> > +	DRM_FORMAT_XRGB8888,
> > +	DRM_FORMAT_ARGB8888,
> > +	DRM_FORMAT_XBGR8888,
> > +	DRM_FORMAT_ABGR8888,
> > +	DRM_FORMAT_RGB888,
> > +	DRM_FORMAT_BGR888,
> > +	DRM_FORMAT_RGB565,
> > +	DRM_FORMAT_BGR565,
> > +	DRM_FORMAT_NV12,
> > +	DRM_FORMAT_NV16,
> > +	DRM_FORMAT_NV24,
> > +	DRM_FORMAT_YVYU,
> > +	DRM_FORMAT_VYUY,
> > +};
> > +
> > +static const uint32_t formats_win_lite[] = {
> > +	DRM_FORMAT_XRGB8888,
> > +	DRM_FORMAT_ARGB8888,
> > +	DRM_FORMAT_XBGR8888,
> > +	DRM_FORMAT_ABGR8888,
> > +	DRM_FORMAT_RGB888,
> > +	DRM_FORMAT_BGR888,
> > +	DRM_FORMAT_RGB565,
> > +	DRM_FORMAT_BGR565,
> > +};
> > +
> > +static const uint64_t format_modifiers[] = {
> > +	DRM_FORMAT_MOD_LINEAR,
> > +	DRM_FORMAT_MOD_INVALID,
> > +};
> > +
> > +static const uint64_t format_modifiers_afbc[] = {
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_SPARSE),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_YTR),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_CBR),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_YTR |
> > +				AFBC_FORMAT_MOD_SPARSE),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_CBR |
> > +				AFBC_FORMAT_MOD_SPARSE),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_YTR |
> > +				AFBC_FORMAT_MOD_CBR),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_YTR |
> > +				AFBC_FORMAT_MOD_CBR |
> > +				AFBC_FORMAT_MOD_SPARSE),
> > +
> > +	/* SPLIT mandates SPARSE, RGB modes mandates YTR */
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_YTR |
> > +				AFBC_FORMAT_MOD_SPARSE |
> > +				AFBC_FORMAT_MOD_SPLIT),
> > +	DRM_FORMAT_MOD_INVALID,
> > +};
> > +
> > +static const struct vop2_video_port_data rk3568_vop_video_ports[] = {
> > +	{
> > +		.id = 0,
> > +		.feature = VOP_FEATURE_OUTPUT_10BIT,
> > +		.gamma_lut_len = 1024,
> > +		.cubic_lut_len = 9 * 9 * 9,
> > +		.max_output = { 4096, 2304 },
> > +		.pre_scan_max_dly = { 69, 53, 53, 42 },
> > +		.offset = 0xc00,
> > +	}, {
> > +		.id = 1,
> > +		.gamma_lut_len = 1024,
> > +		.max_output = { 2048, 1536 },
> > +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> > +		.offset = 0xd00,
> > +	}, {
> > +		.id = 2,
> > +		.gamma_lut_len = 1024,
> > +		.max_output = { 1920, 1080 },
> > +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> > +		.offset = 0xe00,
> > +	},
> > +};
> > +
> > +/*
> > + * rk3568 vop with 2 cluster, 2 esmart win, 2 smart win.
> > + * Every cluster can work as 4K win or split into two win.
> > + * All win in cluster support AFBCD.
> > + *
> > + * Every esmart win and smart win support 4 Multi-region.
> > + *
> > + * Scale filter mode:
> > + *
> > + * * Cluster:  bicubic for horizontal scale up, others use bilinear
> > + * * ESmart:
> > + *    * nearest-neighbor/bilinear/bicubic for scale up
> > + *    * nearest-neighbor/bilinear/average for scale down
> > + *
> > + *
> > + * @TODO describe the wind like cpu-map dt nodes;
> > + */
> > +static const struct vop2_win_data rk3568_vop_win_data[] = {
> > +	{
> > +		.name = "Smart0-win0",
> > +		.phys_id = ROCKCHIP_VOP2_SMART0,
> > +		.base = 0x1c00,
> > +		.formats = formats_win_lite,
> > +		.nformats = ARRAY_SIZE(formats_win_lite),
> > +		.format_modifiers = format_modifiers,
> > +		.layer_sel_id = 3,
> > +		.supported_rotations = DRM_MODE_REFLECT_Y,
> > +		.type = DRM_PLANE_TYPE_PRIMARY,
> > +		.max_upscale_factor = 8,
> > +		.max_downscale_factor = 8,
> > +		.dly = { 20, 47, 41 },
> > +	}, {
> > +		.name = "Smart1-win0",
> > +		.phys_id = ROCKCHIP_VOP2_SMART1,
> > +		.formats = formats_win_lite,
> > +		.nformats = ARRAY_SIZE(formats_win_lite),
> > +		.format_modifiers = format_modifiers,
> > +		.base = 0x1e00,
> > +		.layer_sel_id = 7,
> > +		.supported_rotations = DRM_MODE_REFLECT_Y,
> > +		.type = DRM_PLANE_TYPE_PRIMARY,
> > +		.max_upscale_factor = 8,
> > +		.max_downscale_factor = 8,
> > +		.dly = { 20, 47, 41 },
> > +	}, {
> > +		.name = "Esmart1-win0",
> > +		.phys_id = ROCKCHIP_VOP2_ESMART1,
> > +		.formats = formats_win_full_10bit_yuyv,
> > +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> > +		.format_modifiers = format_modifiers,
> > +		.base = 0x1a00,
> > +		.layer_sel_id = 6,
> > +		.supported_rotations = DRM_MODE_REFLECT_Y,
> > +		.type = DRM_PLANE_TYPE_PRIMARY,
> > +		.max_upscale_factor = 8,
> > +		.max_downscale_factor = 8,
> > +		.dly = { 20, 47, 41 },
> > +	}, {
> > +		.name = "Esmart0-win0",
> > +		.phys_id = ROCKCHIP_VOP2_ESMART0,
> > +		.formats = formats_win_full_10bit_yuyv,
> > +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> > +		.format_modifiers = format_modifiers,
> > +		.base = 0x1800,
> > +		.layer_sel_id = 2,
> > +		.supported_rotations = DRM_MODE_REFLECT_Y,
> > +		.type = DRM_PLANE_TYPE_OVERLAY,
> > +		.max_upscale_factor = 8,
> > +		.max_downscale_factor = 8,
> > +		.dly = { 20, 47, 41 },
> > +	}, {
> > +		.name = "Cluster0-win0",
> > +		.phys_id = ROCKCHIP_VOP2_CLUSTER0,
> > +		.base = 0x1000,
> > +		.formats = formats_win_full_10bit,
> > +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> > +		.format_modifiers = format_modifiers_afbc,
> > +		.layer_sel_id = 0,
> > +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> > +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> > +		.max_upscale_factor = 4,
> > +		.max_downscale_factor = 4,
> > +		.dly = { 0, 27, 21 },
> > +		.type = DRM_PLANE_TYPE_OVERLAY,
> > +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> > +	}, {
> > +		.name = "Cluster1-win0",
> > +		.phys_id = ROCKCHIP_VOP2_CLUSTER1,
> > +		.base = 0x1200,
> > +		.formats = formats_win_full_10bit,
> > +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> > +		.format_modifiers = format_modifiers_afbc,
> > +		.layer_sel_id = 1,
> > +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> > +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> > +		.type = DRM_PLANE_TYPE_OVERLAY,
> > +		.max_upscale_factor = 4,
> > +		.max_downscale_factor = 4,
> > +		.dly = { 0, 27, 21 },
> > +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> > +	},
> > +};
> > +
> > +static const struct vop2_data rk3566_vop = {
> > +	.nr_vps = 3,
> > +	.max_input = { 4096, 2304 },
> > +	.max_output = { 4096, 2304 },
> > +	.vp = rk3568_vop_video_ports,
> > +	.win = rk3568_vop_win_data,
> > +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> > +	.soc_id = 3566,
> > +};
> > +
> > +static const struct vop2_data rk3568_vop = {
> > +	.nr_vps = 3,
> > +	.max_input = { 4096, 2304 },
> > +	.max_output = { 4096, 2304 },
> > +	.vp = rk3568_vop_video_ports,
> > +	.win = rk3568_vop_win_data,
> > +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> > +	.soc_id = 3568,
> > +};
> > +
> > +static const struct of_device_id vop2_dt_match[] = {
> > +	{
> > +		.compatible = "rockchip,rk3566-vop",
> > +		.data = &rk3566_vop,
> > +	}, {
> > +		.compatible = "rockchip,rk3568-vop",
> > +		.data = &rk3568_vop,
> > +	}, {
> > +	},
> > +};
> > +MODULE_DEVICE_TABLE(of, vop2_dt_match);
> > +
> > +static int vop2_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +
> > +	return component_add(dev, &vop2_component_ops);
> > +}
> > +
> > +static int vop2_remove(struct platform_device *pdev)
> > +{
> > +	component_del(&pdev->dev, &vop2_component_ops);
> > +
> > +	return 0;
> > +}
> > +
> > +struct platform_driver vop2_platform_driver = {
> > +	.probe = vop2_probe,
> > +	.remove = vop2_remove,
> > +	.driver = {
> > +		.name = "rockchip-vop2",
> > +		.of_match_table = of_match_ptr(vop2_dt_match),
> > +	},
> > +};
> 



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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-21 18:18       ` Lucas Stach
  0 siblings, 0 replies; 201+ messages in thread
From: Lucas Stach @ 2022-02-21 18:18 UTC (permalink / raw)
  To: Andy Yan, Sascha Hauer, dri-devel
  Cc: devicetree, Benjamin Gaignard, Heiko Stübner, Sandy Huang,
	Kever Yang, linux-rockchip, Michael Riesch, kernel, Peter Geis,
	tfiga, linux-arm-kernel

Hi Andy,

Am Montag, dem 21.02.2022 um 19:51 +0800 schrieb Andy Yan:
> Hi Sascha:
> 
> On 2/17/22 16:29, Sascha Hauer wrote:
> > From: Andy Yan <andy.yan@rock-chips.com>
> > 
> > The VOP2 unit is found on Rockchip SoCs beginning with rk3566/rk3568.
> > It replaces the VOP unit found in the older Rockchip SoCs.
> > 
> > This driver has been derived from the downstream Rockchip Kernel and
> > heavily modified:
> > 
> > - All nonstandard DRM properties have been removed
> > - dropped struct vop2_plane_state and pass around less data between
> >    functions
> > - Dropped all DRM_FORMAT_* not known on upstream
> > - rework register access to get rid of excessively used macros
> > - Drop all waiting for framesyncs
> 
> All the waiting sync in the downstream divers are try to fix special 
> problems,
> 
> and some of them inherited from upstream vop driver, for example: the 
> fb_unref_work,
> 
> It submitted by Tomas Figa to upstream vop driver to make the wait for 
> flip and asynchronously cursor work
> 
> right.
> 
> VOP2 share the same hardware design in vblank, so I don't think these 
> code are useless.

We've discussed this quite a bit internally before dropping this custom
frame synchronization. We believe that this is okay to do, as Sascha
also dropped a lot of code that would have required more
synchronization than what the common DRM atomic core code provides.

Fundamentally, a lot of the extra synchronization, also in the
upstream, VOP driver seems like it is working around the fact that the
vblank event might get sent out too early, so references to still
active framebuffers are dropped from the DRM core and userspace is
allowed to make further progress.

With this VOP2 submission, the vblank event is only armed at
atomic_flush time after all the HW programming is in place, which
ensures that even though we still race with the vblank IRQ, we only
loose this race in a predictable way and the only bad thing caused by
the race is a temporary hickup where a page-flip is deferred for one
more vblank cycle, but all the reference counts are still correct and
nothing gets freed prematurely.

Features not included in this submission, like dynamic assignment of
overlay planes to CRTCs, might need additional frame synchronization,
but at the current state of the driver any additional synchronization
is just cargo-cult and not actually needed.

Regards,
Lucas

> 
> [0] 
> https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-5-git-send-email-tfiga@chromium.org/
> 
> [1] 
> https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-6-git-send-email-tfiga@chromium.org/
> 
> [2] 
> https://patchwork.kernel.org/project/linux-rockchip/patch/1473857701-9250-4-git-send-email-tfiga@chromium.org/
> 
> 
> > 
> > The driver is tested with HDMI and MIPI-DSI display on a RK3568-EVB
> > board. Overlay support is tested with the modetest utility. AFBC support
> > on the cluster windows is tested with weston-simple-dmabuf-egl on
> > weston using the (yet to be upstreamed) panfrost driver support.
> > 
> > Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> > 
> > Notes:
> >      Changes since v5:
> >      - consistently use u8/u16/u32 rather than uint8_t/uint16_t/uint32_t
> >      - Use spin_lock rather than spin_lock_irqsave
> >      - replace printk with drm_dbg
> >      - break some overlong lines
> >      
> >      Changes since v4:
> >      - Avoid stack frame overflow by not allocating big array on the stack
> >      
> >      Changes since v3:
> >      - Sort includes
> >      - fix typos
> >      - Drop spinlock
> >      - Use regmap_set_bits()/regmap_clear_bits()
> >      - simplify vop2_scale_factor()
> >      - simplify vop2_afbc_transform_offset()
> >      
> >      Changes since v4:
> >      - Sort nodes alphabetically
> >      
> >      Changes since v3:
> >      - Fix HDMI connector type
> > 
> >   drivers/gpu/drm/rockchip/Kconfig             |    6 +
> >   drivers/gpu/drm/rockchip/Makefile            |    1 +
> >   drivers/gpu/drm/rockchip/rockchip_drm_drv.c  |    1 +
> >   drivers/gpu/drm/rockchip/rockchip_drm_drv.h  |    6 +-
> >   drivers/gpu/drm/rockchip/rockchip_drm_fb.c   |    2 +
> >   drivers/gpu/drm/rockchip/rockchip_drm_vop.h  |   15 +
> >   drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2708 ++++++++++++++++++
> >   drivers/gpu/drm/rockchip/rockchip_drm_vop2.h |  477 +++
> >   drivers/gpu/drm/rockchip/rockchip_vop2_reg.c |  281 ++
> >   9 files changed, 3496 insertions(+), 1 deletion(-)
> >   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> >   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> >   create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> > 
> > diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> > index b9b156308460a..4ff0043f0ee70 100644
> > --- a/drivers/gpu/drm/rockchip/Kconfig
> > +++ b/drivers/gpu/drm/rockchip/Kconfig
> > @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
> >   	  This selects support for the VOP driver. You should enable it
> >   	  on all older SoCs up to RK3399.
> >   
> > +config ROCKCHIP_VOP2
> > +	bool "Rockchip VOP2 driver"
> > +	help
> > +	  This selects support for the VOP2 driver. You should enable it
> > +	  on all newer SoCs beginning form RK3568.
> > +
> >   config ROCKCHIP_ANALOGIX_DP
> >   	bool "Rockchip specific extensions for Analogix DP driver"
> >   	depends on ROCKCHIP_VOP
> > diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> > index dfc5512fdb9f1..3ff7b21c04149 100644
> > --- a/drivers/gpu/drm/rockchip/Makefile
> > +++ b/drivers/gpu/drm/rockchip/Makefile
> > @@ -6,6 +6,7 @@
> >   rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
> >   		rockchip_drm_gem.o
> >   
> > +rockchipdrm-$(CONFIG_ROCKCHIP_VOP2) += rockchip_drm_vop2.o rockchip_vop2_reg.o
> >   rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
> >   rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
> >   rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> > index 82c8faf1fb6b8..95f6c5985fdd7 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> > @@ -459,6 +459,7 @@ static int __init rockchip_drm_init(void)
> >   
> >   	num_rockchip_sub_drivers = 0;
> >   	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
> > +	ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2);
> >   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
> >   				CONFIG_ROCKCHIP_LVDS);
> >   	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> > index d3e42410ae5da..5cb207aaaadd0 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> > @@ -18,7 +18,7 @@
> >   
> >   #define ROCKCHIP_MAX_FB_BUFFER	3
> >   #define ROCKCHIP_MAX_CONNECTOR	2
> > -#define ROCKCHIP_MAX_CRTC	2
> > +#define ROCKCHIP_MAX_CRTC	4
> >   
> >   struct drm_device;
> >   struct drm_connector;
> > @@ -31,6 +31,9 @@ struct rockchip_crtc_state {
> >   	int output_bpc;
> >   	int output_flags;
> >   	bool enable_afbc;
> > +	u32 bus_format;
> > +	u32 bus_flags;
> > +	int color_space;
> >   };
> >   #define to_rockchip_crtc_state(s) \
> >   		container_of(s, struct rockchip_crtc_state, base)
> > @@ -63,6 +66,7 @@ extern struct platform_driver rockchip_dp_driver;
> >   extern struct platform_driver rockchip_lvds_driver;
> >   extern struct platform_driver vop_platform_driver;
> >   extern struct platform_driver rk3066_hdmi_driver;
> > +extern struct platform_driver vop2_platform_driver;
> >   
> >   struct rockchip_encoder {
> >   	struct device_node *port;
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > index 3aa37e177667e..0d2cb4f3922b8 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > @@ -134,4 +134,6 @@ void rockchip_drm_mode_config_init(struct drm_device *dev)
> >   
> >   	dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
> >   	dev->mode_config.helper_private = &rockchip_mode_config_helpers;
> > +
> > +	dev->mode_config.normalize_zpos = true;
> >   }
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> > index 857d97cdc67c6..1e364d7b50e69 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> > @@ -54,9 +54,23 @@ struct vop_afbc {
> >   	struct vop_reg enable;
> >   	struct vop_reg win_sel;
> >   	struct vop_reg format;
> > +	struct vop_reg rb_swap;
> > +	struct vop_reg uv_swap;
> > +	struct vop_reg auto_gating_en;
> > +	struct vop_reg block_split_en;
> > +	struct vop_reg pic_vir_width;
> > +	struct vop_reg tile_num;
> >   	struct vop_reg hreg_block_split;
> > +	struct vop_reg pic_offset;
> >   	struct vop_reg pic_size;
> > +	struct vop_reg dsp_offset;
> > +	struct vop_reg transform_offset;
> >   	struct vop_reg hdr_ptr;
> > +	struct vop_reg half_block_en;
> > +	struct vop_reg xmirror;
> > +	struct vop_reg ymirror;
> > +	struct vop_reg rotate_270;
> > +	struct vop_reg rotate_90;
> >   	struct vop_reg rstn;
> >   };
> >   
> > @@ -410,4 +424,5 @@ static inline int scl_vop_cal_lb_mode(int width, bool is_yuv)
> >   }
> >   
> >   extern const struct component_ops vop_component_ops;
> > +
> >   #endif /* _ROCKCHIP_DRM_VOP_H */
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> > new file mode 100644
> > index 0000000000000..394dd6c583682
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> > @@ -0,0 +1,2708 @@
> > +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> > +/*
> > + * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
> > + * Author: Andy Yan <andy.yan@rock-chips.com>
> > + */
> > +#include <linux/bitfield.h>
> > +#include <linux/clk.h>
> > +#include <linux/component.h>
> > +#include <linux/delay.h>
> > +#include <linux/iopoll.h>
> > +#include <linux/kernel.h>
> > +#include <linux/mfd/syscon.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_graph.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <linux/swab.h>
> > +
> > +#include <drm/drm.h>
> > +#include <drm/drm_atomic.h>
> > +#include <drm/drm_atomic_uapi.h>
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_crtc_helper.h>
> > +#include <drm/drm_debugfs.h>
> > +#include <drm/drm_flip_work.h>
> > +#include <drm/drm_plane_helper.h>
> > +#include <drm/drm_probe_helper.h>
> > +#include <drm/drm_vblank.h>
> > +
> > +#include <uapi/linux/videodev2.h>
> > +#include <dt-bindings/soc/rockchip,vop2.h>
> > +
> > +#include "rockchip_drm_drv.h"
> > +#include "rockchip_drm_gem.h"
> > +#include "rockchip_drm_fb.h"
> > +#include "rockchip_drm_vop2.h"
> > +
> > +/*
> > + * VOP2 architecture
> > + *
> > + +----------+   +-------------+                                                        +-----------+
> > + |  Cluster |   | Sel 1 from 6|                                                        | 1 from 3  |
> > + |  window0 |   |    Layer0   |                                                        |    RGB    |
> > + +----------+   +-------------+              +---------------+    +-------------+      +-----------+
> > + +----------+   +-------------+              |N from 6 layers|    |             |
> > + |  Cluster |   | Sel 1 from 6|              |   Overlay0    +--->| Video Port0 |      +-----------+
> > + |  window1 |   |    Layer1   |              |               |    |             |      | 1 from 3  |
> > + +----------+   +-------------+              +---------------+    +-------------+      |   LVDS    |
> > + +----------+   +-------------+                                                        +-----------+
> > + |  Esmart  |   | Sel 1 from 6|
> > + |  window0 |   |   Layer2    |              +---------------+    +-------------+      +-----------+
> > + +----------+   +-------------+              |N from 6 Layers|    |             | +--> | 1 from 3  |
> > + +----------+   +-------------+   -------->  |   Overlay1    +--->| Video Port1 |      |   MIPI    |
> > + |  Esmart  |   | Sel 1 from 6|   -------->  |               |    |             |      +-----------+
> > + |  Window1 |   |   Layer3    |              +---------------+    +-------------+
> > + +----------+   +-------------+                                                        +-----------+
> > + +----------+   +-------------+                                                        | 1 from 3  |
> > + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |   HDMI    |
> > + |  Window0 |   |    Layer4   |              |N from 6 Layers|    |             |      +-----------+
> > + +----------+   +-------------+              |   Overlay2    +--->| Video Port2 |
> > + +----------+   +-------------+              |               |    |             |      +-----------+
> > + |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |  1 from 3 |
> > + |  Window1 |   |    Layer5   |                                                        |    eDP    |
> > + +----------+   +-------------+                                                        +-----------+
> > + *
> > + */
> > +
> > +enum vop2_data_format {
> > +	VOP2_FMT_ARGB8888 = 0,
> > +	VOP2_FMT_RGB888,
> > +	VOP2_FMT_RGB565,
> > +	VOP2_FMT_XRGB101010,
> > +	VOP2_FMT_YUV420SP,
> > +	VOP2_FMT_YUV422SP,
> > +	VOP2_FMT_YUV444SP,
> > +	VOP2_FMT_YUYV422 = 8,
> > +	VOP2_FMT_YUYV420,
> > +	VOP2_FMT_VYUY422,
> > +	VOP2_FMT_VYUY420,
> > +	VOP2_FMT_YUV420SP_TILE_8x4 = 0x10,
> > +	VOP2_FMT_YUV420SP_TILE_16x2,
> > +	VOP2_FMT_YUV422SP_TILE_8x4,
> > +	VOP2_FMT_YUV422SP_TILE_16x2,
> > +	VOP2_FMT_YUV420SP_10,
> > +	VOP2_FMT_YUV422SP_10,
> > +	VOP2_FMT_YUV444SP_10,
> > +};
> > +
> > +enum vop2_afbc_format {
> > +	VOP2_AFBC_FMT_RGB565,
> > +	VOP2_AFBC_FMT_ARGB2101010 = 2,
> > +	VOP2_AFBC_FMT_YUV420_10BIT,
> > +	VOP2_AFBC_FMT_RGB888,
> > +	VOP2_AFBC_FMT_ARGB8888,
> > +	VOP2_AFBC_FMT_YUV420 = 9,
> > +	VOP2_AFBC_FMT_YUV422 = 0xb,
> > +	VOP2_AFBC_FMT_YUV422_10BIT = 0xe,
> > +	VOP2_AFBC_FMT_INVALID = -1,
> > +};
> > +
> > +union vop2_alpha_ctrl {
> > +	u32 val;
> > +	struct {
> > +		/* [0:1] */
> > +		u32 color_mode:1;
> > +		u32 alpha_mode:1;
> > +		/* [2:3] */
> > +		u32 blend_mode:2;
> > +		u32 alpha_cal_mode:1;
> > +		/* [5:7] */
> > +		u32 factor_mode:3;
> > +		/* [8:9] */
> > +		u32 alpha_en:1;
> > +		u32 src_dst_swap:1;
> > +		u32 reserved:6;
> > +		/* [16:23] */
> > +		u32 glb_alpha:8;
> > +	} bits;
> > +};
> > +
> > +struct vop2_alpha {
> > +	union vop2_alpha_ctrl src_color_ctrl;
> > +	union vop2_alpha_ctrl dst_color_ctrl;
> > +	union vop2_alpha_ctrl src_alpha_ctrl;
> > +	union vop2_alpha_ctrl dst_alpha_ctrl;
> > +};
> > +
> > +struct vop2_alpha_config {
> > +	bool src_premulti_en;
> > +	bool dst_premulti_en;
> > +	bool src_pixel_alpha_en;
> > +	bool dst_pixel_alpha_en;
> > +	u16 src_glb_alpha_value;
> > +	u16 dst_glb_alpha_value;
> > +};
> > +
> > +struct vop2_win {
> > +	struct vop2 *vop2;
> > +	struct drm_plane base;
> > +	const struct vop2_win_data *data;
> > +	struct regmap_field *reg[VOP2_WIN_MAX_REG];
> > +
> > +	/**
> > +	 * @win_id: graphic window id, a cluster may be split into two
> > +	 * graphics windows.
> > +	 */
> > +	u8 win_id;
> > +	u8 delay;
> > +	u32 offset;
> > +
> > +	enum drm_plane_type type;
> > +};
> > +
> > +struct vop2_video_port {
> > +	struct drm_crtc crtc;
> > +	struct vop2 *vop2;
> > +	struct clk *dclk;
> > +	unsigned int id;
> > +	const struct vop2_video_port_regs *regs;
> > +	const struct vop2_video_port_data *data;
> > +
> > +	struct completion dsp_hold_completion;
> > +
> > +	/**
> > +	 * @win_mask: Bitmask of windows attached to the video port;
> > +	 */
> > +	u32 win_mask;
> > +
> > +	struct vop2_win *primary_plane;
> > +	struct drm_pending_vblank_event *event;
> > +
> > +	unsigned int nlayers;
> > +};
> > +
> > +struct vop2 {
> > +	struct device *dev;
> > +	struct drm_device *drm;
> > +	struct vop2_video_port vps[ROCKCHIP_MAX_CRTC];
> > +
> > +	const struct vop2_data *data;
> > +	/*
> > +	 * Number of windows that are registered as plane, may be less than the
> > +	 * total number of hardware windows.
> > +	 */
> > +	u32 registered_num_wins;
> > +
> > +	void __iomem *regs;
> > +	struct regmap *map;
> > +
> > +	struct regmap *grf;
> > +
> > +	/* physical map length of vop2 register */
> > +	u32 len;
> > +
> > +	void __iomem *lut_regs;
> > +
> > +	/* protects crtc enable/disable */
> > +	struct mutex vop2_lock;
> > +
> > +	int irq;
> > +
> > +	/*
> > +	 * Some global resources are shared between all video ports(crtcs), so
> > +	 * we need a ref counter here.
> > +	 */
> > +	unsigned int enable_count;
> > +	struct clk *hclk;
> > +	struct clk *aclk;
> > +
> > +	/* must be put at the end of the struct */
> > +	struct vop2_win win[];
> > +};
> > +
> > +static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
> > +{
> > +	return container_of(crtc, struct vop2_video_port, crtc);
> > +}
> > +
> > +static struct vop2_win *to_vop2_win(struct drm_plane *p)
> > +{
> > +	return container_of(p, struct vop2_win, base);
> > +}
> > +
> > +static void vop2_lock(struct vop2 *vop2)
> > +{
> > +	mutex_lock(&vop2->vop2_lock);
> > +}
> > +
> > +static void vop2_unlock(struct vop2 *vop2)
> > +{
> > +	mutex_unlock(&vop2->vop2_lock);
> > +}
> > +
> > +static void vop2_writel(struct vop2 *vop2, u32 offset, u32 v)
> > +{
> > +	regmap_write(vop2->map, offset, v);
> > +}
> > +
> > +static void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
> > +{
> > +	regmap_write(vp->vop2->map, vp->data->offset + offset, v);
> > +}
> > +
> > +static u32 vop2_readl(struct vop2 *vop2, u32 offset)
> > +{
> > +	u32 val;
> > +
> > +	regmap_read(vop2->map, offset, &val);
> > +
> > +	return val;
> > +}
> > +
> > +static void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v)
> > +{
> > +	regmap_field_write(win->reg[reg], v);
> > +}
> > +
> > +static bool vop2_cluster_window(const struct vop2_win *win)
> > +{
> > +	return win->data->feature & WIN_FEATURE_CLUSTER;
> > +}
> > +
> > +static void vop2_cfg_done(struct vop2_video_port *vp)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +
> > +	regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE,
> > +			BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> > +}
> > +
> > +static void vop2_win_disable(struct vop2_win *win)
> > +{
> > +	vop2_win_write(win, VOP2_WIN_ENABLE, 0);
> > +
> > +	if (vop2_cluster_window(win))
> > +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0);
> > +}
> > +
> > +static enum vop2_data_format vop2_convert_format(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_XRGB8888:
> > +	case DRM_FORMAT_ARGB8888:
> > +	case DRM_FORMAT_XBGR8888:
> > +	case DRM_FORMAT_ABGR8888:
> > +		return VOP2_FMT_ARGB8888;
> > +	case DRM_FORMAT_RGB888:
> > +	case DRM_FORMAT_BGR888:
> > +		return VOP2_FMT_RGB888;
> > +	case DRM_FORMAT_RGB565:
> > +	case DRM_FORMAT_BGR565:
> > +		return VOP2_FMT_RGB565;
> > +	case DRM_FORMAT_NV12:
> > +		return VOP2_FMT_YUV420SP;
> > +	case DRM_FORMAT_NV16:
> > +		return VOP2_FMT_YUV422SP;
> > +	case DRM_FORMAT_NV24:
> > +		return VOP2_FMT_YUV444SP;
> > +	case DRM_FORMAT_YUYV:
> > +	case DRM_FORMAT_YVYU:
> > +		return VOP2_FMT_VYUY422;
> > +	case DRM_FORMAT_VYUY:
> > +	case DRM_FORMAT_UYVY:
> > +		return VOP2_FMT_YUYV422;
> > +	default:
> > +		DRM_ERROR("unsupported format[%08x]\n", format);
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static enum vop2_afbc_format vop2_convert_afbc_format(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_XRGB8888:
> > +	case DRM_FORMAT_ARGB8888:
> > +	case DRM_FORMAT_XBGR8888:
> > +	case DRM_FORMAT_ABGR8888:
> > +		return VOP2_AFBC_FMT_ARGB8888;
> > +	case DRM_FORMAT_RGB888:
> > +	case DRM_FORMAT_BGR888:
> > +		return VOP2_AFBC_FMT_RGB888;
> > +	case DRM_FORMAT_RGB565:
> > +	case DRM_FORMAT_BGR565:
> > +		return VOP2_AFBC_FMT_RGB565;
> > +	case DRM_FORMAT_NV12:
> > +		return VOP2_AFBC_FMT_YUV420;
> > +	case DRM_FORMAT_NV16:
> > +		return VOP2_AFBC_FMT_YUV422;
> > +	default:
> > +		return VOP2_AFBC_FMT_INVALID;
> > +	}
> > +
> > +	return VOP2_AFBC_FMT_INVALID;
> > +}
> > +
> > +static bool vop2_win_rb_swap(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_XBGR8888:
> > +	case DRM_FORMAT_ABGR8888:
> > +	case DRM_FORMAT_BGR888:
> > +	case DRM_FORMAT_BGR565:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool vop2_afbc_rb_swap(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_NV24:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool vop2_afbc_uv_swap(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_NV12:
> > +	case DRM_FORMAT_NV16:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool vop2_win_uv_swap(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_NV12:
> > +	case DRM_FORMAT_NV16:
> > +	case DRM_FORMAT_NV24:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool vop2_win_dither_up(u32 format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_BGR565:
> > +	case DRM_FORMAT_RGB565:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
> > +{
> > +	/*
> > +	 * FIXME:
> > +	 *
> > +	 * There is no media type for YUV444 output,
> > +	 * so when out_mode is AAAA or P888, assume output is YUV444 on
> > +	 * yuv format.
> > +	 *
> > +	 * From H/W testing, YUV444 mode need a rb swap.
> > +	 */
> > +	if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 ||
> > +	    bus_format == MEDIA_BUS_FMT_VYUY8_1X16 ||
> > +	    bus_format == MEDIA_BUS_FMT_YVYU8_2X8 ||
> > +	    bus_format == MEDIA_BUS_FMT_VYUY8_2X8 ||
> > +	    ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
> > +	      bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
> > +	     (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
> > +	      output_mode == ROCKCHIP_OUT_MODE_P888)))
> > +		return true;
> > +	else
> > +		return false;
> > +}
> > +
> > +static bool is_yuv_output(u32 bus_format)
> > +{
> > +	switch (bus_format) {
> > +	case MEDIA_BUS_FMT_YUV8_1X24:
> > +	case MEDIA_BUS_FMT_YUV10_1X30:
> > +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> > +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> > +	case MEDIA_BUS_FMT_YUYV8_2X8:
> > +	case MEDIA_BUS_FMT_YVYU8_2X8:
> > +	case MEDIA_BUS_FMT_UYVY8_2X8:
> > +	case MEDIA_BUS_FMT_VYUY8_2X8:
> > +	case MEDIA_BUS_FMT_YUYV8_1X16:
> > +	case MEDIA_BUS_FMT_YVYU8_1X16:
> > +	case MEDIA_BUS_FMT_UYVY8_1X16:
> > +	case MEDIA_BUS_FMT_VYUY8_1X16:
> > +		return true;
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +
> > +static bool rockchip_afbc(struct drm_plane *plane, u64 modifier)
> > +{
> > +	int i;
> > +
> > +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> > +		return false;
> > +
> > +	for (i = 0 ; i < plane->modifier_count; i++)
> > +		if (plane->modifiers[i] == modifier)
> > +			return true;
> > +
> > +	return false;
> > +
> > +}
> > +
> > +static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
> > +					u64 modifier)
> > +{
> > +	struct vop2_win *win = to_vop2_win(plane);
> > +	struct vop2 *vop2 = win->vop2;
> > +
> > +	if (modifier == DRM_FORMAT_MOD_INVALID)
> > +		return false;
> > +
> > +	if (modifier == DRM_FORMAT_MOD_LINEAR)
> > +		return true;
> > +
> > +	if (!rockchip_afbc(plane, modifier)) {
> > +		drm_err(vop2->drm, "Unsupported format modifier 0x%llx\n",
> > +			modifier);
> > +
> > +		return false;
> > +	}
> > +
> > +	return vop2_convert_afbc_format(format) >= 0;
> > +}
> > +
> > +static u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate,
> > +				      bool afbc_half_block_en)
> > +{
> > +	struct drm_rect *src = &pstate->src;
> > +	struct drm_framebuffer *fb = pstate->fb;
> > +	u32 bpp = fb->format->cpp[0] * 8;
> > +	u32 vir_width = (fb->pitches[0] << 3) / bpp;
> > +	u32 width = drm_rect_width(src) >> 16;
> > +	u32 height = drm_rect_height(src) >> 16;
> > +	u32 act_xoffset = src->x1 >> 16;
> > +	u32 act_yoffset = src->y1 >> 16;
> > +	u32 align16_crop = 0;
> > +	u32 align64_crop = 0;
> > +	u32 height_tmp;
> > +	u8 tx, ty;
> > +	u8 bottom_crop_line_num = 0;
> > +
> > +	/* 16 pixel align */
> > +	if (height & 0xf)
> > +		align16_crop = 16 - (height & 0xf);
> > +
> > +	height_tmp = height + align16_crop;
> > +
> > +	/* 64 pixel align */
> > +	if (height_tmp & 0x3f)
> > +		align64_crop = 64 - (height_tmp & 0x3f);
> > +
> > +	bottom_crop_line_num = align16_crop + align64_crop;
> > +
> > +	switch (pstate->rotation &
> > +		(DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y |
> > +		 DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) {
> > +	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
> > +		tx = 16 - ((act_xoffset + width) & 0xf);
> > +		ty = bottom_crop_line_num - act_yoffset;
> > +		break;
> > +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90:
> > +		tx = bottom_crop_line_num - act_yoffset;
> > +		ty = vir_width - width - act_xoffset;
> > +		break;
> > +	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270:
> > +		tx = act_yoffset;
> > +		ty = act_xoffset;
> > +		break;
> > +	case DRM_MODE_REFLECT_X:
> > +		tx = 16 - ((act_xoffset + width) & 0xf);
> > +		ty = act_yoffset;
> > +		break;
> > +	case DRM_MODE_REFLECT_Y:
> > +		tx = act_xoffset;
> > +		ty = bottom_crop_line_num - act_yoffset;
> > +		break;
> > +	case DRM_MODE_ROTATE_90:
> > +		tx = bottom_crop_line_num - act_yoffset;
> > +		ty = act_xoffset;
> > +		break;
> > +	case DRM_MODE_ROTATE_270:
> > +		tx = act_yoffset;
> > +		ty = vir_width - width - act_xoffset;
> > +		break;
> > +	case 0:
> > +		tx = act_xoffset;
> > +		ty = act_yoffset;
> > +		break;
> > +	}
> > +
> > +	if (afbc_half_block_en)
> > +		ty &= 0x7f;
> > +
> > +#define TRANSFORM_XOFFSET GENMASK(7, 0)
> > +#define TRANSFORM_YOFFSET GENMASK(23, 16)
> > +	return FIELD_PREP(TRANSFORM_XOFFSET, tx) |
> > +		FIELD_PREP(TRANSFORM_YOFFSET, ty);
> > +}
> > +
> > +/*
> > + * A Cluster window has 2048 x 16 line buffer, which can
> > + * works at 2048 x 16(Full) or 4096 x 8 (Half) mode.
> > + * for Cluster_lb_mode register:
> > + * 0: half mode, for plane input width range 2048 ~ 4096
> > + * 1: half mode, for cluster work at 2 * 2048 plane mode
> > + * 2: half mode, for rotate_90/270 mode
> > + *
> > + */
> > +static int vop2_get_cluster_lb_mode(struct vop2_win *win,
> > +				    struct drm_plane_state *pstate)
> > +{
> > +	if ((pstate->rotation & DRM_MODE_ROTATE_270) ||
> > +	    (pstate->rotation & DRM_MODE_ROTATE_90))
> > +		return 2;
> > +	else
> > +		return 0;
> > +}
> > +
> > +static u16 vop2_scale_factor(u32 src, u32 dst)
> > +{
> > +	u32 fac;
> > +	int shift;
> > +
> > +	if (src == dst)
> > +		return 0;
> > +
> > +	if (dst < 2)
> > +		return U16_MAX;
> > +
> > +	if (src < 2)
> > +		return 0;
> > +
> > +	if (src > dst)
> > +		shift = 12;
> > +	else
> > +		shift = 16;
> > +
> > +	src--;
> > +	dst--;
> > +
> > +	fac = DIV_ROUND_UP(src << shift, dst) - 1;
> > +
> > +	if (fac > U16_MAX)
> > +		return U16_MAX;
> > +
> > +	return fac;
> > +}
> > +
> > +static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
> > +			     u32 src_w, u32 src_h, u32 dst_w,
> > +			     u32 dst_h, u32 pixel_format)
> > +{
> > +	const struct drm_format_info *info;
> > +	u16 hor_scl_mode, ver_scl_mode;
> > +	u16 hscl_filter_mode, vscl_filter_mode;
> > +	u8 gt2 = 0;
> > +	u8 gt4 = 0;
> > +	u32 val;
> > +
> > +	info = drm_format_info(pixel_format);
> > +
> > +	if (src_h >= (4 * dst_h)) {
> > +		gt4 = 1;
> > +		src_h >>= 2;
> > +	} else if (src_h >= (2 * dst_h)) {
> > +		gt2 = 1;
> > +		src_h >>= 1;
> > +	}
> > +
> > +	hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> > +	ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> > +
> > +	if (hor_scl_mode == SCALE_UP)
> > +		hscl_filter_mode = VOP2_SCALE_UP_BIC;
> > +	else
> > +		hscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> > +
> > +	if (ver_scl_mode == SCALE_UP)
> > +		vscl_filter_mode = VOP2_SCALE_UP_BIL;
> > +	else
> > +		vscl_filter_mode = VOP2_SCALE_DOWN_BIL;
> > +
> > +	/*
> > +	 * RK3568 VOP Esmart/Smart dsp_w should be even pixel
> > +	 * at scale down mode
> > +	 */
> > +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> > +		if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) {
> > +			drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n",
> > +				win->data->name, dst_w);
> > +			dst_w++;
> > +		}
> > +	}
> > +
> > +	val = vop2_scale_factor(src_w, dst_w);
> > +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val);
> > +	val = vop2_scale_factor(src_h, dst_h);
> > +	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val);
> > +
> > +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4);
> > +	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2);
> > +
> > +	vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode);
> > +	vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode);
> > +
> > +	if (vop2_cluster_window(win))
> > +		return;
> > +
> > +	vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode);
> > +	vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode);
> > +
> > +	if (info->is_yuv) {
> > +		src_w /= info->hsub;
> > +		src_h /= info->vsub;
> > +
> > +		gt4 = gt2 = 0;
> > +
> > +		if (src_h >= (4 * dst_h)) {
> > +			gt4 = 1;
> > +			src_h >>= 2;
> > +		} else if (src_h >= (2 * dst_h)) {
> > +			gt2 = 1;
> > +			src_h >>= 1;
> > +		}
> > +
> > +		hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
> > +		ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
> > +
> > +		val = vop2_scale_factor(src_w, dst_w);
> > +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val);
> > +
> > +		val = vop2_scale_factor(src_h, dst_h);
> > +		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val);
> > +
> > +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4);
> > +		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2);
> > +		vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode);
> > +		vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode);
> > +		vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode);
> > +		vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode);
> > +	}
> > +}
> > +
> > +static int vop2_convert_csc_mode(int csc_mode)
> > +{
> > +	switch (csc_mode) {
> > +	case V4L2_COLORSPACE_SMPTE170M:
> > +	case V4L2_COLORSPACE_470_SYSTEM_M:
> > +	case V4L2_COLORSPACE_470_SYSTEM_BG:
> > +		return CSC_BT601L;
> > +	case V4L2_COLORSPACE_REC709:
> > +	case V4L2_COLORSPACE_SMPTE240M:
> > +	case V4L2_COLORSPACE_DEFAULT:
> > +		return CSC_BT709L;
> > +	case V4L2_COLORSPACE_JPEG:
> > +		return CSC_BT601F;
> > +	case V4L2_COLORSPACE_BT2020:
> > +		return CSC_BT2020;
> > +	default:
> > +		return CSC_BT709L;
> > +	}
> > +}
> > +
> > +/*
> > + * colorspace path:
> > + *      Input        Win csc                     Output
> > + * 1. YUV(2020)  --> Y2R->2020To709->R2Y   --> YUV_OUTPUT(601/709)
> > + *    RGB        --> R2Y                  __/
> > + *
> > + * 2. YUV(2020)  --> bypasss               --> YUV_OUTPUT(2020)
> > + *    RGB        --> 709To2020->R2Y       __/
> > + *
> > + * 3. YUV(2020)  --> Y2R->2020To709        --> RGB_OUTPUT(709)
> > + *    RGB        --> R2Y                  __/
> > + *
> > + * 4. YUV(601/709)-> Y2R->709To2020->R2Y   --> YUV_OUTPUT(2020)
> > + *    RGB        --> 709To2020->R2Y       __/
> > + *
> > + * 5. YUV(601/709)-> bypass                --> YUV_OUTPUT(709)
> > + *    RGB        --> R2Y                  __/
> > + *
> > + * 6. YUV(601/709)-> bypass                --> YUV_OUTPUT(601)
> > + *    RGB        --> R2Y(601)             __/
> > + *
> > + * 7. YUV        --> Y2R(709)              --> RGB_OUTPUT(709)
> > + *    RGB        --> bypass               __/
> > + *
> > + * 8. RGB        --> 709To2020->R2Y        --> YUV_OUTPUT(2020)
> > + *
> > + * 9. RGB        --> R2Y(709)              --> YUV_OUTPUT(709)
> > + *
> > + * 10. RGB       --> R2Y(601)              --> YUV_OUTPUT(601)
> > + *
> > + * 11. RGB       --> bypass                --> RGB_OUTPUT(709)
> > + */
> > +
> > +static void vop2_setup_csc_mode(struct vop2_video_port *vp,
> > +				struct vop2_win *win,
> > +				struct drm_plane_state *pstate)
> > +{
> > +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
> > +	int is_input_yuv = pstate->fb->format->is_yuv;
> > +	int is_output_yuv = is_yuv_output(vcstate->bus_format);
> > +	int input_csc = V4L2_COLORSPACE_DEFAULT;
> > +	int output_csc = vcstate->color_space;
> > +	bool r2y_en, y2r_en;
> > +	int csc_mode;
> > +
> > +	if (is_input_yuv && !is_output_yuv) {
> > +		y2r_en = true;
> > +		r2y_en = false;
> > +		csc_mode = vop2_convert_csc_mode(input_csc);
> > +	} else if (!is_input_yuv && is_output_yuv) {
> > +		y2r_en = false;
> > +		r2y_en = true;
> > +		csc_mode = vop2_convert_csc_mode(output_csc);
> > +	} else {
> > +		y2r_en = false;
> > +		r2y_en = false;
> > +		csc_mode = false;
> > +	}
> > +
> > +	vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en);
> > +	vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en);
> > +	vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
> > +}
> > +
> > +static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +
> > +	vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq);
> > +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq);
> > +}
> > +
> > +static void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +
> > +	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16);
> > +}
> > +
> > +static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
> > +{
> > +	int ret;
> > +
> > +	ret = clk_prepare_enable(vop2->hclk);
> > +	if (ret < 0) {
> > +		drm_err(vop2->drm, "failed to enable hclk - %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = clk_prepare_enable(vop2->aclk);
> > +	if (ret < 0) {
> > +		drm_err(vop2->drm, "failed to enable aclk - %d\n", ret);
> > +		goto err;
> > +	}
> > +
> > +	return 0;
> > +err:
> > +	clk_disable_unprepare(vop2->hclk);
> > +
> > +	return ret;
> > +}
> > +
> > +static void vop2_enable(struct vop2 *vop2)
> > +{
> > +	int ret;
> > +
> > +	ret = pm_runtime_get_sync(vop2->dev);
> > +	if (ret < 0) {
> > +		drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret);
> > +		return;
> > +	}
> > +
> > +	ret = vop2_core_clks_prepare_enable(vop2);
> > +	if (ret) {
> > +		pm_runtime_put_sync(vop2->dev);
> > +		return;
> > +	}
> > +
> > +	if (vop2->data->soc_id == 3566)
> > +		vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
> > +
> > +	vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
> > +
> > +	/*
> > +	 * Disable auto gating, this is a workaround to
> > +	 * avoid display image shift when a window enabled.
> > +	 */
> > +	regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL,
> > +			  RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN);
> > +
> > +	vop2_writel(vop2, RK3568_SYS0_INT_CLR,
> > +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> > +	vop2_writel(vop2, RK3568_SYS0_INT_EN,
> > +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> > +	vop2_writel(vop2, RK3568_SYS1_INT_CLR,
> > +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> > +	vop2_writel(vop2, RK3568_SYS1_INT_EN,
> > +		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
> > +}
> > +
> > +static void vop2_disable(struct vop2 *vop2)
> > +{
> > +	pm_runtime_put_sync(vop2->dev);
> > +
> > +	clk_disable_unprepare(vop2->aclk);
> > +	clk_disable_unprepare(vop2->hclk);
> > +}
> > +
> > +static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
> > +				     struct drm_atomic_state *state)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct vop2 *vop2 = vp->vop2;
> > +	int ret;
> > +
> > +	vop2_lock(vop2);
> > +
> > +	drm_crtc_vblank_off(crtc);
> > +
> > +	/*
> > +	 * Vop standby will take effect at end of current frame,
> > +	 * if dsp hold valid irq happen, it means standby complete.
> > +	 *
> > +	 * we must wait standby complete when we want to disable aclk,
> > +	 * if not, memory bus maybe dead.
> > +	 */
> > +	reinit_completion(&vp->dsp_hold_completion);
> > +
> > +	vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
> > +
> > +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
> > +
> > +	ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
> > +					  msecs_to_jiffies(50));
> > +	if (!ret)
> > +		drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
> > +
> > +	vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
> > +
> > +	clk_disable_unprepare(vp->dclk);
> > +
> > +	vop2->enable_count--;
> > +
> > +	if (!vop2->enable_count)
> > +		vop2_disable(vop2);
> > +
> > +	vop2_unlock(vop2);
> > +
> > +	if (crtc->state->event && !crtc->state->active) {
> > +		spin_lock_irq(&crtc->dev->event_lock);
> > +		drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > +		spin_unlock_irq(&crtc->dev->event_lock);
> > +
> > +		crtc->state->event = NULL;
> > +	}
> > +}
> > +
> > +static int vop2_plane_atomic_check(struct drm_plane *plane,
> > +				   struct drm_atomic_state *astate)
> > +{
> > +	struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane);
> > +	struct drm_framebuffer *fb = pstate->fb;
> > +	struct drm_crtc *crtc = pstate->crtc;
> > +	struct drm_crtc_state *cstate;
> > +	struct vop2_video_port *vp;
> > +	struct vop2 *vop2;
> > +	const struct vop2_data *vop2_data;
> > +	struct drm_rect *dest = &pstate->dst;
> > +	struct drm_rect *src = &pstate->src;
> > +	int min_scale = FRAC_16_16(1, 8);
> > +	int max_scale = FRAC_16_16(8, 1);
> > +	int format;
> > +	int ret;
> > +
> > +	if (!crtc)
> > +		return 0;
> > +
> > +	vp = to_vop2_video_port(crtc);
> > +	vop2 = vp->vop2;
> > +	vop2_data = vop2->data;
> > +
> > +	cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc);
> > +	if (WARN_ON(!cstate))
> > +		return -EINVAL;
> > +
> > +	ret = drm_atomic_helper_check_plane_state(pstate, cstate,
> > +						  min_scale, max_scale,
> > +						  true, true);
> > +	if (ret)
> > +		return ret;
> > +
> > +	if (!pstate->visible)
> > +		return 0;
> > +
> > +	format = vop2_convert_format(fb->format->format);
> > +	if (format < 0)
> > +		return format;
> > +
> > +	if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 ||
> > +	    drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) {
> > +		drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
> > +			  drm_rect_width(src) >> 16, drm_rect_height(src) >> 16,
> > +			  drm_rect_width(dest), drm_rect_height(dest));
> > +		pstate->visible = false;
> > +		return 0;
> > +	}
> > +
> > +	if (drm_rect_width(src) >> 16 > vop2_data->max_input.width ||
> > +	    drm_rect_height(src) >> 16 > vop2_data->max_input.height) {
> > +		drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
> > +			  drm_rect_width(src) >> 16,
> > +			  drm_rect_height(src) >> 16,
> > +			  vop2_data->max_input.width,
> > +			  vop2_data->max_input.height);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/*
> > +	 * Src.x1 can be odd when do clip, but yuv plane start point
> > +	 * need align with 2 pixel.
> > +	 */
> > +	if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
> > +		drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void vop2_plane_atomic_disable(struct drm_plane *plane,
> > +				      struct drm_atomic_state *state)
> > +{
> > +	struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state, plane);
> > +	struct vop2_win *win = to_vop2_win(plane);
> > +	struct vop2 *vop2 = win->vop2;
> > +
> > +	drm_dbg(vop2->drm, "%s disable\n", win->data->name);
> > +
> > +	if (!old_pstate->crtc)
> > +		return;
> > +
> > +	vop2_win_disable(win);
> > +	vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0);
> > +}
> > +
> > +/*
> > + * The color key is 10 bit, so all format should
> > + * convert to 10 bit here.
> > + */
> > +static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
> > +{
> > +	struct drm_plane_state *pstate = plane->state;
> > +	struct drm_framebuffer *fb = pstate->fb;
> > +	struct vop2_win *win = to_vop2_win(plane);
> > +	u32 color_key_en = 0;
> > +	u32 r = 0;
> > +	u32 g = 0;
> > +	u32 b = 0;
> > +
> > +	if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) {
> > +		vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0);
> > +		return;
> > +	}
> > +
> > +	switch (fb->format->format) {
> > +	case DRM_FORMAT_RGB565:
> > +	case DRM_FORMAT_BGR565:
> > +		r = (color_key & 0xf800) >> 11;
> > +		g = (color_key & 0x7e0) >> 5;
> > +		b = (color_key & 0x1f);
> > +		r <<= 5;
> > +		g <<= 4;
> > +		b <<= 5;
> > +		color_key_en = 1;
> > +		break;
> > +	case DRM_FORMAT_XRGB8888:
> > +	case DRM_FORMAT_ARGB8888:
> > +	case DRM_FORMAT_XBGR8888:
> > +	case DRM_FORMAT_ABGR8888:
> > +	case DRM_FORMAT_RGB888:
> > +	case DRM_FORMAT_BGR888:
> > +		r = (color_key & 0xff0000) >> 16;
> > +		g = (color_key & 0xff00) >> 8;
> > +		b = (color_key & 0xff);
> > +		r <<= 2;
> > +		g <<= 2;
> > +		b <<= 2;
> > +		color_key_en = 1;
> > +		break;
> > +	}
> > +
> > +	vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en);
> > +	vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b);
> > +}
> > +
> > +static void vop2_plane_atomic_update(struct drm_plane *plane,
> > +				     struct drm_atomic_state *state)
> > +{
> > +	struct drm_plane_state *pstate = plane->state;
> > +	struct drm_crtc *crtc = pstate->crtc;
> > +	struct vop2_win *win = to_vop2_win(plane);
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
> > +	struct vop2 *vop2 = win->vop2;
> > +	struct drm_framebuffer *fb = pstate->fb;
> > +	u32 bpp = fb->format->cpp[0] * 8;
> > +	u32 actual_w, actual_h, dsp_w, dsp_h;
> > +	u32 act_info, dsp_info;
> > +	u32 format;
> > +	u32 afbc_format;
> > +	u32 rb_swap;
> > +	u32 uv_swap;
> > +	struct drm_rect *src = &pstate->src;
> > +	struct drm_rect *dest = &pstate->dst;
> > +	u32 afbc_tile_num;
> > +	u32 transform_offset;
> > +	bool dither_up;
> > +	bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
> > +	bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
> > +	bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
> > +	bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
> > +	struct rockchip_gem_object *rk_obj;
> > +	unsigned long offset;
> > +	bool afbc_en;
> > +	dma_addr_t yrgb_mst;
> > +	dma_addr_t uv_mst;
> > +
> > +	/*
> > +	 * can't update plane when vop2 is disabled.
> > +	 */
> > +	if (WARN_ON(!crtc))
> > +		return;
> > +
> > +	if (!pstate->visible) {
> > +		vop2_plane_atomic_disable(plane, state);
> > +		return;
> > +	}
> > +
> > +	afbc_en = rockchip_afbc(plane, fb->modifier);
> > +
> > +	offset = (src->x1 >> 16) * fb->format->cpp[0];
> > +
> > +	/*
> > +	 * AFBC HDR_PTR must set to the zero offset of the framebuffer.
> > +	 */
> > +	if (afbc_en)
> > +		offset = 0;
> > +	else if (pstate->rotation & DRM_MODE_REFLECT_Y)
> > +		offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
> > +	else
> > +		offset += (src->y1 >> 16) * fb->pitches[0];
> > +
> > +	rk_obj = to_rockchip_obj(fb->obj[0]);
> > +
> > +	yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
> > +	if (fb->format->is_yuv) {
> > +		int hsub = fb->format->hsub;
> > +		int vsub = fb->format->vsub;
> > +
> > +		offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub;
> > +		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
> > +
> > +		if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en)
> > +			offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub;
> > +
> > +		rk_obj = to_rockchip_obj(fb->obj[0]);
> > +		uv_mst = rk_obj->dma_addr + offset + fb->offsets[1];
> > +	}
> > +
> > +	actual_w = drm_rect_width(src) >> 16;
> > +	actual_h = drm_rect_height(src) >> 16;
> > +	dsp_w = drm_rect_width(dest);
> > +
> > +	if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
> > +		drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
> > +			  vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
> > +		dsp_w = adjusted_mode->hdisplay - dest->x1;
> > +		if (dsp_w < 4)
> > +			dsp_w = 4;
> > +		actual_w = dsp_w * actual_w / drm_rect_width(dest);
> > +	}
> > +
> > +	dsp_h = drm_rect_height(dest);
> > +
> > +	if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
> > +		drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
> > +			  vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
> > +		dsp_h = adjusted_mode->vdisplay - dest->y1;
> > +		if (dsp_h < 4)
> > +			dsp_h = 4;
> > +		actual_h = dsp_h * actual_h / drm_rect_height(dest);
> > +	}
> > +
> > +	/*
> > +	 * This is workaround solution for IC design:
> > +	 * esmart can't support scale down when actual_w % 16 == 1.
> > +	 */
> > +	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
> > +		if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
> > +			drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
> > +				vp->id, win->data->name, actual_w);
> > +			actual_w -= 1;
> > +		}
> > +	}
> > +
> > +	if (afbc_en && actual_w % 4) {
> > +		drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
> > +			  vp->id, win->data->name, actual_w);
> > +		actual_w = ALIGN_DOWN(actual_w, 4);
> > +	}
> > +
> > +	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
> > +	dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff);
> > +
> > +	format = vop2_convert_format(fb->format->format);
> > +
> > +	drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n",
> > +		      vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h,
> > +		      dest->x1, dest->y1,
> > +		      &fb->format->format,
> > +		      afbc_en ? "AFBC" : "", &yrgb_mst);
> > +
> > +	if (afbc_en) {
> > +		u32 stride;
> > +
> > +		/* the afbc superblock is 16 x 16 */
> > +		afbc_format = vop2_convert_afbc_format(fb->format->format);
> > +
> > +		/* Enable color transform for YTR */
> > +		if (fb->modifier & AFBC_FORMAT_MOD_YTR)
> > +			afbc_format |= (1 << 4);
> > +
> > +		afbc_tile_num = ALIGN(actual_w, 16) >> 4;
> > +
> > +		/*
> > +		 * AFBC pic_vir_width is count by pixel, this is different
> > +		 * with WIN_VIR_STRIDE.
> > +		 */
> > +		stride = (fb->pitches[0] << 3) / bpp;
> > +		if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
> > +			drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligened\n",
> > +				  vp->id, win->data->name, stride);
> > +
> > +		rb_swap = vop2_afbc_rb_swap(fb->format->format);
> > +		uv_swap = vop2_afbc_uv_swap(fb->format->format);
> > +		/*
> > +		 * This is a workaround for crazy IC design, Cluster
> > +		 * and Esmart/Smart use different format configuration map:
> > +		 * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart.
> > +		 *
> > +		 * This is one thing we can make the convert simple:
> > +		 * AFBCD decode all the YUV data to YUV444. So we just
> > +		 * set all the yuv 10 bit to YUV444_10.
> > +		 */
> > +		if (fb->format->is_yuv && (bpp == 10))
> > +			format = VOP2_CLUSTER_YUV444_10;
> > +
> > +		if (vop2_cluster_window(win))
> > +			vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_RB_SWAP, rb_swap);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
> > +		if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) {
> > +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0);
> > +			transform_offset = vop2_afbc_transform_offset(pstate, false);
> > +		} else {
> > +			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 1);
> > +			transform_offset = vop2_afbc_transform_offset(pstate, true);
> > +		}
> > +		vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1));
> > +		vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16)));
> > +		vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num);
> > +		vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270);
> > +		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90);
> > +	} else {
> > +		vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4));
> > +	}
> > +
> > +	vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror);
> > +
> > +	if (rotate_90 || rotate_270) {
> > +		act_info = swahw32(act_info);
> > +		actual_w = drm_rect_height(src) >> 16;
> > +		actual_h = drm_rect_width(src) >> 16;
> > +	}
> > +
> > +	vop2_win_write(win, VOP2_WIN_FORMAT, format);
> > +	vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst);
> > +
> > +	rb_swap = vop2_win_rb_swap(fb->format->format);
> > +	vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap);
> > +	if (!vop2_cluster_window(win)) {
> > +		uv_swap = vop2_win_uv_swap(fb->format->format);
> > +		vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
> > +	}
> > +
> > +	if (fb->format->is_yuv) {
> > +		vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4));
> > +		vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst);
> > +	}
> > +
> > +	vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format);
> > +	if (!vop2_cluster_window(win))
> > +		vop2_plane_setup_color_key(plane, 0);
> > +	vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info);
> > +	vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info);
> > +	vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff));
> > +
> > +	vop2_setup_csc_mode(vp, win, pstate);
> > +
> > +	dither_up = vop2_win_dither_up(fb->format->format);
> > +	vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up);
> > +
> > +	vop2_win_write(win, VOP2_WIN_ENABLE, 1);
> > +
> > +	if (vop2_cluster_window(win)) {
> > +		int lb_mode = vop2_get_cluster_lb_mode(win, pstate);
> > +
> > +		vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode);
> > +		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1);
> > +	}
> > +}
> > +
> > +static const struct drm_plane_helper_funcs vop2_plane_helper_funcs = {
> > +	.atomic_check = vop2_plane_atomic_check,
> > +	.atomic_update = vop2_plane_atomic_update,
> > +	.atomic_disable = vop2_plane_atomic_disable,
> > +};
> > +
> > +static const struct drm_plane_funcs vop2_plane_funcs = {
> > +	.update_plane	= drm_atomic_helper_update_plane,
> > +	.disable_plane	= drm_atomic_helper_disable_plane,
> > +	.destroy = drm_plane_cleanup,
> > +	.reset = drm_atomic_helper_plane_reset,
> > +	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> > +	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> > +	.format_mod_supported = rockchip_vop2_mod_supported,
> > +};
> > +
> > +static int vop2_crtc_enable_vblank(struct drm_crtc *crtc)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +
> > +	vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD);
> > +
> > +	return 0;
> > +}
> > +
> > +static void vop2_crtc_disable_vblank(struct drm_crtc *crtc)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +
> > +	vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD);
> > +}
> > +
> > +static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
> > +				 const struct drm_display_mode *mode,
> > +				 struct drm_display_mode *adj_mode)
> > +{
> > +	drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V |
> > +					CRTC_STEREO_DOUBLE);
> > +
> > +	return true;
> > +}
> > +
> > +static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
> > +{
> > +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> > +
> > +	switch (vcstate->bus_format) {
> > +	case MEDIA_BUS_FMT_RGB565_1X16:
> > +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> > +		break;
> > +	case MEDIA_BUS_FMT_RGB666_1X18:
> > +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> > +	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
> > +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
> > +		*dsp_ctrl |= RGB888_TO_RGB666;
> > +		break;
> > +	case MEDIA_BUS_FMT_YUV8_1X24:
> > +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> > +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +
> > +	if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA)
> > +		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
> > +
> > +	*dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL,
> > +				DITHER_DOWN_ALLEGRO);
> > +}
> > +
> > +static void vop2_post_config(struct drm_crtc *crtc)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> > +	u16 vtotal = mode->crtc_vtotal;
> > +	u16 hdisplay = mode->crtc_hdisplay;
> > +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> > +	u16 vdisplay = mode->crtc_vdisplay;
> > +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> > +	u32 left_margin = 100, right_margin = 100;
> > +	u32 top_margin = 100, bottom_margin = 100;
> > +	u16 hsize = hdisplay * (left_margin + right_margin) / 200;
> > +	u16 vsize = vdisplay * (top_margin + bottom_margin) / 200;
> > +	u16 hact_end, vact_end;
> > +	u32 val;
> > +
> > +	vsize = rounddown(vsize, 2);
> > +	hsize = rounddown(hsize, 2);
> > +	hact_st += hdisplay * (100 - left_margin) / 200;
> > +	hact_end = hact_st + hsize;
> > +	val = hact_st << 16;
> > +	val |= hact_end;
> > +	vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val);
> > +	vact_st += vdisplay * (100 - top_margin) / 200;
> > +	vact_end = vact_st + vsize;
> > +	val = vact_st << 16;
> > +	val |= vact_end;
> > +	vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val);
> > +	val = scl_cal_scale2(vdisplay, vsize) << 16;
> > +	val |= scl_cal_scale2(hdisplay, hsize);
> > +	vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val);
> > +
> > +	val = 0;
> > +	if (hdisplay != hsize)
> > +		val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN;
> > +	if (vdisplay != vsize)
> > +		val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN;
> > +	vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val);
> > +
> > +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> > +		u16 vact_st_f1 = vtotal + vact_st + 1;
> > +		u16 vact_end_f1 = vact_st_f1 + vsize;
> > +
> > +		val = vact_st_f1 << 16 | vact_end_f1;
> > +		vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
> > +	}
> > +
> > +	vop2_vp_write(vp, RK3568_VP_DSP_BG, 0);
> > +}
> > +
> > +static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
> > +				u32 polflags)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +	u32 die, dip;
> > +
> > +	die = vop2_readl(vop2, RK3568_DSP_IF_EN);
> > +	dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
> > +
> > +	switch (id) {
> > +	case RK3568_VOP2_EP_RGB:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_RGB |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
> > +		if (polflags & POLFLAG_DCLK_INV)
> > +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
> > +		else
> > +			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16));
> > +		break;
> > +	case RK3568_VOP2_EP_HDMI:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_HDMI |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id);
> > +		break;
> > +	case RK3568_VOP2_EP_EDP:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_EDP |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id);
> > +		break;
> > +	case RK3568_VOP2_EP_MIPI0:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id);
> > +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> > +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> > +		break;
> > +	case RK3568_VOP2_EP_MIPI1:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
> > +		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
> > +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
> > +		break;
> > +	case RK3568_VOP2_EP_LVDS0:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id);
> > +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> > +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> > +		break;
> > +	case RK3568_VOP2_EP_LVDS1:
> > +		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX;
> > +		die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 |
> > +			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id);
> > +		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
> > +		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
> > +		break;
> > +	default:
> > +		return;
> > +	};
> > +
> > +	dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
> > +
> > +	vop2_writel(vop2, RK3568_DSP_IF_EN, die);
> > +	vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
> > +}
> > +
> > +static int us_to_vertical_line(struct drm_display_mode *mode, int us)
> > +{
> > +	return us * mode->clock / mode->htotal / 1000;
> > +}
> > +
> > +static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
> > +				    struct drm_atomic_state *state)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct vop2 *vop2 = vp->vop2;
> > +	const struct vop2_data *vop2_data = vop2->data;
> > +	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
> > +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> > +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> > +	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> > +	unsigned long clock = mode->crtc_clock * 1000;
> > +	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
> > +	u16 hdisplay = mode->crtc_hdisplay;
> > +	u16 htotal = mode->crtc_htotal;
> > +	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
> > +	u16 hact_end = hact_st + hdisplay;
> > +	u16 vdisplay = mode->crtc_vdisplay;
> > +	u16 vtotal = mode->crtc_vtotal;
> > +	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
> > +	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
> > +	u16 vact_end = vact_st + vdisplay;
> > +	u8 out_mode;
> > +	u32 dsp_ctrl = 0;
> > +	int act_end;
> > +	u32 val, polflags;
> > +	int ret;
> > +	struct drm_encoder *encoder;
> > +
> > +	drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n",
> > +		hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
> > +		drm_mode_vrefresh(mode), vcstate->output_type, vp->id);
> > +
> > +	vop2_lock(vop2);
> > +
> > +	ret = clk_prepare_enable(vp->dclk);
> > +	if (ret < 0) {
> > +		drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
> > +			      vp->id, ret);
> > +		return;
> > +	}
> > +
> > +	if (!vop2->enable_count)
> > +		vop2_enable(vop2);
> > +
> > +	vop2->enable_count++;
> > +
> > +	vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY);
> > +
> > +	polflags = 0;
> > +	if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
> > +		polflags |= POLFLAG_DCLK_INV;
> > +	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> > +		polflags |= BIT(HSYNC_POSITIVE);
> > +	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> > +		polflags |= BIT(VSYNC_POSITIVE);
> > +
> > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > +		struct device_node *node, *parent;
> > +
> > +		parent = of_get_parent(rkencoder->port);
> > +
> > +		for_each_endpoint_of_node(parent, node) {
> > +			struct device_node *crtc_port = of_graph_get_remote_port(node);
> > +			struct device_node *epn;
> > +			struct of_endpoint endpoint;
> > +
> > +			if (crtc->port != crtc_port) {
> > +				of_node_put(crtc_port);
> > +				continue;
> > +			}
> > +
> > +			of_node_put(crtc_port);
> > +
> > +			epn = of_graph_get_remote_endpoint(node);
> > +			of_graph_parse_endpoint(epn, &endpoint);
> > +			of_node_put(epn);
> > +
> > +			drm_dbg(vop2->drm, "vp%d is connected to %s, id %d\n",
> > +					   vp->id, encoder->name, endpoint.id);
> > +			rk3568_set_intf_mux(vp, endpoint.id, polflags);
> > +		}
> > +		of_node_put(parent);
> > +	}
> > +
> > +	if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
> > +	     !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT))
> > +		out_mode = ROCKCHIP_OUT_MODE_P888;
> > +	else
> > +		out_mode = vcstate->output_mode;
> > +
> > +	dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode);
> > +
> > +	if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP;
> > +
> > +	if (is_yuv_output(vcstate->bus_format))
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y;
> > +
> > +	vop2_dither_setup(crtc, &dsp_ctrl);
> > +
> > +	vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len);
> > +	val = hact_st << 16;
> > +	val |= hact_end;
> > +	vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val);
> > +
> > +	val = vact_st << 16;
> > +	val |= vact_end;
> > +	vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val);
> > +
> > +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> > +		u16 vact_st_f1 = vtotal + vact_st + 1;
> > +		u16 vact_end_f1 = vact_st_f1 + vdisplay;
> > +
> > +		val = vact_st_f1 << 16 | vact_end_f1;
> > +		vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val);
> > +
> > +		val = vtotal << 16 | (vtotal + vsync_len);
> > +		vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val);
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE;
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL;
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN;
> > +		vtotal += vtotal + 1;
> > +		act_end = vact_end_f1;
> > +	} else {
> > +		act_end = vact_end;
> > +	}
> > +
> > +	vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id),
> > +		    (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end);
> > +
> > +	vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len);
> > +
> > +	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
> > +		dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
> > +		clock *= 2;
> > +	}
> > +
> > +	vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
> > +
> > +	clk_set_rate(vp->dclk, clock);
> > +
> > +	vop2_post_config(crtc);
> > +
> > +	vop2_cfg_done(vp);
> > +
> > +	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
> > +
> > +	drm_crtc_vblank_on(crtc);
> > +
> > +	vop2_unlock(vop2);
> > +}
> > +
> > +static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
> > +				  struct drm_atomic_state *state)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct drm_plane *plane;
> > +	int nplanes = 0;
> > +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> > +
> > +	drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
> > +		nplanes++;
> > +
> > +	if (nplanes > vp->nlayers)
> > +		return -EINVAL;
> > +
> > +	return 0;
> > +}
> > +
> > +static bool is_opaque(u16 alpha)
> > +{
> > +	return (alpha >> 8) == 0xff;
> > +}
> > +
> > +static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config,
> > +			     struct vop2_alpha *alpha)
> > +{
> > +	int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1;
> > +	int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1;
> > +	int src_color_mode = alpha_config->src_premulti_en ?
> > +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> > +	int dst_color_mode = alpha_config->dst_premulti_en ?
> > +				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
> > +
> > +	alpha->src_color_ctrl.val = 0;
> > +	alpha->dst_color_ctrl.val = 0;
> > +	alpha->src_alpha_ctrl.val = 0;
> > +	alpha->dst_alpha_ctrl.val = 0;
> > +
> > +	if (!alpha_config->src_pixel_alpha_en)
> > +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> > +	else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en)
> > +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> > +	else
> > +		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> > +
> > +	alpha->src_color_ctrl.bits.alpha_en = 1;
> > +
> > +	if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) {
> > +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> > +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> > +	} else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) {
> > +		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
> > +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE;
> > +	} else {
> > +		alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL;
> > +		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
> > +	}
> > +	alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8;
> > +	alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> > +	alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> > +
> > +	alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> > +	alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> > +	alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
> > +	alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8;
> > +	alpha->dst_color_ctrl.bits.color_mode = dst_color_mode;
> > +	alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> > +
> > +	alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> > +	alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode;
> > +	alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
> > +	alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE;
> > +
> > +	alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
> > +	if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en)
> > +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX;
> > +	else
> > +		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
> > +	alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION;
> > +	alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
> > +}
> > +
> > +static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id)
> > +{
> > +	struct vop2_video_port *vp;
> > +	int used_layer = 0;
> > +	int i;
> > +
> > +	for (i = 0; i < port_id; i++) {
> > +		vp = &vop2->vps[i];
> > +		used_layer += hweight32(vp->win_mask);
> > +	}
> > +
> > +	return used_layer;
> > +}
> > +
> > +static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win)
> > +{
> > +	u32 offset = (main_win->data->phys_id * 0x10);
> > +	struct vop2_alpha_config alpha_config;
> > +	struct vop2_alpha alpha;
> > +	struct drm_plane_state *bottom_win_pstate;
> > +	bool src_pixel_alpha_en = false;
> > +	u16 src_glb_alpha_val, dst_glb_alpha_val;
> > +	bool premulti_en = false;
> > +	bool swap = false;
> > +
> > +	/* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */
> > +	bottom_win_pstate = main_win->base.state;
> > +	src_glb_alpha_val = 0;
> > +	dst_glb_alpha_val = main_win->base.state->alpha;
> > +
> > +	if (!bottom_win_pstate->fb)
> > +		return;
> > +
> > +	alpha_config.src_premulti_en = premulti_en;
> > +	alpha_config.dst_premulti_en = false;
> > +	alpha_config.src_pixel_alpha_en = src_pixel_alpha_en;
> > +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> > +	alpha_config.src_glb_alpha_value = src_glb_alpha_val;
> > +	alpha_config.dst_glb_alpha_value = dst_glb_alpha_val;
> > +	vop2_parse_alpha(&alpha_config, &alpha);
> > +
> > +	alpha.src_color_ctrl.bits.src_dst_swap = swap;
> > +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset,
> > +		    alpha.src_color_ctrl.val);
> > +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset,
> > +		    alpha.dst_color_ctrl.val);
> > +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset,
> > +		    alpha.src_alpha_ctrl.val);
> > +	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset,
> > +		    alpha.dst_alpha_ctrl.val);
> > +}
> > +
> > +static void vop2_setup_alpha(struct vop2_video_port *vp)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +	struct drm_framebuffer *fb;
> > +	struct vop2_alpha_config alpha_config;
> > +	struct vop2_alpha alpha;
> > +	struct drm_plane *plane;
> > +	int pixel_alpha_en;
> > +	int premulti_en, gpremulti_en = 0;
> > +	int mixer_id;
> > +	u32 offset;
> > +	bool bottom_layer_alpha_en = false;
> > +	u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE;
> > +
> > +	mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id);
> > +	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
> > +
> > +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> > +		struct vop2_win *win = to_vop2_win(plane);
> > +
> > +		if (plane->state->normalized_zpos == 0 &&
> > +		    !is_opaque(plane->state->alpha) &&
> > +		    !vop2_cluster_window(win)) {
> > +			/*
> > +			 * If bottom layer have global alpha effect [except cluster layer,
> > +			 * because cluster have deal with bottom layer global alpha value
> > +			 * at cluster mix], bottom layer mix need deal with global alpha.
> > +			 */
> > +			bottom_layer_alpha_en = true;
> > +			dst_global_alpha = plane->state->alpha;
> > +		}
> > +	}
> > +
> > +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> > +		struct vop2_win *win = to_vop2_win(plane);
> > +		int zpos = plane->state->normalized_zpos;
> > +
> > +		if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
> > +			premulti_en = 1;
> > +		else
> > +			premulti_en = 0;
> > +
> > +		plane = &win->base;
> > +		fb = plane->state->fb;
> > +
> > +		pixel_alpha_en = fb->format->has_alpha;
> > +
> > +		alpha_config.src_premulti_en = premulti_en;
> > +
> > +		if (bottom_layer_alpha_en && zpos == 1) {
> > +			gpremulti_en = premulti_en;
> > +			/* Cd = Cs + (1 - As) * Cd * Agd */
> > +			alpha_config.dst_premulti_en = false;
> > +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> > +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> > +			alpha_config.dst_glb_alpha_value = dst_global_alpha;
> > +		} else if (vop2_cluster_window(win)) {
> > +			/* Mix output data only have pixel alpha */
> > +			alpha_config.dst_premulti_en = true;
> > +			alpha_config.src_pixel_alpha_en = true;
> > +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> > +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> > +		} else {
> > +			/* Cd = Cs + (1 - As) * Cd */
> > +			alpha_config.dst_premulti_en = true;
> > +			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
> > +			alpha_config.src_glb_alpha_value = plane->state->alpha;
> > +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> > +		}
> > +
> > +		vop2_parse_alpha(&alpha_config, &alpha);
> > +
> > +		offset = (mixer_id + zpos - 1) * 0x10;
> > +		vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset,
> > +			    alpha.src_color_ctrl.val);
> > +		vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset,
> > +			    alpha.dst_color_ctrl.val);
> > +		vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset,
> > +			    alpha.src_alpha_ctrl.val);
> > +		vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset,
> > +			    alpha.dst_alpha_ctrl.val);
> > +	}
> > +
> > +	if (vp->id == 0) {
> > +		if (bottom_layer_alpha_en) {
> > +			/* Transfer pixel alpha to hdr mix */
> > +			alpha_config.src_premulti_en = gpremulti_en;
> > +			alpha_config.dst_premulti_en = true;
> > +			alpha_config.src_pixel_alpha_en = true;
> > +			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> > +			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
> > +			vop2_parse_alpha(&alpha_config, &alpha);
> > +
> > +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL,
> > +				alpha.src_color_ctrl.val);
> > +			vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL,
> > +				alpha.dst_color_ctrl.val);
> > +			vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL,
> > +				alpha.src_alpha_ctrl.val);
> > +			vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL,
> > +				alpha.dst_alpha_ctrl.val);
> > +		} else {
> > +			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0);
> > +		}
> > +	}
> > +}
> > +
> > +static void vop2_setup_layer_mixer(struct vop2_video_port *vp)
> > +{
> > +	struct vop2 *vop2 = vp->vop2;
> > +	struct drm_plane *plane;
> > +	u32 layer_sel = 0;
> > +	u32 port_sel;
> > +	unsigned int nlayer, ofs;
> > +	struct drm_display_mode *adjusted_mode;
> > +	u16 hsync_len;
> > +	u16 hdisplay;
> > +	u32 bg_dly;
> > +	u32 pre_scan_dly;
> > +	int i;
> > +	struct vop2_video_port *vp0 = &vop2->vps[0];
> > +	struct vop2_video_port *vp1 = &vop2->vps[1];
> > +	struct vop2_video_port *vp2 = &vop2->vps[2];
> > +
> > +	adjusted_mode = &vp->crtc.state->adjusted_mode;
> > +	hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
> > +	hdisplay = adjusted_mode->crtc_hdisplay;
> > +
> > +	bg_dly = vp->data->pre_scan_max_dly[3];
> > +	vop2_writel(vop2, RK3568_VP_BG_MIX_CTRL(vp->id),
> > +			    FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly));
> > +
> > +	pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len;
> > +	vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly);
> > +
> > +	vop2_writel(vop2, RK3568_OVL_CTRL, 0);
> > +	port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
> > +	port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT;
> > +
> > +	if (vp0->nlayers)
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX,
> > +				     vp0->nlayers - 1);
> > +	else
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8);
> > +
> > +	if (vp1->nlayers)
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX,
> > +				     (vp0->nlayers + vp1->nlayers - 1));
> > +	else
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> > +
> > +	if (vp2->nlayers)
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX,
> > +			(vp2->nlayers + vp1->nlayers + vp0->nlayers - 1));
> > +	else
> > +		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
> > +
> > +	layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
> > +
> > +	ofs = 0;
> > +	for (i = 0; i < vp->id; i++)
> > +		ofs += vop2->vps[i].nlayers;
> > +
> > +	nlayer = 0;
> > +	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
> > +		struct vop2_win *win = to_vop2_win(plane);
> > +
> > +		switch (win->data->phys_id) {
> > +		case ROCKCHIP_VOP2_CLUSTER0:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id);
> > +			break;
> > +		case ROCKCHIP_VOP2_CLUSTER1:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id);
> > +			break;
> > +		case ROCKCHIP_VOP2_ESMART0:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id);
> > +			break;
> > +		case ROCKCHIP_VOP2_ESMART1:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id);
> > +			break;
> > +		case ROCKCHIP_VOP2_SMART0:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART0;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id);
> > +			break;
> > +		case ROCKCHIP_VOP2_SMART1:
> > +			port_sel &= ~RK3568_OVL_PORT_SEL__SMART1;
> > +			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id);
> > +			break;
> > +		}
> > +
> > +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> > +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, win->data->layer_sel_id);
> > +		nlayer++;
> > +	}
> > +
> > +	/* configure unused layers to 0x5 (reserved) */
> > +	for (; nlayer < 3; nlayer++) {
> > +		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
> > +		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5);
> > +	}
> > +
> > +	vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel);
> > +	vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
> > +	vop2_writel(vop2, RK3568_OVL_CTRL, RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD);
> > +}
> > +
> > +static void vop2_setup_dly_for_windows(struct vop2 *vop2)
> > +{
> > +	struct vop2_win *win;
> > +	int i = 0;
> > +	u32 cdly = 0, sdly = 0;
> > +
> > +	for (i = 0; i < vop2->data->win_size; i++) {
> > +		u32 dly;
> > +
> > +		win = &vop2->win[i];
> > +		dly = win->delay;
> > +
> > +		switch (win->data->phys_id) {
> > +		case ROCKCHIP_VOP2_CLUSTER0:
> > +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly);
> > +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly);
> > +			break;
> > +		case ROCKCHIP_VOP2_CLUSTER1:
> > +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly);
> > +			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly);
> > +			break;
> > +		case ROCKCHIP_VOP2_ESMART0:
> > +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly);
> > +			break;
> > +		case ROCKCHIP_VOP2_ESMART1:
> > +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly);
> > +			break;
> > +		case ROCKCHIP_VOP2_SMART0:
> > +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly);
> > +			break;
> > +		case ROCKCHIP_VOP2_SMART1:
> > +			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly);
> > +			break;
> > +		}
> > +	}
> > +
> > +	vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly);
> > +	vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly);
> > +}
> > +
> > +static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
> > +				   struct drm_atomic_state *state)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +	struct vop2 *vop2 = vp->vop2;
> > +	struct drm_plane *plane;
> > +
> > +	vp->win_mask = 0;
> > +
> > +	drm_atomic_crtc_for_each_plane(plane, crtc) {
> > +		struct vop2_win *win = to_vop2_win(plane);
> > +
> > +		win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT];
> > +
> > +		vp->win_mask |= BIT(win->data->phys_id);
> > +
> > +		if (vop2_cluster_window(win))
> > +			vop2_setup_cluster_alpha(vop2, win);
> > +	}
> > +
> > +	if (!vp->win_mask)
> > +		return;
> > +
> > +	vop2_setup_layer_mixer(vp);
> > +	vop2_setup_alpha(vp);
> > +	vop2_setup_dly_for_windows(vop2);
> > +}
> > +
> > +static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
> > +				   struct drm_atomic_state *state)
> > +{
> > +	struct vop2_video_port *vp = to_vop2_video_port(crtc);
> > +
> > +	vop2_post_config(crtc);
> > +
> > +	vop2_cfg_done(vp);
> > +
> > +	spin_lock_irq(&crtc->dev->event_lock);
> > +
> > +	if (crtc->state->event) {
> > +		WARN_ON(drm_crtc_vblank_get(crtc));
> > +		vp->event = crtc->state->event;
> > +		crtc->state->event = NULL;
> > +	}
> > +
> > +	spin_unlock_irq(&crtc->dev->event_lock);
> > +}
> > +
> > +static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
> > +	.mode_fixup = vop2_crtc_mode_fixup,
> > +	.atomic_check = vop2_crtc_atomic_check,
> > +	.atomic_begin = vop2_crtc_atomic_begin,
> > +	.atomic_flush = vop2_crtc_atomic_flush,
> > +	.atomic_enable = vop2_crtc_atomic_enable,
> > +	.atomic_disable = vop2_crtc_atomic_disable,
> > +};
> > +
> > +static void vop2_crtc_reset(struct drm_crtc *crtc)
> > +{
> > +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
> > +
> > +	if (crtc->state) {
> > +		__drm_atomic_helper_crtc_destroy_state(crtc->state);
> > +		kfree(vcstate);
> > +	}
> > +
> > +	vcstate = kzalloc(sizeof(*vcstate), GFP_KERNEL);
> > +	if (!vcstate)
> > +		return;
> > +
> > +	crtc->state = &vcstate->base;
> > +	crtc->state->crtc = crtc;
> > +}
> > +
> > +static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc)
> > +{
> > +	struct rockchip_crtc_state *vcstate, *old_vcstate;
> > +
> > +	old_vcstate = to_rockchip_crtc_state(crtc->state);
> > +
> > +	vcstate = kmemdup(old_vcstate, sizeof(*old_vcstate), GFP_KERNEL);
> > +	if (!vcstate)
> > +		return NULL;
> > +
> > +	__drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
> > +
> > +	return &vcstate->base;
> > +}
> > +
> > +static void vop2_crtc_destroy_state(struct drm_crtc *crtc,
> > +				    struct drm_crtc_state *state)
> > +{
> > +	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state);
> > +
> > +	__drm_atomic_helper_crtc_destroy_state(&vcstate->base);
> > +	kfree(vcstate);
> > +}
> > +
> > +static const struct drm_crtc_funcs vop2_crtc_funcs = {
> > +	.set_config = drm_atomic_helper_set_config,
> > +	.page_flip = drm_atomic_helper_page_flip,
> > +	.destroy = drm_crtc_cleanup,
> > +	.reset = vop2_crtc_reset,
> > +	.atomic_duplicate_state = vop2_crtc_duplicate_state,
> > +	.atomic_destroy_state = vop2_crtc_destroy_state,
> > +	.enable_vblank = vop2_crtc_enable_vblank,
> > +	.disable_vblank = vop2_crtc_disable_vblank,
> > +};
> > +
> > +static irqreturn_t vop2_isr(int irq, void *data)
> > +{
> > +	struct vop2 *vop2 = data;
> > +	const struct vop2_data *vop2_data = vop2->data;
> > +	u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM];
> > +	int ret = IRQ_NONE;
> > +	int i;
> > +
> > +	/*
> > +	 * The irq is shared with the iommu. If the runtime-pm state of the
> > +	 * vop2-device is disabled the irq has to be targeted at the iommu.
> > +	 */
> > +	if (!pm_runtime_get_if_in_use(vop2->dev))
> > +		return IRQ_NONE;
> > +
> > +	for (i = 0; i < vop2_data->nr_vps; i++) {
> > +		struct vop2_video_port *vp = &vop2->vps[i];
> > +		struct drm_crtc *crtc = &vp->crtc;
> > +		u32 irqs;
> > +
> > +		irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
> > +		vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
> > +
> > +		if (irqs & VP_INT_DSP_HOLD_VALID) {
> > +			complete(&vp->dsp_hold_completion);
> > +			ret = IRQ_HANDLED;
> > +		}
> > +
> > +		if (irqs & VP_INT_FS_FIELD) {
> > +			drm_crtc_handle_vblank(crtc);
> > +			spin_lock(&crtc->dev->event_lock);
> > +			if (vp->event) {
> > +				u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
> > +
> > +				if (!(val & BIT(vp->id))) {
> > +					drm_crtc_send_vblank_event(crtc, vp->event);
> > +					vp->event = NULL;
> > +					drm_crtc_vblank_put(crtc);
> > +				}
> > +			}
> > +			spin_unlock(&crtc->dev->event_lock);
> > +
> > +			ret = IRQ_HANDLED;
> > +		}
> > +
> > +		if (irqs & VP_INT_POST_BUF_EMPTY) {
> > +			drm_err_ratelimited(vop2->drm,
> > +					    "POST_BUF_EMPTY irq err at vp%d\n",
> > +					    vp->id);
> > +			ret = IRQ_HANDLED;
> > +		}
> > +	}
> > +
> > +	axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS);
> > +	vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]);
> > +	axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS);
> > +	vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]);
> > +
> > +	for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) {
> > +		if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) {
> > +			drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n");
> > +			ret = IRQ_HANDLED;
> > +		}
> > +	}
> > +
> > +	pm_runtime_put(vop2->dev);
> > +
> > +	return ret;
> > +}
> > +
> > +static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win,
> > +			   unsigned long possible_crtcs)
> > +{
> > +	const struct vop2_win_data *win_data = win->data;
> > +	unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
> > +				  BIT(DRM_MODE_BLEND_PREMULTI) |
> > +				  BIT(DRM_MODE_BLEND_COVERAGE);
> > +	int ret;
> > +
> > +	ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs,
> > +				       &vop2_plane_funcs, win_data->formats,
> > +				       win_data->nformats,
> > +				       win_data->format_modifiers,
> > +				       win->type, win_data->name);
> > +	if (ret) {
> > +		drm_err(vop2->drm, "failed to initialize plane %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs);
> > +
> > +	if (win->data->supported_rotations)
> > +		drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0,
> > +						   DRM_MODE_ROTATE_0 |
> > +						   win->data->supported_rotations);
> > +	drm_plane_create_alpha_property(&win->base);
> > +	drm_plane_create_blend_mode_property(&win->base, blend_caps);
> > +	drm_plane_create_zpos_property(&win->base, win->win_id, 0,
> > +				       vop2->registered_num_wins - 1);
> > +
> > +	return 0;
> > +}
> > +
> > +static struct vop2_video_port *get_activated_vp(struct vop2 *vop2, int n)
> > +{
> > +	int i, id = 0;
> > +
> > +	for (i = 0; i < vop2->data->nr_vps; i++) {
> > +		struct vop2_video_port *vp = &vop2->vps[i];
> > +
> > +		if (!vp->crtc.port)
> > +			continue;
> > +
> > +		if (n == id)
> > +			return vp;
> > +		id++;
> > +	}
> > +
> > +	return NULL;
> > +}
> > +
> > +#define NR_LAYERS 6
> > +
> > +static int vop2_create_crtc(struct vop2 *vop2)
> > +{
> > +	const struct vop2_data *vop2_data = vop2->data;
> > +	struct drm_device *drm = vop2->drm;
> > +	struct device *dev = vop2->dev;
> > +	struct drm_plane *plane;
> > +	struct device_node *port;
> > +	struct vop2_video_port *vp;
> > +	u32 possible_crtcs;
> > +	int i, nvp, nvps = 0;
> > +	int ret;
> > +
> > +	for (i = 0; i < vop2_data->nr_vps; i++) {
> > +		const struct vop2_video_port_data *vp_data;
> > +		struct device_node *np;
> > +		char dclk_name[9];
> > +
> > +		vp_data = &vop2_data->vp[i];
> > +		vp = &vop2->vps[i];
> > +		vp->vop2 = vop2;
> > +		vp->id = vp_data->id;
> > +		vp->regs = vp_data->regs;
> > +		vp->data = vp_data;
> > +
> > +		snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
> > +		vp->dclk = devm_clk_get(vop2->dev, dclk_name);
> > +		if (IS_ERR(vp->dclk)) {
> > +			drm_err(vop2->drm, "failed to get %s\n", dclk_name);
> > +			return PTR_ERR(vp->dclk);
> > +		}
> > +
> > +		np = of_graph_get_remote_node(dev->of_node, i, -1);
> > +		if (!np) {
> > +			drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
> > +			continue;
> > +		}
> > +		of_node_put(np);
> > +
> > +		port = of_graph_get_port_by_id(dev->of_node, i);
> > +		if (!port) {
> > +			drm_err(vop2->drm, "no port node found for video_port%d\n", i);
> > +			return -ENOENT;
> > +		}
> > +
> > +		vp->crtc.port = port;
> > +		nvps++;
> > +	}
> > +
> > +	nvp = 0;
> > +	for (i = 0; i < vop2->registered_num_wins; i++) {
> > +		struct vop2_win *win = &vop2->win[i];
> > +
> > +		if (win->type == DRM_PLANE_TYPE_PRIMARY) {
> > +			vp = get_activated_vp(vop2, nvp);
> > +
> > +			if (vp) {
> > +				possible_crtcs = BIT(nvp);
> > +				vp->primary_plane = win;
> > +
> > +				nvp++;
> > +			} else {
> > +				/* change the unused primary window to overlay window */
> > +				win->type = DRM_PLANE_TYPE_OVERLAY;
> > +			}
> > +		}
> > +
> > +		if (win->type == DRM_PLANE_TYPE_OVERLAY)
> > +			possible_crtcs = (1 << vop2_data->nr_vps) - 1;
> > +
> > +		ret = vop2_plane_init(vop2, win, possible_crtcs);
> > +
> > +		if (ret) {
> > +			drm_err(vop2->drm, "failed to init plane %s: %d\n",
> > +				win->data->name, ret);
> > +			return ret;
> > +		}
> > +	}
> > +
> > +	for (i = 0; i < vop2_data->nr_vps; i++) {
> > +		vp = &vop2->vps[i];
> > +
> > +		if (!vp->crtc.port)
> > +			continue;
> > +
> > +		plane = &vp->primary_plane->base;
> > +
> > +		ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL,
> > +						&vop2_crtc_funcs,
> > +						"video_port%d", vp->id);
> > +		if (ret) {
> > +			drm_err(vop2->drm, "crtc init for video_port%d failed\n", i);
> > +			return ret;
> > +		}
> > +
> > +		drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs);
> > +
> > +		init_completion(&vp->dsp_hold_completion);
> > +	}
> > +
> > +	for (i = 0; i < vop2->data->nr_vps; i++) {
> > +		struct vop2_video_port *vp = &vop2->vps[i];
> > +
> > +		if (vp->crtc.port)
> > +			vp->nlayers = NR_LAYERS / nvps;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void vop2_destroy_crtc(struct drm_crtc *crtc)
> > +{
> > +	of_node_put(crtc->port);
> > +
> > +	/*
> > +	 * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane()
> > +	 * references the CRTC.
> > +	 */
> > +	drm_crtc_cleanup(crtc);
> > +}
> > +
> > +static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = {
> > +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0),
> > +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5),
> > +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14),
> > +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18),
> > +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31),
> > +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31),
> > +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31),
> > +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31),
> > +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31),
> > +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19),
> > +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15),
> > +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31),
> > +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8),
> > +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9),
> > +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11),
> > +
> > +	/* Scale */
> > +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15),
> > +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31),
> > +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15),
> > +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13),
> > +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3),
> > +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28),
> > +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29),
> > +
> > +	/* cluster regs */
> > +	[VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1),
> > +	[VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0),
> > +	[VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7),
> > +
> > +	/* afbc regs */
> > +	[VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6),
> > +	[VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9),
> > +	[VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10),
> > +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4),
> > +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7),
> > +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8),
> > +	[VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31),
> > +	[VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31),
> > +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15),
> > +	[VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31),
> > +	[VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31),
> > +	[VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31),
> > +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31),
> > +	[VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0),
> > +	[VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1),
> > +	[VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2),
> > +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3),
> > +	[VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff },
> > +};
> > +
> > +static int vop2_cluster_init(struct vop2_win *win)
> > +{
> > +	struct vop2 *vop2 = win->vop2;
> > +	struct reg_field *cluster_regs;
> > +	int ret, i;
> > +
> > +	cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs),
> > +			       GFP_KERNEL);
> > +	if (!cluster_regs)
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++)
> > +		if (cluster_regs[i].reg != 0xffffffff)
> > +			cluster_regs[i].reg += win->offset;
> > +
> > +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> > +					    cluster_regs,
> > +					    ARRAY_SIZE(vop2_cluster_regs));
> > +
> > +	kfree(cluster_regs);
> > +
> > +	return ret;
> > +};
> > +
> > +static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = {
> > +	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0),
> > +	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5),
> > +	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12),
> > +	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14),
> > +	[VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16),
> > +	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31),
> > +	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31),
> > +	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28),
> > +	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31),
> > +	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31),
> > +	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17),
> > +	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15),
> > +	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31),
> > +	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0),
> > +	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1),
> > +	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3),
> > +	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31),
> > +	[VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29),
> > +	[VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31),
> > +
> > +	/* Scale */
> > +	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15),
> > +	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31),
> > +	[VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15),
> > +	[VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31),
> > +	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1),
> > +	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3),
> > +	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5),
> > +	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7),
> > +	[VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9),
> > +	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11),
> > +	[VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13),
> > +	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15),
> > +	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17),
> > +	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8),
> > +	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9),
> > +	[VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10),
> > +	[VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11),
> > +	[VOP2_WIN_XMIRROR] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff },
> > +	[VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff },
> > +};
> > +
> > +static int vop2_esmart_init(struct vop2_win *win)
> > +{
> > +	struct vop2 *vop2 = win->vop2;
> > +	struct reg_field *esmart_regs;
> > +	int ret, i;
> > +
> > +	esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs),
> > +			       GFP_KERNEL);
> > +	if (!esmart_regs)
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++)
> > +		if (esmart_regs[i].reg != 0xffffffff)
> > +			esmart_regs[i].reg += win->offset;
> > +
> > +	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
> > +					    esmart_regs,
> > +					    ARRAY_SIZE(vop2_esmart_regs));
> > +
> > +	kfree(esmart_regs);
> > +
> > +	return ret;
> > +};
> > +
> > +static int vop2_win_init(struct vop2 *vop2)
> > +{
> > +	const struct vop2_data *vop2_data = vop2->data;
> > +	struct vop2_win *win;
> > +	int i, ret;
> > +
> > +	for (i = 0; i < vop2_data->win_size; i++) {
> > +		const struct vop2_win_data *win_data = &vop2_data->win[i];
> > +
> > +		win = &vop2->win[i];
> > +		win->data = win_data;
> > +		win->type = win_data->type;
> > +		win->offset = win_data->base;
> > +		win->win_id = i;
> > +		win->vop2 = vop2;
> > +		if (vop2_cluster_window(win))
> > +			ret = vop2_cluster_init(win);
> > +		else
> > +			ret = vop2_esmart_init(win);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> > +	vop2->registered_num_wins = vop2_data->win_size;
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * The window registers are only updated when config done is written.
> > + * Until that they read back the old value. As we read-modify-write
> > + * these registers mark them as non-volatile. This makes sure we read
> > + * the new values from the regmap register cache.
> > + */
> > +static const struct regmap_range vop2_nonvolatile_range[] = {
> > +	regmap_reg_range(0x1000, 0x23ff),
> > +};
> > +
> > +static const struct regmap_access_table vop2_volatile_table = {
> > +	.no_ranges = vop2_nonvolatile_range,
> > +	.n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range),
> > +};
> > +
> > +static const struct regmap_config vop2_regmap_config = {
> > +	.reg_bits	= 32,
> > +	.val_bits	= 32,
> > +	.reg_stride	= 4,
> > +	.max_register	= 0x3000,
> > +	.name		= "vop2",
> > +	.volatile_table	= &vop2_volatile_table,
> > +	.cache_type	= REGCACHE_RBTREE,
> > +};
> > +
> > +static int vop2_bind(struct device *dev, struct device *master, void *data)
> > +{
> > +	struct platform_device *pdev = to_platform_device(dev);
> > +	const struct vop2_data *vop2_data;
> > +	struct drm_device *drm = data;
> > +	struct vop2 *vop2;
> > +	struct resource *res;
> > +	size_t alloc_size;
> > +	int ret;
> > +
> > +	vop2_data = of_device_get_match_data(dev);
> > +	if (!vop2_data)
> > +		return -ENODEV;
> > +
> > +	/* Allocate vop2 struct and its vop2_win array */
> > +	alloc_size = sizeof(*vop2) + sizeof(*vop2->win) * vop2_data->win_size;
> > +	vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
> > +	if (!vop2)
> > +		return -ENOMEM;
> > +
> > +	vop2->dev = dev;
> > +	vop2->data = vop2_data;
> > +	vop2->drm = drm;
> > +
> > +	dev_set_drvdata(dev, vop2);
> > +
> > +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
> > +	if (!res) {
> > +		drm_err(vop2->drm, "failed to get vop2 register byname\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	vop2->regs = devm_ioremap_resource(dev, res);
> > +	if (IS_ERR(vop2->regs))
> > +		return PTR_ERR(vop2->regs);
> > +	vop2->len = resource_size(res);
> > +
> > +	vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config);
> > +
> > +	ret = vop2_win_init(vop2);
> > +	if (ret)
> > +		return ret;
> > +
> > +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma_lut");
> > +	if (res) {
> > +		vop2->lut_regs = devm_ioremap_resource(dev, res);
> > +		if (IS_ERR(vop2->lut_regs))
> > +			return PTR_ERR(vop2->lut_regs);
> > +	}
> > +
> > +	vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
> > +
> > +	vop2->hclk = devm_clk_get(vop2->dev, "hclk");
> > +	if (IS_ERR(vop2->hclk)) {
> > +		drm_err(vop2->drm, "failed to get hclk source\n");
> > +		return PTR_ERR(vop2->hclk);
> > +	}
> > +
> > +	vop2->aclk = devm_clk_get(vop2->dev, "aclk");
> > +	if (IS_ERR(vop2->aclk)) {
> > +		drm_err(vop2->drm, "failed to get aclk source\n");
> > +		return PTR_ERR(vop2->aclk);
> > +	}
> > +
> > +	vop2->irq = platform_get_irq(pdev, 0);
> > +	if (vop2->irq < 0) {
> > +		drm_err(vop2->drm, "cannot find irq for vop2\n");
> > +		return vop2->irq;
> > +	}
> > +
> > +	mutex_init(&vop2->vop2_lock);
> > +
> > +	ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev);
> > +	if (ret) {
> > +		drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = vop2_create_crtc(vop2);
> > +	if (ret)
> > +		return ret;
> > +
> > +	pm_runtime_enable(&pdev->dev);
> > +
> > +	return 0;
> > +}
> > +
> > +static void vop2_unbind(struct device *dev, struct device *master, void *data)
> > +{
> > +	struct vop2 *vop2 = dev_get_drvdata(dev);
> > +	struct drm_device *drm = vop2->drm;
> > +	struct list_head *plane_list = &drm->mode_config.plane_list;
> > +	struct list_head *crtc_list = &drm->mode_config.crtc_list;
> > +	struct drm_crtc *crtc, *tmpc;
> > +	struct drm_plane *plane, *tmpp;
> > +
> > +	rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
> > +
> > +	pm_runtime_disable(dev);
> > +
> > +	list_for_each_entry_safe(plane, tmpp, plane_list, head)
> > +		drm_plane_cleanup(plane);
> > +
> > +	list_for_each_entry_safe(crtc, tmpc, crtc_list, head)
> > +		vop2_destroy_crtc(crtc);
> > +}
> > +
> > +const struct component_ops vop2_component_ops = {
> > +	.bind = vop2_bind,
> > +	.unbind = vop2_unbind,
> > +};
> > +EXPORT_SYMBOL_GPL(vop2_component_ops);
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> > new file mode 100644
> > index 0000000000000..c727093a06d68
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
> > @@ -0,0 +1,477 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> > + * Author:Mark Yao <mark.yao@rock-chips.com>
> > + */
> > +
> > +#ifndef _ROCKCHIP_DRM_VOP2_H
> > +#define _ROCKCHIP_DRM_VOP2_H
> > +
> > +#include "rockchip_drm_vop.h"
> > +
> > +#include <linux/regmap.h>
> > +#include <drm/drm_modes.h>
> > +
> > +#define VOP_FEATURE_OUTPUT_10BIT        BIT(0)
> > +
> > +#define WIN_FEATURE_AFBDC		BIT(0)
> > +#define WIN_FEATURE_CLUSTER		BIT(1)
> > +
> > +/*
> > + *  the delay number of a window in different mode.
> > + */
> > +enum win_dly_mode {
> > +	VOP2_DLY_MODE_DEFAULT,   /**< default mode */
> > +	VOP2_DLY_MODE_HISO_S,    /** HDR in SDR out mode, as a SDR window */
> > +	VOP2_DLY_MODE_HIHO_H,    /** HDR in HDR out mode, as a HDR window */
> > +	VOP2_DLY_MODE_MAX,
> > +};
> > +
> > +struct vop_rect {
> > +	int width;
> > +	int height;
> > +};
> > +
> > +enum vop2_scale_up_mode {
> > +	VOP2_SCALE_UP_NRST_NBOR,
> > +	VOP2_SCALE_UP_BIL,
> > +	VOP2_SCALE_UP_BIC,
> > +};
> > +
> > +enum vop2_scale_down_mode {
> > +	VOP2_SCALE_DOWN_NRST_NBOR,
> > +	VOP2_SCALE_DOWN_BIL,
> > +	VOP2_SCALE_DOWN_AVG,
> > +};
> > +
> > +enum vop2_win_regs {
> > +	VOP2_WIN_ENABLE,
> > +	VOP2_WIN_FORMAT,
> > +	VOP2_WIN_CSC_MODE,
> > +	VOP2_WIN_XMIRROR,
> > +	VOP2_WIN_YMIRROR,
> > +	VOP2_WIN_RB_SWAP,
> > +	VOP2_WIN_UV_SWAP,
> > +	VOP2_WIN_ACT_INFO,
> > +	VOP2_WIN_DSP_INFO,
> > +	VOP2_WIN_DSP_ST,
> > +	VOP2_WIN_YRGB_MST,
> > +	VOP2_WIN_UV_MST,
> > +	VOP2_WIN_YRGB_VIR,
> > +	VOP2_WIN_UV_VIR,
> > +	VOP2_WIN_YUV_CLIP,
> > +	VOP2_WIN_Y2R_EN,
> > +	VOP2_WIN_R2Y_EN,
> > +	VOP2_WIN_COLOR_KEY,
> > +	VOP2_WIN_COLOR_KEY_EN,
> > +	VOP2_WIN_DITHER_UP,
> > +
> > +	/* scale regs */
> > +	VOP2_WIN_SCALE_YRGB_X,
> > +	VOP2_WIN_SCALE_YRGB_Y,
> > +	VOP2_WIN_SCALE_CBCR_X,
> > +	VOP2_WIN_SCALE_CBCR_Y,
> > +	VOP2_WIN_YRGB_HOR_SCL_MODE,
> > +	VOP2_WIN_YRGB_HSCL_FILTER_MODE,
> > +	VOP2_WIN_YRGB_VER_SCL_MODE,
> > +	VOP2_WIN_YRGB_VSCL_FILTER_MODE,
> > +	VOP2_WIN_CBCR_VER_SCL_MODE,
> > +	VOP2_WIN_CBCR_HSCL_FILTER_MODE,
> > +	VOP2_WIN_CBCR_HOR_SCL_MODE,
> > +	VOP2_WIN_CBCR_VSCL_FILTER_MODE,
> > +	VOP2_WIN_VSD_CBCR_GT2,
> > +	VOP2_WIN_VSD_CBCR_GT4,
> > +	VOP2_WIN_VSD_YRGB_GT2,
> > +	VOP2_WIN_VSD_YRGB_GT4,
> > +	VOP2_WIN_BIC_COE_SEL,
> > +
> > +	/* cluster regs */
> > +	VOP2_WIN_CLUSTER_ENABLE,
> > +	VOP2_WIN_AFBC_ENABLE,
> > +	VOP2_WIN_CLUSTER_LB_MODE,
> > +
> > +	/* afbc regs */
> > +	VOP2_WIN_AFBC_FORMAT,
> > +	VOP2_WIN_AFBC_RB_SWAP,
> > +	VOP2_WIN_AFBC_UV_SWAP,
> > +	VOP2_WIN_AFBC_AUTO_GATING_EN,
> > +	VOP2_WIN_AFBC_BLOCK_SPLIT_EN,
> > +	VOP2_WIN_AFBC_PIC_VIR_WIDTH,
> > +	VOP2_WIN_AFBC_TILE_NUM,
> > +	VOP2_WIN_AFBC_PIC_OFFSET,
> > +	VOP2_WIN_AFBC_PIC_SIZE,
> > +	VOP2_WIN_AFBC_DSP_OFFSET,
> > +	VOP2_WIN_AFBC_TRANSFORM_OFFSET,
> > +	VOP2_WIN_AFBC_HDR_PTR,
> > +	VOP2_WIN_AFBC_HALF_BLOCK_EN,
> > +	VOP2_WIN_AFBC_ROTATE_270,
> > +	VOP2_WIN_AFBC_ROTATE_90,
> > +	VOP2_WIN_MAX_REG,
> > +};
> > +
> > +struct vop2_win_data {
> > +	const char *name;
> > +	unsigned int phys_id;
> > +
> > +	u32 base;
> > +	enum drm_plane_type type;
> > +
> > +	u32 nformats;
> > +	const u32 *formats;
> > +	const uint64_t *format_modifiers;
> > +	const unsigned int supported_rotations;
> > +
> > +	/**
> > +	 * @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2
> > +	 */
> > +	unsigned int layer_sel_id;
> > +	uint64_t feature;
> > +
> > +	unsigned int max_upscale_factor;
> > +	unsigned int max_downscale_factor;
> > +	const u8 dly[VOP2_DLY_MODE_MAX];
> > +};
> > +
> > +struct vop2_video_port_data {
> > +	unsigned int id;
> > +	u32 feature;
> > +	u16 gamma_lut_len;
> > +	u16 cubic_lut_len;
> > +	struct vop_rect max_output;
> > +	const u8 pre_scan_max_dly[4];
> > +	const struct vop2_video_port_regs *regs;
> > +	unsigned int offset;
> > +};
> > +
> > +struct vop2_data {
> > +	u8 nr_vps;
> > +	const struct vop2_ctrl *ctrl;
> > +	const struct vop2_win_data *win;
> > +	const struct vop2_video_port_data *vp;
> > +	const struct vop_csc_table *csc_table;
> > +	struct vop_rect max_input;
> > +	struct vop_rect max_output;
> > +
> > +	unsigned int win_size;
> > +	unsigned int soc_id;
> > +};
> > +
> > +/* interrupt define */
> > +#define FS_NEW_INTR			BIT(4)
> > +#define ADDR_SAME_INTR			BIT(5)
> > +#define LINE_FLAG1_INTR			BIT(6)
> > +#define WIN0_EMPTY_INTR			BIT(7)
> > +#define WIN1_EMPTY_INTR			BIT(8)
> > +#define WIN2_EMPTY_INTR			BIT(9)
> > +#define WIN3_EMPTY_INTR			BIT(10)
> > +#define HWC_EMPTY_INTR			BIT(11)
> > +#define POST_BUF_EMPTY_INTR		BIT(12)
> > +#define PWM_GEN_INTR			BIT(13)
> > +#define DMA_FINISH_INTR			BIT(14)
> > +#define FS_FIELD_INTR			BIT(15)
> > +#define FE_INTR				BIT(16)
> > +#define WB_UV_FIFO_FULL_INTR		BIT(17)
> > +#define WB_YRGB_FIFO_FULL_INTR		BIT(18)
> > +#define WB_COMPLETE_INTR		BIT(19)
> > +
> > +/*
> > + * display output interface supported by rockchip lcdc
> > + */
> > +#define ROCKCHIP_OUT_MODE_P888		0
> > +#define ROCKCHIP_OUT_MODE_BT1120	0
> > +#define ROCKCHIP_OUT_MODE_P666		1
> > +#define ROCKCHIP_OUT_MODE_P565		2
> > +#define ROCKCHIP_OUT_MODE_BT656		5
> > +#define ROCKCHIP_OUT_MODE_S888		8
> > +#define ROCKCHIP_OUT_MODE_S888_DUMMY	12
> > +#define ROCKCHIP_OUT_MODE_YUV420	14
> > +/* for use special outface */
> > +#define ROCKCHIP_OUT_MODE_AAAA		15
> > +
> > +enum vop_csc_format {
> > +	CSC_BT601L,
> > +	CSC_BT709L,
> > +	CSC_BT601F,
> > +	CSC_BT2020,
> > +};
> > +
> > +enum src_factor_mode {
> > +	SRC_FAC_ALPHA_ZERO,
> > +	SRC_FAC_ALPHA_ONE,
> > +	SRC_FAC_ALPHA_DST,
> > +	SRC_FAC_ALPHA_DST_INVERSE,
> > +	SRC_FAC_ALPHA_SRC,
> > +	SRC_FAC_ALPHA_SRC_GLOBAL,
> > +};
> > +
> > +enum dst_factor_mode {
> > +	DST_FAC_ALPHA_ZERO,
> > +	DST_FAC_ALPHA_ONE,
> > +	DST_FAC_ALPHA_SRC,
> > +	DST_FAC_ALPHA_SRC_INVERSE,
> > +	DST_FAC_ALPHA_DST,
> > +	DST_FAC_ALPHA_DST_GLOBAL,
> > +};
> > +
> > +#define RK3568_GRF_VO_CON1			0x0364
> > +/* System registers definition */
> > +#define RK3568_REG_CFG_DONE			0x000
> > +#define RK3568_VERSION_INFO			0x004
> > +#define RK3568_SYS_AUTO_GATING_CTRL		0x008
> > +#define RK3568_SYS_AXI_LUT_CTRL			0x024
> > +#define RK3568_DSP_IF_EN			0x028
> > +#define RK3568_DSP_IF_CTRL			0x02c
> > +#define RK3568_DSP_IF_POL			0x030
> > +#define RK3568_WB_CTRL				0x40
> > +#define RK3568_WB_XSCAL_FACTOR			0x44
> > +#define RK3568_WB_YRGB_MST			0x48
> > +#define RK3568_WB_CBR_MST			0x4C
> > +#define RK3568_OTP_WIN_EN			0x050
> > +#define RK3568_LUT_PORT_SEL			0x058
> > +#define RK3568_SYS_STATUS0			0x060
> > +#define RK3568_VP_LINE_FLAG(vp)			(0x70 + (vp) * 0x4)
> > +#define RK3568_SYS0_INT_EN			0x80
> > +#define RK3568_SYS0_INT_CLR			0x84
> > +#define RK3568_SYS0_INT_STATUS			0x88
> > +#define RK3568_SYS1_INT_EN			0x90
> > +#define RK3568_SYS1_INT_CLR			0x94
> > +#define RK3568_SYS1_INT_STATUS			0x98
> > +#define RK3568_VP_INT_EN(vp)			(0xA0 + (vp) * 0x10)
> > +#define RK3568_VP_INT_CLR(vp)			(0xA4 + (vp) * 0x10)
> > +#define RK3568_VP_INT_STATUS(vp)		(0xA8 + (vp) * 0x10)
> > +#define RK3568_VP_INT_RAW_STATUS(vp)		(0xAC + (vp) * 0x10)
> > +
> > +/* Video Port registers definition */
> > +#define RK3568_VP_DSP_CTRL			0x00
> > +#define RK3568_VP_MIPI_CTRL			0x04
> > +#define RK3568_VP_COLOR_BAR_CTRL		0x08
> > +#define RK3568_VP_3D_LUT_CTRL			0x10
> > +#define RK3568_VP_3D_LUT_MST			0x20
> > +#define RK3568_VP_DSP_BG			0x2C
> > +#define RK3568_VP_PRE_SCAN_HTIMING		0x30
> > +#define RK3568_VP_POST_DSP_HACT_INFO		0x34
> > +#define RK3568_VP_POST_DSP_VACT_INFO		0x38
> > +#define RK3568_VP_POST_SCL_FACTOR_YRGB		0x3C
> > +#define RK3568_VP_POST_SCL_CTRL			0x40
> > +#define RK3568_VP_POST_DSP_VACT_INFO_F1		0x44
> > +#define RK3568_VP_DSP_HTOTAL_HS_END		0x48
> > +#define RK3568_VP_DSP_HACT_ST_END		0x4C
> > +#define RK3568_VP_DSP_VTOTAL_VS_END		0x50
> > +#define RK3568_VP_DSP_VACT_ST_END		0x54
> > +#define RK3568_VP_DSP_VS_ST_END_F1		0x58
> > +#define RK3568_VP_DSP_VACT_ST_END_F1		0x5C
> > +#define RK3568_VP_BCSH_CTRL			0x60
> > +#define RK3568_VP_BCSH_BCS			0x64
> > +#define RK3568_VP_BCSH_H			0x68
> > +#define RK3568_VP_BCSH_COLOR_BAR		0x6C
> > +
> > +/* Overlay registers definition    */
> > +#define RK3568_OVL_CTRL				0x600
> > +#define RK3568_OVL_LAYER_SEL			0x604
> > +#define RK3568_OVL_PORT_SEL			0x608
> > +#define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL	0x610
> > +#define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL	0x614
> > +#define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL	0x618
> > +#define RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL	0x61C
> > +#define RK3568_MIX0_SRC_COLOR_CTRL		0x650
> > +#define RK3568_MIX0_DST_COLOR_CTRL		0x654
> > +#define RK3568_MIX0_SRC_ALPHA_CTRL		0x658
> > +#define RK3568_MIX0_DST_ALPHA_CTRL		0x65C
> > +#define RK3568_HDR0_SRC_COLOR_CTRL		0x6C0
> > +#define RK3568_HDR0_DST_COLOR_CTRL		0x6C4
> > +#define RK3568_HDR0_SRC_ALPHA_CTRL		0x6C8
> > +#define RK3568_HDR0_DST_ALPHA_CTRL		0x6CC
> > +#define RK3568_VP_BG_MIX_CTRL(vp)		(0x6E0 + (vp) * 4)
> > +#define RK3568_CLUSTER_DLY_NUM			0x6F0
> > +#define RK3568_SMART_DLY_NUM			0x6F8
> > +
> > +/* Cluster register definition, offset relative to window base */
> > +#define RK3568_CLUSTER_WIN_CTRL0		0x00
> > +#define RK3568_CLUSTER_WIN_CTRL1		0x04
> > +#define RK3568_CLUSTER_WIN_YRGB_MST		0x10
> > +#define RK3568_CLUSTER_WIN_CBR_MST		0x14
> > +#define RK3568_CLUSTER_WIN_VIR			0x18
> > +#define RK3568_CLUSTER_WIN_ACT_INFO		0x20
> > +#define RK3568_CLUSTER_WIN_DSP_INFO		0x24
> > +#define RK3568_CLUSTER_WIN_DSP_ST		0x28
> > +#define RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB	0x30
> > +#define RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET	0x3C
> > +#define RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL	0x50
> > +#define RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE	0x54
> > +#define RK3568_CLUSTER_WIN_AFBCD_HDR_PTR	0x58
> > +#define RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH	0x5C
> > +#define RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE	0x60
> > +#define RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET	0x64
> > +#define RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET	0x68
> > +#define RK3568_CLUSTER_WIN_AFBCD_CTRL		0x6C
> > +
> > +#define RK3568_CLUSTER_CTRL			0x100
> > +
> > +/* (E)smart register definition, offset relative to window base */
> > +#define RK3568_SMART_CTRL0			0x00
> > +#define RK3568_SMART_CTRL1			0x04
> > +#define RK3568_SMART_REGION0_CTRL		0x10
> > +#define RK3568_SMART_REGION0_YRGB_MST		0x14
> > +#define RK3568_SMART_REGION0_CBR_MST		0x18
> > +#define RK3568_SMART_REGION0_VIR		0x1C
> > +#define RK3568_SMART_REGION0_ACT_INFO		0x20
> > +#define RK3568_SMART_REGION0_DSP_INFO		0x24
> > +#define RK3568_SMART_REGION0_DSP_ST		0x28
> > +#define RK3568_SMART_REGION0_SCL_CTRL		0x30
> > +#define RK3568_SMART_REGION0_SCL_FACTOR_YRGB	0x34
> > +#define RK3568_SMART_REGION0_SCL_FACTOR_CBR	0x38
> > +#define RK3568_SMART_REGION0_SCL_OFFSET		0x3C
> > +#define RK3568_SMART_REGION1_CTRL		0x40
> > +#define RK3568_SMART_REGION1_YRGB_MST		0x44
> > +#define RK3568_SMART_REGION1_CBR_MST		0x48
> > +#define RK3568_SMART_REGION1_VIR		0x4C
> > +#define RK3568_SMART_REGION1_ACT_INFO		0x50
> > +#define RK3568_SMART_REGION1_DSP_INFO		0x54
> > +#define RK3568_SMART_REGION1_DSP_ST		0x58
> > +#define RK3568_SMART_REGION1_SCL_CTRL		0x60
> > +#define RK3568_SMART_REGION1_SCL_FACTOR_YRGB	0x64
> > +#define RK3568_SMART_REGION1_SCL_FACTOR_CBR	0x68
> > +#define RK3568_SMART_REGION1_SCL_OFFSET		0x6C
> > +#define RK3568_SMART_REGION2_CTRL		0x70
> > +#define RK3568_SMART_REGION2_YRGB_MST		0x74
> > +#define RK3568_SMART_REGION2_CBR_MST		0x78
> > +#define RK3568_SMART_REGION2_VIR		0x7C
> > +#define RK3568_SMART_REGION2_ACT_INFO		0x80
> > +#define RK3568_SMART_REGION2_DSP_INFO		0x84
> > +#define RK3568_SMART_REGION2_DSP_ST		0x88
> > +#define RK3568_SMART_REGION2_SCL_CTRL		0x90
> > +#define RK3568_SMART_REGION2_SCL_FACTOR_YRGB	0x94
> > +#define RK3568_SMART_REGION2_SCL_FACTOR_CBR	0x98
> > +#define RK3568_SMART_REGION2_SCL_OFFSET		0x9C
> > +#define RK3568_SMART_REGION3_CTRL		0xA0
> > +#define RK3568_SMART_REGION3_YRGB_MST		0xA4
> > +#define RK3568_SMART_REGION3_CBR_MST		0xA8
> > +#define RK3568_SMART_REGION3_VIR		0xAC
> > +#define RK3568_SMART_REGION3_ACT_INFO		0xB0
> > +#define RK3568_SMART_REGION3_DSP_INFO		0xB4
> > +#define RK3568_SMART_REGION3_DSP_ST		0xB8
> > +#define RK3568_SMART_REGION3_SCL_CTRL		0xC0
> > +#define RK3568_SMART_REGION3_SCL_FACTOR_YRGB	0xC4
> > +#define RK3568_SMART_REGION3_SCL_FACTOR_CBR	0xC8
> > +#define RK3568_SMART_REGION3_SCL_OFFSET		0xCC
> > +#define RK3568_SMART_COLOR_KEY_CTRL		0xD0
> > +
> > +/* HDR register definition */
> > +#define RK3568_HDR_LUT_CTRL			0x2000
> > +#define RK3568_HDR_LUT_MST			0x2004
> > +#define RK3568_SDR2HDR_CTRL			0x2010
> > +#define RK3568_HDR2SDR_CTRL			0x2020
> > +#define RK3568_HDR2SDR_SRC_RANGE		0x2024
> > +#define RK3568_HDR2SDR_NORMFACEETF		0x2028
> > +#define RK3568_HDR2SDR_DST_RANGE		0x202C
> > +#define RK3568_HDR2SDR_NORMFACCGAMMA		0x2030
> > +#define RK3568_HDR_EETF_OETF_Y0			0x203C
> > +#define RK3568_HDR_SAT_Y0			0x20C0
> > +#define RK3568_HDR_EOTF_OETF_Y0			0x20F0
> > +#define RK3568_HDR_OETF_DX_POW1			0x2200
> > +#define RK3568_HDR_OETF_XN1			0x2300
> > +
> > +#define RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN		BIT(15)
> > +
> > +#define RK3568_VP_DSP_CTRL__STANDBY			BIT(31)
> > +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_MODE		BIT(20)
> > +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL		GENMASK(19, 18)
> > +#define RK3568_VP_DSP_CTRL__DITHER_DOWN_EN		BIT(17)
> > +#define RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN		BIT(16)
> > +#define RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y		BIT(15)
> > +#define RK3568_VP_DSP_CTRL__DSP_RB_SWAP			BIT(9)
> > +#define RK3568_VP_DSP_CTRL__DSP_INTERLACE		BIT(7)
> > +#define RK3568_VP_DSP_CTRL__DSP_FILED_POL		BIT(6)
> > +#define RK3568_VP_DSP_CTRL__P2I_EN			BIT(5)
> > +#define RK3568_VP_DSP_CTRL__CORE_DCLK_DIV		BIT(4)
> > +#define RK3568_VP_DSP_CTRL__OUT_MODE			GENMASK(3, 0)
> > +
> > +#define RK3568_VP_POST_SCL_CTRL__VSCALEDOWN		BIT(1)
> > +#define RK3568_VP_POST_SCL_CTRL__HSCALEDOWN		BIT(0)
> > +
> > +#define RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX		GENMASK(26, 25)
> > +#define RK3568_SYS_DSP_INFACE_EN_LVDS1			BIT(24)
> > +#define RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX		GENMASK(22, 21)
> > +#define RK3568_SYS_DSP_INFACE_EN_MIPI1			BIT(20)
> > +#define RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX		GENMASK(19, 18)
> > +#define RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX		GENMASK(17, 16)
> > +#define RK3568_SYS_DSP_INFACE_EN_EDP_MUX		GENMASK(15, 14)
> > +#define RK3568_SYS_DSP_INFACE_EN_HDMI_MUX		GENMASK(11, 10)
> > +#define RK3568_SYS_DSP_INFACE_EN_RGB_MUX		GENMASK(9, 8)
> > +#define RK3568_SYS_DSP_INFACE_EN_LVDS0			BIT(5)
> > +#define RK3568_SYS_DSP_INFACE_EN_MIPI0			BIT(4)
> > +#define RK3568_SYS_DSP_INFACE_EN_EDP			BIT(3)
> > +#define RK3568_SYS_DSP_INFACE_EN_HDMI			BIT(1)
> > +#define RK3568_SYS_DSP_INFACE_EN_RGB			BIT(0)
> > +
> > +#define RK3568_DSP_IF_POL__MIPI_PIN_POL			GENMASK(19, 16)
> > +#define RK3568_DSP_IF_POL__EDP_PIN_POL			GENMASK(15, 12)
> > +#define RK3568_DSP_IF_POL__HDMI_PIN_POL			GENMASK(7, 4)
> > +#define RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL		GENMASK(3, 0)
> > +
> > +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2_PHASE_LOCK	BIT(5)
> > +#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2			BIT(4)
> > +
> > +#define RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN	BIT(31)
> > +
> > +#define RK3568_DSP_IF_POL__CFG_DONE_IMD			BIT(28)
> > +
> > +#define VOP2_SYS_AXI_BUS_NUM				2
> > +
> > +#define VOP2_CLUSTER_YUV444_10				0x12
> > +
> > +#define VOP2_COLOR_KEY_MASK				BIT(31)
> > +
> > +#define RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD		BIT(28)
> > +
> > +#define RK3568_VP_BG_MIX_CTRL__BG_DLY			GENMASK(31, 24)
> > +
> > +#define RK3568_OVL_PORT_SEL__SEL_PORT			GENMASK(31, 16)
> > +#define RK3568_OVL_PORT_SEL__SMART1			GENMASK(31, 30)
> > +#define RK3568_OVL_PORT_SEL__SMART0			GENMASK(29, 28)
> > +#define RK3568_OVL_PORT_SEL__ESMART1			GENMASK(27, 26)
> > +#define RK3568_OVL_PORT_SEL__ESMART0			GENMASK(25, 24)
> > +#define RK3568_OVL_PORT_SEL__CLUSTER1			GENMASK(19, 18)
> > +#define RK3568_OVL_PORT_SEL__CLUSTER0			GENMASK(17, 16)
> > +#define RK3568_OVL_PORT_SET__PORT2_MUX			GENMASK(11, 8)
> > +#define RK3568_OVL_PORT_SET__PORT1_MUX			GENMASK(7, 4)
> > +#define RK3568_OVL_PORT_SET__PORT0_MUX			GENMASK(3, 0)
> > +#define RK3568_OVL_LAYER_SEL__LAYER(layer, x)		((x) << ((layer) * 4))
> > +
> > +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_1		GENMASK(31, 24)
> > +#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_0		GENMASK(23, 16)
> > +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_1		GENMASK(15, 8)
> > +#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_0		GENMASK(7, 0)
> > +
> > +#define RK3568_SMART_DLY_NUM__SMART1			GENMASK(31, 24)
> > +#define RK3568_SMART_DLY_NUM__SMART0			GENMASK(23, 16)
> > +#define RK3568_SMART_DLY_NUM__ESMART1			GENMASK(15, 8)
> > +#define RK3568_SMART_DLY_NUM__ESMART0			GENMASK(7, 0)
> > +
> > +#define VP_INT_DSP_HOLD_VALID	BIT(6)
> > +#define VP_INT_FS_FIELD		BIT(5)
> > +#define VP_INT_POST_BUF_EMPTY	BIT(4)
> > +#define VP_INT_LINE_FLAG1	BIT(3)
> > +#define VP_INT_LINE_FLAG0	BIT(2)
> > +#define VOP2_INT_BUS_ERRPR	BIT(1)
> > +#define VP_INT_FS		BIT(0)
> > +
> > +#define POLFLAG_DCLK_INV	BIT(3)
> > +
> > +enum vop2_layer_phy_id {
> > +	ROCKCHIP_VOP2_CLUSTER0 = 0,
> > +	ROCKCHIP_VOP2_CLUSTER1,
> > +	ROCKCHIP_VOP2_ESMART0,
> > +	ROCKCHIP_VOP2_ESMART1,
> > +	ROCKCHIP_VOP2_SMART0,
> > +	ROCKCHIP_VOP2_SMART1,
> > +	ROCKCHIP_VOP2_CLUSTER2,
> > +	ROCKCHIP_VOP2_CLUSTER3,
> > +	ROCKCHIP_VOP2_ESMART2,
> > +	ROCKCHIP_VOP2_ESMART3,
> > +	ROCKCHIP_VOP2_PHY_ID_INVALID = -1,
> > +};
> > +
> > +extern const struct component_ops vop2_component_ops;
> > +
> > +#endif /* _ROCKCHIP_DRM_VOP2_H */
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> > new file mode 100644
> > index 0000000000000..9bf0637bf8e26
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
> > @@ -0,0 +1,281 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Copyright (C) Rockchip Electronics Co.Ltd
> > + * Author: Andy Yan <andy.yan@rock-chips.com>
> > + */
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/component.h>
> > +#include <linux/mod_devicetable.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/of.h>
> > +#include <drm/drm_fourcc.h>
> > +#include <drm/drm_plane.h>
> > +#include <drm/drm_print.h>
> > +
> > +#include "rockchip_drm_vop2.h"
> > +
> > +static const uint32_t formats_win_full_10bit[] = {
> > +	DRM_FORMAT_XRGB8888,
> > +	DRM_FORMAT_ARGB8888,
> > +	DRM_FORMAT_XBGR8888,
> > +	DRM_FORMAT_ABGR8888,
> > +	DRM_FORMAT_RGB888,
> > +	DRM_FORMAT_BGR888,
> > +	DRM_FORMAT_RGB565,
> > +	DRM_FORMAT_BGR565,
> > +	DRM_FORMAT_NV12,
> > +	DRM_FORMAT_NV16,
> > +	DRM_FORMAT_NV24,
> > +};
> > +
> > +static const uint32_t formats_win_full_10bit_yuyv[] = {
> > +	DRM_FORMAT_XRGB8888,
> > +	DRM_FORMAT_ARGB8888,
> > +	DRM_FORMAT_XBGR8888,
> > +	DRM_FORMAT_ABGR8888,
> > +	DRM_FORMAT_RGB888,
> > +	DRM_FORMAT_BGR888,
> > +	DRM_FORMAT_RGB565,
> > +	DRM_FORMAT_BGR565,
> > +	DRM_FORMAT_NV12,
> > +	DRM_FORMAT_NV16,
> > +	DRM_FORMAT_NV24,
> > +	DRM_FORMAT_YVYU,
> > +	DRM_FORMAT_VYUY,
> > +};
> > +
> > +static const uint32_t formats_win_lite[] = {
> > +	DRM_FORMAT_XRGB8888,
> > +	DRM_FORMAT_ARGB8888,
> > +	DRM_FORMAT_XBGR8888,
> > +	DRM_FORMAT_ABGR8888,
> > +	DRM_FORMAT_RGB888,
> > +	DRM_FORMAT_BGR888,
> > +	DRM_FORMAT_RGB565,
> > +	DRM_FORMAT_BGR565,
> > +};
> > +
> > +static const uint64_t format_modifiers[] = {
> > +	DRM_FORMAT_MOD_LINEAR,
> > +	DRM_FORMAT_MOD_INVALID,
> > +};
> > +
> > +static const uint64_t format_modifiers_afbc[] = {
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_SPARSE),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_YTR),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_CBR),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_YTR |
> > +				AFBC_FORMAT_MOD_SPARSE),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_CBR |
> > +				AFBC_FORMAT_MOD_SPARSE),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_YTR |
> > +				AFBC_FORMAT_MOD_CBR),
> > +
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_YTR |
> > +				AFBC_FORMAT_MOD_CBR |
> > +				AFBC_FORMAT_MOD_SPARSE),
> > +
> > +	/* SPLIT mandates SPARSE, RGB modes mandates YTR */
> > +	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
> > +				AFBC_FORMAT_MOD_YTR |
> > +				AFBC_FORMAT_MOD_SPARSE |
> > +				AFBC_FORMAT_MOD_SPLIT),
> > +	DRM_FORMAT_MOD_INVALID,
> > +};
> > +
> > +static const struct vop2_video_port_data rk3568_vop_video_ports[] = {
> > +	{
> > +		.id = 0,
> > +		.feature = VOP_FEATURE_OUTPUT_10BIT,
> > +		.gamma_lut_len = 1024,
> > +		.cubic_lut_len = 9 * 9 * 9,
> > +		.max_output = { 4096, 2304 },
> > +		.pre_scan_max_dly = { 69, 53, 53, 42 },
> > +		.offset = 0xc00,
> > +	}, {
> > +		.id = 1,
> > +		.gamma_lut_len = 1024,
> > +		.max_output = { 2048, 1536 },
> > +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> > +		.offset = 0xd00,
> > +	}, {
> > +		.id = 2,
> > +		.gamma_lut_len = 1024,
> > +		.max_output = { 1920, 1080 },
> > +		.pre_scan_max_dly = { 40, 40, 40, 40 },
> > +		.offset = 0xe00,
> > +	},
> > +};
> > +
> > +/*
> > + * rk3568 vop with 2 cluster, 2 esmart win, 2 smart win.
> > + * Every cluster can work as 4K win or split into two win.
> > + * All win in cluster support AFBCD.
> > + *
> > + * Every esmart win and smart win support 4 Multi-region.
> > + *
> > + * Scale filter mode:
> > + *
> > + * * Cluster:  bicubic for horizontal scale up, others use bilinear
> > + * * ESmart:
> > + *    * nearest-neighbor/bilinear/bicubic for scale up
> > + *    * nearest-neighbor/bilinear/average for scale down
> > + *
> > + *
> > + * @TODO describe the wind like cpu-map dt nodes;
> > + */
> > +static const struct vop2_win_data rk3568_vop_win_data[] = {
> > +	{
> > +		.name = "Smart0-win0",
> > +		.phys_id = ROCKCHIP_VOP2_SMART0,
> > +		.base = 0x1c00,
> > +		.formats = formats_win_lite,
> > +		.nformats = ARRAY_SIZE(formats_win_lite),
> > +		.format_modifiers = format_modifiers,
> > +		.layer_sel_id = 3,
> > +		.supported_rotations = DRM_MODE_REFLECT_Y,
> > +		.type = DRM_PLANE_TYPE_PRIMARY,
> > +		.max_upscale_factor = 8,
> > +		.max_downscale_factor = 8,
> > +		.dly = { 20, 47, 41 },
> > +	}, {
> > +		.name = "Smart1-win0",
> > +		.phys_id = ROCKCHIP_VOP2_SMART1,
> > +		.formats = formats_win_lite,
> > +		.nformats = ARRAY_SIZE(formats_win_lite),
> > +		.format_modifiers = format_modifiers,
> > +		.base = 0x1e00,
> > +		.layer_sel_id = 7,
> > +		.supported_rotations = DRM_MODE_REFLECT_Y,
> > +		.type = DRM_PLANE_TYPE_PRIMARY,
> > +		.max_upscale_factor = 8,
> > +		.max_downscale_factor = 8,
> > +		.dly = { 20, 47, 41 },
> > +	}, {
> > +		.name = "Esmart1-win0",
> > +		.phys_id = ROCKCHIP_VOP2_ESMART1,
> > +		.formats = formats_win_full_10bit_yuyv,
> > +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> > +		.format_modifiers = format_modifiers,
> > +		.base = 0x1a00,
> > +		.layer_sel_id = 6,
> > +		.supported_rotations = DRM_MODE_REFLECT_Y,
> > +		.type = DRM_PLANE_TYPE_PRIMARY,
> > +		.max_upscale_factor = 8,
> > +		.max_downscale_factor = 8,
> > +		.dly = { 20, 47, 41 },
> > +	}, {
> > +		.name = "Esmart0-win0",
> > +		.phys_id = ROCKCHIP_VOP2_ESMART0,
> > +		.formats = formats_win_full_10bit_yuyv,
> > +		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
> > +		.format_modifiers = format_modifiers,
> > +		.base = 0x1800,
> > +		.layer_sel_id = 2,
> > +		.supported_rotations = DRM_MODE_REFLECT_Y,
> > +		.type = DRM_PLANE_TYPE_OVERLAY,
> > +		.max_upscale_factor = 8,
> > +		.max_downscale_factor = 8,
> > +		.dly = { 20, 47, 41 },
> > +	}, {
> > +		.name = "Cluster0-win0",
> > +		.phys_id = ROCKCHIP_VOP2_CLUSTER0,
> > +		.base = 0x1000,
> > +		.formats = formats_win_full_10bit,
> > +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> > +		.format_modifiers = format_modifiers_afbc,
> > +		.layer_sel_id = 0,
> > +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> > +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> > +		.max_upscale_factor = 4,
> > +		.max_downscale_factor = 4,
> > +		.dly = { 0, 27, 21 },
> > +		.type = DRM_PLANE_TYPE_OVERLAY,
> > +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> > +	}, {
> > +		.name = "Cluster1-win0",
> > +		.phys_id = ROCKCHIP_VOP2_CLUSTER1,
> > +		.base = 0x1200,
> > +		.formats = formats_win_full_10bit,
> > +		.nformats = ARRAY_SIZE(formats_win_full_10bit),
> > +		.format_modifiers = format_modifiers_afbc,
> > +		.layer_sel_id = 1,
> > +		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
> > +					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
> > +		.type = DRM_PLANE_TYPE_OVERLAY,
> > +		.max_upscale_factor = 4,
> > +		.max_downscale_factor = 4,
> > +		.dly = { 0, 27, 21 },
> > +		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
> > +	},
> > +};
> > +
> > +static const struct vop2_data rk3566_vop = {
> > +	.nr_vps = 3,
> > +	.max_input = { 4096, 2304 },
> > +	.max_output = { 4096, 2304 },
> > +	.vp = rk3568_vop_video_ports,
> > +	.win = rk3568_vop_win_data,
> > +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> > +	.soc_id = 3566,
> > +};
> > +
> > +static const struct vop2_data rk3568_vop = {
> > +	.nr_vps = 3,
> > +	.max_input = { 4096, 2304 },
> > +	.max_output = { 4096, 2304 },
> > +	.vp = rk3568_vop_video_ports,
> > +	.win = rk3568_vop_win_data,
> > +	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
> > +	.soc_id = 3568,
> > +};
> > +
> > +static const struct of_device_id vop2_dt_match[] = {
> > +	{
> > +		.compatible = "rockchip,rk3566-vop",
> > +		.data = &rk3566_vop,
> > +	}, {
> > +		.compatible = "rockchip,rk3568-vop",
> > +		.data = &rk3568_vop,
> > +	}, {
> > +	},
> > +};
> > +MODULE_DEVICE_TABLE(of, vop2_dt_match);
> > +
> > +static int vop2_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +
> > +	return component_add(dev, &vop2_component_ops);
> > +}
> > +
> > +static int vop2_remove(struct platform_device *pdev)
> > +{
> > +	component_del(&pdev->dev, &vop2_component_ops);
> > +
> > +	return 0;
> > +}
> > +
> > +struct platform_driver vop2_platform_driver = {
> > +	.probe = vop2_probe,
> > +	.remove = vop2_remove,
> > +	.driver = {
> > +		.name = "rockchip-vop2",
> > +		.of_match_table = of_match_ptr(vop2_dt_match),
> > +	},
> > +};
> 



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

* Re: [PATCH v6 23/23] dt-bindings: display: rockchip: dw-hdmi: fix ports description
  2022-02-17  8:29   ` Sascha Hauer
  (?)
  (?)
@ 2022-02-23 15:55     ` Rob Herring
  -1 siblings, 0 replies; 201+ messages in thread
From: Rob Herring @ 2022-02-23 15:55 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: Benjamin Gaignard, Peter Geis, linux-rockchip, devicetree,
	Sandy Huang, Andy Yan, Heiko Stübner, linux-arm-kernel,
	kernel, Michael Riesch, dri-devel

On Thu, 17 Feb 2022 09:29:54 +0100, Sascha Hauer wrote:
> Current port description doesn't cover all possible cases. It currently
> expects one single port with two endpoints.
> 
> When the HDMI connector is described in the device tree there can be two
> ports, first one going to the VOP and the second one going to the connector.
> 
> Also on SoCs which only have a single VOP there will be only one
> endpoint instead of two.
> 
> This patch addresses both issues. With this there can either be a single
> port ("port") , or two of them ("port@0", "port@1") when the connector
> is also in the device tree. Also the first or only port can either have
> one endpoint ("endpoint") for single VOP SoCs or two ("endpoint@0",
> "endpoint@1") for dual VOP SoCs.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
> 
> Notes:
>     Changes since v5:
>     - new patch
> 
>  .../display/rockchip/rockchip,dw-hdmi.yaml    | 24 +++++++------------
>  1 file changed, 9 insertions(+), 15 deletions(-)
> 

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

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

* Re: [PATCH v6 23/23] dt-bindings: display: rockchip: dw-hdmi: fix ports description
@ 2022-02-23 15:55     ` Rob Herring
  0 siblings, 0 replies; 201+ messages in thread
From: Rob Herring @ 2022-02-23 15:55 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: Benjamin Gaignard, Peter Geis, linux-rockchip, devicetree,
	Sandy Huang, Andy Yan, Heiko Stübner, linux-arm-kernel,
	kernel, Michael Riesch, dri-devel

On Thu, 17 Feb 2022 09:29:54 +0100, Sascha Hauer wrote:
> Current port description doesn't cover all possible cases. It currently
> expects one single port with two endpoints.
> 
> When the HDMI connector is described in the device tree there can be two
> ports, first one going to the VOP and the second one going to the connector.
> 
> Also on SoCs which only have a single VOP there will be only one
> endpoint instead of two.
> 
> This patch addresses both issues. With this there can either be a single
> port ("port") , or two of them ("port@0", "port@1") when the connector
> is also in the device tree. Also the first or only port can either have
> one endpoint ("endpoint") for single VOP SoCs or two ("endpoint@0",
> "endpoint@1") for dual VOP SoCs.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
> 
> Notes:
>     Changes since v5:
>     - new patch
> 
>  .../display/rockchip/rockchip,dw-hdmi.yaml    | 24 +++++++------------
>  1 file changed, 9 insertions(+), 15 deletions(-)
> 

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

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

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

* Re: [PATCH v6 23/23] dt-bindings: display: rockchip: dw-hdmi: fix ports description
@ 2022-02-23 15:55     ` Rob Herring
  0 siblings, 0 replies; 201+ messages in thread
From: Rob Herring @ 2022-02-23 15:55 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: devicetree, Benjamin Gaignard, Sandy Huang, dri-devel,
	linux-rockchip, Michael Riesch, Peter Geis, Andy Yan, kernel,
	linux-arm-kernel

On Thu, 17 Feb 2022 09:29:54 +0100, Sascha Hauer wrote:
> Current port description doesn't cover all possible cases. It currently
> expects one single port with two endpoints.
> 
> When the HDMI connector is described in the device tree there can be two
> ports, first one going to the VOP and the second one going to the connector.
> 
> Also on SoCs which only have a single VOP there will be only one
> endpoint instead of two.
> 
> This patch addresses both issues. With this there can either be a single
> port ("port") , or two of them ("port@0", "port@1") when the connector
> is also in the device tree. Also the first or only port can either have
> one endpoint ("endpoint") for single VOP SoCs or two ("endpoint@0",
> "endpoint@1") for dual VOP SoCs.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
> 
> Notes:
>     Changes since v5:
>     - new patch
> 
>  .../display/rockchip/rockchip,dw-hdmi.yaml    | 24 +++++++------------
>  1 file changed, 9 insertions(+), 15 deletions(-)
> 

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

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

* Re: [PATCH v6 23/23] dt-bindings: display: rockchip: dw-hdmi: fix ports description
@ 2022-02-23 15:55     ` Rob Herring
  0 siblings, 0 replies; 201+ messages in thread
From: Rob Herring @ 2022-02-23 15:55 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: Benjamin Gaignard, Peter Geis, linux-rockchip, devicetree,
	Sandy Huang, Andy Yan, Heiko Stübner, linux-arm-kernel,
	kernel, Michael Riesch, dri-devel

On Thu, 17 Feb 2022 09:29:54 +0100, Sascha Hauer wrote:
> Current port description doesn't cover all possible cases. It currently
> expects one single port with two endpoints.
> 
> When the HDMI connector is described in the device tree there can be two
> ports, first one going to the VOP and the second one going to the connector.
> 
> Also on SoCs which only have a single VOP there will be only one
> endpoint instead of two.
> 
> This patch addresses both issues. With this there can either be a single
> port ("port") , or two of them ("port@0", "port@1") when the connector
> is also in the device tree. Also the first or only port can either have
> one endpoint ("endpoint") for single VOP SoCs or two ("endpoint@0",
> "endpoint@1") for dual VOP SoCs.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
> 
> Notes:
>     Changes since v5:
>     - new patch
> 
>  .../display/rockchip/rockchip,dw-hdmi.yaml    | 24 +++++++------------
>  1 file changed, 9 insertions(+), 15 deletions(-)
> 

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

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
  2022-02-17 13:24     ` Dmitry Osipenko
  (?)
  (?)
@ 2022-02-24  7:47       ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-24  7:47 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

On Thu, Feb 17, 2022 at 04:24:29PM +0300, Dmitry Osipenko wrote:
> 17.02.2022 11:29, Sascha Hauer пишет:
> > @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
> >  	  This selects support for the VOP driver. You should enable it
> >  	  on all older SoCs up to RK3399.
> >  
> > +config ROCKCHIP_VOP2
> > +	bool "Rockchip VOP2 driver"
> > +	help
> > +	  This selects support for the VOP2 driver. You should enable it
> > +	  on all newer SoCs beginning form RK3568.
> 
> s/form/from/
> 
> The ROCKCHIP_VOP option is "default y". Do you really want "default n"
> for the VOP2?

ROCKCHIP_VOP is only default y to keep the VOP driver enabled for
existing defconfig that were generated before the introduction of
that symbol.
We don't have this problem for VOP2, so no need to make it default y.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24  7:47       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-24  7:47 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

On Thu, Feb 17, 2022 at 04:24:29PM +0300, Dmitry Osipenko wrote:
> 17.02.2022 11:29, Sascha Hauer пишет:
> > @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
> >  	  This selects support for the VOP driver. You should enable it
> >  	  on all older SoCs up to RK3399.
> >  
> > +config ROCKCHIP_VOP2
> > +	bool "Rockchip VOP2 driver"
> > +	help
> > +	  This selects support for the VOP2 driver. You should enable it
> > +	  on all newer SoCs beginning form RK3568.
> 
> s/form/from/
> 
> The ROCKCHIP_VOP option is "default y". Do you really want "default n"
> for the VOP2?

ROCKCHIP_VOP is only default y to keep the VOP driver enabled for
existing defconfig that were generated before the introduction of
that symbol.
We don't have this problem for VOP2, so no need to make it default y.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24  7:47       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-24  7:47 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: devicetree, Benjamin Gaignard, kernel, Sandy Huang, dri-devel,
	linux-rockchip, Michael Riesch, Peter Geis, Andy Yan,
	linux-arm-kernel

On Thu, Feb 17, 2022 at 04:24:29PM +0300, Dmitry Osipenko wrote:
> 17.02.2022 11:29, Sascha Hauer пишет:
> > @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
> >  	  This selects support for the VOP driver. You should enable it
> >  	  on all older SoCs up to RK3399.
> >  
> > +config ROCKCHIP_VOP2
> > +	bool "Rockchip VOP2 driver"
> > +	help
> > +	  This selects support for the VOP2 driver. You should enable it
> > +	  on all newer SoCs beginning form RK3568.
> 
> s/form/from/
> 
> The ROCKCHIP_VOP option is "default y". Do you really want "default n"
> for the VOP2?

ROCKCHIP_VOP is only default y to keep the VOP driver enabled for
existing defconfig that were generated before the introduction of
that symbol.
We don't have this problem for VOP2, so no need to make it default y.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24  7:47       ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-24  7:47 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

On Thu, Feb 17, 2022 at 04:24:29PM +0300, Dmitry Osipenko wrote:
> 17.02.2022 11:29, Sascha Hauer пишет:
> > @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
> >  	  This selects support for the VOP driver. You should enable it
> >  	  on all older SoCs up to RK3399.
> >  
> > +config ROCKCHIP_VOP2
> > +	bool "Rockchip VOP2 driver"
> > +	help
> > +	  This selects support for the VOP2 driver. You should enable it
> > +	  on all newer SoCs beginning form RK3568.
> 
> s/form/from/
> 
> The ROCKCHIP_VOP option is "default y". Do you really want "default n"
> for the VOP2?

ROCKCHIP_VOP is only default y to keep the VOP driver enabled for
existing defconfig that were generated before the introduction of
that symbol.
We don't have this problem for VOP2, so no need to make it default y.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
  2022-02-19  7:35               ` Andy Yan
  (?)
  (?)
@ 2022-02-24  8:19                 ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-24  8:19 UTC (permalink / raw)
  To: Andy Yan
  Cc: Heiko Stübner, dri-devel, linux-arm-kernel, linux-rockchip,
	devicetree, kernel, Benjamin Gaignard, Michael Riesch,
	Sandy Huang, Peter Geis

On Sat, Feb 19, 2022 at 03:35:12PM +0800, Andy Yan wrote:
> Hi Sascha:
> 
> On 2/18/22 16:00, Sascha Hauer wrote:
> > On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
> > > Hi Sascha:
> > > 
> > > On 2/17/22 22:06, Heiko Stübner wrote:
> > > > Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
> > > > > Hi Andy,
> > > > > 
> > > > > Please trim the context in your answers to the relevant parts, it makes
> > > > > it easier to find the things you said.
> > > > > 
> > > > > On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> > > > > > Hi Sascha:
> > > > > > 
> > > > > > > +
> > > > > > > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > > > > > > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > > > > > > +		struct device_node *node, *parent;
> > > > > > > +
> > > > > > > +		parent = of_get_parent(rkencoder->port);
> > > > > > > +
> > > > > > > +		for_each_endpoint_of_node(parent, node) {
> > > > > > Is there any hurt directly use our downstream vendor kernel method here: use
> > > > > > vcstate->output_if set by encoder driver to get which interface we should
> > > > > > enable here?
> > > > > There is no vcstate->output_if in mainline currently. Ok, we could add
> > > > > that. The other thing is that there are multiple HDMI interfaces and
> > > > > the id of the HDMI encoder is encoded into output_if. Downstream kernel
> > > > > adds OF aliases to the HDMI ports. I didn't want to go that route
> > > > > because it doesn't seem to be very elegant to me.
> > > aliases is a very comm strategy in device tree world.
> > Yes, but not for retrieving bit offsets into registers. Normally aliases
> > can be changed at board level without confusing drivers.
> > 
> > > And your method also
> > > add need additional dt binds to define RK3568_VOP2_EP_xxx
> > > > > > You method is ok with device tree,  but it tied up this driver to device
> > > > > > tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> > > > > > driver can be much more flexible.
> > > > > The current rockchip drm driver seems to be pretty much tied to device
> > > > > tree. There are probably many other places that need parallel paths for
> > > > > ACPI support, I think we can delay this particular part until we see the
> > > > > whole picture. In the end we can still retrieve the output_if
> > > > > information differently with ACPI while still retrieving the information
> > > > > from the device tree the way we are doing currently.
> > > The current driver only reference device thee at driver initial, we not wrap
> > > 
> > > device tree related things in other parts, so if we extend it to support
> > > ACPI,
> > > 
> > > we just need modify the initial code, this make things easier.
> > The device tree parsing could be moved out of vop2_crtc_atomic_enable()
> > into some initialisation path. In the end it's static information,
> > there's no need to do it repeatedly in atomic_enable.
> 
> This could be one solution, the repeatedly parsing device tree in
> atomic_enable is also my concern.
> 
> In addition, there are 2 HDMI, 2 eDP, 2 MIPI on the coming rk3588, so it's
> better to consider give position
> 
> for HDMI1, EDP1, in  include/dt-bindings/soc/rockchip,vop2.h

The defines are rk3568 specific. rk3588 would use a set of rk3588
specific defines along with a rk3588_set_intf_mux().

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24  8:19                 ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-24  8:19 UTC (permalink / raw)
  To: Andy Yan
  Cc: devicetree, Benjamin Gaignard, Sandy Huang, dri-devel,
	linux-rockchip, Michael Riesch, kernel, Peter Geis,
	linux-arm-kernel

On Sat, Feb 19, 2022 at 03:35:12PM +0800, Andy Yan wrote:
> Hi Sascha:
> 
> On 2/18/22 16:00, Sascha Hauer wrote:
> > On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
> > > Hi Sascha:
> > > 
> > > On 2/17/22 22:06, Heiko Stübner wrote:
> > > > Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
> > > > > Hi Andy,
> > > > > 
> > > > > Please trim the context in your answers to the relevant parts, it makes
> > > > > it easier to find the things you said.
> > > > > 
> > > > > On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> > > > > > Hi Sascha:
> > > > > > 
> > > > > > > +
> > > > > > > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > > > > > > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > > > > > > +		struct device_node *node, *parent;
> > > > > > > +
> > > > > > > +		parent = of_get_parent(rkencoder->port);
> > > > > > > +
> > > > > > > +		for_each_endpoint_of_node(parent, node) {
> > > > > > Is there any hurt directly use our downstream vendor kernel method here: use
> > > > > > vcstate->output_if set by encoder driver to get which interface we should
> > > > > > enable here?
> > > > > There is no vcstate->output_if in mainline currently. Ok, we could add
> > > > > that. The other thing is that there are multiple HDMI interfaces and
> > > > > the id of the HDMI encoder is encoded into output_if. Downstream kernel
> > > > > adds OF aliases to the HDMI ports. I didn't want to go that route
> > > > > because it doesn't seem to be very elegant to me.
> > > aliases is a very comm strategy in device tree world.
> > Yes, but not for retrieving bit offsets into registers. Normally aliases
> > can be changed at board level without confusing drivers.
> > 
> > > And your method also
> > > add need additional dt binds to define RK3568_VOP2_EP_xxx
> > > > > > You method is ok with device tree,  but it tied up this driver to device
> > > > > > tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> > > > > > driver can be much more flexible.
> > > > > The current rockchip drm driver seems to be pretty much tied to device
> > > > > tree. There are probably many other places that need parallel paths for
> > > > > ACPI support, I think we can delay this particular part until we see the
> > > > > whole picture. In the end we can still retrieve the output_if
> > > > > information differently with ACPI while still retrieving the information
> > > > > from the device tree the way we are doing currently.
> > > The current driver only reference device thee at driver initial, we not wrap
> > > 
> > > device tree related things in other parts, so if we extend it to support
> > > ACPI,
> > > 
> > > we just need modify the initial code, this make things easier.
> > The device tree parsing could be moved out of vop2_crtc_atomic_enable()
> > into some initialisation path. In the end it's static information,
> > there's no need to do it repeatedly in atomic_enable.
> 
> This could be one solution, the repeatedly parsing device tree in
> atomic_enable is also my concern.
> 
> In addition, there are 2 HDMI, 2 eDP, 2 MIPI on the coming rk3588, so it's
> better to consider give position
> 
> for HDMI1, EDP1, in  include/dt-bindings/soc/rockchip,vop2.h

The defines are rk3568 specific. rk3588 would use a set of rk3588
specific defines along with a rk3588_set_intf_mux().

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24  8:19                 ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-24  8:19 UTC (permalink / raw)
  To: Andy Yan
  Cc: Heiko Stübner, dri-devel, linux-arm-kernel, linux-rockchip,
	devicetree, kernel, Benjamin Gaignard, Michael Riesch,
	Sandy Huang, Peter Geis

On Sat, Feb 19, 2022 at 03:35:12PM +0800, Andy Yan wrote:
> Hi Sascha:
> 
> On 2/18/22 16:00, Sascha Hauer wrote:
> > On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
> > > Hi Sascha:
> > > 
> > > On 2/17/22 22:06, Heiko Stübner wrote:
> > > > Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
> > > > > Hi Andy,
> > > > > 
> > > > > Please trim the context in your answers to the relevant parts, it makes
> > > > > it easier to find the things you said.
> > > > > 
> > > > > On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> > > > > > Hi Sascha:
> > > > > > 
> > > > > > > +
> > > > > > > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > > > > > > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > > > > > > +		struct device_node *node, *parent;
> > > > > > > +
> > > > > > > +		parent = of_get_parent(rkencoder->port);
> > > > > > > +
> > > > > > > +		for_each_endpoint_of_node(parent, node) {
> > > > > > Is there any hurt directly use our downstream vendor kernel method here: use
> > > > > > vcstate->output_if set by encoder driver to get which interface we should
> > > > > > enable here?
> > > > > There is no vcstate->output_if in mainline currently. Ok, we could add
> > > > > that. The other thing is that there are multiple HDMI interfaces and
> > > > > the id of the HDMI encoder is encoded into output_if. Downstream kernel
> > > > > adds OF aliases to the HDMI ports. I didn't want to go that route
> > > > > because it doesn't seem to be very elegant to me.
> > > aliases is a very comm strategy in device tree world.
> > Yes, but not for retrieving bit offsets into registers. Normally aliases
> > can be changed at board level without confusing drivers.
> > 
> > > And your method also
> > > add need additional dt binds to define RK3568_VOP2_EP_xxx
> > > > > > You method is ok with device tree,  but it tied up this driver to device
> > > > > > tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> > > > > > driver can be much more flexible.
> > > > > The current rockchip drm driver seems to be pretty much tied to device
> > > > > tree. There are probably many other places that need parallel paths for
> > > > > ACPI support, I think we can delay this particular part until we see the
> > > > > whole picture. In the end we can still retrieve the output_if
> > > > > information differently with ACPI while still retrieving the information
> > > > > from the device tree the way we are doing currently.
> > > The current driver only reference device thee at driver initial, we not wrap
> > > 
> > > device tree related things in other parts, so if we extend it to support
> > > ACPI,
> > > 
> > > we just need modify the initial code, this make things easier.
> > The device tree parsing could be moved out of vop2_crtc_atomic_enable()
> > into some initialisation path. In the end it's static information,
> > there's no need to do it repeatedly in atomic_enable.
> 
> This could be one solution, the repeatedly parsing device tree in
> atomic_enable is also my concern.
> 
> In addition, there are 2 HDMI, 2 eDP, 2 MIPI on the coming rk3588, so it's
> better to consider give position
> 
> for HDMI1, EDP1, in  include/dt-bindings/soc/rockchip,vop2.h

The defines are rk3568 specific. rk3588 would use a set of rk3588
specific defines along with a rk3588_set_intf_mux().

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24  8:19                 ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-24  8:19 UTC (permalink / raw)
  To: Andy Yan
  Cc: Heiko Stübner, dri-devel, linux-arm-kernel, linux-rockchip,
	devicetree, kernel, Benjamin Gaignard, Michael Riesch,
	Sandy Huang, Peter Geis

On Sat, Feb 19, 2022 at 03:35:12PM +0800, Andy Yan wrote:
> Hi Sascha:
> 
> On 2/18/22 16:00, Sascha Hauer wrote:
> > On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
> > > Hi Sascha:
> > > 
> > > On 2/17/22 22:06, Heiko Stübner wrote:
> > > > Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
> > > > > Hi Andy,
> > > > > 
> > > > > Please trim the context in your answers to the relevant parts, it makes
> > > > > it easier to find the things you said.
> > > > > 
> > > > > On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> > > > > > Hi Sascha:
> > > > > > 
> > > > > > > +
> > > > > > > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > > > > > > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > > > > > > +		struct device_node *node, *parent;
> > > > > > > +
> > > > > > > +		parent = of_get_parent(rkencoder->port);
> > > > > > > +
> > > > > > > +		for_each_endpoint_of_node(parent, node) {
> > > > > > Is there any hurt directly use our downstream vendor kernel method here: use
> > > > > > vcstate->output_if set by encoder driver to get which interface we should
> > > > > > enable here?
> > > > > There is no vcstate->output_if in mainline currently. Ok, we could add
> > > > > that. The other thing is that there are multiple HDMI interfaces and
> > > > > the id of the HDMI encoder is encoded into output_if. Downstream kernel
> > > > > adds OF aliases to the HDMI ports. I didn't want to go that route
> > > > > because it doesn't seem to be very elegant to me.
> > > aliases is a very comm strategy in device tree world.
> > Yes, but not for retrieving bit offsets into registers. Normally aliases
> > can be changed at board level without confusing drivers.
> > 
> > > And your method also
> > > add need additional dt binds to define RK3568_VOP2_EP_xxx
> > > > > > You method is ok with device tree,  but it tied up this driver to device
> > > > > > tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> > > > > > driver can be much more flexible.
> > > > > The current rockchip drm driver seems to be pretty much tied to device
> > > > > tree. There are probably many other places that need parallel paths for
> > > > > ACPI support, I think we can delay this particular part until we see the
> > > > > whole picture. In the end we can still retrieve the output_if
> > > > > information differently with ACPI while still retrieving the information
> > > > > from the device tree the way we are doing currently.
> > > The current driver only reference device thee at driver initial, we not wrap
> > > 
> > > device tree related things in other parts, so if we extend it to support
> > > ACPI,
> > > 
> > > we just need modify the initial code, this make things easier.
> > The device tree parsing could be moved out of vop2_crtc_atomic_enable()
> > into some initialisation path. In the end it's static information,
> > there's no need to do it repeatedly in atomic_enable.
> 
> This could be one solution, the repeatedly parsing device tree in
> atomic_enable is also my concern.
> 
> In addition, there are 2 HDMI, 2 eDP, 2 MIPI on the coming rk3588, so it's
> better to consider give position
> 
> for HDMI1, EDP1, in  include/dt-bindings/soc/rockchip,vop2.h

The defines are rk3568 specific. rk3588 would use a set of rk3588
specific defines along with a rk3588_set_intf_mux().

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
  2022-02-24  8:19                 ` Sascha Hauer
  (?)
  (?)
@ 2022-02-24 10:54                   ` Andy Yan
  -1 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-24 10:54 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: Heiko Stübner, dri-devel, linux-arm-kernel, linux-rockchip,
	devicetree, kernel, Benjamin Gaignard, Michael Riesch,
	Sandy Huang, Peter Geis

Hi Sascha:

On 2/24/22 16:19, Sascha Hauer wrote:
> On Sat, Feb 19, 2022 at 03:35:12PM +0800, Andy Yan wrote:
>> Hi Sascha:
>>
>> On 2/18/22 16:00, Sascha Hauer wrote:
>>> On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
>>>> Hi Sascha:
>>>>
>>>> On 2/17/22 22:06, Heiko Stübner wrote:
>>>>> Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
>>>>>> Hi Andy,
>>>>>>
>>>>>> Please trim the context in your answers to the relevant parts, it makes
>>>>>> it easier to find the things you said.
>>>>>>
>>>>>> On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
>>>>>>> Hi Sascha:
>>>>>>>
>>>>>>>> +
>>>>>>>> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
>>>>>>>> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
>>>>>>>> +		struct device_node *node, *parent;
>>>>>>>> +
>>>>>>>> +		parent = of_get_parent(rkencoder->port);
>>>>>>>> +
>>>>>>>> +		for_each_endpoint_of_node(parent, node) {
>>>>>>> Is there any hurt directly use our downstream vendor kernel method here: use
>>>>>>> vcstate->output_if set by encoder driver to get which interface we should
>>>>>>> enable here?
>>>>>> There is no vcstate->output_if in mainline currently. Ok, we could add
>>>>>> that. The other thing is that there are multiple HDMI interfaces and
>>>>>> the id of the HDMI encoder is encoded into output_if. Downstream kernel
>>>>>> adds OF aliases to the HDMI ports. I didn't want to go that route
>>>>>> because it doesn't seem to be very elegant to me.
>>>> aliases is a very comm strategy in device tree world.
>>> Yes, but not for retrieving bit offsets into registers. Normally aliases
>>> can be changed at board level without confusing drivers.
>>>
>>>> And your method also
>>>> add need additional dt binds to define RK3568_VOP2_EP_xxx
>>>>>>> You method is ok with device tree,  but it tied up this driver to device
>>>>>>> tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
>>>>>>> driver can be much more flexible.
>>>>>> The current rockchip drm driver seems to be pretty much tied to device
>>>>>> tree. There are probably many other places that need parallel paths for
>>>>>> ACPI support, I think we can delay this particular part until we see the
>>>>>> whole picture. In the end we can still retrieve the output_if
>>>>>> information differently with ACPI while still retrieving the information
>>>>>> from the device tree the way we are doing currently.
>>>> The current driver only reference device thee at driver initial, we not wrap
>>>>
>>>> device tree related things in other parts, so if we extend it to support
>>>> ACPI,
>>>>
>>>> we just need modify the initial code, this make things easier.
>>> The device tree parsing could be moved out of vop2_crtc_atomic_enable()
>>> into some initialisation path. In the end it's static information,
>>> there's no need to do it repeatedly in atomic_enable.
>> This could be one solution, the repeatedly parsing device tree in
>> atomic_enable is also my concern.
>>
>> In addition, there are 2 HDMI, 2 eDP, 2 MIPI on the coming rk3588, so it's
>> better to consider give position
>>
>> for HDMI1, EDP1, in  include/dt-bindings/soc/rockchip,vop2.h
> The defines are rk3568 specific. rk3588 would use a set of rk3588
> specific defines along with a rk3588_set_intf_mux().


Why not try to share these RK3568_VOP2_EP_XXX across all vop2 even vop 
based rockchip socs?

If make these definition RK3568 specific, we need copy all of it and 
change 3568 to 3588 than add HDMI1, HDMI0, EDP1,EDP0

when rk3588 coming, if there is another rk35xx, we need to the same 
thing again.... but they share same code logic and number,

the only difference is the definition name.


Please take a look at the current upstream vop driver,  it support 13 
socs, when we add support for a new vop , most of

the work is just add registers definition in rockchip_vop_reg.c, we 
don't need to duplicate soc specific code in rockchip_drm_vop.c,

these make the upstream process much easier, and keep the vop driver 
tiny and clean.

> Sascha
>

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

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24 10:54                   ` Andy Yan
  0 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-24 10:54 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: Heiko Stübner, dri-devel, linux-arm-kernel, linux-rockchip,
	devicetree, kernel, Benjamin Gaignard, Michael Riesch,
	Sandy Huang, Peter Geis

Hi Sascha:

On 2/24/22 16:19, Sascha Hauer wrote:
> On Sat, Feb 19, 2022 at 03:35:12PM +0800, Andy Yan wrote:
>> Hi Sascha:
>>
>> On 2/18/22 16:00, Sascha Hauer wrote:
>>> On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
>>>> Hi Sascha:
>>>>
>>>> On 2/17/22 22:06, Heiko Stübner wrote:
>>>>> Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
>>>>>> Hi Andy,
>>>>>>
>>>>>> Please trim the context in your answers to the relevant parts, it makes
>>>>>> it easier to find the things you said.
>>>>>>
>>>>>> On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
>>>>>>> Hi Sascha:
>>>>>>>
>>>>>>>> +
>>>>>>>> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
>>>>>>>> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
>>>>>>>> +		struct device_node *node, *parent;
>>>>>>>> +
>>>>>>>> +		parent = of_get_parent(rkencoder->port);
>>>>>>>> +
>>>>>>>> +		for_each_endpoint_of_node(parent, node) {
>>>>>>> Is there any hurt directly use our downstream vendor kernel method here: use
>>>>>>> vcstate->output_if set by encoder driver to get which interface we should
>>>>>>> enable here?
>>>>>> There is no vcstate->output_if in mainline currently. Ok, we could add
>>>>>> that. The other thing is that there are multiple HDMI interfaces and
>>>>>> the id of the HDMI encoder is encoded into output_if. Downstream kernel
>>>>>> adds OF aliases to the HDMI ports. I didn't want to go that route
>>>>>> because it doesn't seem to be very elegant to me.
>>>> aliases is a very comm strategy in device tree world.
>>> Yes, but not for retrieving bit offsets into registers. Normally aliases
>>> can be changed at board level without confusing drivers.
>>>
>>>> And your method also
>>>> add need additional dt binds to define RK3568_VOP2_EP_xxx
>>>>>>> You method is ok with device tree,  but it tied up this driver to device
>>>>>>> tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
>>>>>>> driver can be much more flexible.
>>>>>> The current rockchip drm driver seems to be pretty much tied to device
>>>>>> tree. There are probably many other places that need parallel paths for
>>>>>> ACPI support, I think we can delay this particular part until we see the
>>>>>> whole picture. In the end we can still retrieve the output_if
>>>>>> information differently with ACPI while still retrieving the information
>>>>>> from the device tree the way we are doing currently.
>>>> The current driver only reference device thee at driver initial, we not wrap
>>>>
>>>> device tree related things in other parts, so if we extend it to support
>>>> ACPI,
>>>>
>>>> we just need modify the initial code, this make things easier.
>>> The device tree parsing could be moved out of vop2_crtc_atomic_enable()
>>> into some initialisation path. In the end it's static information,
>>> there's no need to do it repeatedly in atomic_enable.
>> This could be one solution, the repeatedly parsing device tree in
>> atomic_enable is also my concern.
>>
>> In addition, there are 2 HDMI, 2 eDP, 2 MIPI on the coming rk3588, so it's
>> better to consider give position
>>
>> for HDMI1, EDP1, in  include/dt-bindings/soc/rockchip,vop2.h
> The defines are rk3568 specific. rk3588 would use a set of rk3588
> specific defines along with a rk3588_set_intf_mux().


Why not try to share these RK3568_VOP2_EP_XXX across all vop2 even vop 
based rockchip socs?

If make these definition RK3568 specific, we need copy all of it and 
change 3568 to 3588 than add HDMI1, HDMI0, EDP1,EDP0

when rk3588 coming, if there is another rk35xx, we need to the same 
thing again.... but they share same code logic and number,

the only difference is the definition name.


Please take a look at the current upstream vop driver,  it support 13 
socs, when we add support for a new vop , most of

the work is just add registers definition in rockchip_vop_reg.c, we 
don't need to duplicate soc specific code in rockchip_drm_vop.c,

these make the upstream process much easier, and keep the vop driver 
tiny and clean.

> Sascha
>

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24 10:54                   ` Andy Yan
  0 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-24 10:54 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: Heiko Stübner, dri-devel, linux-arm-kernel, linux-rockchip,
	devicetree, kernel, Benjamin Gaignard, Michael Riesch,
	Sandy Huang, Peter Geis

Hi Sascha:

On 2/24/22 16:19, Sascha Hauer wrote:
> On Sat, Feb 19, 2022 at 03:35:12PM +0800, Andy Yan wrote:
>> Hi Sascha:
>>
>> On 2/18/22 16:00, Sascha Hauer wrote:
>>> On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
>>>> Hi Sascha:
>>>>
>>>> On 2/17/22 22:06, Heiko Stübner wrote:
>>>>> Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
>>>>>> Hi Andy,
>>>>>>
>>>>>> Please trim the context in your answers to the relevant parts, it makes
>>>>>> it easier to find the things you said.
>>>>>>
>>>>>> On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
>>>>>>> Hi Sascha:
>>>>>>>
>>>>>>>> +
>>>>>>>> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
>>>>>>>> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
>>>>>>>> +		struct device_node *node, *parent;
>>>>>>>> +
>>>>>>>> +		parent = of_get_parent(rkencoder->port);
>>>>>>>> +
>>>>>>>> +		for_each_endpoint_of_node(parent, node) {
>>>>>>> Is there any hurt directly use our downstream vendor kernel method here: use
>>>>>>> vcstate->output_if set by encoder driver to get which interface we should
>>>>>>> enable here?
>>>>>> There is no vcstate->output_if in mainline currently. Ok, we could add
>>>>>> that. The other thing is that there are multiple HDMI interfaces and
>>>>>> the id of the HDMI encoder is encoded into output_if. Downstream kernel
>>>>>> adds OF aliases to the HDMI ports. I didn't want to go that route
>>>>>> because it doesn't seem to be very elegant to me.
>>>> aliases is a very comm strategy in device tree world.
>>> Yes, but not for retrieving bit offsets into registers. Normally aliases
>>> can be changed at board level without confusing drivers.
>>>
>>>> And your method also
>>>> add need additional dt binds to define RK3568_VOP2_EP_xxx
>>>>>>> You method is ok with device tree,  but it tied up this driver to device
>>>>>>> tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
>>>>>>> driver can be much more flexible.
>>>>>> The current rockchip drm driver seems to be pretty much tied to device
>>>>>> tree. There are probably many other places that need parallel paths for
>>>>>> ACPI support, I think we can delay this particular part until we see the
>>>>>> whole picture. In the end we can still retrieve the output_if
>>>>>> information differently with ACPI while still retrieving the information
>>>>>> from the device tree the way we are doing currently.
>>>> The current driver only reference device thee at driver initial, we not wrap
>>>>
>>>> device tree related things in other parts, so if we extend it to support
>>>> ACPI,
>>>>
>>>> we just need modify the initial code, this make things easier.
>>> The device tree parsing could be moved out of vop2_crtc_atomic_enable()
>>> into some initialisation path. In the end it's static information,
>>> there's no need to do it repeatedly in atomic_enable.
>> This could be one solution, the repeatedly parsing device tree in
>> atomic_enable is also my concern.
>>
>> In addition, there are 2 HDMI, 2 eDP, 2 MIPI on the coming rk3588, so it's
>> better to consider give position
>>
>> for HDMI1, EDP1, in  include/dt-bindings/soc/rockchip,vop2.h
> The defines are rk3568 specific. rk3588 would use a set of rk3588
> specific defines along with a rk3588_set_intf_mux().


Why not try to share these RK3568_VOP2_EP_XXX across all vop2 even vop 
based rockchip socs?

If make these definition RK3568 specific, we need copy all of it and 
change 3568 to 3588 than add HDMI1, HDMI0, EDP1,EDP0

when rk3588 coming, if there is another rk35xx, we need to the same 
thing again.... but they share same code logic and number,

the only difference is the definition name.


Please take a look at the current upstream vop driver,  it support 13 
socs, when we add support for a new vop , most of

the work is just add registers definition in rockchip_vop_reg.c, we 
don't need to duplicate soc specific code in rockchip_drm_vop.c,

these make the upstream process much easier, and keep the vop driver 
tiny and clean.

> Sascha
>

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24 10:54                   ` Andy Yan
  0 siblings, 0 replies; 201+ messages in thread
From: Andy Yan @ 2022-02-24 10:54 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: devicetree, Benjamin Gaignard, Sandy Huang, dri-devel,
	linux-rockchip, Michael Riesch, kernel, Peter Geis,
	linux-arm-kernel

Hi Sascha:

On 2/24/22 16:19, Sascha Hauer wrote:
> On Sat, Feb 19, 2022 at 03:35:12PM +0800, Andy Yan wrote:
>> Hi Sascha:
>>
>> On 2/18/22 16:00, Sascha Hauer wrote:
>>> On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
>>>> Hi Sascha:
>>>>
>>>> On 2/17/22 22:06, Heiko Stübner wrote:
>>>>> Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
>>>>>> Hi Andy,
>>>>>>
>>>>>> Please trim the context in your answers to the relevant parts, it makes
>>>>>> it easier to find the things you said.
>>>>>>
>>>>>> On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
>>>>>>> Hi Sascha:
>>>>>>>
>>>>>>>> +
>>>>>>>> +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
>>>>>>>> +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
>>>>>>>> +		struct device_node *node, *parent;
>>>>>>>> +
>>>>>>>> +		parent = of_get_parent(rkencoder->port);
>>>>>>>> +
>>>>>>>> +		for_each_endpoint_of_node(parent, node) {
>>>>>>> Is there any hurt directly use our downstream vendor kernel method here: use
>>>>>>> vcstate->output_if set by encoder driver to get which interface we should
>>>>>>> enable here?
>>>>>> There is no vcstate->output_if in mainline currently. Ok, we could add
>>>>>> that. The other thing is that there are multiple HDMI interfaces and
>>>>>> the id of the HDMI encoder is encoded into output_if. Downstream kernel
>>>>>> adds OF aliases to the HDMI ports. I didn't want to go that route
>>>>>> because it doesn't seem to be very elegant to me.
>>>> aliases is a very comm strategy in device tree world.
>>> Yes, but not for retrieving bit offsets into registers. Normally aliases
>>> can be changed at board level without confusing drivers.
>>>
>>>> And your method also
>>>> add need additional dt binds to define RK3568_VOP2_EP_xxx
>>>>>>> You method is ok with device tree,  but it tied up this driver to device
>>>>>>> tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
>>>>>>> driver can be much more flexible.
>>>>>> The current rockchip drm driver seems to be pretty much tied to device
>>>>>> tree. There are probably many other places that need parallel paths for
>>>>>> ACPI support, I think we can delay this particular part until we see the
>>>>>> whole picture. In the end we can still retrieve the output_if
>>>>>> information differently with ACPI while still retrieving the information
>>>>>> from the device tree the way we are doing currently.
>>>> The current driver only reference device thee at driver initial, we not wrap
>>>>
>>>> device tree related things in other parts, so if we extend it to support
>>>> ACPI,
>>>>
>>>> we just need modify the initial code, this make things easier.
>>> The device tree parsing could be moved out of vop2_crtc_atomic_enable()
>>> into some initialisation path. In the end it's static information,
>>> there's no need to do it repeatedly in atomic_enable.
>> This could be one solution, the repeatedly parsing device tree in
>> atomic_enable is also my concern.
>>
>> In addition, there are 2 HDMI, 2 eDP, 2 MIPI on the coming rk3588, so it's
>> better to consider give position
>>
>> for HDMI1, EDP1, in  include/dt-bindings/soc/rockchip,vop2.h
> The defines are rk3568 specific. rk3588 would use a set of rk3588
> specific defines along with a rk3588_set_intf_mux().


Why not try to share these RK3568_VOP2_EP_XXX across all vop2 even vop 
based rockchip socs?

If make these definition RK3568 specific, we need copy all of it and 
change 3568 to 3588 than add HDMI1, HDMI0, EDP1,EDP0

when rk3588 coming, if there is another rk35xx, we need to the same 
thing again.... but they share same code logic and number,

the only difference is the definition name.


Please take a look at the current upstream vop driver,  it support 13 
socs, when we add support for a new vop , most of

the work is just add registers definition in rockchip_vop_reg.c, we 
don't need to duplicate soc specific code in rockchip_drm_vop.c,

these make the upstream process much easier, and keep the vop driver 
tiny and clean.

> Sascha
>

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
  2022-02-24 10:54                   ` Andy Yan
  (?)
  (?)
@ 2022-02-24 12:50                     ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-24 12:50 UTC (permalink / raw)
  To: Andy Yan
  Cc: Heiko Stübner, dri-devel, linux-arm-kernel, linux-rockchip,
	devicetree, kernel, Benjamin Gaignard, Michael Riesch,
	Sandy Huang, Peter Geis

On Thu, Feb 24, 2022 at 06:54:35PM +0800, Andy Yan wrote:
> Hi Sascha:
> 
> On 2/24/22 16:19, Sascha Hauer wrote:
> > On Sat, Feb 19, 2022 at 03:35:12PM +0800, Andy Yan wrote:
> > > Hi Sascha:
> > > 
> > > On 2/18/22 16:00, Sascha Hauer wrote:
> > > > On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
> > > > > Hi Sascha:
> > > > > 
> > > > > On 2/17/22 22:06, Heiko Stübner wrote:
> > > > > > Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
> > > > > > > Hi Andy,
> > > > > > > 
> > > > > > > Please trim the context in your answers to the relevant parts, it makes
> > > > > > > it easier to find the things you said.
> > > > > > > 
> > > > > > > On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> > > > > > > > Hi Sascha:
> > > > > > > > 
> > > > > > > > > +
> > > > > > > > > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > > > > > > > > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > > > > > > > > +		struct device_node *node, *parent;
> > > > > > > > > +
> > > > > > > > > +		parent = of_get_parent(rkencoder->port);
> > > > > > > > > +
> > > > > > > > > +		for_each_endpoint_of_node(parent, node) {
> > > > > > > > Is there any hurt directly use our downstream vendor kernel method here: use
> > > > > > > > vcstate->output_if set by encoder driver to get which interface we should
> > > > > > > > enable here?
> > > > > > > There is no vcstate->output_if in mainline currently. Ok, we could add
> > > > > > > that. The other thing is that there are multiple HDMI interfaces and
> > > > > > > the id of the HDMI encoder is encoded into output_if. Downstream kernel
> > > > > > > adds OF aliases to the HDMI ports. I didn't want to go that route
> > > > > > > because it doesn't seem to be very elegant to me.
> > > > > aliases is a very comm strategy in device tree world.
> > > > Yes, but not for retrieving bit offsets into registers. Normally aliases
> > > > can be changed at board level without confusing drivers.
> > > > 
> > > > > And your method also
> > > > > add need additional dt binds to define RK3568_VOP2_EP_xxx
> > > > > > > > You method is ok with device tree,  but it tied up this driver to device
> > > > > > > > tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> > > > > > > > driver can be much more flexible.
> > > > > > > The current rockchip drm driver seems to be pretty much tied to device
> > > > > > > tree. There are probably many other places that need parallel paths for
> > > > > > > ACPI support, I think we can delay this particular part until we see the
> > > > > > > whole picture. In the end we can still retrieve the output_if
> > > > > > > information differently with ACPI while still retrieving the information
> > > > > > > from the device tree the way we are doing currently.
> > > > > The current driver only reference device thee at driver initial, we not wrap
> > > > > 
> > > > > device tree related things in other parts, so if we extend it to support
> > > > > ACPI,
> > > > > 
> > > > > we just need modify the initial code, this make things easier.
> > > > The device tree parsing could be moved out of vop2_crtc_atomic_enable()
> > > > into some initialisation path. In the end it's static information,
> > > > there's no need to do it repeatedly in atomic_enable.
> > > This could be one solution, the repeatedly parsing device tree in
> > > atomic_enable is also my concern.
> > > 
> > > In addition, there are 2 HDMI, 2 eDP, 2 MIPI on the coming rk3588, so it's
> > > better to consider give position
> > > 
> > > for HDMI1, EDP1, in  include/dt-bindings/soc/rockchip,vop2.h
> > The defines are rk3568 specific. rk3588 would use a set of rk3588
> > specific defines along with a rk3588_set_intf_mux().
> 
> 
> Why not try to share these RK3568_VOP2_EP_XXX across all vop2 even vop based
> rockchip socs?
> 
> If make these definition RK3568 specific, we need copy all of it and change
> 3568 to 3588 than add HDMI1, HDMI0, EDP1,EDP0
> 
> when rk3588 coming, if there is another rk35xx, we need to the same thing
> again.... but they share same code logic and number,

I can make the defines RK3568 agnostic and use ROCKCHIP_ as prefix. The
actual numbers don't matter much, so we can add new interfaces or
instances thereof at the end with the next free number.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24 12:50                     ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-24 12:50 UTC (permalink / raw)
  To: Andy Yan
  Cc: devicetree, Benjamin Gaignard, Sandy Huang, dri-devel,
	linux-rockchip, Michael Riesch, kernel, Peter Geis,
	linux-arm-kernel

On Thu, Feb 24, 2022 at 06:54:35PM +0800, Andy Yan wrote:
> Hi Sascha:
> 
> On 2/24/22 16:19, Sascha Hauer wrote:
> > On Sat, Feb 19, 2022 at 03:35:12PM +0800, Andy Yan wrote:
> > > Hi Sascha:
> > > 
> > > On 2/18/22 16:00, Sascha Hauer wrote:
> > > > On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
> > > > > Hi Sascha:
> > > > > 
> > > > > On 2/17/22 22:06, Heiko Stübner wrote:
> > > > > > Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
> > > > > > > Hi Andy,
> > > > > > > 
> > > > > > > Please trim the context in your answers to the relevant parts, it makes
> > > > > > > it easier to find the things you said.
> > > > > > > 
> > > > > > > On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> > > > > > > > Hi Sascha:
> > > > > > > > 
> > > > > > > > > +
> > > > > > > > > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > > > > > > > > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > > > > > > > > +		struct device_node *node, *parent;
> > > > > > > > > +
> > > > > > > > > +		parent = of_get_parent(rkencoder->port);
> > > > > > > > > +
> > > > > > > > > +		for_each_endpoint_of_node(parent, node) {
> > > > > > > > Is there any hurt directly use our downstream vendor kernel method here: use
> > > > > > > > vcstate->output_if set by encoder driver to get which interface we should
> > > > > > > > enable here?
> > > > > > > There is no vcstate->output_if in mainline currently. Ok, we could add
> > > > > > > that. The other thing is that there are multiple HDMI interfaces and
> > > > > > > the id of the HDMI encoder is encoded into output_if. Downstream kernel
> > > > > > > adds OF aliases to the HDMI ports. I didn't want to go that route
> > > > > > > because it doesn't seem to be very elegant to me.
> > > > > aliases is a very comm strategy in device tree world.
> > > > Yes, but not for retrieving bit offsets into registers. Normally aliases
> > > > can be changed at board level without confusing drivers.
> > > > 
> > > > > And your method also
> > > > > add need additional dt binds to define RK3568_VOP2_EP_xxx
> > > > > > > > You method is ok with device tree,  but it tied up this driver to device
> > > > > > > > tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> > > > > > > > driver can be much more flexible.
> > > > > > > The current rockchip drm driver seems to be pretty much tied to device
> > > > > > > tree. There are probably many other places that need parallel paths for
> > > > > > > ACPI support, I think we can delay this particular part until we see the
> > > > > > > whole picture. In the end we can still retrieve the output_if
> > > > > > > information differently with ACPI while still retrieving the information
> > > > > > > from the device tree the way we are doing currently.
> > > > > The current driver only reference device thee at driver initial, we not wrap
> > > > > 
> > > > > device tree related things in other parts, so if we extend it to support
> > > > > ACPI,
> > > > > 
> > > > > we just need modify the initial code, this make things easier.
> > > > The device tree parsing could be moved out of vop2_crtc_atomic_enable()
> > > > into some initialisation path. In the end it's static information,
> > > > there's no need to do it repeatedly in atomic_enable.
> > > This could be one solution, the repeatedly parsing device tree in
> > > atomic_enable is also my concern.
> > > 
> > > In addition, there are 2 HDMI, 2 eDP, 2 MIPI on the coming rk3588, so it's
> > > better to consider give position
> > > 
> > > for HDMI1, EDP1, in  include/dt-bindings/soc/rockchip,vop2.h
> > The defines are rk3568 specific. rk3588 would use a set of rk3588
> > specific defines along with a rk3588_set_intf_mux().
> 
> 
> Why not try to share these RK3568_VOP2_EP_XXX across all vop2 even vop based
> rockchip socs?
> 
> If make these definition RK3568 specific, we need copy all of it and change
> 3568 to 3588 than add HDMI1, HDMI0, EDP1,EDP0
> 
> when rk3588 coming, if there is another rk35xx, we need to the same thing
> again.... but they share same code logic and number,

I can make the defines RK3568 agnostic and use ROCKCHIP_ as prefix. The
actual numbers don't matter much, so we can add new interfaces or
instances thereof at the end with the next free number.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24 12:50                     ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-24 12:50 UTC (permalink / raw)
  To: Andy Yan
  Cc: Heiko Stübner, dri-devel, linux-arm-kernel, linux-rockchip,
	devicetree, kernel, Benjamin Gaignard, Michael Riesch,
	Sandy Huang, Peter Geis

On Thu, Feb 24, 2022 at 06:54:35PM +0800, Andy Yan wrote:
> Hi Sascha:
> 
> On 2/24/22 16:19, Sascha Hauer wrote:
> > On Sat, Feb 19, 2022 at 03:35:12PM +0800, Andy Yan wrote:
> > > Hi Sascha:
> > > 
> > > On 2/18/22 16:00, Sascha Hauer wrote:
> > > > On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
> > > > > Hi Sascha:
> > > > > 
> > > > > On 2/17/22 22:06, Heiko Stübner wrote:
> > > > > > Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
> > > > > > > Hi Andy,
> > > > > > > 
> > > > > > > Please trim the context in your answers to the relevant parts, it makes
> > > > > > > it easier to find the things you said.
> > > > > > > 
> > > > > > > On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> > > > > > > > Hi Sascha:
> > > > > > > > 
> > > > > > > > > +
> > > > > > > > > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > > > > > > > > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > > > > > > > > +		struct device_node *node, *parent;
> > > > > > > > > +
> > > > > > > > > +		parent = of_get_parent(rkencoder->port);
> > > > > > > > > +
> > > > > > > > > +		for_each_endpoint_of_node(parent, node) {
> > > > > > > > Is there any hurt directly use our downstream vendor kernel method here: use
> > > > > > > > vcstate->output_if set by encoder driver to get which interface we should
> > > > > > > > enable here?
> > > > > > > There is no vcstate->output_if in mainline currently. Ok, we could add
> > > > > > > that. The other thing is that there are multiple HDMI interfaces and
> > > > > > > the id of the HDMI encoder is encoded into output_if. Downstream kernel
> > > > > > > adds OF aliases to the HDMI ports. I didn't want to go that route
> > > > > > > because it doesn't seem to be very elegant to me.
> > > > > aliases is a very comm strategy in device tree world.
> > > > Yes, but not for retrieving bit offsets into registers. Normally aliases
> > > > can be changed at board level without confusing drivers.
> > > > 
> > > > > And your method also
> > > > > add need additional dt binds to define RK3568_VOP2_EP_xxx
> > > > > > > > You method is ok with device tree,  but it tied up this driver to device
> > > > > > > > tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> > > > > > > > driver can be much more flexible.
> > > > > > > The current rockchip drm driver seems to be pretty much tied to device
> > > > > > > tree. There are probably many other places that need parallel paths for
> > > > > > > ACPI support, I think we can delay this particular part until we see the
> > > > > > > whole picture. In the end we can still retrieve the output_if
> > > > > > > information differently with ACPI while still retrieving the information
> > > > > > > from the device tree the way we are doing currently.
> > > > > The current driver only reference device thee at driver initial, we not wrap
> > > > > 
> > > > > device tree related things in other parts, so if we extend it to support
> > > > > ACPI,
> > > > > 
> > > > > we just need modify the initial code, this make things easier.
> > > > The device tree parsing could be moved out of vop2_crtc_atomic_enable()
> > > > into some initialisation path. In the end it's static information,
> > > > there's no need to do it repeatedly in atomic_enable.
> > > This could be one solution, the repeatedly parsing device tree in
> > > atomic_enable is also my concern.
> > > 
> > > In addition, there are 2 HDMI, 2 eDP, 2 MIPI on the coming rk3588, so it's
> > > better to consider give position
> > > 
> > > for HDMI1, EDP1, in  include/dt-bindings/soc/rockchip,vop2.h
> > The defines are rk3568 specific. rk3588 would use a set of rk3588
> > specific defines along with a rk3588_set_intf_mux().
> 
> 
> Why not try to share these RK3568_VOP2_EP_XXX across all vop2 even vop based
> rockchip socs?
> 
> If make these definition RK3568 specific, we need copy all of it and change
> 3568 to 3588 than add HDMI1, HDMI0, EDP1,EDP0
> 
> when rk3588 coming, if there is another rk35xx, we need to the same thing
> again.... but they share same code logic and number,

I can make the defines RK3568 agnostic and use ROCKCHIP_ as prefix. The
actual numbers don't matter much, so we can add new interfaces or
instances thereof at the end with the next free number.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24 12:50                     ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-24 12:50 UTC (permalink / raw)
  To: Andy Yan
  Cc: Heiko Stübner, dri-devel, linux-arm-kernel, linux-rockchip,
	devicetree, kernel, Benjamin Gaignard, Michael Riesch,
	Sandy Huang, Peter Geis

On Thu, Feb 24, 2022 at 06:54:35PM +0800, Andy Yan wrote:
> Hi Sascha:
> 
> On 2/24/22 16:19, Sascha Hauer wrote:
> > On Sat, Feb 19, 2022 at 03:35:12PM +0800, Andy Yan wrote:
> > > Hi Sascha:
> > > 
> > > On 2/18/22 16:00, Sascha Hauer wrote:
> > > > On Fri, Feb 18, 2022 at 11:50:32AM +0800, Andy Yan wrote:
> > > > > Hi Sascha:
> > > > > 
> > > > > On 2/17/22 22:06, Heiko Stübner wrote:
> > > > > > Am Donnerstag, 17. Februar 2022, 14:58:23 CET schrieb Sascha Hauer:
> > > > > > > Hi Andy,
> > > > > > > 
> > > > > > > Please trim the context in your answers to the relevant parts, it makes
> > > > > > > it easier to find the things you said.
> > > > > > > 
> > > > > > > On Thu, Feb 17, 2022 at 08:00:11PM +0800, Andy Yan wrote:
> > > > > > > > Hi Sascha:
> > > > > > > > 
> > > > > > > > > +
> > > > > > > > > +	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> > > > > > > > > +		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> > > > > > > > > +		struct device_node *node, *parent;
> > > > > > > > > +
> > > > > > > > > +		parent = of_get_parent(rkencoder->port);
> > > > > > > > > +
> > > > > > > > > +		for_each_endpoint_of_node(parent, node) {
> > > > > > > > Is there any hurt directly use our downstream vendor kernel method here: use
> > > > > > > > vcstate->output_if set by encoder driver to get which interface we should
> > > > > > > > enable here?
> > > > > > > There is no vcstate->output_if in mainline currently. Ok, we could add
> > > > > > > that. The other thing is that there are multiple HDMI interfaces and
> > > > > > > the id of the HDMI encoder is encoded into output_if. Downstream kernel
> > > > > > > adds OF aliases to the HDMI ports. I didn't want to go that route
> > > > > > > because it doesn't seem to be very elegant to me.
> > > > > aliases is a very comm strategy in device tree world.
> > > > Yes, but not for retrieving bit offsets into registers. Normally aliases
> > > > can be changed at board level without confusing drivers.
> > > > 
> > > > > And your method also
> > > > > add need additional dt binds to define RK3568_VOP2_EP_xxx
> > > > > > > > You method is ok with device tree,  but it tied up this driver to device
> > > > > > > > tree, we are now tring to extend vop2 driver work with ACPI, so we hope this
> > > > > > > > driver can be much more flexible.
> > > > > > > The current rockchip drm driver seems to be pretty much tied to device
> > > > > > > tree. There are probably many other places that need parallel paths for
> > > > > > > ACPI support, I think we can delay this particular part until we see the
> > > > > > > whole picture. In the end we can still retrieve the output_if
> > > > > > > information differently with ACPI while still retrieving the information
> > > > > > > from the device tree the way we are doing currently.
> > > > > The current driver only reference device thee at driver initial, we not wrap
> > > > > 
> > > > > device tree related things in other parts, so if we extend it to support
> > > > > ACPI,
> > > > > 
> > > > > we just need modify the initial code, this make things easier.
> > > > The device tree parsing could be moved out of vop2_crtc_atomic_enable()
> > > > into some initialisation path. In the end it's static information,
> > > > there's no need to do it repeatedly in atomic_enable.
> > > This could be one solution, the repeatedly parsing device tree in
> > > atomic_enable is also my concern.
> > > 
> > > In addition, there are 2 HDMI, 2 eDP, 2 MIPI on the coming rk3588, so it's
> > > better to consider give position
> > > 
> > > for HDMI1, EDP1, in  include/dt-bindings/soc/rockchip,vop2.h
> > The defines are rk3568 specific. rk3588 would use a set of rk3588
> > specific defines along with a rk3588_set_intf_mux().
> 
> 
> Why not try to share these RK3568_VOP2_EP_XXX across all vop2 even vop based
> rockchip socs?
> 
> If make these definition RK3568 specific, we need copy all of it and change
> 3568 to 3588 than add HDMI1, HDMI0, EDP1,EDP0
> 
> when rk3588 coming, if there is another rk35xx, we need to the same thing
> again.... but they share same code logic and number,

I can make the defines RK3568 agnostic and use ROCKCHIP_ as prefix. The
actual numbers don't matter much, so we can add new interfaces or
instances thereof at the end with the next free number.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
  2022-02-24  7:47       ` Sascha Hauer
  (?)
  (?)
@ 2022-02-24 14:36         ` Dmitry Osipenko
  -1 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-24 14:36 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

On 2/24/22 10:47, Sascha Hauer wrote:
> On Thu, Feb 17, 2022 at 04:24:29PM +0300, Dmitry Osipenko wrote:
>> 17.02.2022 11:29, Sascha Hauer пишет:
>>> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
>>>  	  This selects support for the VOP driver. You should enable it
>>>  	  on all older SoCs up to RK3399.
>>>  
>>> +config ROCKCHIP_VOP2
>>> +	bool "Rockchip VOP2 driver"
>>> +	help
>>> +	  This selects support for the VOP2 driver. You should enable it
>>> +	  on all newer SoCs beginning form RK3568.
>>
>> s/form/from/
>>
>> The ROCKCHIP_VOP option is "default y". Do you really want "default n"
>> for the VOP2?
> 
> ROCKCHIP_VOP is only default y to keep the VOP driver enabled for
> existing defconfig that were generated before the introduction of
> that symbol.
> We don't have this problem for VOP2, so no need to make it default y.

To me it will be more consistent of you'll have both defaulting to y,
since both options are behind DRM_ROCKCHIP.

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24 14:36         ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-24 14:36 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

On 2/24/22 10:47, Sascha Hauer wrote:
> On Thu, Feb 17, 2022 at 04:24:29PM +0300, Dmitry Osipenko wrote:
>> 17.02.2022 11:29, Sascha Hauer пишет:
>>> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
>>>  	  This selects support for the VOP driver. You should enable it
>>>  	  on all older SoCs up to RK3399.
>>>  
>>> +config ROCKCHIP_VOP2
>>> +	bool "Rockchip VOP2 driver"
>>> +	help
>>> +	  This selects support for the VOP2 driver. You should enable it
>>> +	  on all newer SoCs beginning form RK3568.
>>
>> s/form/from/
>>
>> The ROCKCHIP_VOP option is "default y". Do you really want "default n"
>> for the VOP2?
> 
> ROCKCHIP_VOP is only default y to keep the VOP driver enabled for
> existing defconfig that were generated before the introduction of
> that symbol.
> We don't have this problem for VOP2, so no need to make it default y.

To me it will be more consistent of you'll have both defaulting to y,
since both options are behind DRM_ROCKCHIP.

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

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24 14:36         ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-24 14:36 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: dri-devel, devicetree, Benjamin Gaignard, Peter Geis,
	Sandy Huang, linux-rockchip, Michael Riesch, kernel, Andy Yan,
	linux-arm-kernel

On 2/24/22 10:47, Sascha Hauer wrote:
> On Thu, Feb 17, 2022 at 04:24:29PM +0300, Dmitry Osipenko wrote:
>> 17.02.2022 11:29, Sascha Hauer пишет:
>>> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
>>>  	  This selects support for the VOP driver. You should enable it
>>>  	  on all older SoCs up to RK3399.
>>>  
>>> +config ROCKCHIP_VOP2
>>> +	bool "Rockchip VOP2 driver"
>>> +	help
>>> +	  This selects support for the VOP2 driver. You should enable it
>>> +	  on all newer SoCs beginning form RK3568.
>>
>> s/form/from/
>>
>> The ROCKCHIP_VOP option is "default y". Do you really want "default n"
>> for the VOP2?
> 
> ROCKCHIP_VOP is only default y to keep the VOP driver enabled for
> existing defconfig that were generated before the introduction of
> that symbol.
> We don't have this problem for VOP2, so no need to make it default y.

To me it will be more consistent of you'll have both defaulting to y,
since both options are behind DRM_ROCKCHIP.

_______________________________________________
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] 201+ messages in thread

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24 14:36         ` Dmitry Osipenko
  0 siblings, 0 replies; 201+ messages in thread
From: Dmitry Osipenko @ 2022-02-24 14:36 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: devicetree, Benjamin Gaignard, kernel, Sandy Huang, dri-devel,
	linux-rockchip, Michael Riesch, Peter Geis, Andy Yan,
	linux-arm-kernel

On 2/24/22 10:47, Sascha Hauer wrote:
> On Thu, Feb 17, 2022 at 04:24:29PM +0300, Dmitry Osipenko wrote:
>> 17.02.2022 11:29, Sascha Hauer пишет:
>>> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
>>>  	  This selects support for the VOP driver. You should enable it
>>>  	  on all older SoCs up to RK3399.
>>>  
>>> +config ROCKCHIP_VOP2
>>> +	bool "Rockchip VOP2 driver"
>>> +	help
>>> +	  This selects support for the VOP2 driver. You should enable it
>>> +	  on all newer SoCs beginning form RK3568.
>>
>> s/form/from/
>>
>> The ROCKCHIP_VOP option is "default y". Do you really want "default n"
>> for the VOP2?
> 
> ROCKCHIP_VOP is only default y to keep the VOP driver enabled for
> existing defconfig that were generated before the introduction of
> that symbol.
> We don't have this problem for VOP2, so no need to make it default y.

To me it will be more consistent of you'll have both defaulting to y,
since both options are behind DRM_ROCKCHIP.

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
  2022-02-24 14:36         ` Dmitry Osipenko
  (?)
  (?)
@ 2022-02-24 14:47           ` Sascha Hauer
  -1 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-24 14:47 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Heiko Stuebner, dri-devel, devicetree, Benjamin Gaignard,
	Peter Geis, Sandy Huang, linux-rockchip, Michael Riesch, kernel,
	Andy Yan, linux-arm-kernel

On Thu, Feb 24, 2022 at 05:36:29PM +0300, Dmitry Osipenko wrote:
> On 2/24/22 10:47, Sascha Hauer wrote:
> > On Thu, Feb 17, 2022 at 04:24:29PM +0300, Dmitry Osipenko wrote:
> >> 17.02.2022 11:29, Sascha Hauer пишет:
> >>> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
> >>>  	  This selects support for the VOP driver. You should enable it
> >>>  	  on all older SoCs up to RK3399.
> >>>  
> >>> +config ROCKCHIP_VOP2
> >>> +	bool "Rockchip VOP2 driver"
> >>> +	help
> >>> +	  This selects support for the VOP2 driver. You should enable it
> >>> +	  on all newer SoCs beginning form RK3568.
> >>
> >> s/form/from/
> >>
> >> The ROCKCHIP_VOP option is "default y". Do you really want "default n"
> >> for the VOP2?
> > 
> > ROCKCHIP_VOP is only default y to keep the VOP driver enabled for
> > existing defconfig that were generated before the introduction of
> > that symbol.
> > We don't have this problem for VOP2, so no need to make it default y.
> 
> To me it will be more consistent of you'll have both defaulting to y,
> since both options are behind DRM_ROCKCHIP.

New drivers should not be enabled by default, at least that's what I
have been told before. The VOP driver is enabled by default for the
reasons explained. But yes, you are right, it's more consistent to have
the same default on both drivers. Personally I don't care much, for now
I just follow what Heiko suggests as he is the one who hopefully merges
these patches ;)

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24 14:47           ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-24 14:47 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: devicetree, Benjamin Gaignard, Sandy Huang, dri-devel,
	linux-rockchip, Michael Riesch, Peter Geis, Andy Yan, kernel,
	linux-arm-kernel

On Thu, Feb 24, 2022 at 05:36:29PM +0300, Dmitry Osipenko wrote:
> On 2/24/22 10:47, Sascha Hauer wrote:
> > On Thu, Feb 17, 2022 at 04:24:29PM +0300, Dmitry Osipenko wrote:
> >> 17.02.2022 11:29, Sascha Hauer пишет:
> >>> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
> >>>  	  This selects support for the VOP driver. You should enable it
> >>>  	  on all older SoCs up to RK3399.
> >>>  
> >>> +config ROCKCHIP_VOP2
> >>> +	bool "Rockchip VOP2 driver"
> >>> +	help
> >>> +	  This selects support for the VOP2 driver. You should enable it
> >>> +	  on all newer SoCs beginning form RK3568.
> >>
> >> s/form/from/
> >>
> >> The ROCKCHIP_VOP option is "default y". Do you really want "default n"
> >> for the VOP2?
> > 
> > ROCKCHIP_VOP is only default y to keep the VOP driver enabled for
> > existing defconfig that were generated before the introduction of
> > that symbol.
> > We don't have this problem for VOP2, so no need to make it default y.
> 
> To me it will be more consistent of you'll have both defaulting to y,
> since both options are behind DRM_ROCKCHIP.

New drivers should not be enabled by default, at least that's what I
have been told before. The VOP driver is enabled by default for the
reasons explained. But yes, you are right, it's more consistent to have
the same default on both drivers. Personally I don't care much, for now
I just follow what Heiko suggests as he is the one who hopefully merges
these patches ;)

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24 14:47           ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-24 14:47 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Heiko Stuebner, dri-devel, devicetree, Benjamin Gaignard,
	Peter Geis, Sandy Huang, linux-rockchip, Michael Riesch, kernel,
	Andy Yan, linux-arm-kernel

On Thu, Feb 24, 2022 at 05:36:29PM +0300, Dmitry Osipenko wrote:
> On 2/24/22 10:47, Sascha Hauer wrote:
> > On Thu, Feb 17, 2022 at 04:24:29PM +0300, Dmitry Osipenko wrote:
> >> 17.02.2022 11:29, Sascha Hauer пишет:
> >>> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
> >>>  	  This selects support for the VOP driver. You should enable it
> >>>  	  on all older SoCs up to RK3399.
> >>>  
> >>> +config ROCKCHIP_VOP2
> >>> +	bool "Rockchip VOP2 driver"
> >>> +	help
> >>> +	  This selects support for the VOP2 driver. You should enable it
> >>> +	  on all newer SoCs beginning form RK3568.
> >>
> >> s/form/from/
> >>
> >> The ROCKCHIP_VOP option is "default y". Do you really want "default n"
> >> for the VOP2?
> > 
> > ROCKCHIP_VOP is only default y to keep the VOP driver enabled for
> > existing defconfig that were generated before the introduction of
> > that symbol.
> > We don't have this problem for VOP2, so no need to make it default y.
> 
> To me it will be more consistent of you'll have both defaulting to y,
> since both options are behind DRM_ROCKCHIP.

New drivers should not be enabled by default, at least that's what I
have been told before. The VOP driver is enabled by default for the
reasons explained. But yes, you are right, it's more consistent to have
the same default on both drivers. Personally I don't care much, for now
I just follow what Heiko suggests as he is the one who hopefully merges
these patches ;)

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

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

* Re: [PATCH v6 21/23] drm: rockchip: Add VOP2 driver
@ 2022-02-24 14:47           ` Sascha Hauer
  0 siblings, 0 replies; 201+ messages in thread
From: Sascha Hauer @ 2022-02-24 14:47 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Heiko Stuebner, dri-devel, devicetree, Benjamin Gaignard,
	Peter Geis, Sandy Huang, linux-rockchip, Michael Riesch, kernel,
	Andy Yan, linux-arm-kernel

On Thu, Feb 24, 2022 at 05:36:29PM +0300, Dmitry Osipenko wrote:
> On 2/24/22 10:47, Sascha Hauer wrote:
> > On Thu, Feb 17, 2022 at 04:24:29PM +0300, Dmitry Osipenko wrote:
> >> 17.02.2022 11:29, Sascha Hauer пишет:
> >>> @@ -28,6 +28,12 @@ config ROCKCHIP_VOP
> >>>  	  This selects support for the VOP driver. You should enable it
> >>>  	  on all older SoCs up to RK3399.
> >>>  
> >>> +config ROCKCHIP_VOP2
> >>> +	bool "Rockchip VOP2 driver"
> >>> +	help
> >>> +	  This selects support for the VOP2 driver. You should enable it
> >>> +	  on all newer SoCs beginning form RK3568.
> >>
> >> s/form/from/
> >>
> >> The ROCKCHIP_VOP option is "default y". Do you really want "default n"
> >> for the VOP2?
> > 
> > ROCKCHIP_VOP is only default y to keep the VOP driver enabled for
> > existing defconfig that were generated before the introduction of
> > that symbol.
> > We don't have this problem for VOP2, so no need to make it default y.
> 
> To me it will be more consistent of you'll have both defaulting to y,
> since both options are behind DRM_ROCKCHIP.

New drivers should not be enabled by default, at least that's what I
have been told before. The VOP driver is enabled by default for the
reasons explained. But yes, you are right, it's more consistent to have
the same default on both drivers. Personally I don't care much, for now
I just follow what Heiko suggests as he is the one who hopefully merges
these patches ;)

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
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] 201+ messages in thread

end of thread, other threads:[~2022-02-25  8:42 UTC | newest]

Thread overview: 201+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-17  8:29 [PATCH v6 00/23] drm/rockchip: RK356x VOP2 support Sascha Hauer
2022-02-17  8:29 ` Sascha Hauer
2022-02-17  8:29 ` Sascha Hauer
2022-02-17  8:29 ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 01/23] drm/rockchip: Embed drm_encoder into rockchip_decoder Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 02/23] drm/rockchip: dw_hdmi: rename vpll clock to reference clock Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17 13:20   ` Dmitry Osipenko
2022-02-17 13:20     ` Dmitry Osipenko
2022-02-17 13:20     ` Dmitry Osipenko
2022-02-17 13:20     ` Dmitry Osipenko
2022-02-17 13:37     ` Sascha Hauer
2022-02-17 13:37       ` Sascha Hauer
2022-02-17 13:37       ` Sascha Hauer
2022-02-17 13:37       ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 03/23] dt-bindings: display: rockchip: dw-hdmi: use "ref" as clock name Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 04/23] arm64: dts: rockchip: rk3399: rename HDMI ref clock to 'ref' Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 05/23] drm/rockchip: dw_hdmi: add rk3568 support Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 06/23] dt-bindings: display: rockchip: dw-hdmi: Add compatible for rk3568 HDMI Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 07/23] drm/rockchip: dw_hdmi: add regulator support Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17 13:18   ` Dmitry Osipenko
2022-02-17 13:18     ` Dmitry Osipenko
2022-02-17 13:18     ` Dmitry Osipenko
2022-02-17 13:18     ` Dmitry Osipenko
2022-02-17  8:29 ` [PATCH v6 08/23] dt-bindings: display: rockchip: dw-hdmi: Add " Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 09/23] drm/rockchip: dw_hdmi: Add support for hclk Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17 12:35   ` Dmitry Osipenko
2022-02-17 12:35     ` Dmitry Osipenko
2022-02-17 12:35     ` Dmitry Osipenko
2022-02-17 12:35     ` Dmitry Osipenko
2022-02-17 15:00     ` Sascha Hauer
2022-02-17 15:00       ` Sascha Hauer
2022-02-17 15:00       ` Sascha Hauer
2022-02-17 15:00       ` Sascha Hauer
2022-02-17 16:59       ` Dmitry Osipenko
2022-02-17 16:59         ` Dmitry Osipenko
2022-02-17 16:59         ` Dmitry Osipenko
2022-02-17 16:59         ` Dmitry Osipenko
2022-02-17  8:29 ` [PATCH v6 10/23] dt-bindings: display: rockchip: dw-hdmi: Add additional clock Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 11/23] drm/rockchip: dw_hdmi: Use auto-generated tables Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 12/23] drm/rockchip: dw_hdmi: drop mode_valid hook Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 13/23] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17 14:00   ` Dmitry Osipenko
2022-02-17 14:00     ` Dmitry Osipenko
2022-02-17 14:00     ` Dmitry Osipenko
2022-02-17 14:00     ` Dmitry Osipenko
2022-02-17 15:12     ` Sascha Hauer
2022-02-17 15:12       ` Sascha Hauer
2022-02-17 15:12       ` Sascha Hauer
2022-02-17 15:12       ` Sascha Hauer
2022-02-17 17:00       ` Dmitry Osipenko
2022-02-17 17:00         ` Dmitry Osipenko
2022-02-17 17:00         ` Dmitry Osipenko
2022-02-17 17:00         ` Dmitry Osipenko
2022-02-17  8:29 ` [PATCH v6 14/23] drm/rockchip: dw_hdmi: add default 594Mhz clk for 4K@60hz Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 15/23] dt-bindings: display: rockchip: dw-hdmi: Make unwedge pinctrl optional Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 16/23] arm64: dts: rockchip: rk356x: Add VOP2 nodes Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 17/23] arm64: dts: rockchip: rk356x: Add HDMI nodes Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-21  8:54   ` Aw: " Frank Wunderlich
2022-02-21  8:54     ` Frank Wunderlich
2022-02-21  8:54     ` Frank Wunderlich
2022-02-21  8:54     ` Frank Wunderlich
2022-02-21  9:53     ` Sascha Hauer
2022-02-21  9:53       ` Sascha Hauer
2022-02-21  9:53       ` Sascha Hauer
2022-02-21  9:53       ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 18/23] arm64: dts: rockchip: rk3568-evb: Enable VOP2 and hdmi Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 19/23] arm64: dts: rockchip: enable vop2 and hdmi tx on quartz64a Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 20/23] drm/rockchip: Make VOP driver optional Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 21/23] drm: rockchip: Add VOP2 driver Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17 12:00   ` Andy Yan
2022-02-17 12:00     ` Andy Yan
2022-02-17 12:00     ` Andy Yan
2022-02-17 13:58     ` Sascha Hauer
2022-02-17 13:58       ` Sascha Hauer
2022-02-17 13:58       ` Sascha Hauer
2022-02-17 13:58       ` Sascha Hauer
2022-02-17 14:06       ` Heiko Stübner
2022-02-17 14:06         ` Heiko Stübner
2022-02-17 14:06         ` Heiko Stübner
2022-02-17 14:06         ` Heiko Stübner
2022-02-18  3:50         ` Andy Yan
2022-02-18  3:50           ` Andy Yan
2022-02-18  3:50           ` Andy Yan
2022-02-18  3:50           ` Andy Yan
2022-02-18  8:00           ` Sascha Hauer
2022-02-18  8:00             ` Sascha Hauer
2022-02-18  8:00             ` Sascha Hauer
2022-02-18  8:00             ` Sascha Hauer
2022-02-19  7:35             ` Andy Yan
2022-02-19  7:35               ` Andy Yan
2022-02-19  7:35               ` Andy Yan
2022-02-19  7:35               ` Andy Yan
2022-02-24  8:19               ` Sascha Hauer
2022-02-24  8:19                 ` Sascha Hauer
2022-02-24  8:19                 ` Sascha Hauer
2022-02-24  8:19                 ` Sascha Hauer
2022-02-24 10:54                 ` Andy Yan
2022-02-24 10:54                   ` Andy Yan
2022-02-24 10:54                   ` Andy Yan
2022-02-24 10:54                   ` Andy Yan
2022-02-24 12:50                   ` Sascha Hauer
2022-02-24 12:50                     ` Sascha Hauer
2022-02-24 12:50                     ` Sascha Hauer
2022-02-24 12:50                     ` Sascha Hauer
2022-02-17 13:24   ` Dmitry Osipenko
2022-02-17 13:24     ` Dmitry Osipenko
2022-02-17 13:24     ` Dmitry Osipenko
2022-02-17 13:24     ` Dmitry Osipenko
2022-02-24  7:47     ` Sascha Hauer
2022-02-24  7:47       ` Sascha Hauer
2022-02-24  7:47       ` Sascha Hauer
2022-02-24  7:47       ` Sascha Hauer
2022-02-24 14:36       ` Dmitry Osipenko
2022-02-24 14:36         ` Dmitry Osipenko
2022-02-24 14:36         ` Dmitry Osipenko
2022-02-24 14:36         ` Dmitry Osipenko
2022-02-24 14:47         ` Sascha Hauer
2022-02-24 14:47           ` Sascha Hauer
2022-02-24 14:47           ` Sascha Hauer
2022-02-24 14:47           ` Sascha Hauer
2022-02-21 11:51   ` Andy Yan
2022-02-21 11:51     ` Andy Yan
2022-02-21 11:51     ` Andy Yan
2022-02-21 18:18     ` Lucas Stach
2022-02-21 18:18       ` Lucas Stach
2022-02-21 18:18       ` Lucas Stach
2022-02-17  8:29 ` [PATCH v6 22/23] dt-bindings: display: rockchip: Add binding for VOP2 Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29 ` [PATCH v6 23/23] dt-bindings: display: rockchip: dw-hdmi: fix ports description Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-17  8:29   ` Sascha Hauer
2022-02-23 15:55   ` Rob Herring
2022-02-23 15:55     ` Rob Herring
2022-02-23 15:55     ` Rob Herring
2022-02-23 15:55     ` Rob Herring
2022-02-17 10:07 ` [PATCH v6 00/23] drm/rockchip: RK356x VOP2 support Frank Wunderlich

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.