All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v17 00/10] Add MT8195 DisplayPort driver
@ 2022-09-01  4:41 ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

This patch is separated from v10 which is including dp driver, phy driver
and dpintf driver. This series is only contained the DisplayPort driver.

This series can be tested using 5.19-rc2 kernel and I test it in MT8195
Tomato Chromebook. Modetest these modes:

for eDP:
  #0 2256x1504 60.00 2256 2304 2336 2536 1504 1507 1513 1549 235690 flags: phsync, nvsync; type: preferred, driver
  #1 2256x1504 48.00 2256 2304 2336 2536 1504 1507 1513 1549 188550 flags: phsync, nvsync; type: driver

for DP:
  #0 1920x1080 60.00 1920 2008 2052 2200 1080 1084 1089 1125 148500 flags: phsync, pvsync; type: preferred, driver
  #1 1920x1080 59.94 1920 2008 2052 2200 1080 1084 1089 1125 148352 flags: phsync, pvsync; type: driver
  #2 1920x1080 50.00 1920 2448 2492 2640 1080 1084 1089 1125 148500 flags: phsync, pvsync; type: driver
  #3 1680x1050 59.95 1680 1784 1960 2240 1050 1053 1059 1089 146250 flags: nhsync, pvsync; type: driver
  #4 1600x900 60.00 1600 1624 1704 1800 900 901 904 1000 108000 flags: phsync, pvsync; type: driver
  #5 1280x1024 60.02 1280 1328 1440 1688 1024 1025 1028 1066 108000 flags: phsync, pvsync; type: driver
  #6 1280x800 59.81 1280 1352 1480 1680 800 803 809 831 83500 flags: nhsync, pvsync; type: driver
  #7 1280x720 60.00 1280 1390 1430 1650 720 725 730 750 74250 flags: phsync, pvsync; type: driver
  #8 1280x720 59.94 1280 1390 1430 1650 720 725 730 750 74176 flags: phsync, pvsync; type: driver
  #9 1280x720 50.00 1280 1720 1760 1980 720 725 730 750 74250 flags: phsync, pvsync; type: driver
  #10 1024x768 60.00 1024 1048 1184 1344 768 771 777 806 65000 flags: nhsync, nvsync; type: driver
  #11 800x600 60.32 800 840 968 1056 600 601 605 628 40000 flags: phsync, pvsync; type: driver
  #12 720x576 50.00 720 732 796 864 576 581 586 625 27000 flags: nhsync, nvsync; type: driver
  #13 720x480 60.00 720 736 798 858 480 489 495 525 27027 flags: nhsync, nvsync; type: driver
  #14 720x480 59.94 720 736 798 858 480 489 495 525 27000 flags: nhsync, nvsync; type: driver
  #15 640x480 60.00 640 656 752 800 480 490 492 525 25200 flags: nhsync, nvsync; type: driver
  #16 640x480 59.94 640 656 752 800 480 490 492 525 25175 flags: nhsync, nvsync; type: driver

Changes from v16 for dp driver:
dp drivers:
  - Add platform data to separate function.
  - Refine training flow.
  - Refine atomic enable/disable flow.
  - Refine irq flow.
  - Refactor coding style of efuse data.
  - Remove many unused code.

Changes from v15 for dp driver:
dt-binding:
  - Modify maintainers' comments.
common part:
  - Drop modification of cea_sad helpers because we don't use them anymore.
dp drivers:
  - Remove some unused register definitions.
  - Extract the same drivers for training function.
  - Use of device data for feature variables to judge what we want to do instead of using is_edp.
  - Drop retry patch because we don't encounter this issue in current drivers.

Changes from v14 for dp driver:
dt-binding:
  - Add more description for difference of edp and dp.
  - Add description that why we don't need clock property.
common part:
  - Fix reviewers' comments.
dp drivers:
  - Expand drivers to one function of irq handle.
  - Fix reviewers' comments.
  - Remove some redundant check.
  - Remove limitation of 60fps.
  - Add one patch for adding retry.
  - Add unregister flow of audio platform.

Changes from v13 for dp driver:
dt-binding:
  - Move data-lanes to port.
dp drivers:
  - Reporting for data-lanes using port.
  - Remove unnecessary drivers.
  - Refine mtk_dp_aux_transfer().
  - Refine mtk_dp_hpd_isr_handler().
  - Remove fec related drivers.

Changes from v12 for dp driver:
dt-binding:
  - Fix build error.
embedded dp drivers:
  - Revise Kconfig to let this driver independent.
  - Drop some unused/redundant drivers.
  - Move some features to patches of external dp and audio.
  - Refine format error control flow.
  - Add error control of write register functions.
  - Use mtk sip common definitions.

Changes from v11 for dp driver:
dt-binding:
  - Use data-lanes to determine the max supported lane numbers.
  - Add mhz to max-linkrate to show the units.
embedded dp drivers:
  - Modify Makefile.
  - Drop some unused/redundant drivers.
  - Move some features to patches of external dp and audio.
  - Modify break condition of training loop to control cr/eq fail.
  - Replace some function/definition with ones of common drm drivers.
  - Remove dp_lock mutex because it's only locked in power_on/off.
  - Add drm_dp_aux_(un)register in mtk_dp_bridge_(de)attach.

Changes from v10 for dp driver:
- Drop return value for write registers to make code more clear.
- Refine training state.
- Add property for dt-binding.
- Add new bug fix patches for audio and suspend.
- Rebase to v5.19-rc1.

Changes from v9:
- The DP-Phy is back to being a child device of the DP driver (as in v8)
- hot plug detection has been added back to Embedded Display Port... as
  after discussing with mediatek experts, this is needed eventhough the
  Embedded Display port is not un-pluggable
- rebased on linux-next
- simplified/split train_handler function, as suggested by Rex
- added comments on the sleep/delays present in the code
- removed previous patch introducing retries when receiving AUX_DEFER as
  this is already handled in the dp_aux framework
- added max-lane and max-linkrate device tree u8 properties instead of
  hardcoded #defines

Older revisions:
RFC - https://lore.kernel.org/linux-mediatek/20210816192523.1739365-1-msp@baylibre.com/
v1  - https://lore.kernel.org/linux-mediatek/20210906193529.718845-1-msp@baylibre.com/
v2  - https://lore.kernel.org/linux-mediatek/20210920084424.231825-1-msp@baylibre.com/
v3  - https://lore.kernel.org/linux-mediatek/20211001094443.2770169-1-msp@baylibre.com/
v4  - https://lore.kernel.org/linux-mediatek/20211011094624.3416029-1-msp@baylibre.com/
v5  - https://lore.kernel.org/all/20211021092707.3562523-1-msp@baylibre.com/
v6  - https://lore.kernel.org/linux-mediatek/20211110130623.20553-1-granquet@baylibre.com/
v7  - https://lore.kernel.org/linux-mediatek/20211217150854.2081-1-granquet@baylibre.com/
v8  - https://lore.kernel.org/linux-mediatek/20220218145437.18563-1-granquet@baylibre.com/
v9  - https://lore.kernel.org/all/20220327223927.20848-1-granquet@baylibre.com/
v10 - https://lore.kernel.org/all/20220523104758.29531-1-granquet@baylibre.com/
v11 - https://lore.kernel.org/r/20220610105522.13449-1-rex-bc.chen@mediatek.com
v12 - https://lore.kernel.org/all/20220627080341.5087-1-rex-bc.chen@mediatek.com/
v13 - https://lore.kernel.org/all/20220701062808.18596-1-rex-bc.chen@mediatek.com/
v14 - https://lore.kernel.org/all/20220712111223.13080-1-rex-bc.chen@mediatek.com/
v15 - https://lore.kernel.org/all/20220727045035.32225-1-rex-bc.chen@mediatek.com/
v16 - https://lore.kernel.org/all/20220805101459.3386-1-rex-bc.chen@mediatek.com/

Bo-Chen Chen (4):
  drm/mediatek: dp: Add multiple bridge types support
  drm/mediatek: dp: Add multiple smc commands support
  drm/mediatek: dp: Add multiple calibration data formats support
  drm/mediatek: dp: Determine device of next_bridge

Guillaume Ranquet (2):
  drm/mediatek: dp: Add MT8195 External DisplayPort support
  drm/mediatek: dp: Audio support for MT8195

Jitao Shi (1):
  drm/mediatek: dp: Add hpd debounce

Markus Schneider-Pargmann (3):
  dt-bindings: mediatek,dp: Add Display Port binding
  video/hdmi: Add audio_infoframe packing for DP
  drm/mediatek: Add MT8195 Embedded DisplayPort driver

 .../display/mediatek/mediatek,dp.yaml         |  116 +
 drivers/gpu/drm/mediatek/Kconfig              |    9 +
 drivers/gpu/drm/mediatek/Makefile             |    2 +
 drivers/gpu/drm/mediatek/mtk_dp.c             | 2661 +++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_dp_reg.h         |  356 +++
 drivers/video/hdmi.c                          |   82 +-
 include/drm/display/drm_dp.h                  |    2 +
 include/linux/hdmi.h                          |    7 +-
 8 files changed, 3215 insertions(+), 20 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h

-- 
2.18.0


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

* [PATCH v17 00/10] Add MT8195 DisplayPort driver
@ 2022-09-01  4:41 ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen,
	linux-mediatek, wenst, linux-arm-kernel,
	angelogioacchino.delregno

This patch is separated from v10 which is including dp driver, phy driver
and dpintf driver. This series is only contained the DisplayPort driver.

This series can be tested using 5.19-rc2 kernel and I test it in MT8195
Tomato Chromebook. Modetest these modes:

for eDP:
  #0 2256x1504 60.00 2256 2304 2336 2536 1504 1507 1513 1549 235690 flags: phsync, nvsync; type: preferred, driver
  #1 2256x1504 48.00 2256 2304 2336 2536 1504 1507 1513 1549 188550 flags: phsync, nvsync; type: driver

for DP:
  #0 1920x1080 60.00 1920 2008 2052 2200 1080 1084 1089 1125 148500 flags: phsync, pvsync; type: preferred, driver
  #1 1920x1080 59.94 1920 2008 2052 2200 1080 1084 1089 1125 148352 flags: phsync, pvsync; type: driver
  #2 1920x1080 50.00 1920 2448 2492 2640 1080 1084 1089 1125 148500 flags: phsync, pvsync; type: driver
  #3 1680x1050 59.95 1680 1784 1960 2240 1050 1053 1059 1089 146250 flags: nhsync, pvsync; type: driver
  #4 1600x900 60.00 1600 1624 1704 1800 900 901 904 1000 108000 flags: phsync, pvsync; type: driver
  #5 1280x1024 60.02 1280 1328 1440 1688 1024 1025 1028 1066 108000 flags: phsync, pvsync; type: driver
  #6 1280x800 59.81 1280 1352 1480 1680 800 803 809 831 83500 flags: nhsync, pvsync; type: driver
  #7 1280x720 60.00 1280 1390 1430 1650 720 725 730 750 74250 flags: phsync, pvsync; type: driver
  #8 1280x720 59.94 1280 1390 1430 1650 720 725 730 750 74176 flags: phsync, pvsync; type: driver
  #9 1280x720 50.00 1280 1720 1760 1980 720 725 730 750 74250 flags: phsync, pvsync; type: driver
  #10 1024x768 60.00 1024 1048 1184 1344 768 771 777 806 65000 flags: nhsync, nvsync; type: driver
  #11 800x600 60.32 800 840 968 1056 600 601 605 628 40000 flags: phsync, pvsync; type: driver
  #12 720x576 50.00 720 732 796 864 576 581 586 625 27000 flags: nhsync, nvsync; type: driver
  #13 720x480 60.00 720 736 798 858 480 489 495 525 27027 flags: nhsync, nvsync; type: driver
  #14 720x480 59.94 720 736 798 858 480 489 495 525 27000 flags: nhsync, nvsync; type: driver
  #15 640x480 60.00 640 656 752 800 480 490 492 525 25200 flags: nhsync, nvsync; type: driver
  #16 640x480 59.94 640 656 752 800 480 490 492 525 25175 flags: nhsync, nvsync; type: driver

Changes from v16 for dp driver:
dp drivers:
  - Add platform data to separate function.
  - Refine training flow.
  - Refine atomic enable/disable flow.
  - Refine irq flow.
  - Refactor coding style of efuse data.
  - Remove many unused code.

Changes from v15 for dp driver:
dt-binding:
  - Modify maintainers' comments.
common part:
  - Drop modification of cea_sad helpers because we don't use them anymore.
dp drivers:
  - Remove some unused register definitions.
  - Extract the same drivers for training function.
  - Use of device data for feature variables to judge what we want to do instead of using is_edp.
  - Drop retry patch because we don't encounter this issue in current drivers.

Changes from v14 for dp driver:
dt-binding:
  - Add more description for difference of edp and dp.
  - Add description that why we don't need clock property.
common part:
  - Fix reviewers' comments.
dp drivers:
  - Expand drivers to one function of irq handle.
  - Fix reviewers' comments.
  - Remove some redundant check.
  - Remove limitation of 60fps.
  - Add one patch for adding retry.
  - Add unregister flow of audio platform.

Changes from v13 for dp driver:
dt-binding:
  - Move data-lanes to port.
dp drivers:
  - Reporting for data-lanes using port.
  - Remove unnecessary drivers.
  - Refine mtk_dp_aux_transfer().
  - Refine mtk_dp_hpd_isr_handler().
  - Remove fec related drivers.

Changes from v12 for dp driver:
dt-binding:
  - Fix build error.
embedded dp drivers:
  - Revise Kconfig to let this driver independent.
  - Drop some unused/redundant drivers.
  - Move some features to patches of external dp and audio.
  - Refine format error control flow.
  - Add error control of write register functions.
  - Use mtk sip common definitions.

Changes from v11 for dp driver:
dt-binding:
  - Use data-lanes to determine the max supported lane numbers.
  - Add mhz to max-linkrate to show the units.
embedded dp drivers:
  - Modify Makefile.
  - Drop some unused/redundant drivers.
  - Move some features to patches of external dp and audio.
  - Modify break condition of training loop to control cr/eq fail.
  - Replace some function/definition with ones of common drm drivers.
  - Remove dp_lock mutex because it's only locked in power_on/off.
  - Add drm_dp_aux_(un)register in mtk_dp_bridge_(de)attach.

Changes from v10 for dp driver:
- Drop return value for write registers to make code more clear.
- Refine training state.
- Add property for dt-binding.
- Add new bug fix patches for audio and suspend.
- Rebase to v5.19-rc1.

Changes from v9:
- The DP-Phy is back to being a child device of the DP driver (as in v8)
- hot plug detection has been added back to Embedded Display Port... as
  after discussing with mediatek experts, this is needed eventhough the
  Embedded Display port is not un-pluggable
- rebased on linux-next
- simplified/split train_handler function, as suggested by Rex
- added comments on the sleep/delays present in the code
- removed previous patch introducing retries when receiving AUX_DEFER as
  this is already handled in the dp_aux framework
- added max-lane and max-linkrate device tree u8 properties instead of
  hardcoded #defines

Older revisions:
RFC - https://lore.kernel.org/linux-mediatek/20210816192523.1739365-1-msp@baylibre.com/
v1  - https://lore.kernel.org/linux-mediatek/20210906193529.718845-1-msp@baylibre.com/
v2  - https://lore.kernel.org/linux-mediatek/20210920084424.231825-1-msp@baylibre.com/
v3  - https://lore.kernel.org/linux-mediatek/20211001094443.2770169-1-msp@baylibre.com/
v4  - https://lore.kernel.org/linux-mediatek/20211011094624.3416029-1-msp@baylibre.com/
v5  - https://lore.kernel.org/all/20211021092707.3562523-1-msp@baylibre.com/
v6  - https://lore.kernel.org/linux-mediatek/20211110130623.20553-1-granquet@baylibre.com/
v7  - https://lore.kernel.org/linux-mediatek/20211217150854.2081-1-granquet@baylibre.com/
v8  - https://lore.kernel.org/linux-mediatek/20220218145437.18563-1-granquet@baylibre.com/
v9  - https://lore.kernel.org/all/20220327223927.20848-1-granquet@baylibre.com/
v10 - https://lore.kernel.org/all/20220523104758.29531-1-granquet@baylibre.com/
v11 - https://lore.kernel.org/r/20220610105522.13449-1-rex-bc.chen@mediatek.com
v12 - https://lore.kernel.org/all/20220627080341.5087-1-rex-bc.chen@mediatek.com/
v13 - https://lore.kernel.org/all/20220701062808.18596-1-rex-bc.chen@mediatek.com/
v14 - https://lore.kernel.org/all/20220712111223.13080-1-rex-bc.chen@mediatek.com/
v15 - https://lore.kernel.org/all/20220727045035.32225-1-rex-bc.chen@mediatek.com/
v16 - https://lore.kernel.org/all/20220805101459.3386-1-rex-bc.chen@mediatek.com/

Bo-Chen Chen (4):
  drm/mediatek: dp: Add multiple bridge types support
  drm/mediatek: dp: Add multiple smc commands support
  drm/mediatek: dp: Add multiple calibration data formats support
  drm/mediatek: dp: Determine device of next_bridge

Guillaume Ranquet (2):
  drm/mediatek: dp: Add MT8195 External DisplayPort support
  drm/mediatek: dp: Audio support for MT8195

Jitao Shi (1):
  drm/mediatek: dp: Add hpd debounce

Markus Schneider-Pargmann (3):
  dt-bindings: mediatek,dp: Add Display Port binding
  video/hdmi: Add audio_infoframe packing for DP
  drm/mediatek: Add MT8195 Embedded DisplayPort driver

 .../display/mediatek/mediatek,dp.yaml         |  116 +
 drivers/gpu/drm/mediatek/Kconfig              |    9 +
 drivers/gpu/drm/mediatek/Makefile             |    2 +
 drivers/gpu/drm/mediatek/mtk_dp.c             | 2661 +++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_dp_reg.h         |  356 +++
 drivers/video/hdmi.c                          |   82 +-
 include/drm/display/drm_dp.h                  |    2 +
 include/linux/hdmi.h                          |    7 +-
 8 files changed, 3215 insertions(+), 20 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h

-- 
2.18.0


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

* [PATCH v17 00/10] Add MT8195 DisplayPort driver
@ 2022-09-01  4:41 ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

This patch is separated from v10 which is including dp driver, phy driver
and dpintf driver. This series is only contained the DisplayPort driver.

This series can be tested using 5.19-rc2 kernel and I test it in MT8195
Tomato Chromebook. Modetest these modes:

for eDP:
  #0 2256x1504 60.00 2256 2304 2336 2536 1504 1507 1513 1549 235690 flags: phsync, nvsync; type: preferred, driver
  #1 2256x1504 48.00 2256 2304 2336 2536 1504 1507 1513 1549 188550 flags: phsync, nvsync; type: driver

for DP:
  #0 1920x1080 60.00 1920 2008 2052 2200 1080 1084 1089 1125 148500 flags: phsync, pvsync; type: preferred, driver
  #1 1920x1080 59.94 1920 2008 2052 2200 1080 1084 1089 1125 148352 flags: phsync, pvsync; type: driver
  #2 1920x1080 50.00 1920 2448 2492 2640 1080 1084 1089 1125 148500 flags: phsync, pvsync; type: driver
  #3 1680x1050 59.95 1680 1784 1960 2240 1050 1053 1059 1089 146250 flags: nhsync, pvsync; type: driver
  #4 1600x900 60.00 1600 1624 1704 1800 900 901 904 1000 108000 flags: phsync, pvsync; type: driver
  #5 1280x1024 60.02 1280 1328 1440 1688 1024 1025 1028 1066 108000 flags: phsync, pvsync; type: driver
  #6 1280x800 59.81 1280 1352 1480 1680 800 803 809 831 83500 flags: nhsync, pvsync; type: driver
  #7 1280x720 60.00 1280 1390 1430 1650 720 725 730 750 74250 flags: phsync, pvsync; type: driver
  #8 1280x720 59.94 1280 1390 1430 1650 720 725 730 750 74176 flags: phsync, pvsync; type: driver
  #9 1280x720 50.00 1280 1720 1760 1980 720 725 730 750 74250 flags: phsync, pvsync; type: driver
  #10 1024x768 60.00 1024 1048 1184 1344 768 771 777 806 65000 flags: nhsync, nvsync; type: driver
  #11 800x600 60.32 800 840 968 1056 600 601 605 628 40000 flags: phsync, pvsync; type: driver
  #12 720x576 50.00 720 732 796 864 576 581 586 625 27000 flags: nhsync, nvsync; type: driver
  #13 720x480 60.00 720 736 798 858 480 489 495 525 27027 flags: nhsync, nvsync; type: driver
  #14 720x480 59.94 720 736 798 858 480 489 495 525 27000 flags: nhsync, nvsync; type: driver
  #15 640x480 60.00 640 656 752 800 480 490 492 525 25200 flags: nhsync, nvsync; type: driver
  #16 640x480 59.94 640 656 752 800 480 490 492 525 25175 flags: nhsync, nvsync; type: driver

Changes from v16 for dp driver:
dp drivers:
  - Add platform data to separate function.
  - Refine training flow.
  - Refine atomic enable/disable flow.
  - Refine irq flow.
  - Refactor coding style of efuse data.
  - Remove many unused code.

Changes from v15 for dp driver:
dt-binding:
  - Modify maintainers' comments.
common part:
  - Drop modification of cea_sad helpers because we don't use them anymore.
dp drivers:
  - Remove some unused register definitions.
  - Extract the same drivers for training function.
  - Use of device data for feature variables to judge what we want to do instead of using is_edp.
  - Drop retry patch because we don't encounter this issue in current drivers.

Changes from v14 for dp driver:
dt-binding:
  - Add more description for difference of edp and dp.
  - Add description that why we don't need clock property.
common part:
  - Fix reviewers' comments.
dp drivers:
  - Expand drivers to one function of irq handle.
  - Fix reviewers' comments.
  - Remove some redundant check.
  - Remove limitation of 60fps.
  - Add one patch for adding retry.
  - Add unregister flow of audio platform.

Changes from v13 for dp driver:
dt-binding:
  - Move data-lanes to port.
dp drivers:
  - Reporting for data-lanes using port.
  - Remove unnecessary drivers.
  - Refine mtk_dp_aux_transfer().
  - Refine mtk_dp_hpd_isr_handler().
  - Remove fec related drivers.

Changes from v12 for dp driver:
dt-binding:
  - Fix build error.
embedded dp drivers:
  - Revise Kconfig to let this driver independent.
  - Drop some unused/redundant drivers.
  - Move some features to patches of external dp and audio.
  - Refine format error control flow.
  - Add error control of write register functions.
  - Use mtk sip common definitions.

Changes from v11 for dp driver:
dt-binding:
  - Use data-lanes to determine the max supported lane numbers.
  - Add mhz to max-linkrate to show the units.
embedded dp drivers:
  - Modify Makefile.
  - Drop some unused/redundant drivers.
  - Move some features to patches of external dp and audio.
  - Modify break condition of training loop to control cr/eq fail.
  - Replace some function/definition with ones of common drm drivers.
  - Remove dp_lock mutex because it's only locked in power_on/off.
  - Add drm_dp_aux_(un)register in mtk_dp_bridge_(de)attach.

Changes from v10 for dp driver:
- Drop return value for write registers to make code more clear.
- Refine training state.
- Add property for dt-binding.
- Add new bug fix patches for audio and suspend.
- Rebase to v5.19-rc1.

Changes from v9:
- The DP-Phy is back to being a child device of the DP driver (as in v8)
- hot plug detection has been added back to Embedded Display Port... as
  after discussing with mediatek experts, this is needed eventhough the
  Embedded Display port is not un-pluggable
- rebased on linux-next
- simplified/split train_handler function, as suggested by Rex
- added comments on the sleep/delays present in the code
- removed previous patch introducing retries when receiving AUX_DEFER as
  this is already handled in the dp_aux framework
- added max-lane and max-linkrate device tree u8 properties instead of
  hardcoded #defines

Older revisions:
RFC - https://lore.kernel.org/linux-mediatek/20210816192523.1739365-1-msp@baylibre.com/
v1  - https://lore.kernel.org/linux-mediatek/20210906193529.718845-1-msp@baylibre.com/
v2  - https://lore.kernel.org/linux-mediatek/20210920084424.231825-1-msp@baylibre.com/
v3  - https://lore.kernel.org/linux-mediatek/20211001094443.2770169-1-msp@baylibre.com/
v4  - https://lore.kernel.org/linux-mediatek/20211011094624.3416029-1-msp@baylibre.com/
v5  - https://lore.kernel.org/all/20211021092707.3562523-1-msp@baylibre.com/
v6  - https://lore.kernel.org/linux-mediatek/20211110130623.20553-1-granquet@baylibre.com/
v7  - https://lore.kernel.org/linux-mediatek/20211217150854.2081-1-granquet@baylibre.com/
v8  - https://lore.kernel.org/linux-mediatek/20220218145437.18563-1-granquet@baylibre.com/
v9  - https://lore.kernel.org/all/20220327223927.20848-1-granquet@baylibre.com/
v10 - https://lore.kernel.org/all/20220523104758.29531-1-granquet@baylibre.com/
v11 - https://lore.kernel.org/r/20220610105522.13449-1-rex-bc.chen@mediatek.com
v12 - https://lore.kernel.org/all/20220627080341.5087-1-rex-bc.chen@mediatek.com/
v13 - https://lore.kernel.org/all/20220701062808.18596-1-rex-bc.chen@mediatek.com/
v14 - https://lore.kernel.org/all/20220712111223.13080-1-rex-bc.chen@mediatek.com/
v15 - https://lore.kernel.org/all/20220727045035.32225-1-rex-bc.chen@mediatek.com/
v16 - https://lore.kernel.org/all/20220805101459.3386-1-rex-bc.chen@mediatek.com/

Bo-Chen Chen (4):
  drm/mediatek: dp: Add multiple bridge types support
  drm/mediatek: dp: Add multiple smc commands support
  drm/mediatek: dp: Add multiple calibration data formats support
  drm/mediatek: dp: Determine device of next_bridge

Guillaume Ranquet (2):
  drm/mediatek: dp: Add MT8195 External DisplayPort support
  drm/mediatek: dp: Audio support for MT8195

Jitao Shi (1):
  drm/mediatek: dp: Add hpd debounce

Markus Schneider-Pargmann (3):
  dt-bindings: mediatek,dp: Add Display Port binding
  video/hdmi: Add audio_infoframe packing for DP
  drm/mediatek: Add MT8195 Embedded DisplayPort driver

 .../display/mediatek/mediatek,dp.yaml         |  116 +
 drivers/gpu/drm/mediatek/Kconfig              |    9 +
 drivers/gpu/drm/mediatek/Makefile             |    2 +
 drivers/gpu/drm/mediatek/mtk_dp.c             | 2661 +++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_dp_reg.h         |  356 +++
 drivers/video/hdmi.c                          |   82 +-
 include/drm/display/drm_dp.h                  |    2 +
 include/linux/hdmi.h                          |    7 +-
 8 files changed, 3215 insertions(+), 20 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h

-- 
2.18.0


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

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

* [PATCH v17 01/10] dt-bindings: mediatek,dp: Add Display Port binding
  2022-09-01  4:41 ` Bo-Chen Chen
  (?)
@ 2022-09-01  4:41   ` Bo-Chen Chen
  -1 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

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

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

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

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

diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
new file mode 100644
index 000000000000..ff781f2174a0
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
@@ -0,0 +1,116 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/mediatek/mediatek,dp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek Display Port Controller
+
+maintainers:
+  - Chun-Kuang Hu <chunkuang.hu@kernel.org>
+  - Jitao shi <jitao.shi@mediatek.com>
+
+description: |
+  MediaTek DP and eDP are different hardwares and there are some features
+  which are not supported for eDP. For example, audio is not supported for
+  eDP. Therefore, we need to use two different compatibles to describe them.
+  In addition, We just need to enable the power domain of DP, so the clock
+  of DP is generated by itself and we are not using other PLL to generate
+  clocks.
+
+properties:
+  compatible:
+    enum:
+      - mediatek,mt8195-dp-tx
+      - mediatek,mt8195-edp-tx
+
+  reg:
+    maxItems: 1
+
+  nvmem-cells:
+    maxItems: 1
+    description: efuse data for display port calibration
+
+  nvmem-cell-names:
+    const: dp_calibration_data
+
+  power-domains:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Input endpoint of the controller, usually dp_intf
+
+      port@1:
+        $ref: /schemas/graph.yaml#/$defs/port-base
+        unevaluatedProperties: false
+        description: Output endpoint of the controller
+        properties:
+          endpoint:
+            $ref: /schemas/media/video-interfaces.yaml#
+            unevaluatedProperties: false
+            properties:
+              data-lanes:
+                description: |
+                  number of lanes supported by the hardware.
+                  The possible values:
+                  0       - For 1 lane enabled in IP.
+                  0 1     - For 2 lanes enabled in IP.
+                  0 1 2 3 - For 4 lanes enabled in IP.
+                minItems: 1
+                maxItems: 4
+            required:
+              - data-lanes
+
+    required:
+      - port@0
+      - port@1
+
+  max-linkrate-mhz:
+    enum: [ 1620, 2700, 5400, 8100 ]
+    description: maximum link rate supported by the hardware.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - ports
+  - max-linkrate-mhz
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/power/mt8195-power.h>
+    dptx@1c600000 {
+        compatible = "mediatek,mt8195-dp-tx";
+        reg = <0x1c600000 0x8000>;
+        power-domains = <&spm MT8195_POWER_DOMAIN_DP_TX>;
+        interrupts = <GIC_SPI 458 IRQ_TYPE_LEVEL_HIGH 0>;
+        max-linkrate-mhz = <8100>;
+
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            port@0 {
+                reg = <0>;
+                dptx_in: endpoint {
+                    remote-endpoint = <&dp_intf0_out>;
+                };
+            };
+            port@1 {
+                reg = <1>;
+                dptx_out: endpoint {
+                    data-lanes = <0 1 2 3>;
+                };
+            };
+        };
+    };
-- 
2.18.0


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

* [PATCH v17 01/10] dt-bindings: mediatek,dp: Add Display Port binding
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen,
	linux-mediatek, wenst, linux-arm-kernel,
	angelogioacchino.delregno

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

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

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

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

diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
new file mode 100644
index 000000000000..ff781f2174a0
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
@@ -0,0 +1,116 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/mediatek/mediatek,dp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek Display Port Controller
+
+maintainers:
+  - Chun-Kuang Hu <chunkuang.hu@kernel.org>
+  - Jitao shi <jitao.shi@mediatek.com>
+
+description: |
+  MediaTek DP and eDP are different hardwares and there are some features
+  which are not supported for eDP. For example, audio is not supported for
+  eDP. Therefore, we need to use two different compatibles to describe them.
+  In addition, We just need to enable the power domain of DP, so the clock
+  of DP is generated by itself and we are not using other PLL to generate
+  clocks.
+
+properties:
+  compatible:
+    enum:
+      - mediatek,mt8195-dp-tx
+      - mediatek,mt8195-edp-tx
+
+  reg:
+    maxItems: 1
+
+  nvmem-cells:
+    maxItems: 1
+    description: efuse data for display port calibration
+
+  nvmem-cell-names:
+    const: dp_calibration_data
+
+  power-domains:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Input endpoint of the controller, usually dp_intf
+
+      port@1:
+        $ref: /schemas/graph.yaml#/$defs/port-base
+        unevaluatedProperties: false
+        description: Output endpoint of the controller
+        properties:
+          endpoint:
+            $ref: /schemas/media/video-interfaces.yaml#
+            unevaluatedProperties: false
+            properties:
+              data-lanes:
+                description: |
+                  number of lanes supported by the hardware.
+                  The possible values:
+                  0       - For 1 lane enabled in IP.
+                  0 1     - For 2 lanes enabled in IP.
+                  0 1 2 3 - For 4 lanes enabled in IP.
+                minItems: 1
+                maxItems: 4
+            required:
+              - data-lanes
+
+    required:
+      - port@0
+      - port@1
+
+  max-linkrate-mhz:
+    enum: [ 1620, 2700, 5400, 8100 ]
+    description: maximum link rate supported by the hardware.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - ports
+  - max-linkrate-mhz
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/power/mt8195-power.h>
+    dptx@1c600000 {
+        compatible = "mediatek,mt8195-dp-tx";
+        reg = <0x1c600000 0x8000>;
+        power-domains = <&spm MT8195_POWER_DOMAIN_DP_TX>;
+        interrupts = <GIC_SPI 458 IRQ_TYPE_LEVEL_HIGH 0>;
+        max-linkrate-mhz = <8100>;
+
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            port@0 {
+                reg = <0>;
+                dptx_in: endpoint {
+                    remote-endpoint = <&dp_intf0_out>;
+                };
+            };
+            port@1 {
+                reg = <1>;
+                dptx_out: endpoint {
+                    data-lanes = <0 1 2 3>;
+                };
+            };
+        };
+    };
-- 
2.18.0


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

* [PATCH v17 01/10] dt-bindings: mediatek,dp: Add Display Port binding
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

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

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

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

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

diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
new file mode 100644
index 000000000000..ff781f2174a0
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
@@ -0,0 +1,116 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/mediatek/mediatek,dp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek Display Port Controller
+
+maintainers:
+  - Chun-Kuang Hu <chunkuang.hu@kernel.org>
+  - Jitao shi <jitao.shi@mediatek.com>
+
+description: |
+  MediaTek DP and eDP are different hardwares and there are some features
+  which are not supported for eDP. For example, audio is not supported for
+  eDP. Therefore, we need to use two different compatibles to describe them.
+  In addition, We just need to enable the power domain of DP, so the clock
+  of DP is generated by itself and we are not using other PLL to generate
+  clocks.
+
+properties:
+  compatible:
+    enum:
+      - mediatek,mt8195-dp-tx
+      - mediatek,mt8195-edp-tx
+
+  reg:
+    maxItems: 1
+
+  nvmem-cells:
+    maxItems: 1
+    description: efuse data for display port calibration
+
+  nvmem-cell-names:
+    const: dp_calibration_data
+
+  power-domains:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Input endpoint of the controller, usually dp_intf
+
+      port@1:
+        $ref: /schemas/graph.yaml#/$defs/port-base
+        unevaluatedProperties: false
+        description: Output endpoint of the controller
+        properties:
+          endpoint:
+            $ref: /schemas/media/video-interfaces.yaml#
+            unevaluatedProperties: false
+            properties:
+              data-lanes:
+                description: |
+                  number of lanes supported by the hardware.
+                  The possible values:
+                  0       - For 1 lane enabled in IP.
+                  0 1     - For 2 lanes enabled in IP.
+                  0 1 2 3 - For 4 lanes enabled in IP.
+                minItems: 1
+                maxItems: 4
+            required:
+              - data-lanes
+
+    required:
+      - port@0
+      - port@1
+
+  max-linkrate-mhz:
+    enum: [ 1620, 2700, 5400, 8100 ]
+    description: maximum link rate supported by the hardware.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - ports
+  - max-linkrate-mhz
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/power/mt8195-power.h>
+    dptx@1c600000 {
+        compatible = "mediatek,mt8195-dp-tx";
+        reg = <0x1c600000 0x8000>;
+        power-domains = <&spm MT8195_POWER_DOMAIN_DP_TX>;
+        interrupts = <GIC_SPI 458 IRQ_TYPE_LEVEL_HIGH 0>;
+        max-linkrate-mhz = <8100>;
+
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            port@0 {
+                reg = <0>;
+                dptx_in: endpoint {
+                    remote-endpoint = <&dp_intf0_out>;
+                };
+            };
+            port@1 {
+                reg = <1>;
+                dptx_out: endpoint {
+                    data-lanes = <0 1 2 3>;
+                };
+            };
+        };
+    };
-- 
2.18.0


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

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

* [PATCH v17 02/10] video/hdmi: Add audio_infoframe packing for DP
  2022-09-01  4:41 ` Bo-Chen Chen
  (?)
@ 2022-09-01  4:41   ` Bo-Chen Chen
  -1 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

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

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

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

Also constify the frame parameter in hdmi_audio_infoframe_check() as
it is passed to hdmi_audio_infoframe_check_only() which expects a const.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/video/hdmi.c         | 82 +++++++++++++++++++++++++++---------
 include/drm/display/drm_dp.h |  2 +
 include/linux/hdmi.h         |  7 ++-
 3 files changed, 71 insertions(+), 20 deletions(-)

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


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

* [PATCH v17 02/10] video/hdmi: Add audio_infoframe packing for DP
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen,
	linux-mediatek, wenst, linux-arm-kernel,
	angelogioacchino.delregno

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

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

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

Also constify the frame parameter in hdmi_audio_infoframe_check() as
it is passed to hdmi_audio_infoframe_check_only() which expects a const.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/video/hdmi.c         | 82 +++++++++++++++++++++++++++---------
 include/drm/display/drm_dp.h |  2 +
 include/linux/hdmi.h         |  7 ++-
 3 files changed, 71 insertions(+), 20 deletions(-)

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


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

* [PATCH v17 02/10] video/hdmi: Add audio_infoframe packing for DP
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

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

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

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

Also constify the frame parameter in hdmi_audio_infoframe_check() as
it is passed to hdmi_audio_infoframe_check_only() which expects a const.

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/video/hdmi.c         | 82 +++++++++++++++++++++++++++---------
 include/drm/display/drm_dp.h |  2 +
 include/linux/hdmi.h         |  7 ++-
 3 files changed, 71 insertions(+), 20 deletions(-)

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


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

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

* [PATCH v17 03/10] drm/mediatek: Add MT8195 Embedded DisplayPort driver
  2022-09-01  4:41 ` Bo-Chen Chen
  (?)
@ 2022-09-01  4:41   ` Bo-Chen Chen
  -1 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

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

This patch adds a embedded displayport driver for the MediaTek mt8195 SoC.

It supports the MT8195, the embedded DisplayPort units. It offers
DisplayPort 1.4 with up to 4 lanes.

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

This driver is based on an initial version by
Jitao shi <jitao.shi@mediatek.com>

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/Kconfig      |    9 +
 drivers/gpu/drm/mediatek/Makefile     |    2 +
 drivers/gpu/drm/mediatek/mtk_dp.c     | 1999 +++++++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_dp_reg.h |  305 ++++
 4 files changed, 2315 insertions(+)
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h

diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
index 2976d21e9a34..e66f4a3b6be0 100644
--- a/drivers/gpu/drm/mediatek/Kconfig
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -21,6 +21,15 @@ config DRM_MEDIATEK
 	  This driver provides kernel mode setting and
 	  buffer management to userspace.
 
+config DRM_MEDIATEK_DP
+	tristate "DRM DPTX Support for MediaTek SoCs"
+	depends on DRM_MEDIATEK
+	select PHY_MTK_DP
+	select DRM_DISPLAY_HELPER
+	select DRM_DISPLAY_DP_HELPER
+	help
+	  DRM/KMS Display Port driver for MediaTek SoCs.
+
 config DRM_MEDIATEK_HDMI
 	tristate "DRM HDMI Support for Mediatek SoCs"
 	depends on DRM_MEDIATEK
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index 6e604a933ed0..3517d1c65cd7 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -23,3 +23,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
 			  mtk_hdmi_ddc.o
 
 obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
+
+obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o
diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
new file mode 100644
index 000000000000..e2ec9b02b1aa
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -0,0 +1,1999 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019-2022 MediaTek Inc.
+ * Copyright (c) 2022 BayLibre
+ */
+
+#include <drm/display/drm_dp.h>
+#include <drm/display/drm_dp_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/media-bus-format.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soc/mediatek/mtk_sip_svc.h>
+#include <video/videomode.h>
+
+#include "mtk_dp_reg.h"
+
+#define MTK_DP_SIP_CONTROL_AARCH32	MTK_SIP_SMC_CMD(0x523)
+#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE	(BIT(0) | BIT(5))
+
+#define MTK_DP_THREAD_CABLE_STATE_CHG	BIT(0)
+#define MTK_DP_THREAD_HPD_EVENT		BIT(1)
+
+#define MTK_DP_4P1T 4
+#define MTK_DP_HDE 2
+#define MTK_DP_PIX_PER_ADDR 2
+#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
+#define MTK_DP_TBC_BUF_READ_START_ADDR 0x8
+#define MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY 5
+#define MTK_DP_TRAIN_DOWNSCALE_RETRY 10
+
+enum {
+	MTK_DP_CAL_GLB_BIAS_TRIM = 0,
+	MTK_DP_CAL_CLKTX_IMPSE,
+	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0,
+	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1,
+	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2,
+	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3,
+	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0,
+	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1,
+	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2,
+	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3,
+	MTK_DP_CAL_MAX,
+};
+
+struct mtk_dp_train_info {
+	bool sink_ssc;
+	bool cable_plugged_in;
+	/* link_rate is in multiple of 0.27Gbps */
+	int link_rate;
+	int lane_count;
+	unsigned int channel_eq_pattern;
+};
+
+struct mtk_dp_info {
+	enum dp_pixelformat format;
+	struct videomode vm;
+};
+
+struct mtk_dp_efuse_fmt {
+	unsigned short idx;
+	unsigned short shift;
+	unsigned short mask;
+	unsigned short min_val;
+	unsigned short max_val;
+	unsigned short default_val;
+};
+
+struct mtk_dp {
+	bool enabled;
+	u8 max_lanes;
+	u8 max_linkrate;
+	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
+	u32 cal_data[MTK_DP_CAL_MAX];
+	u32 irq_thread_handle;
+	/* irq_thread_lock is used to protect irq_thread_handle */
+	spinlock_t irq_thread_lock;
+
+	struct device *dev;
+	struct drm_bridge bridge;
+	struct drm_bridge *next_bridge;
+	struct drm_connector *conn;
+	struct drm_device *drm_dev;
+	struct drm_dp_aux aux;
+
+	struct mtk_dp_info info;
+	struct mtk_dp_train_info train_info;
+
+	struct platform_device *phy_dev;
+	struct phy *phy;
+	struct regmap *regs;
+};
+
+static const struct mtk_dp_efuse_fmt mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
+	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
+		.idx = 3,
+		.shift = 27,
+		.mask = 0x1f,
+		.min_val = 1,
+		.max_val = 0x1e,
+		.default_val = 0xf,
+	},
+	[MTK_DP_CAL_CLKTX_IMPSE] = {
+		.idx = 0,
+		.shift = 9,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = {
+		.idx = 2,
+		.shift = 28,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = {
+		.idx = 2,
+		.shift = 20,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = {
+		.idx = 2,
+		.shift = 12,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = {
+		.idx = 2,
+		.shift = 4,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = {
+		.idx = 2,
+		.shift = 24,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = {
+		.idx = 2,
+		.shift = 16,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = {
+		.idx = 2,
+		.shift = 8,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = {
+		.idx = 2,
+		.shift = 0,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+};
+
+static struct regmap_config mtk_dp_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = SEC_OFFSET + 0x90,
+	.name = "mtk-dp-registers",
+};
+
+static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
+{
+	return container_of(b, struct mtk_dp, bridge);
+}
+
+static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
+{
+	u32 read_val;
+	int ret;
+
+	ret = regmap_read(mtk_dp->regs, offset, &read_val);
+	if (ret) {
+		dev_err(mtk_dp->dev, "Failed to read register 0x%x: %d\n",
+			offset, ret);
+		return 0;
+	}
+
+	return read_val;
+}
+
+static int mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
+{
+	int ret = regmap_write(mtk_dp->regs, offset, val);
+
+	if (ret)
+		dev_err(mtk_dp->dev,
+			"Failed to write register 0x%x with value 0x%x\n",
+			offset, val);
+	return ret;
+}
+
+static int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset,
+			      u32 val, u32 mask)
+{
+	int ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
+
+	if (ret)
+		dev_err(mtk_dp->dev,
+			"Failed to update register 0x%x with value 0x%x, mask 0x%x\n",
+			offset, val, mask);
+	return ret;
+}
+
+static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32 offset, u8 *buf,
+				    size_t length)
+{
+	int i;
+
+	/* 2 bytes per register */
+	for (i = 0; i < length; i += 2) {
+		u32 val = buf[i] | (i + 1 < length ? buf[i + 1] << 8 : 0);
+
+		if (mtk_dp_write(mtk_dp, offset + i * 2, val))
+			return;
+	}
+}
+
+static void mtk_dp_msa_bypass_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	u32 mask = HTOTAL_SEL_DP_ENC0_P0 | VTOTAL_SEL_DP_ENC0_P0 |
+		   HSTART_SEL_DP_ENC0_P0 | VSTART_SEL_DP_ENC0_P0 |
+		   HWIDTH_SEL_DP_ENC0_P0 | VHEIGHT_SEL_DP_ENC0_P0 |
+		   HSP_SEL_DP_ENC0_P0 | HSW_SEL_DP_ENC0_P0 |
+		   VSP_SEL_DP_ENC0_P0 | VSW_SEL_DP_ENC0_P0;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, enable ? 0 : mask, mask);
+}
+
+static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
+{
+	struct drm_display_mode mode;
+	struct videomode *vm = &mtk_dp->info.vm;
+
+	drm_display_mode_from_videomode(vm, &mode);
+
+	/* horizontal */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010,
+			   mode.htotal, HTOTAL_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
+			   vm->hsync_len + vm->hback_porch,
+			   HSTART_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
+			   vm->hsync_len, HSW_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
+			   0, HSP_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020,
+			   vm->hactive, HWIDTH_SW_DP_ENC0_P0_MASK);
+
+	/* vertical */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014,
+			   mode.vtotal, VTOTAL_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
+			   vm->vsync_len + vm->vback_porch,
+			   VSTART_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
+			   vm->vsync_len, VSW_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
+			   0, VSP_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024,
+			   vm->vactive, VHEIGHT_SW_DP_ENC0_P0_MASK);
+
+	/* horizontal */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064,
+			   vm->hactive, HDE_NUM_LAST_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154,
+			   mode.htotal, PGEN_HTOTAL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
+			   vm->hfront_porch,
+			   PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C,
+			   vm->hsync_len,
+			   PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
+			   vm->hback_porch + vm->hsync_len,
+			   PGEN_HFDE_START_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164,
+			   vm->hactive,
+			   PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
+
+	/* vertical */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168,
+			   mode.vtotal,
+			   PGEN_VTOTAL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
+			   vm->vfront_porch,
+			   PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170,
+			   vm->vsync_len,
+			   PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
+			   vm->vback_porch + vm->vsync_len,
+			   PGEN_VFDE_START_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178,
+			   vm->vactive,
+			   PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
+}
+
+static int mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
+				   enum dp_pixelformat color_format)
+{
+	u32 val;
+
+	/* update MISC0 */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
+			   color_format << DP_TEST_COLOR_FORMAT_SHIFT,
+			   DP_TEST_COLOR_FORMAT_MASK);
+
+	switch (color_format) {
+	case DP_PIXELFORMAT_YUV422:
+		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
+		break;
+	case DP_PIXELFORMAT_RGB:
+		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
+		break;
+	default:
+		drm_warn(mtk_dp->drm_dev, "Unsupported color format: %d\n",
+			 color_format);
+		return -EINVAL;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   val, PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
+	return 0;
+}
+
+static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp)
+{
+	/* Only support 8 bits currently */
+	/* Update MISC0 */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
+			   DP_MSA_MISC_8_BPC, DP_TEST_BIT_DEPTH_MASK);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT,
+			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_config_mn_mode(struct mtk_dp *mtk_dp)
+{
+	/* 0: hw mode, 1: sw mode */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
+			   0, VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32 val)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   val, SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   VIDEO_MN_GEN_EN_DP_ENC0_P0,
+			   VIDEO_MN_GEN_EN_DP_ENC0_P0);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
+			   SDP_DOWN_CNT_DP_ENC0_P0_VAL,
+			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
+			   SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL,
+			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
+			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL << 8,
+			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
+			   FIFO_READ_START_POINT_DP_ENC1_P0_VAL << 12,
+			   FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
+	mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368, DP_ENC1_P0_3368_VAL);
+}
+
+static void mtk_dp_pg_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038,
+			   enable ? VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK : 0,
+			   VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
+			   PGEN_PATTERN_SEL_VAL << 4, PGEN_PATTERN_SEL_MASK);
+}
+
+static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640, DP_AUX_P0_3640_VAL);
+}
+
+static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32 addr)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644,
+			   cmd, MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648,
+			   addr, MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C,
+			   addr >> 16, MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_aux_clear_fifo(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
+			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0,
+			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0 |
+			   PHY_FIFO_RST_AUX_TX_P0_MASK |
+			   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
+			   AUX_TX_REQUEST_READY_AUX_TX_P0,
+			   AUX_TX_REQUEST_READY_AUX_TX_P0);
+}
+
+static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8 *buf,
+				       size_t length)
+{
+	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf, length);
+}
+
+static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
+				    size_t length, int read_delay)
+{
+	int read_pos;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
+			   0, AUX_RD_MODE_AUX_TX_P0_MASK);
+
+	for (read_pos = 0; read_pos < length; read_pos++) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
+				   AUX_RX_FIFO_READ_PULSE_TX_P0,
+				   AUX_RX_FIFO_READ_PULSE_TX_P0);
+
+		/* Hardware needs time to update the data */
+		usleep_range(read_delay, read_delay * 2);
+		buf[read_pos] = (u8)(mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3620) &
+				     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK);
+	}
+}
+
+static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t length)
+{
+	if (length > 0) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
+				   (length - 1) << 12,
+				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
+				   0,
+				   AUX_NO_LENGTH_AUX_TX_P0 |
+				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	} else {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
+				   AUX_NO_LENGTH_AUX_TX_P0,
+				   AUX_NO_LENGTH_AUX_TX_P0 |
+				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	}
+}
+
+static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp, bool is_read)
+{
+	int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
+
+	while (--wait_reply) {
+		u32 aux_irq_status;
+
+		if (is_read) {
+			u32 fifo_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3618);
+
+			if (fifo_status &
+			    (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
+			     AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
+				return 0;
+			}
+		}
+
+		aux_irq_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3640);
+		if (aux_irq_status & AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
+			return 0;
+
+		if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0)
+			return -ETIMEDOUT;
+
+		/* Give the hardware a chance to reach completion before retrying */
+		usleep_range(100, 500);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool is_read, u8 cmd,
+				  u32 addr, u8 *buf, size_t length)
+{
+	int ret;
+	u32 reply_cmd;
+
+	if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
+			(cmd == DP_AUX_NATIVE_READ && !length)))
+		return -EINVAL;
+
+	if (!is_read)
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
+				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0,
+				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0);
+
+	/* We need to clear fifo and irq before sending commands to the sink device. */
+	mtk_dp_aux_clear_fifo(mtk_dp);
+	mtk_dp_aux_irq_clear(mtk_dp);
+
+	mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
+	mtk_dp_aux_set_length(mtk_dp, length);
+
+	if (!is_read) {
+		if (length)
+			mtk_dp_aux_fill_write_fifo(mtk_dp, buf, length);
+
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
+				   AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK,
+				   AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK);
+	}
+
+	mtk_dp_aux_request_ready(mtk_dp);
+
+	/* Wait for feedback from sink device. */
+	ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
+
+	reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
+		    AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
+
+	if (ret || reply_cmd) {
+		u32 phy_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3628) &
+				 AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
+		if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
+			drm_err(mtk_dp->drm_dev,
+				"AUX Rx Aux hang, need SW reset\n");
+			return -EIO;
+		}
+
+		return -ETIMEDOUT;
+	}
+
+	if (!length) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
+				   0,
+				   AUX_NO_LENGTH_AUX_TX_P0 |
+				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	} else if (is_read) {
+		int read_delay;
+
+		if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
+		    cmd == DP_AUX_I2C_READ)
+			read_delay = 500;
+		else
+			read_delay = 100;
+
+		mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length, read_delay);
+	}
+
+	return 0;
+}
+
+static void mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int lane_num,
+					  int swing_val, int preemphasis)
+{
+	u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
+
+	dev_dbg(mtk_dp->dev,
+		"link training: swing_val = 0x%x, pre-emphasis = 0x%x\n",
+		swing_val, preemphasis);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+			   swing_val << (DP_TX0_VOLT_SWING_SHIFT + lane_shift),
+			   DP_TX0_VOLT_SWING_MASK << lane_shift);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+			   preemphasis << (DP_TX0_PRE_EMPH_SHIFT + lane_shift),
+			   DP_TX0_PRE_EMPH_MASK << lane_shift);
+}
+
+static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+			   0,
+			   DP_TX0_VOLT_SWING_MASK |
+			   DP_TX1_VOLT_SWING_MASK |
+			   DP_TX2_VOLT_SWING_MASK |
+			   DP_TX3_VOLT_SWING_MASK |
+			   DP_TX0_PRE_EMPH_MASK |
+			   DP_TX1_PRE_EMPH_MASK |
+			   DP_TX2_PRE_EMPH_MASK |
+			   DP_TX3_PRE_EMPH_MASK);
+}
+
+static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
+{
+	u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
+			 SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
+
+	if (irq_status) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
+				   irq_status, SW_IRQ_CLR_DP_TRANS_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
+				   0, SW_IRQ_CLR_DP_TRANS_P0_MASK);
+	}
+
+	return irq_status;
+}
+
+static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
+{
+	u32 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
+			  IRQ_STATUS_DP_TRANS_P0_MASK) >> 12;
+
+	if (irq_status) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
+				   irq_status, IRQ_CLR_DP_TRANS_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
+				   0, IRQ_CLR_DP_TRANS_P0_MASK);
+	}
+
+	return irq_status;
+}
+
+static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
+			   enable ? 0 :
+			   IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
+			   IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
+			   IRQ_MASK_DP_TRANS_P0_INT_IRQ,
+			   IRQ_MASK_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
+			   XTAL_FREQ_DP_TRANS_P0_DEFAULT,
+			   XTAL_FREQ_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
+			   FEC_CLOCK_EN_MODE_DP_TRANS_P0,
+			   FEC_CLOCK_EN_MODE_DP_TRANS_P0);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
+			   AUDIO_CH_SRC_SEL_DP_ENC0_P0,
+			   AUDIO_CH_SRC_SEL_DP_ENC0_P0);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
+			   0, SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK,
+			   IRQ_MASK_AUX_TOP_IRQ, IRQ_MASK_AUX_TOP_IRQ);
+}
+
+static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp *mtk_dp)
+{
+	u32 val;
+	/* Debounce threshold */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   8, HPD_DEB_THD_DP_TRANS_P0_MASK);
+
+	val = (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
+	       HPD_INT_THD_DP_TRANS_P0_UPPER_1100US) << 4;
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   val, HPD_INT_THD_DP_TRANS_P0_MASK);
+
+	/*
+	 * Connect threshold 1.5ms + 5 x 0.1ms = 2ms
+	 * Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
+	 */
+	val = (5 << 8) | (5 << 12);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   val,
+			   HPD_DISC_THD_DP_TRANS_P0_MASK |
+			   HPD_CONN_THD_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
+			   HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
+			   HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
+{
+	/* modify timeout threshold = 0x1595 */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C,
+			   AUX_TIMEOUT_THR_AUX_TX_P0_VAL,
+			   AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658,
+			   0, AUX_TX_OV_EN_AUX_TX_P0_MASK);
+	/* 25 for 26M */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
+			   AUX_TX_OVER_SAMPLE_RATE_FOR_26M << 8,
+			   AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
+	/* 13 for 26M */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
+			   AUX_RX_UI_CNT_THR_AUX_FOR_26M,
+			   AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
+			   MTK_ATOP_EN_AUX_TX_P0,
+			   MTK_ATOP_EN_AUX_TX_P0);
+}
+
+static void mtk_dp_initialize_digital_settings(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
+			   0, VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
+			   BS2BS_MODE_DP_ENC1_P0_VAL << 12,
+			   BS2BS_MODE_DP_ENC1_P0_MASK);
+
+	/* dp tx encoder reset all sw */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
+			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0,
+			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
+
+	/* Wait for sw reset to complete */
+	usleep_range(1000, 5000);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
+			   0, DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
+}
+
+static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
+			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0,
+			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
+
+	/* Wait for sw reset to complete */
+	usleep_range(1000, 5000);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
+			   0, DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
+}
+
+static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
+			   lanes == 0 ? 0 : DP_TRANS_DUMMY_RW_0,
+			   DP_TRANS_DUMMY_RW_0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
+			   lanes, LANE_NUM_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
+			   lanes << 2, LANE_NUM_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
+{
+	const struct mtk_dp_efuse_fmt *fmt;
+	struct device *dev = mtk_dp->dev;
+	struct nvmem_cell *cell;
+	u32 *cal_data = mtk_dp->cal_data;
+	u32 *buf;
+	int i;
+	size_t len;
+
+	cell = nvmem_cell_get(dev, "dp_calibration_data");
+	if (IS_ERR(cell)) {
+		dev_warn(dev, "Failed to get nvmem cell dp_calibration_data\n");
+		goto use_default_val;
+	}
+
+	buf = (u32 *)nvmem_cell_read(cell, &len);
+	nvmem_cell_put(cell);
+
+	if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
+		dev_warn(dev, "Failed to read nvmem_cell_read\n");
+
+		if (!IS_ERR(buf))
+			kfree(buf);
+
+		goto use_default_val;
+	}
+
+	for (i = 0; i < MTK_DP_CAL_MAX; i++) {
+		fmt = &mtk_dp_efuse_data[i];
+		cal_data[i] = (buf[fmt->idx] >> fmt->shift) & fmt->mask;
+
+		if (cal_data[i] < fmt->min_val || cal_data[i] > fmt->max_val) {
+			dev_warn(mtk_dp->dev, "Invalid efuse data, idx = %d\n", i);
+			kfree(buf);
+			goto use_default_val;
+		}
+	}
+	kfree(buf);
+
+	return;
+
+use_default_val:
+	dev_warn(mtk_dp->dev, "Use default calibration data\n");
+	for (i = 0; i < MTK_DP_CAL_MAX; i++)
+		cal_data[i] = mtk_dp_efuse_data[i].default_val;
+}
+
+static void mtk_dp_set_calibration_data(struct mtk_dp *mtk_dp)
+{
+	u32 *cal_data = mtk_dp->cal_data;
+
+	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
+			   cal_data[MTK_DP_CAL_CLKTX_IMPSE] << 20,
+			   RG_CKM_PT0_CKTX_IMPSEL);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
+			   cal_data[MTK_DP_CAL_GLB_BIAS_TRIM] << 16,
+			   RG_XTP_GLB_BIAS_INTR_CTRL);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] << 12,
+			   RG_XTP_LN0_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] << 16,
+			   RG_XTP_LN0_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] << 12,
+			   RG_XTP_LN1_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] << 16,
+			   RG_XTP_LN1_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] << 12,
+			   RG_XTP_LN2_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] << 16,
+			   RG_XTP_LN2_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] << 12,
+			   RG_XTP_LN3_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] << 16,
+			   RG_XTP_LN3_TX_IMPSEL_NMOS);
+}
+
+static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
+				u32 link_rate, int lane_count)
+{
+	int ret;
+	union phy_configure_opts phy_opts = {
+		.dp = {
+			.link_rate = drm_dp_bw_code_to_link_rate(link_rate) / 100,
+			.set_rate = 1,
+			.lanes = lane_count,
+			.set_lanes = 1,
+			.ssc = mtk_dp->train_info.sink_ssc,
+		}
+	};
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP,
+			   DP_PWR_STATE_MASK);
+
+	ret = phy_configure(mtk_dp->phy, &phy_opts);
+	if (ret)
+		return ret;
+
+	mtk_dp_set_calibration_data(mtk_dp);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL_LANE, DP_PWR_STATE_MASK);
+
+	return 0;
+}
+
+static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool enable)
+{
+	u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
+		  POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
+		  POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
+		  POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580,
+			   enable ? val : 0, val);
+}
+
+static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int pattern)
+{
+	/* TPS1 */
+	if (pattern == 1)
+		mtk_dp_set_idle_pattern(mtk_dp, false);
+
+	mtk_dp_update_bits(mtk_dp,
+			   MTK_DP_TRANS_P0_3400,
+			   pattern ? BIT(pattern - 1) << 12 : 0,
+			   PATTERN1_EN_DP_TRANS_P0_MASK |
+			   PATTERN2_EN_DP_TRANS_P0_MASK |
+			   PATTERN3_EN_DP_TRANS_P0_MASK |
+			   PATTERN4_EN_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
+			   ENHANCED_FRAME_EN_DP_ENC0_P0,
+			   ENHANCED_FRAME_EN_DP_ENC0_P0);
+}
+
+static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
+			   enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
+			   DP_SCR_EN_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
+{
+	struct arm_smccc_res res;
+	u32 val = VIDEO_MUTE_SEL_DP_ENC0_P0 |
+		  (enable ? VIDEO_MUTE_SW_DP_ENC0_P0 : 0);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
+			   val,
+			   VIDEO_MUTE_SEL_DP_ENC0_P0 |
+			   VIDEO_MUTE_SW_DP_ENC0_P0);
+
+	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32,
+		      MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable,
+		      0, 0, 0, 0, 0, &res);
+
+	dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
+		MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable, res.a0, res.a1);
+}
+
+static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
+			   0, SW_RST_B_PHYD);
+
+	/* Wait for power enable */
+	usleep_range(10, 200);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
+			   SW_RST_B_PHYD, SW_RST_B_PHYD);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL, DP_PWR_STATE_MASK);
+	mtk_dp_write(mtk_dp, MTK_DP_1040,
+		     RG_DPAUX_RX_VALID_DEGLITCH_EN | RG_XTP_GLB_CKDET_EN |
+		     RG_DPAUX_RX_EN);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_0034, 0, DA_CKM_CKTX0_EN_FORCE_EN);
+}
+
+static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_0034,
+			   DA_CKM_CKTX0_EN_FORCE_EN, DA_CKM_CKTX0_EN_FORCE_EN);
+
+	/* Disable RX */
+	mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
+	mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
+		     0x550 | FUSE_SEL | MEM_ISO_EN);
+}
+
+static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
+{
+	mtk_dp->train_info.link_rate = DP_LINK_BW_5_4;
+	mtk_dp->train_info.lane_count = mtk_dp->max_lanes;
+	mtk_dp->train_info.cable_plugged_in = false;
+
+	mtk_dp->info.format = DP_PIXELFORMAT_RGB;
+	memset(&mtk_dp->info.vm, 0, sizeof(struct videomode));
+}
+
+static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
+{
+	u32 sram_read_start = min_t(u32, MTK_DP_TBC_BUF_READ_START_ADDR,
+				    mtk_dp->info.vm.hactive /
+				    mtk_dp->train_info.lane_count /
+				    MTK_DP_4P1T / MTK_DP_HDE /
+				    MTK_DP_PIX_PER_ADDR);
+	mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
+	mtk_dp_setup_encoder(mtk_dp);
+}
+
+static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_setup_tu(mtk_dp);
+}
+
+static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes,
+					  u8 dpcd_adjust_req[2])
+{
+	int lane;
+
+	for (lane = 0; lane < lanes; ++lane) {
+		u8 val;
+		u8 swing;
+		u8 preemphasis;
+		int index = lane / 2;
+		int shift = lane % 2 ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
+
+		swing = (dpcd_adjust_req[index] >> shift) &
+			DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
+		preemphasis = ((dpcd_adjust_req[index] >> shift) &
+			       DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
+			      DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
+		val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
+		      preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+		if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
+			val |= DP_TRAIN_MAX_SWING_REACHED;
+		if (preemphasis == 3)
+			val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+		mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing, preemphasis);
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET + lane,
+				   val);
+	}
+}
+
+static void mtk_dp_pattern(struct mtk_dp *mtk_dp, bool is_tps1)
+{
+	int pattern;
+	unsigned int aux_offset;
+
+	if (is_tps1) {
+		pattern = 1;
+		aux_offset = DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1;
+	} else {
+		aux_offset = mtk_dp->train_info.channel_eq_pattern;
+
+		switch (mtk_dp->train_info.channel_eq_pattern) {
+		case DP_TRAINING_PATTERN_4:
+			pattern = 4;
+			break;
+		case DP_TRAINING_PATTERN_3:
+			pattern = 3;
+			aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
+			break;
+		case DP_TRAINING_PATTERN_2:
+		default:
+			pattern = 2;
+			aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
+			break;
+		}
+	}
+
+	mtk_dp_train_set_pattern(mtk_dp, pattern);
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET, aux_offset);
+}
+
+static int mtk_dp_train_setting(struct mtk_dp *mtk_dp, u8 target_link_rate,
+				u8 target_lane_count)
+{
+	int ret;
+
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET, target_link_rate);
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
+			   target_lane_count | DP_LANE_COUNT_ENHANCED_FRAME_EN);
+
+	if (mtk_dp->train_info.sink_ssc)
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
+				   DP_SPREAD_AMP_0_5);
+
+	mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
+	ret = mtk_dp_phy_configure(mtk_dp, target_link_rate, target_lane_count);
+	if (ret)
+		return ret;
+
+	dev_dbg(mtk_dp->dev,
+		"Link train target_link_rate = 0x%x, target_lane_count = 0x%x\n",
+		target_link_rate, target_lane_count);
+
+	return 0;
+}
+
+static int mtk_dp_train_cr(struct mtk_dp *mtk_dp, u8 target_lane_count)
+{
+	u8 lane_adjust[2] = {};
+	u8 link_status[DP_LINK_STATUS_SIZE] = {};
+	u8 prev_lane_adjust = 0xff;
+	int train_retries = 0;
+	int voltage_retries = 0;
+
+	mtk_dp_pattern(mtk_dp, true);
+
+	/* In DP spec 1.4, the retry count of CR is defined as 10. */
+	do {
+		train_retries++;
+		if (!mtk_dp->train_info.cable_plugged_in) {
+			mtk_dp_train_set_pattern(mtk_dp, 0);
+			return -ENODEV;
+		}
+
+		drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
+				 lane_adjust, sizeof(lane_adjust));
+		mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
+					      lane_adjust);
+
+		drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
+						       mtk_dp->rx_cap);
+
+		/* check link status from sink device */
+		drm_dp_dpcd_read_link_status(&mtk_dp->aux, link_status);
+		if (drm_dp_clock_recovery_ok(link_status,
+					     target_lane_count)) {
+			dev_dbg(mtk_dp->dev, "Link train CR pass\n");
+			return 0;
+		}
+
+		/*
+		 * In DP spec 1.4, if current voltage level is the same
+		 * with previous voltage level, we need to retry 5 times.
+		 */
+		if (prev_lane_adjust == link_status[4]) {
+			voltage_retries++;
+			/*
+			 * Condition of CR fail:
+			 * 1. Failed to pass CR using the same voltage
+			 *    level over five times.
+			 * 2. Failed to pass CR when the current voltage
+			 *    level is the same with previous voltage
+			 *    level and reach max voltage level (3).
+			 */
+			if (voltage_retries > MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY ||
+			    (prev_lane_adjust & DP_ADJUST_VOLTAGE_SWING_LANE0_MASK) == 3) {
+				dev_dbg(mtk_dp->dev, "Link train CR fail\n");
+				break;
+			}
+		} else {
+			/*
+			 * If the voltage level is changed, we need to
+			 * re-calculate this retry count.
+			 */
+			voltage_retries = 0;
+		}
+		prev_lane_adjust = link_status[4];
+	} while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
+
+	/* Failed to train CR, and disable pattern. */
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+			   DP_TRAINING_PATTERN_DISABLE);
+	mtk_dp_train_set_pattern(mtk_dp, 0);
+
+	return -ETIMEDOUT;
+}
+
+static int mtk_dp_train_eq(struct mtk_dp *mtk_dp, u8 target_lane_count)
+{
+	u8 lane_adjust[2] = {};
+	u8 link_status[DP_LINK_STATUS_SIZE] = {};
+	int train_retries = 0;
+
+	mtk_dp_pattern(mtk_dp, false);
+
+	do {
+		train_retries++;
+		if (!mtk_dp->train_info.cable_plugged_in) {
+			mtk_dp_train_set_pattern(mtk_dp, 0);
+			return -ENODEV;
+		}
+
+		drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
+				 lane_adjust, sizeof(lane_adjust));
+		mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
+					      lane_adjust);
+
+		drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
+						   mtk_dp->rx_cap);
+
+		/* check link status from sink device */
+		drm_dp_dpcd_read_link_status(&mtk_dp->aux, link_status);
+		if (drm_dp_channel_eq_ok(link_status, target_lane_count)) {
+			dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
+
+			/* Training done, and disable pattern. */
+			drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+					   DP_TRAINING_PATTERN_DISABLE);
+			mtk_dp_train_set_pattern(mtk_dp, 0);
+			return 0;
+		}
+		dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
+	} while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
+
+	/* Failed to train EQ, and disable pattern. */
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+			   DP_TRAINING_PATTERN_DISABLE);
+	mtk_dp_train_set_pattern(mtk_dp, 0);
+
+	return -ETIMEDOUT;
+}
+
+static int mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
+{
+	u8 val;
+	ssize_t ret;
+
+	drm_dp_read_dpcd_caps(&mtk_dp->aux, mtk_dp->rx_cap);
+
+	if (drm_dp_tps4_supported(mtk_dp->rx_cap))
+		mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_4;
+	else if (drm_dp_tps3_supported(mtk_dp->rx_cap))
+		mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_3;
+	else
+		mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_2;
+
+	mtk_dp->train_info.sink_ssc = drm_dp_max_downspread(mtk_dp->rx_cap);
+
+	ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
+	if (ret < 1) {
+		drm_err(mtk_dp->drm_dev, "Read mstm cap failed\n");
+		return ret == 0 ? -EIO : ret;
+	}
+
+	if (val & DP_MST_CAP) {
+		/* Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 */
+		ret = drm_dp_dpcd_readb(&mtk_dp->aux,
+					DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
+					&val);
+		if (ret < 1) {
+			drm_err(mtk_dp->drm_dev, "Read irq vector failed\n");
+			return ret == 0 ? -EIO : ret;
+		}
+
+		if (val)
+			drm_dp_dpcd_writeb(&mtk_dp->aux,
+					   DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
+					   val);
+	}
+
+	return 0;
+}
+
+static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
+{
+	phy_reset(mtk_dp->phy);
+	mtk_dp_reset_swing_pre_emphasis(mtk_dp);
+}
+
+static int mtk_dp_training(struct mtk_dp *mtk_dp)
+{
+	int ret;
+	u8 lane_count, link_rate, train_limit, max_link_rate;
+
+	link_rate = min_t(u8, mtk_dp->max_linkrate,
+			  mtk_dp->rx_cap[DP_MAX_LINK_RATE]);
+	max_link_rate = link_rate;
+	lane_count = min_t(u8, mtk_dp->max_lanes,
+			   drm_dp_max_lane_count(mtk_dp->rx_cap));
+
+	/*
+	 * TPS are generated by the hardware pattern generator. From the
+	 * hardware setting we need to disable this scramble setting before
+	 * use the TPS pattern generator.
+	 */
+	mtk_dp_training_set_scramble(mtk_dp, false);
+
+	for (train_limit = 6; train_limit > 0; train_limit--) {
+		mtk_dp_train_change_mode(mtk_dp);
+
+		ret = mtk_dp_train_setting(mtk_dp, link_rate, lane_count);
+		if (ret)
+			return ret;
+
+		ret = mtk_dp_train_cr(mtk_dp, lane_count);
+		if (ret == -ENODEV) {
+			return ret;
+		} else if (ret) {
+			/* reduce link rate */
+			switch (link_rate) {
+			case DP_LINK_BW_1_62:
+				lane_count = lane_count / 2;
+				link_rate = max_link_rate;
+				if (lane_count == 0)
+					return -EIO;
+				break;
+			case DP_LINK_BW_2_7:
+				link_rate = DP_LINK_BW_1_62;
+				break;
+			case DP_LINK_BW_5_4:
+				link_rate = DP_LINK_BW_2_7;
+				break;
+			case DP_LINK_BW_8_1:
+				link_rate = DP_LINK_BW_5_4;
+				break;
+			default:
+				return -EINVAL;
+			};
+			continue;
+		}
+
+		ret = mtk_dp_train_eq(mtk_dp, lane_count);
+		if (ret == -ENODEV) {
+			return ret;
+		} else if (ret) {
+			/* reduce lane count */
+			if (lane_count == 0)
+				return -EIO;
+			lane_count /= 2;
+			continue;
+		}
+
+		/* if we can run to this, training is done. */
+		break;
+	}
+
+	if (train_limit == 0)
+		return -ETIMEDOUT;
+
+	mtk_dp->train_info.link_rate = link_rate;
+	mtk_dp->train_info.lane_count = lane_count;
+
+	/*
+	 * After training done, we need to output normal stream instead of TPS,
+	 * so we need to enable scramble.
+	 */
+	mtk_dp_training_set_scramble(mtk_dp, true);
+	mtk_dp_set_enhanced_frame_mode(mtk_dp);
+
+	return 0;
+}
+
+static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	/* the mute sequence is different between enable and disable */
+	if (enable) {
+		mtk_dp_msa_bypass_enable(mtk_dp, false);
+		mtk_dp_pg_enable(mtk_dp, false);
+		mtk_dp_set_tx_out(mtk_dp);
+		mtk_dp_video_mute(mtk_dp, false);
+	} else {
+		mtk_dp_video_mute(mtk_dp, true);
+		mtk_dp_pg_enable(mtk_dp, true);
+		mtk_dp_msa_bypass_enable(mtk_dp, true);
+	}
+}
+
+static int mtk_dp_video_config(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_config_mn_mode(mtk_dp);
+	mtk_dp_set_msa(mtk_dp);
+	mtk_dp_set_color_depth(mtk_dp);
+	return mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
+}
+
+static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_set_idle_pattern(mtk_dp, true);
+	mtk_dp_initialize_priv_data(mtk_dp);
+
+	mtk_dp_initialize_settings(mtk_dp);
+	mtk_dp_initialize_aux_settings(mtk_dp);
+	mtk_dp_initialize_digital_settings(mtk_dp);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
+			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0,
+			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0);
+	mtk_dp_initialize_hpd_detect_settings(mtk_dp);
+
+	mtk_dp_digital_sw_reset(mtk_dp);
+}
+
+static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
+{
+	struct mtk_dp *mtk_dp = dev;
+	unsigned long flags;
+	u32 status;
+
+	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
+	status = mtk_dp->irq_thread_handle;
+	mtk_dp->irq_thread_handle = 0;
+	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
+
+	if (status & MTK_DP_THREAD_CABLE_STATE_CHG)
+		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
+
+	if (status & MTK_DP_THREAD_HPD_EVENT)
+		dev_dbg(mtk_dp->dev, "Receive IRQ from sink devices\n");
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
+{
+	struct mtk_dp *mtk_dp = dev;
+	bool cable_sta_chg = false;
+	unsigned long flags;
+	u32 irq_status = mtk_dp_swirq_get_clear(mtk_dp) |
+			 mtk_dp_hwirq_get_clear(mtk_dp);
+
+	if (!irq_status)
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
+
+	if (irq_status & MTK_DP_HPD_INTERRUPT)
+		mtk_dp->irq_thread_handle |= MTK_DP_THREAD_HPD_EVENT;
+
+	/* Cable state is changed. */
+	if (irq_status != MTK_DP_HPD_INTERRUPT) {
+		mtk_dp->irq_thread_handle |= MTK_DP_THREAD_CABLE_STATE_CHG;
+		cable_sta_chg = true;
+	}
+
+	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
+
+	if (cable_sta_chg) {
+		if (!!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
+		       HPD_DB_DP_TRANS_P0_MASK))
+			mtk_dp->train_info.cable_plugged_in = true;
+		else
+			mtk_dp->train_info.cable_plugged_in = false;
+	}
+
+	return IRQ_WAKE_THREAD;
+}
+
+static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
+			   struct platform_device *pdev)
+{
+	struct device_node *endpoint;
+	struct device *dev = &pdev->dev;
+	int ret;
+	void __iomem *base;
+	u32 linkrate;
+	int len;
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	mtk_dp->regs = devm_regmap_init_mmio(dev, base, &mtk_dp_regmap_config);
+	if (IS_ERR(mtk_dp->regs))
+		return PTR_ERR(mtk_dp->regs);
+
+	endpoint = of_graph_get_endpoint_by_regs(pdev->dev.of_node, 1, -1);
+	len = of_property_count_elems_of_size(endpoint,
+					      "data-lanes", sizeof(u32));
+	if (len < 0 || len > 4 || len == 3) {
+		dev_err(dev, "invalid data lane size: %d\n", len);
+		return -EINVAL;
+	}
+
+	mtk_dp->max_lanes = len;
+
+	ret = device_property_read_u32(dev, "max-linkrate-mhz", &linkrate);
+	if (ret) {
+		dev_err(dev, "failed to read max linkrate: %d\n", ret);
+		return ret;
+	}
+
+	mtk_dp->max_linkrate = drm_dp_link_rate_to_bw_code(linkrate * 100);
+
+	return 0;
+}
+
+static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
+				    struct drm_connector *connector)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	bool enabled = mtk_dp->enabled;
+	struct edid *new_edid = NULL;
+
+	if (!enabled) {
+		drm_bridge_chain_pre_enable(bridge);
+
+		/* power on aux */
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+				   DP_PWR_STATE_BANDGAP_TPLL_LANE,
+				   DP_PWR_STATE_MASK);
+
+		/* power on panel */
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+		usleep_range(2000, 5000);
+	}
+
+	new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
+
+	/*
+	 * Parse capability here to let atomic_get_input_bus_fmts and
+	 * mode_valid use the capability to calculate sink bitrates.
+	 */
+	if (mtk_dp_parse_capabilities(mtk_dp)) {
+		drm_err(mtk_dp->drm_dev, "Can't parse capabilities\n");
+		new_edid = NULL;
+	}
+
+	if (!enabled) {
+		/* power off panel */
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
+		usleep_range(2000, 3000);
+
+		/* power off aux */
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+				   DP_PWR_STATE_BANDGAP_TPLL,
+				   DP_PWR_STATE_MASK);
+
+		drm_bridge_chain_post_disable(bridge);
+	}
+
+	return new_edid;
+}
+
+static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
+				   struct drm_dp_aux_msg *msg)
+{
+	struct mtk_dp *mtk_dp;
+	bool is_read;
+	u8 request;
+	size_t accessed_bytes = 0;
+	int ret;
+
+	mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
+
+	if (!mtk_dp->train_info.cable_plugged_in) {
+		ret = -EAGAIN;
+		goto err;
+	}
+
+	switch (msg->request) {
+	case DP_AUX_I2C_MOT:
+	case DP_AUX_I2C_WRITE:
+	case DP_AUX_NATIVE_WRITE:
+	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
+	case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
+		request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
+		is_read = false;
+		break;
+	case DP_AUX_I2C_READ:
+	case DP_AUX_NATIVE_READ:
+	case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
+		request = msg->request;
+		is_read = true;
+		break;
+	default:
+		drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
+			msg->request);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	do {
+		size_t to_access = min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
+					 msg->size - accessed_bytes);
+
+		ret = mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
+					     msg->address + accessed_bytes,
+					     msg->buffer + accessed_bytes,
+					     to_access);
+
+		if (ret) {
+			drm_info(mtk_dp->drm_dev,
+				 "Failed to do AUX transfer: %d\n", ret);
+			goto err;
+		}
+		accessed_bytes += to_access;
+	} while (accessed_bytes < msg->size);
+
+	msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
+	return msg->size;
+err:
+	msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
+	return ret;
+}
+
+static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
+{
+	int ret;
+
+	ret = phy_init(mtk_dp->phy);
+	if (ret) {
+		dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
+		return ret;
+	}
+
+	mtk_dp_init_port(mtk_dp);
+	mtk_dp_power_enable(mtk_dp);
+
+	return 0;
+}
+
+static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_power_disable(mtk_dp);
+	phy_exit(mtk_dp->phy);
+}
+
+static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
+				enum drm_bridge_attach_flags flags)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	int ret;
+
+	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+		dev_err(mtk_dp->dev, "Driver does not provide a connector!");
+		return -EINVAL;
+	}
+
+	mtk_dp->aux.drm_dev = bridge->dev;
+	ret = drm_dp_aux_register(&mtk_dp->aux);
+	if (ret) {
+		dev_err(mtk_dp->dev,
+			"failed to register DP AUX channel: %d\n", ret);
+		return ret;
+	}
+
+	ret = mtk_dp_poweron(mtk_dp);
+	if (ret)
+		goto err_aux_register;
+
+	if (mtk_dp->next_bridge) {
+		ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
+					&mtk_dp->bridge, flags);
+		if (ret) {
+			drm_warn(mtk_dp->drm_dev,
+				 "Failed to attach external bridge: %d\n", ret);
+			goto err_bridge_attach;
+		}
+	}
+
+	mtk_dp->drm_dev = bridge->dev;
+
+	mtk_dp_hwirq_enable(mtk_dp, true);
+
+	return 0;
+
+err_bridge_attach:
+	mtk_dp_poweroff(mtk_dp);
+err_aux_register:
+	drm_dp_aux_unregister(&mtk_dp->aux);
+	return ret;
+}
+
+static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+
+	mtk_dp_hwirq_enable(mtk_dp, false);
+	mtk_dp->drm_dev = NULL;
+	mtk_dp_poweroff(mtk_dp);
+	drm_dp_aux_unregister(&mtk_dp->aux);
+}
+
+static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
+					struct drm_bridge_state *old_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	int ret;
+
+	mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
+								bridge->encoder);
+	if (!mtk_dp->conn) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as connector is missing\n");
+		return;
+	}
+
+	/* power on aux */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL_LANE,
+			   DP_PWR_STATE_MASK);
+
+	if (mtk_dp->train_info.cable_plugged_in) {
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+		usleep_range(2000, 5000);
+	}
+
+	/* Training */
+	ret = mtk_dp_training(mtk_dp);
+	if (ret) {
+		drm_err(mtk_dp->drm_dev, "Training failed, %d\n", ret);
+		goto power_off_aux;
+	}
+
+	ret = mtk_dp_video_config(mtk_dp);
+	if (ret)
+		goto power_off_aux;
+
+	mtk_dp_video_enable(mtk_dp, true);
+
+	mtk_dp->enabled = true;
+
+	return;
+power_off_aux:
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL,
+			   DP_PWR_STATE_MASK);
+}
+
+static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
+					 struct drm_bridge_state *old_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+
+	mtk_dp->enabled = false;
+	mtk_dp_video_enable(mtk_dp, false);
+
+	if (mtk_dp->train_info.cable_plugged_in) {
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
+		usleep_range(2000, 3000);
+	}
+
+	/* power off aux */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL,
+			   DP_PWR_STATE_MASK);
+
+	/* Ensure the sink is muted */
+	msleep(20);
+}
+
+static enum drm_mode_status
+mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
+			 const struct drm_display_info *info,
+			 const struct drm_display_mode *mode)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	u32 bpp = info->color_formats & DRM_COLOR_FORMAT_YCBCR422 ? 16 : 24;
+	u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
+			      drm_dp_max_lane_count(mtk_dp->rx_cap),
+			 drm_dp_bw_code_to_link_rate(mtk_dp->max_linkrate) *
+			 mtk_dp->max_lanes);
+
+	if (rate < mode->clock * bpp / 8)
+		return MODE_CLOCK_HIGH;
+
+	return MODE_OK;
+}
+
+static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
+						     struct drm_bridge_state *bridge_state,
+						     struct drm_crtc_state *crtc_state,
+						     struct drm_connector_state *conn_state,
+						     unsigned int *num_output_fmts)
+{
+	u32 *output_fmts;
+
+	*num_output_fmts = 0;
+	output_fmts = kmalloc(sizeof(*output_fmts), GFP_KERNEL);
+	if (!output_fmts)
+		return NULL;
+	*num_output_fmts = 1;
+	output_fmts[0] = MEDIA_BUS_FMT_FIXED;
+	return output_fmts;
+}
+
+static const u32 mt8195_input_fmts[] = {
+	MEDIA_BUS_FMT_RGB888_1X24,
+	MEDIA_BUS_FMT_YUV8_1X24,
+	MEDIA_BUS_FMT_YUYV8_1X16,
+};
+
+static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+						    struct drm_bridge_state *bridge_state,
+						    struct drm_crtc_state *crtc_state,
+						    struct drm_connector_state *conn_state,
+						    u32 output_fmt,
+						    unsigned int *num_input_fmts)
+{
+	u32 *input_fmts;
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+	struct drm_display_info *display_info =
+		&conn_state->connector->display_info;
+	u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
+			      drm_dp_max_lane_count(mtk_dp->rx_cap),
+			 drm_dp_bw_code_to_link_rate(mtk_dp->max_linkrate) *
+			 mtk_dp->max_lanes);
+
+	*num_input_fmts = 0;
+
+	/*
+	 * If the linkrate is smaller than datarate of RGB888, larger than
+	 * datarate of YUV422 and sink device supports YUV422, we output YUV422
+	 * format. Use this condition, we can support more resolution.
+	 */
+	if ((rate < (mode->clock * 24 / 8)) &&
+	    (rate > (mode->clock * 16 / 8)) &&
+	    (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) {
+		input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL);
+		if (!input_fmts)
+			return NULL;
+		*num_input_fmts = 1;
+		input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
+	} else {
+		input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts),
+				     sizeof(*input_fmts),
+				     GFP_KERNEL);
+		if (!input_fmts)
+			return NULL;
+
+		*num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
+		memcpy(input_fmts, mt8195_input_fmts, sizeof(mt8195_input_fmts));
+	}
+
+	return input_fmts;
+}
+
+static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
+				      struct drm_bridge_state *bridge_state,
+				      struct drm_crtc_state *crtc_state,
+				      struct drm_connector_state *conn_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	struct drm_crtc *crtc = conn_state->crtc;
+	unsigned int input_bus_format;
+
+	input_bus_format = bridge_state->input_bus_cfg.format;
+
+	dev_dbg(mtk_dp->dev, "input format 0x%04x, output format 0x%04x\n",
+		bridge_state->input_bus_cfg.format,
+		 bridge_state->output_bus_cfg.format);
+
+	if (input_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
+		mtk_dp->info.format = DP_PIXELFORMAT_YUV422;
+	else
+		mtk_dp->info.format = DP_PIXELFORMAT_RGB;
+
+	if (!crtc) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as connector state doesn't have a crtc\n");
+		return -EINVAL;
+	}
+
+	drm_display_mode_to_videomode(&crtc_state->adjusted_mode, &mtk_dp->info.vm);
+
+	return 0;
+}
+
+static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
+	.atomic_check = mtk_dp_bridge_atomic_check,
+	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+	.atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts,
+	.atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts,
+	.atomic_reset = drm_atomic_helper_bridge_reset,
+	.attach = mtk_dp_bridge_attach,
+	.detach = mtk_dp_bridge_detach,
+	.atomic_enable = mtk_dp_bridge_atomic_enable,
+	.atomic_disable = mtk_dp_bridge_atomic_disable,
+	.mode_valid = mtk_dp_bridge_mode_valid,
+	.get_edid = mtk_dp_get_edid,
+};
+
+static int mtk_dp_probe(struct platform_device *pdev)
+{
+	struct mtk_dp *mtk_dp;
+	struct device *dev = &pdev->dev;
+	int ret, irq_num;
+
+	mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
+	if (!mtk_dp)
+		return -ENOMEM;
+
+	mtk_dp->dev = dev;
+
+	irq_num = platform_get_irq(pdev, 0);
+	if (irq_num < 0)
+		return dev_err_probe(dev, irq_num,
+				     "failed to request dp irq resource\n");
+
+	mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
+	if (IS_ERR(mtk_dp->next_bridge))
+		return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge),
+				     "Failed to get bridge\n");
+
+	ret = mtk_dp_dt_parse(mtk_dp, pdev);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to parse dt\n");
+
+	drm_dp_aux_init(&mtk_dp->aux);
+	mtk_dp->aux.name = "aux_mtk_dp";
+	mtk_dp->aux.transfer = mtk_dp_aux_transfer;
+
+	spin_lock_init(&mtk_dp->irq_thread_lock);
+
+	ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
+					mtk_dp_hpd_event_thread,
+					IRQ_TYPE_LEVEL_HIGH, dev_name(dev),
+					mtk_dp);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "failed to request mediatek dptx irq\n");
+
+	platform_set_drvdata(pdev, mtk_dp);
+
+	mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
+							PLATFORM_DEVID_AUTO,
+							&mtk_dp->regs,
+							sizeof(struct regmap *));
+	if (IS_ERR(mtk_dp->phy_dev))
+		return dev_err_probe(dev, PTR_ERR(mtk_dp->phy_dev),
+				     "Failed to create device mediatek-dp-phy\n");
+
+	mtk_dp_get_calibration_data(mtk_dp);
+
+	mtk_dp->phy = devm_phy_get(&mtk_dp->phy_dev->dev, "dp");
+
+	if (IS_ERR(mtk_dp->phy)) {
+		platform_device_unregister(mtk_dp->phy_dev);
+		return dev_err_probe(dev, PTR_ERR(mtk_dp->phy),
+				     "Failed to get phy\n");
+	}
+
+	mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
+	mtk_dp->bridge.of_node = dev->of_node;
+
+	mtk_dp->bridge.ops =
+		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
+	mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
+
+	drm_bridge_add(&mtk_dp->bridge);
+
+	pm_runtime_enable(dev);
+	pm_runtime_get_sync(dev);
+
+	return 0;
+}
+
+static int mtk_dp_remove(struct platform_device *pdev)
+{
+	struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
+
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	drm_bridge_remove(&mtk_dp->bridge);
+	platform_device_unregister(mtk_dp->phy_dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_dp_suspend(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_power_disable(mtk_dp);
+	mtk_dp_hwirq_enable(mtk_dp, false);
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static int mtk_dp_resume(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	pm_runtime_get_sync(dev);
+	mtk_dp_init_port(mtk_dp);
+	mtk_dp_hwirq_enable(mtk_dp, true);
+	mtk_dp_power_enable(mtk_dp);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
+
+static const struct of_device_id mtk_dp_of_match[] = {
+	{ .compatible = "mediatek,mt8195-edp-tx" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
+
+struct platform_driver mtk_dp_driver = {
+	.probe = mtk_dp_probe,
+	.remove = mtk_dp_remove,
+	.driver = {
+		.name = "mediatek-drm-dp",
+		.of_match_table = mtk_dp_of_match,
+		.pm = &mtk_dp_pm_ops,
+	},
+};
+
+module_platform_driver(mtk_dp_driver);
+
+MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
+MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
+MODULE_AUTHOR("Bo-Chen Chen <rex-bc.chen@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
new file mode 100644
index 000000000000..3f01ba44871f
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
@@ -0,0 +1,305 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019-2022 MediaTek Inc.
+ * Copyright (c) 2022 BayLibre
+ */
+#ifndef _MTK_DP_REG_H_
+#define _MTK_DP_REG_H_
+
+#define SEC_OFFSET	0x4000
+
+#define MTK_DP_HPD_DISCONNECT		BIT(1)
+#define MTK_DP_HPD_CONNECT		BIT(2)
+#define MTK_DP_HPD_INTERRUPT		BIT(3)
+
+/* offset: 0x0 */
+#define DP_PHY_GLB_BIAS_GEN_00		0x0
+#define RG_XTP_GLB_BIAS_INTR_CTRL		GENMASK(20, 16)
+#define DP_PHY_GLB_DPAUX_TX		0x8
+#define RG_CKM_PT0_CKTX_IMPSEL			GENMASK(23, 20)
+#define MTK_DP_0034			0x34
+#define DA_XTP_GLB_CKDET_EN_FORCE_VAL		BIT(15)
+#define DA_XTP_GLB_CKDET_EN_FORCE_EN		BIT(14)
+#define DA_CKM_INTCKTX_EN_FORCE_VAL		BIT(13)
+#define DA_CKM_INTCKTX_EN_FORCE_EN		BIT(12)
+#define DA_CKM_CKTX0_EN_FORCE_VAL		BIT(11)
+#define DA_CKM_CKTX0_EN_FORCE_EN		BIT(10)
+#define DA_CKM_XTAL_CK_FORCE_VAL		BIT(9)
+#define DA_CKM_XTAL_CK_FORCE_EN			BIT(8)
+#define DA_CKM_BIAS_LPF_EN_FORCE_VAL		BIT(7)
+#define DA_CKM_BIAS_LPF_EN_FORCE_EN		BIT(6)
+#define DA_CKM_BIAS_EN_FORCE_VAL		BIT(5)
+#define DA_CKM_BIAS_EN_FORCE_EN			BIT(4)
+#define DA_XTP_GLB_AVD10_ON_FORCE_VAL		BIT(3)
+#define DA_XTP_GLB_AVD10_ON_FORCE		BIT(2)
+#define DA_XTP_GLB_LDO_EN_FORCE_VAL		BIT(1)
+#define DA_XTP_GLB_LDO_EN_FORCE_EN		BIT(0)
+#define DP_PHY_LANE_TX_0		0x104
+#define RG_XTP_LN0_TX_IMPSEL_PMOS		GENMASK(15, 12)
+#define RG_XTP_LN0_TX_IMPSEL_NMOS		GENMASK(19, 16)
+#define DP_PHY_LANE_TX_1		0x204
+#define RG_XTP_LN1_TX_IMPSEL_PMOS		GENMASK(15, 12)
+#define RG_XTP_LN1_TX_IMPSEL_NMOS		GENMASK(19, 16)
+#define DP_PHY_LANE_TX_2		0x304
+#define RG_XTP_LN2_TX_IMPSEL_PMOS		GENMASK(15, 12)
+#define RG_XTP_LN2_TX_IMPSEL_NMOS		GENMASK(19, 16)
+#define DP_PHY_LANE_TX_3		0x404
+#define RG_XTP_LN3_TX_IMPSEL_PMOS		GENMASK(15, 12)
+#define RG_XTP_LN3_TX_IMPSEL_NMOS		GENMASK(19, 16)
+#define MTK_DP_1040			0x1040
+#define RG_DPAUX_RX_VALID_DEGLITCH_EN		BIT(2)
+#define RG_XTP_GLB_CKDET_EN			BIT(1)
+#define RG_DPAUX_RX_EN				BIT(0)
+
+/* offset: TOP_OFFSET (0x2000) */
+#define MTK_DP_TOP_PWR_STATE		0x2000
+#define DP_PWR_STATE_MASK			GENMASK(1, 0)
+#define DP_PWR_STATE_BANDGAP			BIT(0)
+#define DP_PWR_STATE_BANDGAP_TPLL		BIT(1)
+#define DP_PWR_STATE_BANDGAP_TPLL_LANE		GENMASK(1, 0)
+#define MTK_DP_TOP_SWING_EMP		0x2004
+#define DP_TX0_VOLT_SWING_MASK			GENMASK(1, 0)
+#define DP_TX0_VOLT_SWING_SHIFT			0
+#define DP_TX0_PRE_EMPH_MASK			GENMASK(3, 2)
+#define DP_TX0_PRE_EMPH_SHIFT			2
+#define DP_TX1_VOLT_SWING_MASK			GENMASK(9, 8)
+#define DP_TX1_VOLT_SWING_SHIFT			8
+#define DP_TX1_PRE_EMPH_MASK			GENMASK(11, 10)
+#define DP_TX2_VOLT_SWING_MASK			GENMASK(17, 16)
+#define DP_TX2_PRE_EMPH_MASK			GENMASK(19, 18)
+#define DP_TX3_VOLT_SWING_MASK			GENMASK(25, 24)
+#define DP_TX3_PRE_EMPH_MASK			GENMASK(27, 26)
+#define MTK_DP_TOP_RESET_AND_PROBE	0x2020
+#define SW_RST_B_PHYD				BIT(4)
+#define MTK_DP_TOP_IRQ_MASK		0x202c
+#define IRQ_MASK_AUX_TOP_IRQ			BIT(2)
+#define MTK_DP_TOP_MEM_PD		0x2038
+#define MEM_ISO_EN				BIT(0)
+#define FUSE_SEL				BIT(2)
+
+/* offset: ENC0_OFFSET (0x3000) */
+#define MTK_DP_ENC0_P0_3000			0x3000
+#define LANE_NUM_DP_ENC0_P0_MASK			GENMASK(1, 0)
+#define VIDEO_MUTE_SW_DP_ENC0_P0			BIT(2)
+#define VIDEO_MUTE_SEL_DP_ENC0_P0			BIT(3)
+#define ENHANCED_FRAME_EN_DP_ENC0_P0			BIT(4)
+#define MTK_DP_ENC0_P0_3004			0x3004
+#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK		BIT(8)
+#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0		BIT(9)
+#define MTK_DP_ENC0_P0_3010			0x3010
+#define HTOTAL_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3014			0x3014
+#define VTOTAL_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3018			0x3018
+#define HSTART_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_301C			0x301c
+#define VSTART_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3020			0x3020
+#define HWIDTH_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3024			0x3024
+#define VHEIGHT_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3028			0x3028
+#define HSW_SW_DP_ENC0_P0_MASK				GENMASK(14, 0)
+#define HSP_SW_DP_ENC0_P0_MASK				BIT(15)
+#define MTK_DP_ENC0_P0_302C			0x302c
+#define VSW_SW_DP_ENC0_P0_MASK				GENMASK(14, 0)
+#define VSP_SW_DP_ENC0_P0_MASK				BIT(15)
+#define MTK_DP_ENC0_P0_3030			0x3030
+#define HTOTAL_SEL_DP_ENC0_P0				BIT(0)
+#define VTOTAL_SEL_DP_ENC0_P0				BIT(1)
+#define HSTART_SEL_DP_ENC0_P0				BIT(2)
+#define VSTART_SEL_DP_ENC0_P0				BIT(3)
+#define HWIDTH_SEL_DP_ENC0_P0				BIT(4)
+#define VHEIGHT_SEL_DP_ENC0_P0				BIT(5)
+#define HSP_SEL_DP_ENC0_P0				BIT(6)
+#define HSW_SEL_DP_ENC0_P0				BIT(7)
+#define VSP_SEL_DP_ENC0_P0				BIT(8)
+#define VSW_SEL_DP_ENC0_P0				BIT(9)
+#define MTK_DP_ENC0_P0_3034			0x3034
+#define MTK_DP_ENC0_P0_3038			0x3038
+#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK		BIT(11)
+#define MTK_DP_ENC0_P0_303C			0x303c
+#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK		GENMASK(5, 0)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK		GENMASK(10, 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT		(0 << 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT		(1 << 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT		(2 << 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT		(3 << 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT		(4 << 8)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK		GENMASK(14, 12)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB		(0 << 12)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422		(1 << 12)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420		(2 << 12)
+#define VIDEO_MN_GEN_EN_DP_ENC0_P0			BIT(15)
+#define MTK_DP_ENC0_P0_3040			0x3040
+#define SDP_DOWN_CNT_DP_ENC0_P0_VAL			0x20
+#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK		GENMASK(11, 0)
+#define MTK_DP_ENC0_P0_304C			0x304c
+#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK			BIT(2)
+#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK		BIT(8)
+#define MTK_DP_ENC0_P0_3064			0x3064
+#define HDE_NUM_LAST_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3154			0x3154
+#define PGEN_HTOTAL_DP_ENC0_P0_MASK			GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_3158			0x3158
+#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK		GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_315C			0x315c
+#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK		GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_3160			0x3160
+#define PGEN_HFDE_START_DP_ENC0_P0_MASK			GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_3164			0x3164
+#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK		GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_3168			0x3168
+#define PGEN_VTOTAL_DP_ENC0_P0_MASK			GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_316C			0x316c
+#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK		GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_3170			0x3170
+#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK		GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_3174			0x3174
+#define PGEN_VFDE_START_DP_ENC0_P0_MASK			GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_3178			0x3178
+#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK		GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_31B0			0x31b0
+#define PGEN_PATTERN_SEL_VAL				4
+#define PGEN_PATTERN_SEL_MASK				GENMASK(6, 4)
+#define MTK_DP_ENC0_P0_31EC			0x31ec
+#define AUDIO_CH_SRC_SEL_DP_ENC0_P0			BIT(4)
+#define ISRC1_HB3_DP_ENC0_P0_MASK			GENMASK(15, 8)
+
+/* offset: ENC1_OFFSET (0x3200) */
+#define MTK_DP_ENC1_P0_3300			0x3300
+#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL		2
+#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK		GENMASK(9, 8)
+#define MTK_DP_ENC1_P0_3364			0x3364
+#define SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL		0x20
+#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK	GENMASK(11, 0)
+#define FIFO_READ_START_POINT_DP_ENC1_P0_VAL		4
+#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK		GENMASK(15, 12)
+#define MTK_DP_ENC1_P0_3368			0x3368
+#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0	BIT(0)
+#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0		BIT(4)
+#define SDP_DP13_EN_DP_ENC1_P0				BIT(8)
+#define BS2BS_MODE_DP_ENC1_P0				BIT(12)
+#define BS2BS_MODE_DP_ENC1_P0_MASK			GENMASK(13, 12)
+#define BS2BS_MODE_DP_ENC1_P0_VAL			1
+#define DP_ENC1_P0_3368_VAL				(VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0 | \
+							 VIDEO_STABLE_CNT_THRD_DP_ENC1_P0 | \
+							 SDP_DP13_EN_DP_ENC1_P0 | \
+							 BS2BS_MODE_DP_ENC1_P0)
+
+/* offset: TRANS_OFFSET (0x3400) */
+#define MTK_DP_TRANS_P0_3400				0x3400
+#define PATTERN1_EN_DP_TRANS_P0_MASK				BIT(12)
+#define PATTERN2_EN_DP_TRANS_P0_MASK				BIT(13)
+#define PATTERN3_EN_DP_TRANS_P0_MASK				BIT(14)
+#define PATTERN4_EN_DP_TRANS_P0_MASK				BIT(15)
+#define MTK_DP_TRANS_P0_3404				0x3404
+#define DP_SCR_EN_DP_TRANS_P0_MASK				BIT(0)
+#define MTK_DP_TRANS_P0_340C				0x340c
+#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0		BIT(13)
+#define MTK_DP_TRANS_P0_3410				0x3410
+#define HPD_DEB_THD_DP_TRANS_P0_MASK				GENMASK(3, 0)
+#define HPD_INT_THD_DP_TRANS_P0_MASK				GENMASK(7, 4)
+#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US			(2 << 4)
+#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US			(2 << 6)
+#define HPD_DISC_THD_DP_TRANS_P0_MASK				GENMASK(11, 8)
+#define HPD_CONN_THD_DP_TRANS_P0_MASK				GENMASK(15, 12)
+#define MTK_DP_TRANS_P0_3414				0x3414
+#define HPD_DB_DP_TRANS_P0_MASK					BIT(2)
+#define MTK_DP_TRANS_P0_3418				0x3418
+#define IRQ_CLR_DP_TRANS_P0_MASK				GENMASK(3, 0)
+#define IRQ_MASK_DP_TRANS_P0_MASK				GENMASK(7, 4)
+#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ				(BIT(1) << 4)
+#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ				(BIT(2) << 4)
+#define IRQ_MASK_DP_TRANS_P0_INT_IRQ				(BIT(3) << 4)
+#define IRQ_STATUS_DP_TRANS_P0_MASK				GENMASK(15, 12)
+#define MTK_DP_TRANS_P0_342C				0x342c
+#define XTAL_FREQ_DP_TRANS_P0_DEFAULT				(BIT(0) | BIT(3) | BIT(5) | BIT(6))
+#define XTAL_FREQ_DP_TRANS_P0_MASK				GENMASK(7, 0)
+#define MTK_DP_TRANS_P0_3430				0x3430
+#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK			GENMASK(1, 0)
+#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT		BIT(1)
+#define MTK_DP_TRANS_P0_34A4				0x34a4
+#define LANE_NUM_DP_TRANS_P0_MASK				GENMASK(3, 2)
+#define MTK_DP_TRANS_P0_3540				0x3540
+#define FEC_EN_DP_TRANS_P0_MASK					BIT(0)
+#define FEC_CLOCK_EN_MODE_DP_TRANS_P0				BIT(3)
+#define MTK_DP_TRANS_P0_3580				0x3580
+#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK		BIT(8)
+#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK		BIT(9)
+#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK		BIT(10)
+#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK		BIT(11)
+#define MTK_DP_TRANS_P0_35C8				0x35c8
+#define SW_IRQ_CLR_DP_TRANS_P0_MASK				GENMASK(15, 0)
+#define SW_IRQ_STATUS_DP_TRANS_P0_MASK				GENMASK(15, 0)
+#define MTK_DP_TRANS_P0_35D0				0x35d0
+#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_TRANS_P0_35F0				0x35f0
+#define DP_TRANS_DUMMY_RW_0					BIT(3)
+#define DP_TRANS_DUMMY_RW_0_MASK				GENMASK(3, 2)
+
+/* offset: AUX_OFFSET (0x3600) */
+#define MTK_DP_AUX_P0_360C			0x360c
+#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK			GENMASK(12, 0)
+#define AUX_TIMEOUT_THR_AUX_TX_P0_VAL			0x1595
+#define MTK_DP_AUX_P0_3614			0x3614
+#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK		GENMASK(6, 0)
+#define AUX_RX_UI_CNT_THR_AUX_FOR_26M			13
+#define MTK_DP_AUX_P0_3618			0x3618
+#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK			BIT(9)
+#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK	GENMASK(3, 0)
+#define MTK_DP_AUX_P0_3620			0x3620
+#define AUX_RD_MODE_AUX_TX_P0_MASK			BIT(9)
+#define AUX_RX_FIFO_READ_PULSE_TX_P0			BIT(8)
+#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK		GENMASK(7, 0)
+#define MTK_DP_AUX_P0_3624			0x3624
+#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK		GENMASK(3, 0)
+#define MTK_DP_AUX_P0_3628			0x3628
+#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK			GENMASK(9, 0)
+#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE		BIT(0)
+#define MTK_DP_AUX_P0_362C			0x362c
+#define AUX_NO_LENGTH_AUX_TX_P0				BIT(0)
+#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK		BIT(1)
+#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK		GENMASK(15, 2)
+#define MTK_DP_AUX_P0_3630			0x3630
+#define AUX_TX_REQUEST_READY_AUX_TX_P0			BIT(3)
+#define MTK_DP_AUX_P0_3634			0x3634
+#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK		GENMASK(15, 8)
+#define AUX_TX_OVER_SAMPLE_RATE_FOR_26M			25
+#define MTK_DP_AUX_P0_3640			0x3640
+#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(6)
+#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(5)
+#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(4)
+#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0			BIT(3)
+#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0			BIT(2)
+#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0			BIT(1)
+#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0			BIT(0)
+#define DP_AUX_P0_3640_VAL				(AUX_400US_TIMEOUT_IRQ_AUX_TX_P0 | \
+							 AUX_RX_DATA_RECV_IRQ_AUX_TX_P0 | \
+							 AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0 | \
+							 AUX_RX_CMD_RECV_IRQ_AUX_TX_P0 | \
+							 AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0 | \
+							 AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0 | \
+							 AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
+#define MTK_DP_AUX_P0_3644			0x3644
+#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK		GENMASK(3, 0)
+#define MTK_DP_AUX_P0_3648			0x3648
+#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK		GENMASK(15, 0)
+#define MTK_DP_AUX_P0_364C			0x364c
+#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK		GENMASK(3, 0)
+#define MTK_DP_AUX_P0_3650			0x3650
+#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK			GENMASK(15, 12)
+#define PHY_FIFO_RST_AUX_TX_P0_MASK			BIT(9)
+#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0			BIT(8)
+#define MTK_DP_AUX_P0_3658			0x3658
+#define AUX_TX_OV_EN_AUX_TX_P0_MASK			BIT(0)
+#define MTK_DP_AUX_P0_3690			0x3690
+#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0		BIT(8)
+#define MTK_DP_AUX_P0_3704			0x3704
+#define AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK	BIT(1)
+#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0		BIT(2)
+#define MTK_DP_AUX_P0_3708			0x3708
+#define MTK_DP_AUX_P0_37C8			0x37c8
+#define MTK_ATOP_EN_AUX_TX_P0				BIT(0)
+
+#endif /*_MTK_DP_REG_H_*/
-- 
2.18.0


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

* [PATCH v17 03/10] drm/mediatek: Add MT8195 Embedded DisplayPort driver
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen,
	linux-mediatek, wenst, linux-arm-kernel,
	angelogioacchino.delregno

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

This patch adds a embedded displayport driver for the MediaTek mt8195 SoC.

It supports the MT8195, the embedded DisplayPort units. It offers
DisplayPort 1.4 with up to 4 lanes.

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

This driver is based on an initial version by
Jitao shi <jitao.shi@mediatek.com>

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/Kconfig      |    9 +
 drivers/gpu/drm/mediatek/Makefile     |    2 +
 drivers/gpu/drm/mediatek/mtk_dp.c     | 1999 +++++++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_dp_reg.h |  305 ++++
 4 files changed, 2315 insertions(+)
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h

diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
index 2976d21e9a34..e66f4a3b6be0 100644
--- a/drivers/gpu/drm/mediatek/Kconfig
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -21,6 +21,15 @@ config DRM_MEDIATEK
 	  This driver provides kernel mode setting and
 	  buffer management to userspace.
 
+config DRM_MEDIATEK_DP
+	tristate "DRM DPTX Support for MediaTek SoCs"
+	depends on DRM_MEDIATEK
+	select PHY_MTK_DP
+	select DRM_DISPLAY_HELPER
+	select DRM_DISPLAY_DP_HELPER
+	help
+	  DRM/KMS Display Port driver for MediaTek SoCs.
+
 config DRM_MEDIATEK_HDMI
 	tristate "DRM HDMI Support for Mediatek SoCs"
 	depends on DRM_MEDIATEK
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index 6e604a933ed0..3517d1c65cd7 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -23,3 +23,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
 			  mtk_hdmi_ddc.o
 
 obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
+
+obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o
diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
new file mode 100644
index 000000000000..e2ec9b02b1aa
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -0,0 +1,1999 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019-2022 MediaTek Inc.
+ * Copyright (c) 2022 BayLibre
+ */
+
+#include <drm/display/drm_dp.h>
+#include <drm/display/drm_dp_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/media-bus-format.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soc/mediatek/mtk_sip_svc.h>
+#include <video/videomode.h>
+
+#include "mtk_dp_reg.h"
+
+#define MTK_DP_SIP_CONTROL_AARCH32	MTK_SIP_SMC_CMD(0x523)
+#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE	(BIT(0) | BIT(5))
+
+#define MTK_DP_THREAD_CABLE_STATE_CHG	BIT(0)
+#define MTK_DP_THREAD_HPD_EVENT		BIT(1)
+
+#define MTK_DP_4P1T 4
+#define MTK_DP_HDE 2
+#define MTK_DP_PIX_PER_ADDR 2
+#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
+#define MTK_DP_TBC_BUF_READ_START_ADDR 0x8
+#define MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY 5
+#define MTK_DP_TRAIN_DOWNSCALE_RETRY 10
+
+enum {
+	MTK_DP_CAL_GLB_BIAS_TRIM = 0,
+	MTK_DP_CAL_CLKTX_IMPSE,
+	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0,
+	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1,
+	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2,
+	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3,
+	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0,
+	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1,
+	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2,
+	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3,
+	MTK_DP_CAL_MAX,
+};
+
+struct mtk_dp_train_info {
+	bool sink_ssc;
+	bool cable_plugged_in;
+	/* link_rate is in multiple of 0.27Gbps */
+	int link_rate;
+	int lane_count;
+	unsigned int channel_eq_pattern;
+};
+
+struct mtk_dp_info {
+	enum dp_pixelformat format;
+	struct videomode vm;
+};
+
+struct mtk_dp_efuse_fmt {
+	unsigned short idx;
+	unsigned short shift;
+	unsigned short mask;
+	unsigned short min_val;
+	unsigned short max_val;
+	unsigned short default_val;
+};
+
+struct mtk_dp {
+	bool enabled;
+	u8 max_lanes;
+	u8 max_linkrate;
+	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
+	u32 cal_data[MTK_DP_CAL_MAX];
+	u32 irq_thread_handle;
+	/* irq_thread_lock is used to protect irq_thread_handle */
+	spinlock_t irq_thread_lock;
+
+	struct device *dev;
+	struct drm_bridge bridge;
+	struct drm_bridge *next_bridge;
+	struct drm_connector *conn;
+	struct drm_device *drm_dev;
+	struct drm_dp_aux aux;
+
+	struct mtk_dp_info info;
+	struct mtk_dp_train_info train_info;
+
+	struct platform_device *phy_dev;
+	struct phy *phy;
+	struct regmap *regs;
+};
+
+static const struct mtk_dp_efuse_fmt mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
+	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
+		.idx = 3,
+		.shift = 27,
+		.mask = 0x1f,
+		.min_val = 1,
+		.max_val = 0x1e,
+		.default_val = 0xf,
+	},
+	[MTK_DP_CAL_CLKTX_IMPSE] = {
+		.idx = 0,
+		.shift = 9,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = {
+		.idx = 2,
+		.shift = 28,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = {
+		.idx = 2,
+		.shift = 20,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = {
+		.idx = 2,
+		.shift = 12,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = {
+		.idx = 2,
+		.shift = 4,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = {
+		.idx = 2,
+		.shift = 24,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = {
+		.idx = 2,
+		.shift = 16,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = {
+		.idx = 2,
+		.shift = 8,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = {
+		.idx = 2,
+		.shift = 0,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+};
+
+static struct regmap_config mtk_dp_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = SEC_OFFSET + 0x90,
+	.name = "mtk-dp-registers",
+};
+
+static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
+{
+	return container_of(b, struct mtk_dp, bridge);
+}
+
+static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
+{
+	u32 read_val;
+	int ret;
+
+	ret = regmap_read(mtk_dp->regs, offset, &read_val);
+	if (ret) {
+		dev_err(mtk_dp->dev, "Failed to read register 0x%x: %d\n",
+			offset, ret);
+		return 0;
+	}
+
+	return read_val;
+}
+
+static int mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
+{
+	int ret = regmap_write(mtk_dp->regs, offset, val);
+
+	if (ret)
+		dev_err(mtk_dp->dev,
+			"Failed to write register 0x%x with value 0x%x\n",
+			offset, val);
+	return ret;
+}
+
+static int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset,
+			      u32 val, u32 mask)
+{
+	int ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
+
+	if (ret)
+		dev_err(mtk_dp->dev,
+			"Failed to update register 0x%x with value 0x%x, mask 0x%x\n",
+			offset, val, mask);
+	return ret;
+}
+
+static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32 offset, u8 *buf,
+				    size_t length)
+{
+	int i;
+
+	/* 2 bytes per register */
+	for (i = 0; i < length; i += 2) {
+		u32 val = buf[i] | (i + 1 < length ? buf[i + 1] << 8 : 0);
+
+		if (mtk_dp_write(mtk_dp, offset + i * 2, val))
+			return;
+	}
+}
+
+static void mtk_dp_msa_bypass_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	u32 mask = HTOTAL_SEL_DP_ENC0_P0 | VTOTAL_SEL_DP_ENC0_P0 |
+		   HSTART_SEL_DP_ENC0_P0 | VSTART_SEL_DP_ENC0_P0 |
+		   HWIDTH_SEL_DP_ENC0_P0 | VHEIGHT_SEL_DP_ENC0_P0 |
+		   HSP_SEL_DP_ENC0_P0 | HSW_SEL_DP_ENC0_P0 |
+		   VSP_SEL_DP_ENC0_P0 | VSW_SEL_DP_ENC0_P0;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, enable ? 0 : mask, mask);
+}
+
+static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
+{
+	struct drm_display_mode mode;
+	struct videomode *vm = &mtk_dp->info.vm;
+
+	drm_display_mode_from_videomode(vm, &mode);
+
+	/* horizontal */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010,
+			   mode.htotal, HTOTAL_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
+			   vm->hsync_len + vm->hback_porch,
+			   HSTART_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
+			   vm->hsync_len, HSW_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
+			   0, HSP_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020,
+			   vm->hactive, HWIDTH_SW_DP_ENC0_P0_MASK);
+
+	/* vertical */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014,
+			   mode.vtotal, VTOTAL_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
+			   vm->vsync_len + vm->vback_porch,
+			   VSTART_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
+			   vm->vsync_len, VSW_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
+			   0, VSP_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024,
+			   vm->vactive, VHEIGHT_SW_DP_ENC0_P0_MASK);
+
+	/* horizontal */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064,
+			   vm->hactive, HDE_NUM_LAST_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154,
+			   mode.htotal, PGEN_HTOTAL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
+			   vm->hfront_porch,
+			   PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C,
+			   vm->hsync_len,
+			   PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
+			   vm->hback_porch + vm->hsync_len,
+			   PGEN_HFDE_START_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164,
+			   vm->hactive,
+			   PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
+
+	/* vertical */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168,
+			   mode.vtotal,
+			   PGEN_VTOTAL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
+			   vm->vfront_porch,
+			   PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170,
+			   vm->vsync_len,
+			   PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
+			   vm->vback_porch + vm->vsync_len,
+			   PGEN_VFDE_START_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178,
+			   vm->vactive,
+			   PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
+}
+
+static int mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
+				   enum dp_pixelformat color_format)
+{
+	u32 val;
+
+	/* update MISC0 */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
+			   color_format << DP_TEST_COLOR_FORMAT_SHIFT,
+			   DP_TEST_COLOR_FORMAT_MASK);
+
+	switch (color_format) {
+	case DP_PIXELFORMAT_YUV422:
+		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
+		break;
+	case DP_PIXELFORMAT_RGB:
+		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
+		break;
+	default:
+		drm_warn(mtk_dp->drm_dev, "Unsupported color format: %d\n",
+			 color_format);
+		return -EINVAL;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   val, PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
+	return 0;
+}
+
+static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp)
+{
+	/* Only support 8 bits currently */
+	/* Update MISC0 */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
+			   DP_MSA_MISC_8_BPC, DP_TEST_BIT_DEPTH_MASK);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT,
+			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_config_mn_mode(struct mtk_dp *mtk_dp)
+{
+	/* 0: hw mode, 1: sw mode */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
+			   0, VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32 val)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   val, SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   VIDEO_MN_GEN_EN_DP_ENC0_P0,
+			   VIDEO_MN_GEN_EN_DP_ENC0_P0);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
+			   SDP_DOWN_CNT_DP_ENC0_P0_VAL,
+			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
+			   SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL,
+			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
+			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL << 8,
+			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
+			   FIFO_READ_START_POINT_DP_ENC1_P0_VAL << 12,
+			   FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
+	mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368, DP_ENC1_P0_3368_VAL);
+}
+
+static void mtk_dp_pg_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038,
+			   enable ? VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK : 0,
+			   VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
+			   PGEN_PATTERN_SEL_VAL << 4, PGEN_PATTERN_SEL_MASK);
+}
+
+static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640, DP_AUX_P0_3640_VAL);
+}
+
+static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32 addr)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644,
+			   cmd, MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648,
+			   addr, MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C,
+			   addr >> 16, MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_aux_clear_fifo(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
+			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0,
+			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0 |
+			   PHY_FIFO_RST_AUX_TX_P0_MASK |
+			   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
+			   AUX_TX_REQUEST_READY_AUX_TX_P0,
+			   AUX_TX_REQUEST_READY_AUX_TX_P0);
+}
+
+static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8 *buf,
+				       size_t length)
+{
+	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf, length);
+}
+
+static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
+				    size_t length, int read_delay)
+{
+	int read_pos;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
+			   0, AUX_RD_MODE_AUX_TX_P0_MASK);
+
+	for (read_pos = 0; read_pos < length; read_pos++) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
+				   AUX_RX_FIFO_READ_PULSE_TX_P0,
+				   AUX_RX_FIFO_READ_PULSE_TX_P0);
+
+		/* Hardware needs time to update the data */
+		usleep_range(read_delay, read_delay * 2);
+		buf[read_pos] = (u8)(mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3620) &
+				     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK);
+	}
+}
+
+static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t length)
+{
+	if (length > 0) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
+				   (length - 1) << 12,
+				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
+				   0,
+				   AUX_NO_LENGTH_AUX_TX_P0 |
+				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	} else {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
+				   AUX_NO_LENGTH_AUX_TX_P0,
+				   AUX_NO_LENGTH_AUX_TX_P0 |
+				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	}
+}
+
+static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp, bool is_read)
+{
+	int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
+
+	while (--wait_reply) {
+		u32 aux_irq_status;
+
+		if (is_read) {
+			u32 fifo_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3618);
+
+			if (fifo_status &
+			    (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
+			     AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
+				return 0;
+			}
+		}
+
+		aux_irq_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3640);
+		if (aux_irq_status & AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
+			return 0;
+
+		if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0)
+			return -ETIMEDOUT;
+
+		/* Give the hardware a chance to reach completion before retrying */
+		usleep_range(100, 500);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool is_read, u8 cmd,
+				  u32 addr, u8 *buf, size_t length)
+{
+	int ret;
+	u32 reply_cmd;
+
+	if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
+			(cmd == DP_AUX_NATIVE_READ && !length)))
+		return -EINVAL;
+
+	if (!is_read)
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
+				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0,
+				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0);
+
+	/* We need to clear fifo and irq before sending commands to the sink device. */
+	mtk_dp_aux_clear_fifo(mtk_dp);
+	mtk_dp_aux_irq_clear(mtk_dp);
+
+	mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
+	mtk_dp_aux_set_length(mtk_dp, length);
+
+	if (!is_read) {
+		if (length)
+			mtk_dp_aux_fill_write_fifo(mtk_dp, buf, length);
+
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
+				   AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK,
+				   AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK);
+	}
+
+	mtk_dp_aux_request_ready(mtk_dp);
+
+	/* Wait for feedback from sink device. */
+	ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
+
+	reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
+		    AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
+
+	if (ret || reply_cmd) {
+		u32 phy_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3628) &
+				 AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
+		if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
+			drm_err(mtk_dp->drm_dev,
+				"AUX Rx Aux hang, need SW reset\n");
+			return -EIO;
+		}
+
+		return -ETIMEDOUT;
+	}
+
+	if (!length) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
+				   0,
+				   AUX_NO_LENGTH_AUX_TX_P0 |
+				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	} else if (is_read) {
+		int read_delay;
+
+		if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
+		    cmd == DP_AUX_I2C_READ)
+			read_delay = 500;
+		else
+			read_delay = 100;
+
+		mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length, read_delay);
+	}
+
+	return 0;
+}
+
+static void mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int lane_num,
+					  int swing_val, int preemphasis)
+{
+	u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
+
+	dev_dbg(mtk_dp->dev,
+		"link training: swing_val = 0x%x, pre-emphasis = 0x%x\n",
+		swing_val, preemphasis);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+			   swing_val << (DP_TX0_VOLT_SWING_SHIFT + lane_shift),
+			   DP_TX0_VOLT_SWING_MASK << lane_shift);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+			   preemphasis << (DP_TX0_PRE_EMPH_SHIFT + lane_shift),
+			   DP_TX0_PRE_EMPH_MASK << lane_shift);
+}
+
+static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+			   0,
+			   DP_TX0_VOLT_SWING_MASK |
+			   DP_TX1_VOLT_SWING_MASK |
+			   DP_TX2_VOLT_SWING_MASK |
+			   DP_TX3_VOLT_SWING_MASK |
+			   DP_TX0_PRE_EMPH_MASK |
+			   DP_TX1_PRE_EMPH_MASK |
+			   DP_TX2_PRE_EMPH_MASK |
+			   DP_TX3_PRE_EMPH_MASK);
+}
+
+static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
+{
+	u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
+			 SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
+
+	if (irq_status) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
+				   irq_status, SW_IRQ_CLR_DP_TRANS_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
+				   0, SW_IRQ_CLR_DP_TRANS_P0_MASK);
+	}
+
+	return irq_status;
+}
+
+static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
+{
+	u32 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
+			  IRQ_STATUS_DP_TRANS_P0_MASK) >> 12;
+
+	if (irq_status) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
+				   irq_status, IRQ_CLR_DP_TRANS_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
+				   0, IRQ_CLR_DP_TRANS_P0_MASK);
+	}
+
+	return irq_status;
+}
+
+static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
+			   enable ? 0 :
+			   IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
+			   IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
+			   IRQ_MASK_DP_TRANS_P0_INT_IRQ,
+			   IRQ_MASK_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
+			   XTAL_FREQ_DP_TRANS_P0_DEFAULT,
+			   XTAL_FREQ_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
+			   FEC_CLOCK_EN_MODE_DP_TRANS_P0,
+			   FEC_CLOCK_EN_MODE_DP_TRANS_P0);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
+			   AUDIO_CH_SRC_SEL_DP_ENC0_P0,
+			   AUDIO_CH_SRC_SEL_DP_ENC0_P0);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
+			   0, SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK,
+			   IRQ_MASK_AUX_TOP_IRQ, IRQ_MASK_AUX_TOP_IRQ);
+}
+
+static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp *mtk_dp)
+{
+	u32 val;
+	/* Debounce threshold */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   8, HPD_DEB_THD_DP_TRANS_P0_MASK);
+
+	val = (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
+	       HPD_INT_THD_DP_TRANS_P0_UPPER_1100US) << 4;
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   val, HPD_INT_THD_DP_TRANS_P0_MASK);
+
+	/*
+	 * Connect threshold 1.5ms + 5 x 0.1ms = 2ms
+	 * Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
+	 */
+	val = (5 << 8) | (5 << 12);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   val,
+			   HPD_DISC_THD_DP_TRANS_P0_MASK |
+			   HPD_CONN_THD_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
+			   HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
+			   HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
+{
+	/* modify timeout threshold = 0x1595 */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C,
+			   AUX_TIMEOUT_THR_AUX_TX_P0_VAL,
+			   AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658,
+			   0, AUX_TX_OV_EN_AUX_TX_P0_MASK);
+	/* 25 for 26M */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
+			   AUX_TX_OVER_SAMPLE_RATE_FOR_26M << 8,
+			   AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
+	/* 13 for 26M */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
+			   AUX_RX_UI_CNT_THR_AUX_FOR_26M,
+			   AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
+			   MTK_ATOP_EN_AUX_TX_P0,
+			   MTK_ATOP_EN_AUX_TX_P0);
+}
+
+static void mtk_dp_initialize_digital_settings(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
+			   0, VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
+			   BS2BS_MODE_DP_ENC1_P0_VAL << 12,
+			   BS2BS_MODE_DP_ENC1_P0_MASK);
+
+	/* dp tx encoder reset all sw */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
+			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0,
+			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
+
+	/* Wait for sw reset to complete */
+	usleep_range(1000, 5000);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
+			   0, DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
+}
+
+static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
+			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0,
+			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
+
+	/* Wait for sw reset to complete */
+	usleep_range(1000, 5000);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
+			   0, DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
+}
+
+static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
+			   lanes == 0 ? 0 : DP_TRANS_DUMMY_RW_0,
+			   DP_TRANS_DUMMY_RW_0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
+			   lanes, LANE_NUM_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
+			   lanes << 2, LANE_NUM_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
+{
+	const struct mtk_dp_efuse_fmt *fmt;
+	struct device *dev = mtk_dp->dev;
+	struct nvmem_cell *cell;
+	u32 *cal_data = mtk_dp->cal_data;
+	u32 *buf;
+	int i;
+	size_t len;
+
+	cell = nvmem_cell_get(dev, "dp_calibration_data");
+	if (IS_ERR(cell)) {
+		dev_warn(dev, "Failed to get nvmem cell dp_calibration_data\n");
+		goto use_default_val;
+	}
+
+	buf = (u32 *)nvmem_cell_read(cell, &len);
+	nvmem_cell_put(cell);
+
+	if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
+		dev_warn(dev, "Failed to read nvmem_cell_read\n");
+
+		if (!IS_ERR(buf))
+			kfree(buf);
+
+		goto use_default_val;
+	}
+
+	for (i = 0; i < MTK_DP_CAL_MAX; i++) {
+		fmt = &mtk_dp_efuse_data[i];
+		cal_data[i] = (buf[fmt->idx] >> fmt->shift) & fmt->mask;
+
+		if (cal_data[i] < fmt->min_val || cal_data[i] > fmt->max_val) {
+			dev_warn(mtk_dp->dev, "Invalid efuse data, idx = %d\n", i);
+			kfree(buf);
+			goto use_default_val;
+		}
+	}
+	kfree(buf);
+
+	return;
+
+use_default_val:
+	dev_warn(mtk_dp->dev, "Use default calibration data\n");
+	for (i = 0; i < MTK_DP_CAL_MAX; i++)
+		cal_data[i] = mtk_dp_efuse_data[i].default_val;
+}
+
+static void mtk_dp_set_calibration_data(struct mtk_dp *mtk_dp)
+{
+	u32 *cal_data = mtk_dp->cal_data;
+
+	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
+			   cal_data[MTK_DP_CAL_CLKTX_IMPSE] << 20,
+			   RG_CKM_PT0_CKTX_IMPSEL);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
+			   cal_data[MTK_DP_CAL_GLB_BIAS_TRIM] << 16,
+			   RG_XTP_GLB_BIAS_INTR_CTRL);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] << 12,
+			   RG_XTP_LN0_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] << 16,
+			   RG_XTP_LN0_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] << 12,
+			   RG_XTP_LN1_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] << 16,
+			   RG_XTP_LN1_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] << 12,
+			   RG_XTP_LN2_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] << 16,
+			   RG_XTP_LN2_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] << 12,
+			   RG_XTP_LN3_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] << 16,
+			   RG_XTP_LN3_TX_IMPSEL_NMOS);
+}
+
+static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
+				u32 link_rate, int lane_count)
+{
+	int ret;
+	union phy_configure_opts phy_opts = {
+		.dp = {
+			.link_rate = drm_dp_bw_code_to_link_rate(link_rate) / 100,
+			.set_rate = 1,
+			.lanes = lane_count,
+			.set_lanes = 1,
+			.ssc = mtk_dp->train_info.sink_ssc,
+		}
+	};
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP,
+			   DP_PWR_STATE_MASK);
+
+	ret = phy_configure(mtk_dp->phy, &phy_opts);
+	if (ret)
+		return ret;
+
+	mtk_dp_set_calibration_data(mtk_dp);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL_LANE, DP_PWR_STATE_MASK);
+
+	return 0;
+}
+
+static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool enable)
+{
+	u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
+		  POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
+		  POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
+		  POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580,
+			   enable ? val : 0, val);
+}
+
+static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int pattern)
+{
+	/* TPS1 */
+	if (pattern == 1)
+		mtk_dp_set_idle_pattern(mtk_dp, false);
+
+	mtk_dp_update_bits(mtk_dp,
+			   MTK_DP_TRANS_P0_3400,
+			   pattern ? BIT(pattern - 1) << 12 : 0,
+			   PATTERN1_EN_DP_TRANS_P0_MASK |
+			   PATTERN2_EN_DP_TRANS_P0_MASK |
+			   PATTERN3_EN_DP_TRANS_P0_MASK |
+			   PATTERN4_EN_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
+			   ENHANCED_FRAME_EN_DP_ENC0_P0,
+			   ENHANCED_FRAME_EN_DP_ENC0_P0);
+}
+
+static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
+			   enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
+			   DP_SCR_EN_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
+{
+	struct arm_smccc_res res;
+	u32 val = VIDEO_MUTE_SEL_DP_ENC0_P0 |
+		  (enable ? VIDEO_MUTE_SW_DP_ENC0_P0 : 0);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
+			   val,
+			   VIDEO_MUTE_SEL_DP_ENC0_P0 |
+			   VIDEO_MUTE_SW_DP_ENC0_P0);
+
+	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32,
+		      MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable,
+		      0, 0, 0, 0, 0, &res);
+
+	dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
+		MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable, res.a0, res.a1);
+}
+
+static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
+			   0, SW_RST_B_PHYD);
+
+	/* Wait for power enable */
+	usleep_range(10, 200);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
+			   SW_RST_B_PHYD, SW_RST_B_PHYD);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL, DP_PWR_STATE_MASK);
+	mtk_dp_write(mtk_dp, MTK_DP_1040,
+		     RG_DPAUX_RX_VALID_DEGLITCH_EN | RG_XTP_GLB_CKDET_EN |
+		     RG_DPAUX_RX_EN);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_0034, 0, DA_CKM_CKTX0_EN_FORCE_EN);
+}
+
+static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_0034,
+			   DA_CKM_CKTX0_EN_FORCE_EN, DA_CKM_CKTX0_EN_FORCE_EN);
+
+	/* Disable RX */
+	mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
+	mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
+		     0x550 | FUSE_SEL | MEM_ISO_EN);
+}
+
+static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
+{
+	mtk_dp->train_info.link_rate = DP_LINK_BW_5_4;
+	mtk_dp->train_info.lane_count = mtk_dp->max_lanes;
+	mtk_dp->train_info.cable_plugged_in = false;
+
+	mtk_dp->info.format = DP_PIXELFORMAT_RGB;
+	memset(&mtk_dp->info.vm, 0, sizeof(struct videomode));
+}
+
+static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
+{
+	u32 sram_read_start = min_t(u32, MTK_DP_TBC_BUF_READ_START_ADDR,
+				    mtk_dp->info.vm.hactive /
+				    mtk_dp->train_info.lane_count /
+				    MTK_DP_4P1T / MTK_DP_HDE /
+				    MTK_DP_PIX_PER_ADDR);
+	mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
+	mtk_dp_setup_encoder(mtk_dp);
+}
+
+static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_setup_tu(mtk_dp);
+}
+
+static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes,
+					  u8 dpcd_adjust_req[2])
+{
+	int lane;
+
+	for (lane = 0; lane < lanes; ++lane) {
+		u8 val;
+		u8 swing;
+		u8 preemphasis;
+		int index = lane / 2;
+		int shift = lane % 2 ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
+
+		swing = (dpcd_adjust_req[index] >> shift) &
+			DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
+		preemphasis = ((dpcd_adjust_req[index] >> shift) &
+			       DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
+			      DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
+		val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
+		      preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+		if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
+			val |= DP_TRAIN_MAX_SWING_REACHED;
+		if (preemphasis == 3)
+			val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+		mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing, preemphasis);
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET + lane,
+				   val);
+	}
+}
+
+static void mtk_dp_pattern(struct mtk_dp *mtk_dp, bool is_tps1)
+{
+	int pattern;
+	unsigned int aux_offset;
+
+	if (is_tps1) {
+		pattern = 1;
+		aux_offset = DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1;
+	} else {
+		aux_offset = mtk_dp->train_info.channel_eq_pattern;
+
+		switch (mtk_dp->train_info.channel_eq_pattern) {
+		case DP_TRAINING_PATTERN_4:
+			pattern = 4;
+			break;
+		case DP_TRAINING_PATTERN_3:
+			pattern = 3;
+			aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
+			break;
+		case DP_TRAINING_PATTERN_2:
+		default:
+			pattern = 2;
+			aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
+			break;
+		}
+	}
+
+	mtk_dp_train_set_pattern(mtk_dp, pattern);
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET, aux_offset);
+}
+
+static int mtk_dp_train_setting(struct mtk_dp *mtk_dp, u8 target_link_rate,
+				u8 target_lane_count)
+{
+	int ret;
+
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET, target_link_rate);
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
+			   target_lane_count | DP_LANE_COUNT_ENHANCED_FRAME_EN);
+
+	if (mtk_dp->train_info.sink_ssc)
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
+				   DP_SPREAD_AMP_0_5);
+
+	mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
+	ret = mtk_dp_phy_configure(mtk_dp, target_link_rate, target_lane_count);
+	if (ret)
+		return ret;
+
+	dev_dbg(mtk_dp->dev,
+		"Link train target_link_rate = 0x%x, target_lane_count = 0x%x\n",
+		target_link_rate, target_lane_count);
+
+	return 0;
+}
+
+static int mtk_dp_train_cr(struct mtk_dp *mtk_dp, u8 target_lane_count)
+{
+	u8 lane_adjust[2] = {};
+	u8 link_status[DP_LINK_STATUS_SIZE] = {};
+	u8 prev_lane_adjust = 0xff;
+	int train_retries = 0;
+	int voltage_retries = 0;
+
+	mtk_dp_pattern(mtk_dp, true);
+
+	/* In DP spec 1.4, the retry count of CR is defined as 10. */
+	do {
+		train_retries++;
+		if (!mtk_dp->train_info.cable_plugged_in) {
+			mtk_dp_train_set_pattern(mtk_dp, 0);
+			return -ENODEV;
+		}
+
+		drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
+				 lane_adjust, sizeof(lane_adjust));
+		mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
+					      lane_adjust);
+
+		drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
+						       mtk_dp->rx_cap);
+
+		/* check link status from sink device */
+		drm_dp_dpcd_read_link_status(&mtk_dp->aux, link_status);
+		if (drm_dp_clock_recovery_ok(link_status,
+					     target_lane_count)) {
+			dev_dbg(mtk_dp->dev, "Link train CR pass\n");
+			return 0;
+		}
+
+		/*
+		 * In DP spec 1.4, if current voltage level is the same
+		 * with previous voltage level, we need to retry 5 times.
+		 */
+		if (prev_lane_adjust == link_status[4]) {
+			voltage_retries++;
+			/*
+			 * Condition of CR fail:
+			 * 1. Failed to pass CR using the same voltage
+			 *    level over five times.
+			 * 2. Failed to pass CR when the current voltage
+			 *    level is the same with previous voltage
+			 *    level and reach max voltage level (3).
+			 */
+			if (voltage_retries > MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY ||
+			    (prev_lane_adjust & DP_ADJUST_VOLTAGE_SWING_LANE0_MASK) == 3) {
+				dev_dbg(mtk_dp->dev, "Link train CR fail\n");
+				break;
+			}
+		} else {
+			/*
+			 * If the voltage level is changed, we need to
+			 * re-calculate this retry count.
+			 */
+			voltage_retries = 0;
+		}
+		prev_lane_adjust = link_status[4];
+	} while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
+
+	/* Failed to train CR, and disable pattern. */
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+			   DP_TRAINING_PATTERN_DISABLE);
+	mtk_dp_train_set_pattern(mtk_dp, 0);
+
+	return -ETIMEDOUT;
+}
+
+static int mtk_dp_train_eq(struct mtk_dp *mtk_dp, u8 target_lane_count)
+{
+	u8 lane_adjust[2] = {};
+	u8 link_status[DP_LINK_STATUS_SIZE] = {};
+	int train_retries = 0;
+
+	mtk_dp_pattern(mtk_dp, false);
+
+	do {
+		train_retries++;
+		if (!mtk_dp->train_info.cable_plugged_in) {
+			mtk_dp_train_set_pattern(mtk_dp, 0);
+			return -ENODEV;
+		}
+
+		drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
+				 lane_adjust, sizeof(lane_adjust));
+		mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
+					      lane_adjust);
+
+		drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
+						   mtk_dp->rx_cap);
+
+		/* check link status from sink device */
+		drm_dp_dpcd_read_link_status(&mtk_dp->aux, link_status);
+		if (drm_dp_channel_eq_ok(link_status, target_lane_count)) {
+			dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
+
+			/* Training done, and disable pattern. */
+			drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+					   DP_TRAINING_PATTERN_DISABLE);
+			mtk_dp_train_set_pattern(mtk_dp, 0);
+			return 0;
+		}
+		dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
+	} while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
+
+	/* Failed to train EQ, and disable pattern. */
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+			   DP_TRAINING_PATTERN_DISABLE);
+	mtk_dp_train_set_pattern(mtk_dp, 0);
+
+	return -ETIMEDOUT;
+}
+
+static int mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
+{
+	u8 val;
+	ssize_t ret;
+
+	drm_dp_read_dpcd_caps(&mtk_dp->aux, mtk_dp->rx_cap);
+
+	if (drm_dp_tps4_supported(mtk_dp->rx_cap))
+		mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_4;
+	else if (drm_dp_tps3_supported(mtk_dp->rx_cap))
+		mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_3;
+	else
+		mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_2;
+
+	mtk_dp->train_info.sink_ssc = drm_dp_max_downspread(mtk_dp->rx_cap);
+
+	ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
+	if (ret < 1) {
+		drm_err(mtk_dp->drm_dev, "Read mstm cap failed\n");
+		return ret == 0 ? -EIO : ret;
+	}
+
+	if (val & DP_MST_CAP) {
+		/* Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 */
+		ret = drm_dp_dpcd_readb(&mtk_dp->aux,
+					DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
+					&val);
+		if (ret < 1) {
+			drm_err(mtk_dp->drm_dev, "Read irq vector failed\n");
+			return ret == 0 ? -EIO : ret;
+		}
+
+		if (val)
+			drm_dp_dpcd_writeb(&mtk_dp->aux,
+					   DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
+					   val);
+	}
+
+	return 0;
+}
+
+static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
+{
+	phy_reset(mtk_dp->phy);
+	mtk_dp_reset_swing_pre_emphasis(mtk_dp);
+}
+
+static int mtk_dp_training(struct mtk_dp *mtk_dp)
+{
+	int ret;
+	u8 lane_count, link_rate, train_limit, max_link_rate;
+
+	link_rate = min_t(u8, mtk_dp->max_linkrate,
+			  mtk_dp->rx_cap[DP_MAX_LINK_RATE]);
+	max_link_rate = link_rate;
+	lane_count = min_t(u8, mtk_dp->max_lanes,
+			   drm_dp_max_lane_count(mtk_dp->rx_cap));
+
+	/*
+	 * TPS are generated by the hardware pattern generator. From the
+	 * hardware setting we need to disable this scramble setting before
+	 * use the TPS pattern generator.
+	 */
+	mtk_dp_training_set_scramble(mtk_dp, false);
+
+	for (train_limit = 6; train_limit > 0; train_limit--) {
+		mtk_dp_train_change_mode(mtk_dp);
+
+		ret = mtk_dp_train_setting(mtk_dp, link_rate, lane_count);
+		if (ret)
+			return ret;
+
+		ret = mtk_dp_train_cr(mtk_dp, lane_count);
+		if (ret == -ENODEV) {
+			return ret;
+		} else if (ret) {
+			/* reduce link rate */
+			switch (link_rate) {
+			case DP_LINK_BW_1_62:
+				lane_count = lane_count / 2;
+				link_rate = max_link_rate;
+				if (lane_count == 0)
+					return -EIO;
+				break;
+			case DP_LINK_BW_2_7:
+				link_rate = DP_LINK_BW_1_62;
+				break;
+			case DP_LINK_BW_5_4:
+				link_rate = DP_LINK_BW_2_7;
+				break;
+			case DP_LINK_BW_8_1:
+				link_rate = DP_LINK_BW_5_4;
+				break;
+			default:
+				return -EINVAL;
+			};
+			continue;
+		}
+
+		ret = mtk_dp_train_eq(mtk_dp, lane_count);
+		if (ret == -ENODEV) {
+			return ret;
+		} else if (ret) {
+			/* reduce lane count */
+			if (lane_count == 0)
+				return -EIO;
+			lane_count /= 2;
+			continue;
+		}
+
+		/* if we can run to this, training is done. */
+		break;
+	}
+
+	if (train_limit == 0)
+		return -ETIMEDOUT;
+
+	mtk_dp->train_info.link_rate = link_rate;
+	mtk_dp->train_info.lane_count = lane_count;
+
+	/*
+	 * After training done, we need to output normal stream instead of TPS,
+	 * so we need to enable scramble.
+	 */
+	mtk_dp_training_set_scramble(mtk_dp, true);
+	mtk_dp_set_enhanced_frame_mode(mtk_dp);
+
+	return 0;
+}
+
+static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	/* the mute sequence is different between enable and disable */
+	if (enable) {
+		mtk_dp_msa_bypass_enable(mtk_dp, false);
+		mtk_dp_pg_enable(mtk_dp, false);
+		mtk_dp_set_tx_out(mtk_dp);
+		mtk_dp_video_mute(mtk_dp, false);
+	} else {
+		mtk_dp_video_mute(mtk_dp, true);
+		mtk_dp_pg_enable(mtk_dp, true);
+		mtk_dp_msa_bypass_enable(mtk_dp, true);
+	}
+}
+
+static int mtk_dp_video_config(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_config_mn_mode(mtk_dp);
+	mtk_dp_set_msa(mtk_dp);
+	mtk_dp_set_color_depth(mtk_dp);
+	return mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
+}
+
+static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_set_idle_pattern(mtk_dp, true);
+	mtk_dp_initialize_priv_data(mtk_dp);
+
+	mtk_dp_initialize_settings(mtk_dp);
+	mtk_dp_initialize_aux_settings(mtk_dp);
+	mtk_dp_initialize_digital_settings(mtk_dp);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
+			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0,
+			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0);
+	mtk_dp_initialize_hpd_detect_settings(mtk_dp);
+
+	mtk_dp_digital_sw_reset(mtk_dp);
+}
+
+static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
+{
+	struct mtk_dp *mtk_dp = dev;
+	unsigned long flags;
+	u32 status;
+
+	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
+	status = mtk_dp->irq_thread_handle;
+	mtk_dp->irq_thread_handle = 0;
+	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
+
+	if (status & MTK_DP_THREAD_CABLE_STATE_CHG)
+		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
+
+	if (status & MTK_DP_THREAD_HPD_EVENT)
+		dev_dbg(mtk_dp->dev, "Receive IRQ from sink devices\n");
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
+{
+	struct mtk_dp *mtk_dp = dev;
+	bool cable_sta_chg = false;
+	unsigned long flags;
+	u32 irq_status = mtk_dp_swirq_get_clear(mtk_dp) |
+			 mtk_dp_hwirq_get_clear(mtk_dp);
+
+	if (!irq_status)
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
+
+	if (irq_status & MTK_DP_HPD_INTERRUPT)
+		mtk_dp->irq_thread_handle |= MTK_DP_THREAD_HPD_EVENT;
+
+	/* Cable state is changed. */
+	if (irq_status != MTK_DP_HPD_INTERRUPT) {
+		mtk_dp->irq_thread_handle |= MTK_DP_THREAD_CABLE_STATE_CHG;
+		cable_sta_chg = true;
+	}
+
+	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
+
+	if (cable_sta_chg) {
+		if (!!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
+		       HPD_DB_DP_TRANS_P0_MASK))
+			mtk_dp->train_info.cable_plugged_in = true;
+		else
+			mtk_dp->train_info.cable_plugged_in = false;
+	}
+
+	return IRQ_WAKE_THREAD;
+}
+
+static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
+			   struct platform_device *pdev)
+{
+	struct device_node *endpoint;
+	struct device *dev = &pdev->dev;
+	int ret;
+	void __iomem *base;
+	u32 linkrate;
+	int len;
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	mtk_dp->regs = devm_regmap_init_mmio(dev, base, &mtk_dp_regmap_config);
+	if (IS_ERR(mtk_dp->regs))
+		return PTR_ERR(mtk_dp->regs);
+
+	endpoint = of_graph_get_endpoint_by_regs(pdev->dev.of_node, 1, -1);
+	len = of_property_count_elems_of_size(endpoint,
+					      "data-lanes", sizeof(u32));
+	if (len < 0 || len > 4 || len == 3) {
+		dev_err(dev, "invalid data lane size: %d\n", len);
+		return -EINVAL;
+	}
+
+	mtk_dp->max_lanes = len;
+
+	ret = device_property_read_u32(dev, "max-linkrate-mhz", &linkrate);
+	if (ret) {
+		dev_err(dev, "failed to read max linkrate: %d\n", ret);
+		return ret;
+	}
+
+	mtk_dp->max_linkrate = drm_dp_link_rate_to_bw_code(linkrate * 100);
+
+	return 0;
+}
+
+static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
+				    struct drm_connector *connector)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	bool enabled = mtk_dp->enabled;
+	struct edid *new_edid = NULL;
+
+	if (!enabled) {
+		drm_bridge_chain_pre_enable(bridge);
+
+		/* power on aux */
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+				   DP_PWR_STATE_BANDGAP_TPLL_LANE,
+				   DP_PWR_STATE_MASK);
+
+		/* power on panel */
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+		usleep_range(2000, 5000);
+	}
+
+	new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
+
+	/*
+	 * Parse capability here to let atomic_get_input_bus_fmts and
+	 * mode_valid use the capability to calculate sink bitrates.
+	 */
+	if (mtk_dp_parse_capabilities(mtk_dp)) {
+		drm_err(mtk_dp->drm_dev, "Can't parse capabilities\n");
+		new_edid = NULL;
+	}
+
+	if (!enabled) {
+		/* power off panel */
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
+		usleep_range(2000, 3000);
+
+		/* power off aux */
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+				   DP_PWR_STATE_BANDGAP_TPLL,
+				   DP_PWR_STATE_MASK);
+
+		drm_bridge_chain_post_disable(bridge);
+	}
+
+	return new_edid;
+}
+
+static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
+				   struct drm_dp_aux_msg *msg)
+{
+	struct mtk_dp *mtk_dp;
+	bool is_read;
+	u8 request;
+	size_t accessed_bytes = 0;
+	int ret;
+
+	mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
+
+	if (!mtk_dp->train_info.cable_plugged_in) {
+		ret = -EAGAIN;
+		goto err;
+	}
+
+	switch (msg->request) {
+	case DP_AUX_I2C_MOT:
+	case DP_AUX_I2C_WRITE:
+	case DP_AUX_NATIVE_WRITE:
+	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
+	case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
+		request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
+		is_read = false;
+		break;
+	case DP_AUX_I2C_READ:
+	case DP_AUX_NATIVE_READ:
+	case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
+		request = msg->request;
+		is_read = true;
+		break;
+	default:
+		drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
+			msg->request);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	do {
+		size_t to_access = min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
+					 msg->size - accessed_bytes);
+
+		ret = mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
+					     msg->address + accessed_bytes,
+					     msg->buffer + accessed_bytes,
+					     to_access);
+
+		if (ret) {
+			drm_info(mtk_dp->drm_dev,
+				 "Failed to do AUX transfer: %d\n", ret);
+			goto err;
+		}
+		accessed_bytes += to_access;
+	} while (accessed_bytes < msg->size);
+
+	msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
+	return msg->size;
+err:
+	msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
+	return ret;
+}
+
+static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
+{
+	int ret;
+
+	ret = phy_init(mtk_dp->phy);
+	if (ret) {
+		dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
+		return ret;
+	}
+
+	mtk_dp_init_port(mtk_dp);
+	mtk_dp_power_enable(mtk_dp);
+
+	return 0;
+}
+
+static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_power_disable(mtk_dp);
+	phy_exit(mtk_dp->phy);
+}
+
+static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
+				enum drm_bridge_attach_flags flags)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	int ret;
+
+	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+		dev_err(mtk_dp->dev, "Driver does not provide a connector!");
+		return -EINVAL;
+	}
+
+	mtk_dp->aux.drm_dev = bridge->dev;
+	ret = drm_dp_aux_register(&mtk_dp->aux);
+	if (ret) {
+		dev_err(mtk_dp->dev,
+			"failed to register DP AUX channel: %d\n", ret);
+		return ret;
+	}
+
+	ret = mtk_dp_poweron(mtk_dp);
+	if (ret)
+		goto err_aux_register;
+
+	if (mtk_dp->next_bridge) {
+		ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
+					&mtk_dp->bridge, flags);
+		if (ret) {
+			drm_warn(mtk_dp->drm_dev,
+				 "Failed to attach external bridge: %d\n", ret);
+			goto err_bridge_attach;
+		}
+	}
+
+	mtk_dp->drm_dev = bridge->dev;
+
+	mtk_dp_hwirq_enable(mtk_dp, true);
+
+	return 0;
+
+err_bridge_attach:
+	mtk_dp_poweroff(mtk_dp);
+err_aux_register:
+	drm_dp_aux_unregister(&mtk_dp->aux);
+	return ret;
+}
+
+static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+
+	mtk_dp_hwirq_enable(mtk_dp, false);
+	mtk_dp->drm_dev = NULL;
+	mtk_dp_poweroff(mtk_dp);
+	drm_dp_aux_unregister(&mtk_dp->aux);
+}
+
+static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
+					struct drm_bridge_state *old_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	int ret;
+
+	mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
+								bridge->encoder);
+	if (!mtk_dp->conn) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as connector is missing\n");
+		return;
+	}
+
+	/* power on aux */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL_LANE,
+			   DP_PWR_STATE_MASK);
+
+	if (mtk_dp->train_info.cable_plugged_in) {
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+		usleep_range(2000, 5000);
+	}
+
+	/* Training */
+	ret = mtk_dp_training(mtk_dp);
+	if (ret) {
+		drm_err(mtk_dp->drm_dev, "Training failed, %d\n", ret);
+		goto power_off_aux;
+	}
+
+	ret = mtk_dp_video_config(mtk_dp);
+	if (ret)
+		goto power_off_aux;
+
+	mtk_dp_video_enable(mtk_dp, true);
+
+	mtk_dp->enabled = true;
+
+	return;
+power_off_aux:
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL,
+			   DP_PWR_STATE_MASK);
+}
+
+static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
+					 struct drm_bridge_state *old_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+
+	mtk_dp->enabled = false;
+	mtk_dp_video_enable(mtk_dp, false);
+
+	if (mtk_dp->train_info.cable_plugged_in) {
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
+		usleep_range(2000, 3000);
+	}
+
+	/* power off aux */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL,
+			   DP_PWR_STATE_MASK);
+
+	/* Ensure the sink is muted */
+	msleep(20);
+}
+
+static enum drm_mode_status
+mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
+			 const struct drm_display_info *info,
+			 const struct drm_display_mode *mode)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	u32 bpp = info->color_formats & DRM_COLOR_FORMAT_YCBCR422 ? 16 : 24;
+	u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
+			      drm_dp_max_lane_count(mtk_dp->rx_cap),
+			 drm_dp_bw_code_to_link_rate(mtk_dp->max_linkrate) *
+			 mtk_dp->max_lanes);
+
+	if (rate < mode->clock * bpp / 8)
+		return MODE_CLOCK_HIGH;
+
+	return MODE_OK;
+}
+
+static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
+						     struct drm_bridge_state *bridge_state,
+						     struct drm_crtc_state *crtc_state,
+						     struct drm_connector_state *conn_state,
+						     unsigned int *num_output_fmts)
+{
+	u32 *output_fmts;
+
+	*num_output_fmts = 0;
+	output_fmts = kmalloc(sizeof(*output_fmts), GFP_KERNEL);
+	if (!output_fmts)
+		return NULL;
+	*num_output_fmts = 1;
+	output_fmts[0] = MEDIA_BUS_FMT_FIXED;
+	return output_fmts;
+}
+
+static const u32 mt8195_input_fmts[] = {
+	MEDIA_BUS_FMT_RGB888_1X24,
+	MEDIA_BUS_FMT_YUV8_1X24,
+	MEDIA_BUS_FMT_YUYV8_1X16,
+};
+
+static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+						    struct drm_bridge_state *bridge_state,
+						    struct drm_crtc_state *crtc_state,
+						    struct drm_connector_state *conn_state,
+						    u32 output_fmt,
+						    unsigned int *num_input_fmts)
+{
+	u32 *input_fmts;
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+	struct drm_display_info *display_info =
+		&conn_state->connector->display_info;
+	u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
+			      drm_dp_max_lane_count(mtk_dp->rx_cap),
+			 drm_dp_bw_code_to_link_rate(mtk_dp->max_linkrate) *
+			 mtk_dp->max_lanes);
+
+	*num_input_fmts = 0;
+
+	/*
+	 * If the linkrate is smaller than datarate of RGB888, larger than
+	 * datarate of YUV422 and sink device supports YUV422, we output YUV422
+	 * format. Use this condition, we can support more resolution.
+	 */
+	if ((rate < (mode->clock * 24 / 8)) &&
+	    (rate > (mode->clock * 16 / 8)) &&
+	    (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) {
+		input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL);
+		if (!input_fmts)
+			return NULL;
+		*num_input_fmts = 1;
+		input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
+	} else {
+		input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts),
+				     sizeof(*input_fmts),
+				     GFP_KERNEL);
+		if (!input_fmts)
+			return NULL;
+
+		*num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
+		memcpy(input_fmts, mt8195_input_fmts, sizeof(mt8195_input_fmts));
+	}
+
+	return input_fmts;
+}
+
+static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
+				      struct drm_bridge_state *bridge_state,
+				      struct drm_crtc_state *crtc_state,
+				      struct drm_connector_state *conn_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	struct drm_crtc *crtc = conn_state->crtc;
+	unsigned int input_bus_format;
+
+	input_bus_format = bridge_state->input_bus_cfg.format;
+
+	dev_dbg(mtk_dp->dev, "input format 0x%04x, output format 0x%04x\n",
+		bridge_state->input_bus_cfg.format,
+		 bridge_state->output_bus_cfg.format);
+
+	if (input_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
+		mtk_dp->info.format = DP_PIXELFORMAT_YUV422;
+	else
+		mtk_dp->info.format = DP_PIXELFORMAT_RGB;
+
+	if (!crtc) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as connector state doesn't have a crtc\n");
+		return -EINVAL;
+	}
+
+	drm_display_mode_to_videomode(&crtc_state->adjusted_mode, &mtk_dp->info.vm);
+
+	return 0;
+}
+
+static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
+	.atomic_check = mtk_dp_bridge_atomic_check,
+	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+	.atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts,
+	.atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts,
+	.atomic_reset = drm_atomic_helper_bridge_reset,
+	.attach = mtk_dp_bridge_attach,
+	.detach = mtk_dp_bridge_detach,
+	.atomic_enable = mtk_dp_bridge_atomic_enable,
+	.atomic_disable = mtk_dp_bridge_atomic_disable,
+	.mode_valid = mtk_dp_bridge_mode_valid,
+	.get_edid = mtk_dp_get_edid,
+};
+
+static int mtk_dp_probe(struct platform_device *pdev)
+{
+	struct mtk_dp *mtk_dp;
+	struct device *dev = &pdev->dev;
+	int ret, irq_num;
+
+	mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
+	if (!mtk_dp)
+		return -ENOMEM;
+
+	mtk_dp->dev = dev;
+
+	irq_num = platform_get_irq(pdev, 0);
+	if (irq_num < 0)
+		return dev_err_probe(dev, irq_num,
+				     "failed to request dp irq resource\n");
+
+	mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
+	if (IS_ERR(mtk_dp->next_bridge))
+		return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge),
+				     "Failed to get bridge\n");
+
+	ret = mtk_dp_dt_parse(mtk_dp, pdev);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to parse dt\n");
+
+	drm_dp_aux_init(&mtk_dp->aux);
+	mtk_dp->aux.name = "aux_mtk_dp";
+	mtk_dp->aux.transfer = mtk_dp_aux_transfer;
+
+	spin_lock_init(&mtk_dp->irq_thread_lock);
+
+	ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
+					mtk_dp_hpd_event_thread,
+					IRQ_TYPE_LEVEL_HIGH, dev_name(dev),
+					mtk_dp);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "failed to request mediatek dptx irq\n");
+
+	platform_set_drvdata(pdev, mtk_dp);
+
+	mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
+							PLATFORM_DEVID_AUTO,
+							&mtk_dp->regs,
+							sizeof(struct regmap *));
+	if (IS_ERR(mtk_dp->phy_dev))
+		return dev_err_probe(dev, PTR_ERR(mtk_dp->phy_dev),
+				     "Failed to create device mediatek-dp-phy\n");
+
+	mtk_dp_get_calibration_data(mtk_dp);
+
+	mtk_dp->phy = devm_phy_get(&mtk_dp->phy_dev->dev, "dp");
+
+	if (IS_ERR(mtk_dp->phy)) {
+		platform_device_unregister(mtk_dp->phy_dev);
+		return dev_err_probe(dev, PTR_ERR(mtk_dp->phy),
+				     "Failed to get phy\n");
+	}
+
+	mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
+	mtk_dp->bridge.of_node = dev->of_node;
+
+	mtk_dp->bridge.ops =
+		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
+	mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
+
+	drm_bridge_add(&mtk_dp->bridge);
+
+	pm_runtime_enable(dev);
+	pm_runtime_get_sync(dev);
+
+	return 0;
+}
+
+static int mtk_dp_remove(struct platform_device *pdev)
+{
+	struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
+
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	drm_bridge_remove(&mtk_dp->bridge);
+	platform_device_unregister(mtk_dp->phy_dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_dp_suspend(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_power_disable(mtk_dp);
+	mtk_dp_hwirq_enable(mtk_dp, false);
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static int mtk_dp_resume(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	pm_runtime_get_sync(dev);
+	mtk_dp_init_port(mtk_dp);
+	mtk_dp_hwirq_enable(mtk_dp, true);
+	mtk_dp_power_enable(mtk_dp);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
+
+static const struct of_device_id mtk_dp_of_match[] = {
+	{ .compatible = "mediatek,mt8195-edp-tx" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
+
+struct platform_driver mtk_dp_driver = {
+	.probe = mtk_dp_probe,
+	.remove = mtk_dp_remove,
+	.driver = {
+		.name = "mediatek-drm-dp",
+		.of_match_table = mtk_dp_of_match,
+		.pm = &mtk_dp_pm_ops,
+	},
+};
+
+module_platform_driver(mtk_dp_driver);
+
+MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
+MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
+MODULE_AUTHOR("Bo-Chen Chen <rex-bc.chen@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
new file mode 100644
index 000000000000..3f01ba44871f
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
@@ -0,0 +1,305 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019-2022 MediaTek Inc.
+ * Copyright (c) 2022 BayLibre
+ */
+#ifndef _MTK_DP_REG_H_
+#define _MTK_DP_REG_H_
+
+#define SEC_OFFSET	0x4000
+
+#define MTK_DP_HPD_DISCONNECT		BIT(1)
+#define MTK_DP_HPD_CONNECT		BIT(2)
+#define MTK_DP_HPD_INTERRUPT		BIT(3)
+
+/* offset: 0x0 */
+#define DP_PHY_GLB_BIAS_GEN_00		0x0
+#define RG_XTP_GLB_BIAS_INTR_CTRL		GENMASK(20, 16)
+#define DP_PHY_GLB_DPAUX_TX		0x8
+#define RG_CKM_PT0_CKTX_IMPSEL			GENMASK(23, 20)
+#define MTK_DP_0034			0x34
+#define DA_XTP_GLB_CKDET_EN_FORCE_VAL		BIT(15)
+#define DA_XTP_GLB_CKDET_EN_FORCE_EN		BIT(14)
+#define DA_CKM_INTCKTX_EN_FORCE_VAL		BIT(13)
+#define DA_CKM_INTCKTX_EN_FORCE_EN		BIT(12)
+#define DA_CKM_CKTX0_EN_FORCE_VAL		BIT(11)
+#define DA_CKM_CKTX0_EN_FORCE_EN		BIT(10)
+#define DA_CKM_XTAL_CK_FORCE_VAL		BIT(9)
+#define DA_CKM_XTAL_CK_FORCE_EN			BIT(8)
+#define DA_CKM_BIAS_LPF_EN_FORCE_VAL		BIT(7)
+#define DA_CKM_BIAS_LPF_EN_FORCE_EN		BIT(6)
+#define DA_CKM_BIAS_EN_FORCE_VAL		BIT(5)
+#define DA_CKM_BIAS_EN_FORCE_EN			BIT(4)
+#define DA_XTP_GLB_AVD10_ON_FORCE_VAL		BIT(3)
+#define DA_XTP_GLB_AVD10_ON_FORCE		BIT(2)
+#define DA_XTP_GLB_LDO_EN_FORCE_VAL		BIT(1)
+#define DA_XTP_GLB_LDO_EN_FORCE_EN		BIT(0)
+#define DP_PHY_LANE_TX_0		0x104
+#define RG_XTP_LN0_TX_IMPSEL_PMOS		GENMASK(15, 12)
+#define RG_XTP_LN0_TX_IMPSEL_NMOS		GENMASK(19, 16)
+#define DP_PHY_LANE_TX_1		0x204
+#define RG_XTP_LN1_TX_IMPSEL_PMOS		GENMASK(15, 12)
+#define RG_XTP_LN1_TX_IMPSEL_NMOS		GENMASK(19, 16)
+#define DP_PHY_LANE_TX_2		0x304
+#define RG_XTP_LN2_TX_IMPSEL_PMOS		GENMASK(15, 12)
+#define RG_XTP_LN2_TX_IMPSEL_NMOS		GENMASK(19, 16)
+#define DP_PHY_LANE_TX_3		0x404
+#define RG_XTP_LN3_TX_IMPSEL_PMOS		GENMASK(15, 12)
+#define RG_XTP_LN3_TX_IMPSEL_NMOS		GENMASK(19, 16)
+#define MTK_DP_1040			0x1040
+#define RG_DPAUX_RX_VALID_DEGLITCH_EN		BIT(2)
+#define RG_XTP_GLB_CKDET_EN			BIT(1)
+#define RG_DPAUX_RX_EN				BIT(0)
+
+/* offset: TOP_OFFSET (0x2000) */
+#define MTK_DP_TOP_PWR_STATE		0x2000
+#define DP_PWR_STATE_MASK			GENMASK(1, 0)
+#define DP_PWR_STATE_BANDGAP			BIT(0)
+#define DP_PWR_STATE_BANDGAP_TPLL		BIT(1)
+#define DP_PWR_STATE_BANDGAP_TPLL_LANE		GENMASK(1, 0)
+#define MTK_DP_TOP_SWING_EMP		0x2004
+#define DP_TX0_VOLT_SWING_MASK			GENMASK(1, 0)
+#define DP_TX0_VOLT_SWING_SHIFT			0
+#define DP_TX0_PRE_EMPH_MASK			GENMASK(3, 2)
+#define DP_TX0_PRE_EMPH_SHIFT			2
+#define DP_TX1_VOLT_SWING_MASK			GENMASK(9, 8)
+#define DP_TX1_VOLT_SWING_SHIFT			8
+#define DP_TX1_PRE_EMPH_MASK			GENMASK(11, 10)
+#define DP_TX2_VOLT_SWING_MASK			GENMASK(17, 16)
+#define DP_TX2_PRE_EMPH_MASK			GENMASK(19, 18)
+#define DP_TX3_VOLT_SWING_MASK			GENMASK(25, 24)
+#define DP_TX3_PRE_EMPH_MASK			GENMASK(27, 26)
+#define MTK_DP_TOP_RESET_AND_PROBE	0x2020
+#define SW_RST_B_PHYD				BIT(4)
+#define MTK_DP_TOP_IRQ_MASK		0x202c
+#define IRQ_MASK_AUX_TOP_IRQ			BIT(2)
+#define MTK_DP_TOP_MEM_PD		0x2038
+#define MEM_ISO_EN				BIT(0)
+#define FUSE_SEL				BIT(2)
+
+/* offset: ENC0_OFFSET (0x3000) */
+#define MTK_DP_ENC0_P0_3000			0x3000
+#define LANE_NUM_DP_ENC0_P0_MASK			GENMASK(1, 0)
+#define VIDEO_MUTE_SW_DP_ENC0_P0			BIT(2)
+#define VIDEO_MUTE_SEL_DP_ENC0_P0			BIT(3)
+#define ENHANCED_FRAME_EN_DP_ENC0_P0			BIT(4)
+#define MTK_DP_ENC0_P0_3004			0x3004
+#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK		BIT(8)
+#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0		BIT(9)
+#define MTK_DP_ENC0_P0_3010			0x3010
+#define HTOTAL_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3014			0x3014
+#define VTOTAL_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3018			0x3018
+#define HSTART_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_301C			0x301c
+#define VSTART_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3020			0x3020
+#define HWIDTH_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3024			0x3024
+#define VHEIGHT_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3028			0x3028
+#define HSW_SW_DP_ENC0_P0_MASK				GENMASK(14, 0)
+#define HSP_SW_DP_ENC0_P0_MASK				BIT(15)
+#define MTK_DP_ENC0_P0_302C			0x302c
+#define VSW_SW_DP_ENC0_P0_MASK				GENMASK(14, 0)
+#define VSP_SW_DP_ENC0_P0_MASK				BIT(15)
+#define MTK_DP_ENC0_P0_3030			0x3030
+#define HTOTAL_SEL_DP_ENC0_P0				BIT(0)
+#define VTOTAL_SEL_DP_ENC0_P0				BIT(1)
+#define HSTART_SEL_DP_ENC0_P0				BIT(2)
+#define VSTART_SEL_DP_ENC0_P0				BIT(3)
+#define HWIDTH_SEL_DP_ENC0_P0				BIT(4)
+#define VHEIGHT_SEL_DP_ENC0_P0				BIT(5)
+#define HSP_SEL_DP_ENC0_P0				BIT(6)
+#define HSW_SEL_DP_ENC0_P0				BIT(7)
+#define VSP_SEL_DP_ENC0_P0				BIT(8)
+#define VSW_SEL_DP_ENC0_P0				BIT(9)
+#define MTK_DP_ENC0_P0_3034			0x3034
+#define MTK_DP_ENC0_P0_3038			0x3038
+#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK		BIT(11)
+#define MTK_DP_ENC0_P0_303C			0x303c
+#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK		GENMASK(5, 0)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK		GENMASK(10, 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT		(0 << 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT		(1 << 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT		(2 << 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT		(3 << 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT		(4 << 8)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK		GENMASK(14, 12)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB		(0 << 12)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422		(1 << 12)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420		(2 << 12)
+#define VIDEO_MN_GEN_EN_DP_ENC0_P0			BIT(15)
+#define MTK_DP_ENC0_P0_3040			0x3040
+#define SDP_DOWN_CNT_DP_ENC0_P0_VAL			0x20
+#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK		GENMASK(11, 0)
+#define MTK_DP_ENC0_P0_304C			0x304c
+#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK			BIT(2)
+#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK		BIT(8)
+#define MTK_DP_ENC0_P0_3064			0x3064
+#define HDE_NUM_LAST_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3154			0x3154
+#define PGEN_HTOTAL_DP_ENC0_P0_MASK			GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_3158			0x3158
+#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK		GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_315C			0x315c
+#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK		GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_3160			0x3160
+#define PGEN_HFDE_START_DP_ENC0_P0_MASK			GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_3164			0x3164
+#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK		GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_3168			0x3168
+#define PGEN_VTOTAL_DP_ENC0_P0_MASK			GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_316C			0x316c
+#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK		GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_3170			0x3170
+#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK		GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_3174			0x3174
+#define PGEN_VFDE_START_DP_ENC0_P0_MASK			GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_3178			0x3178
+#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK		GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_31B0			0x31b0
+#define PGEN_PATTERN_SEL_VAL				4
+#define PGEN_PATTERN_SEL_MASK				GENMASK(6, 4)
+#define MTK_DP_ENC0_P0_31EC			0x31ec
+#define AUDIO_CH_SRC_SEL_DP_ENC0_P0			BIT(4)
+#define ISRC1_HB3_DP_ENC0_P0_MASK			GENMASK(15, 8)
+
+/* offset: ENC1_OFFSET (0x3200) */
+#define MTK_DP_ENC1_P0_3300			0x3300
+#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL		2
+#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK		GENMASK(9, 8)
+#define MTK_DP_ENC1_P0_3364			0x3364
+#define SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL		0x20
+#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK	GENMASK(11, 0)
+#define FIFO_READ_START_POINT_DP_ENC1_P0_VAL		4
+#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK		GENMASK(15, 12)
+#define MTK_DP_ENC1_P0_3368			0x3368
+#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0	BIT(0)
+#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0		BIT(4)
+#define SDP_DP13_EN_DP_ENC1_P0				BIT(8)
+#define BS2BS_MODE_DP_ENC1_P0				BIT(12)
+#define BS2BS_MODE_DP_ENC1_P0_MASK			GENMASK(13, 12)
+#define BS2BS_MODE_DP_ENC1_P0_VAL			1
+#define DP_ENC1_P0_3368_VAL				(VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0 | \
+							 VIDEO_STABLE_CNT_THRD_DP_ENC1_P0 | \
+							 SDP_DP13_EN_DP_ENC1_P0 | \
+							 BS2BS_MODE_DP_ENC1_P0)
+
+/* offset: TRANS_OFFSET (0x3400) */
+#define MTK_DP_TRANS_P0_3400				0x3400
+#define PATTERN1_EN_DP_TRANS_P0_MASK				BIT(12)
+#define PATTERN2_EN_DP_TRANS_P0_MASK				BIT(13)
+#define PATTERN3_EN_DP_TRANS_P0_MASK				BIT(14)
+#define PATTERN4_EN_DP_TRANS_P0_MASK				BIT(15)
+#define MTK_DP_TRANS_P0_3404				0x3404
+#define DP_SCR_EN_DP_TRANS_P0_MASK				BIT(0)
+#define MTK_DP_TRANS_P0_340C				0x340c
+#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0		BIT(13)
+#define MTK_DP_TRANS_P0_3410				0x3410
+#define HPD_DEB_THD_DP_TRANS_P0_MASK				GENMASK(3, 0)
+#define HPD_INT_THD_DP_TRANS_P0_MASK				GENMASK(7, 4)
+#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US			(2 << 4)
+#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US			(2 << 6)
+#define HPD_DISC_THD_DP_TRANS_P0_MASK				GENMASK(11, 8)
+#define HPD_CONN_THD_DP_TRANS_P0_MASK				GENMASK(15, 12)
+#define MTK_DP_TRANS_P0_3414				0x3414
+#define HPD_DB_DP_TRANS_P0_MASK					BIT(2)
+#define MTK_DP_TRANS_P0_3418				0x3418
+#define IRQ_CLR_DP_TRANS_P0_MASK				GENMASK(3, 0)
+#define IRQ_MASK_DP_TRANS_P0_MASK				GENMASK(7, 4)
+#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ				(BIT(1) << 4)
+#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ				(BIT(2) << 4)
+#define IRQ_MASK_DP_TRANS_P0_INT_IRQ				(BIT(3) << 4)
+#define IRQ_STATUS_DP_TRANS_P0_MASK				GENMASK(15, 12)
+#define MTK_DP_TRANS_P0_342C				0x342c
+#define XTAL_FREQ_DP_TRANS_P0_DEFAULT				(BIT(0) | BIT(3) | BIT(5) | BIT(6))
+#define XTAL_FREQ_DP_TRANS_P0_MASK				GENMASK(7, 0)
+#define MTK_DP_TRANS_P0_3430				0x3430
+#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK			GENMASK(1, 0)
+#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT		BIT(1)
+#define MTK_DP_TRANS_P0_34A4				0x34a4
+#define LANE_NUM_DP_TRANS_P0_MASK				GENMASK(3, 2)
+#define MTK_DP_TRANS_P0_3540				0x3540
+#define FEC_EN_DP_TRANS_P0_MASK					BIT(0)
+#define FEC_CLOCK_EN_MODE_DP_TRANS_P0				BIT(3)
+#define MTK_DP_TRANS_P0_3580				0x3580
+#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK		BIT(8)
+#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK		BIT(9)
+#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK		BIT(10)
+#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK		BIT(11)
+#define MTK_DP_TRANS_P0_35C8				0x35c8
+#define SW_IRQ_CLR_DP_TRANS_P0_MASK				GENMASK(15, 0)
+#define SW_IRQ_STATUS_DP_TRANS_P0_MASK				GENMASK(15, 0)
+#define MTK_DP_TRANS_P0_35D0				0x35d0
+#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_TRANS_P0_35F0				0x35f0
+#define DP_TRANS_DUMMY_RW_0					BIT(3)
+#define DP_TRANS_DUMMY_RW_0_MASK				GENMASK(3, 2)
+
+/* offset: AUX_OFFSET (0x3600) */
+#define MTK_DP_AUX_P0_360C			0x360c
+#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK			GENMASK(12, 0)
+#define AUX_TIMEOUT_THR_AUX_TX_P0_VAL			0x1595
+#define MTK_DP_AUX_P0_3614			0x3614
+#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK		GENMASK(6, 0)
+#define AUX_RX_UI_CNT_THR_AUX_FOR_26M			13
+#define MTK_DP_AUX_P0_3618			0x3618
+#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK			BIT(9)
+#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK	GENMASK(3, 0)
+#define MTK_DP_AUX_P0_3620			0x3620
+#define AUX_RD_MODE_AUX_TX_P0_MASK			BIT(9)
+#define AUX_RX_FIFO_READ_PULSE_TX_P0			BIT(8)
+#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK		GENMASK(7, 0)
+#define MTK_DP_AUX_P0_3624			0x3624
+#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK		GENMASK(3, 0)
+#define MTK_DP_AUX_P0_3628			0x3628
+#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK			GENMASK(9, 0)
+#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE		BIT(0)
+#define MTK_DP_AUX_P0_362C			0x362c
+#define AUX_NO_LENGTH_AUX_TX_P0				BIT(0)
+#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK		BIT(1)
+#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK		GENMASK(15, 2)
+#define MTK_DP_AUX_P0_3630			0x3630
+#define AUX_TX_REQUEST_READY_AUX_TX_P0			BIT(3)
+#define MTK_DP_AUX_P0_3634			0x3634
+#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK		GENMASK(15, 8)
+#define AUX_TX_OVER_SAMPLE_RATE_FOR_26M			25
+#define MTK_DP_AUX_P0_3640			0x3640
+#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(6)
+#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(5)
+#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(4)
+#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0			BIT(3)
+#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0			BIT(2)
+#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0			BIT(1)
+#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0			BIT(0)
+#define DP_AUX_P0_3640_VAL				(AUX_400US_TIMEOUT_IRQ_AUX_TX_P0 | \
+							 AUX_RX_DATA_RECV_IRQ_AUX_TX_P0 | \
+							 AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0 | \
+							 AUX_RX_CMD_RECV_IRQ_AUX_TX_P0 | \
+							 AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0 | \
+							 AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0 | \
+							 AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
+#define MTK_DP_AUX_P0_3644			0x3644
+#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK		GENMASK(3, 0)
+#define MTK_DP_AUX_P0_3648			0x3648
+#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK		GENMASK(15, 0)
+#define MTK_DP_AUX_P0_364C			0x364c
+#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK		GENMASK(3, 0)
+#define MTK_DP_AUX_P0_3650			0x3650
+#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK			GENMASK(15, 12)
+#define PHY_FIFO_RST_AUX_TX_P0_MASK			BIT(9)
+#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0			BIT(8)
+#define MTK_DP_AUX_P0_3658			0x3658
+#define AUX_TX_OV_EN_AUX_TX_P0_MASK			BIT(0)
+#define MTK_DP_AUX_P0_3690			0x3690
+#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0		BIT(8)
+#define MTK_DP_AUX_P0_3704			0x3704
+#define AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK	BIT(1)
+#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0		BIT(2)
+#define MTK_DP_AUX_P0_3708			0x3708
+#define MTK_DP_AUX_P0_37C8			0x37c8
+#define MTK_ATOP_EN_AUX_TX_P0				BIT(0)
+
+#endif /*_MTK_DP_REG_H_*/
-- 
2.18.0


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

* [PATCH v17 03/10] drm/mediatek: Add MT8195 Embedded DisplayPort driver
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

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

This patch adds a embedded displayport driver for the MediaTek mt8195 SoC.

It supports the MT8195, the embedded DisplayPort units. It offers
DisplayPort 1.4 with up to 4 lanes.

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

This driver is based on an initial version by
Jitao shi <jitao.shi@mediatek.com>

Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/Kconfig      |    9 +
 drivers/gpu/drm/mediatek/Makefile     |    2 +
 drivers/gpu/drm/mediatek/mtk_dp.c     | 1999 +++++++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_dp_reg.h |  305 ++++
 4 files changed, 2315 insertions(+)
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h

diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
index 2976d21e9a34..e66f4a3b6be0 100644
--- a/drivers/gpu/drm/mediatek/Kconfig
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -21,6 +21,15 @@ config DRM_MEDIATEK
 	  This driver provides kernel mode setting and
 	  buffer management to userspace.
 
+config DRM_MEDIATEK_DP
+	tristate "DRM DPTX Support for MediaTek SoCs"
+	depends on DRM_MEDIATEK
+	select PHY_MTK_DP
+	select DRM_DISPLAY_HELPER
+	select DRM_DISPLAY_DP_HELPER
+	help
+	  DRM/KMS Display Port driver for MediaTek SoCs.
+
 config DRM_MEDIATEK_HDMI
 	tristate "DRM HDMI Support for Mediatek SoCs"
 	depends on DRM_MEDIATEK
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index 6e604a933ed0..3517d1c65cd7 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -23,3 +23,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
 			  mtk_hdmi_ddc.o
 
 obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
+
+obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o
diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
new file mode 100644
index 000000000000..e2ec9b02b1aa
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -0,0 +1,1999 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019-2022 MediaTek Inc.
+ * Copyright (c) 2022 BayLibre
+ */
+
+#include <drm/display/drm_dp.h>
+#include <drm/display/drm_dp_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/media-bus-format.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soc/mediatek/mtk_sip_svc.h>
+#include <video/videomode.h>
+
+#include "mtk_dp_reg.h"
+
+#define MTK_DP_SIP_CONTROL_AARCH32	MTK_SIP_SMC_CMD(0x523)
+#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE	(BIT(0) | BIT(5))
+
+#define MTK_DP_THREAD_CABLE_STATE_CHG	BIT(0)
+#define MTK_DP_THREAD_HPD_EVENT		BIT(1)
+
+#define MTK_DP_4P1T 4
+#define MTK_DP_HDE 2
+#define MTK_DP_PIX_PER_ADDR 2
+#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
+#define MTK_DP_TBC_BUF_READ_START_ADDR 0x8
+#define MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY 5
+#define MTK_DP_TRAIN_DOWNSCALE_RETRY 10
+
+enum {
+	MTK_DP_CAL_GLB_BIAS_TRIM = 0,
+	MTK_DP_CAL_CLKTX_IMPSE,
+	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0,
+	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1,
+	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2,
+	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3,
+	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0,
+	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1,
+	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2,
+	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3,
+	MTK_DP_CAL_MAX,
+};
+
+struct mtk_dp_train_info {
+	bool sink_ssc;
+	bool cable_plugged_in;
+	/* link_rate is in multiple of 0.27Gbps */
+	int link_rate;
+	int lane_count;
+	unsigned int channel_eq_pattern;
+};
+
+struct mtk_dp_info {
+	enum dp_pixelformat format;
+	struct videomode vm;
+};
+
+struct mtk_dp_efuse_fmt {
+	unsigned short idx;
+	unsigned short shift;
+	unsigned short mask;
+	unsigned short min_val;
+	unsigned short max_val;
+	unsigned short default_val;
+};
+
+struct mtk_dp {
+	bool enabled;
+	u8 max_lanes;
+	u8 max_linkrate;
+	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
+	u32 cal_data[MTK_DP_CAL_MAX];
+	u32 irq_thread_handle;
+	/* irq_thread_lock is used to protect irq_thread_handle */
+	spinlock_t irq_thread_lock;
+
+	struct device *dev;
+	struct drm_bridge bridge;
+	struct drm_bridge *next_bridge;
+	struct drm_connector *conn;
+	struct drm_device *drm_dev;
+	struct drm_dp_aux aux;
+
+	struct mtk_dp_info info;
+	struct mtk_dp_train_info train_info;
+
+	struct platform_device *phy_dev;
+	struct phy *phy;
+	struct regmap *regs;
+};
+
+static const struct mtk_dp_efuse_fmt mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
+	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
+		.idx = 3,
+		.shift = 27,
+		.mask = 0x1f,
+		.min_val = 1,
+		.max_val = 0x1e,
+		.default_val = 0xf,
+	},
+	[MTK_DP_CAL_CLKTX_IMPSE] = {
+		.idx = 0,
+		.shift = 9,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = {
+		.idx = 2,
+		.shift = 28,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = {
+		.idx = 2,
+		.shift = 20,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = {
+		.idx = 2,
+		.shift = 12,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = {
+		.idx = 2,
+		.shift = 4,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = {
+		.idx = 2,
+		.shift = 24,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = {
+		.idx = 2,
+		.shift = 16,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = {
+		.idx = 2,
+		.shift = 8,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = {
+		.idx = 2,
+		.shift = 0,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+};
+
+static struct regmap_config mtk_dp_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = SEC_OFFSET + 0x90,
+	.name = "mtk-dp-registers",
+};
+
+static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
+{
+	return container_of(b, struct mtk_dp, bridge);
+}
+
+static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
+{
+	u32 read_val;
+	int ret;
+
+	ret = regmap_read(mtk_dp->regs, offset, &read_val);
+	if (ret) {
+		dev_err(mtk_dp->dev, "Failed to read register 0x%x: %d\n",
+			offset, ret);
+		return 0;
+	}
+
+	return read_val;
+}
+
+static int mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
+{
+	int ret = regmap_write(mtk_dp->regs, offset, val);
+
+	if (ret)
+		dev_err(mtk_dp->dev,
+			"Failed to write register 0x%x with value 0x%x\n",
+			offset, val);
+	return ret;
+}
+
+static int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset,
+			      u32 val, u32 mask)
+{
+	int ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
+
+	if (ret)
+		dev_err(mtk_dp->dev,
+			"Failed to update register 0x%x with value 0x%x, mask 0x%x\n",
+			offset, val, mask);
+	return ret;
+}
+
+static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32 offset, u8 *buf,
+				    size_t length)
+{
+	int i;
+
+	/* 2 bytes per register */
+	for (i = 0; i < length; i += 2) {
+		u32 val = buf[i] | (i + 1 < length ? buf[i + 1] << 8 : 0);
+
+		if (mtk_dp_write(mtk_dp, offset + i * 2, val))
+			return;
+	}
+}
+
+static void mtk_dp_msa_bypass_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	u32 mask = HTOTAL_SEL_DP_ENC0_P0 | VTOTAL_SEL_DP_ENC0_P0 |
+		   HSTART_SEL_DP_ENC0_P0 | VSTART_SEL_DP_ENC0_P0 |
+		   HWIDTH_SEL_DP_ENC0_P0 | VHEIGHT_SEL_DP_ENC0_P0 |
+		   HSP_SEL_DP_ENC0_P0 | HSW_SEL_DP_ENC0_P0 |
+		   VSP_SEL_DP_ENC0_P0 | VSW_SEL_DP_ENC0_P0;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, enable ? 0 : mask, mask);
+}
+
+static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
+{
+	struct drm_display_mode mode;
+	struct videomode *vm = &mtk_dp->info.vm;
+
+	drm_display_mode_from_videomode(vm, &mode);
+
+	/* horizontal */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010,
+			   mode.htotal, HTOTAL_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
+			   vm->hsync_len + vm->hback_porch,
+			   HSTART_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
+			   vm->hsync_len, HSW_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
+			   0, HSP_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020,
+			   vm->hactive, HWIDTH_SW_DP_ENC0_P0_MASK);
+
+	/* vertical */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014,
+			   mode.vtotal, VTOTAL_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
+			   vm->vsync_len + vm->vback_porch,
+			   VSTART_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
+			   vm->vsync_len, VSW_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
+			   0, VSP_SW_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024,
+			   vm->vactive, VHEIGHT_SW_DP_ENC0_P0_MASK);
+
+	/* horizontal */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064,
+			   vm->hactive, HDE_NUM_LAST_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154,
+			   mode.htotal, PGEN_HTOTAL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
+			   vm->hfront_porch,
+			   PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C,
+			   vm->hsync_len,
+			   PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
+			   vm->hback_porch + vm->hsync_len,
+			   PGEN_HFDE_START_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164,
+			   vm->hactive,
+			   PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
+
+	/* vertical */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168,
+			   mode.vtotal,
+			   PGEN_VTOTAL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
+			   vm->vfront_porch,
+			   PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170,
+			   vm->vsync_len,
+			   PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
+			   vm->vback_porch + vm->vsync_len,
+			   PGEN_VFDE_START_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178,
+			   vm->vactive,
+			   PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
+}
+
+static int mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
+				   enum dp_pixelformat color_format)
+{
+	u32 val;
+
+	/* update MISC0 */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
+			   color_format << DP_TEST_COLOR_FORMAT_SHIFT,
+			   DP_TEST_COLOR_FORMAT_MASK);
+
+	switch (color_format) {
+	case DP_PIXELFORMAT_YUV422:
+		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
+		break;
+	case DP_PIXELFORMAT_RGB:
+		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
+		break;
+	default:
+		drm_warn(mtk_dp->drm_dev, "Unsupported color format: %d\n",
+			 color_format);
+		return -EINVAL;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   val, PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
+	return 0;
+}
+
+static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp)
+{
+	/* Only support 8 bits currently */
+	/* Update MISC0 */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
+			   DP_MSA_MISC_8_BPC, DP_TEST_BIT_DEPTH_MASK);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT,
+			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_config_mn_mode(struct mtk_dp *mtk_dp)
+{
+	/* 0: hw mode, 1: sw mode */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
+			   0, VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32 val)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   val, SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+			   VIDEO_MN_GEN_EN_DP_ENC0_P0,
+			   VIDEO_MN_GEN_EN_DP_ENC0_P0);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
+			   SDP_DOWN_CNT_DP_ENC0_P0_VAL,
+			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
+			   SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL,
+			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
+			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL << 8,
+			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
+			   FIFO_READ_START_POINT_DP_ENC1_P0_VAL << 12,
+			   FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
+	mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368, DP_ENC1_P0_3368_VAL);
+}
+
+static void mtk_dp_pg_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038,
+			   enable ? VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK : 0,
+			   VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
+			   PGEN_PATTERN_SEL_VAL << 4, PGEN_PATTERN_SEL_MASK);
+}
+
+static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640, DP_AUX_P0_3640_VAL);
+}
+
+static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32 addr)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644,
+			   cmd, MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648,
+			   addr, MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C,
+			   addr >> 16, MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_aux_clear_fifo(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
+			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0,
+			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0 |
+			   PHY_FIFO_RST_AUX_TX_P0_MASK |
+			   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
+			   AUX_TX_REQUEST_READY_AUX_TX_P0,
+			   AUX_TX_REQUEST_READY_AUX_TX_P0);
+}
+
+static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8 *buf,
+				       size_t length)
+{
+	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf, length);
+}
+
+static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
+				    size_t length, int read_delay)
+{
+	int read_pos;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
+			   0, AUX_RD_MODE_AUX_TX_P0_MASK);
+
+	for (read_pos = 0; read_pos < length; read_pos++) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
+				   AUX_RX_FIFO_READ_PULSE_TX_P0,
+				   AUX_RX_FIFO_READ_PULSE_TX_P0);
+
+		/* Hardware needs time to update the data */
+		usleep_range(read_delay, read_delay * 2);
+		buf[read_pos] = (u8)(mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3620) &
+				     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK);
+	}
+}
+
+static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t length)
+{
+	if (length > 0) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
+				   (length - 1) << 12,
+				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
+				   0,
+				   AUX_NO_LENGTH_AUX_TX_P0 |
+				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	} else {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
+				   AUX_NO_LENGTH_AUX_TX_P0,
+				   AUX_NO_LENGTH_AUX_TX_P0 |
+				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	}
+}
+
+static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp, bool is_read)
+{
+	int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
+
+	while (--wait_reply) {
+		u32 aux_irq_status;
+
+		if (is_read) {
+			u32 fifo_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3618);
+
+			if (fifo_status &
+			    (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
+			     AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
+				return 0;
+			}
+		}
+
+		aux_irq_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3640);
+		if (aux_irq_status & AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
+			return 0;
+
+		if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0)
+			return -ETIMEDOUT;
+
+		/* Give the hardware a chance to reach completion before retrying */
+		usleep_range(100, 500);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool is_read, u8 cmd,
+				  u32 addr, u8 *buf, size_t length)
+{
+	int ret;
+	u32 reply_cmd;
+
+	if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
+			(cmd == DP_AUX_NATIVE_READ && !length)))
+		return -EINVAL;
+
+	if (!is_read)
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
+				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0,
+				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0);
+
+	/* We need to clear fifo and irq before sending commands to the sink device. */
+	mtk_dp_aux_clear_fifo(mtk_dp);
+	mtk_dp_aux_irq_clear(mtk_dp);
+
+	mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
+	mtk_dp_aux_set_length(mtk_dp, length);
+
+	if (!is_read) {
+		if (length)
+			mtk_dp_aux_fill_write_fifo(mtk_dp, buf, length);
+
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
+				   AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK,
+				   AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK);
+	}
+
+	mtk_dp_aux_request_ready(mtk_dp);
+
+	/* Wait for feedback from sink device. */
+	ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
+
+	reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
+		    AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
+
+	if (ret || reply_cmd) {
+		u32 phy_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3628) &
+				 AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
+		if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
+			drm_err(mtk_dp->drm_dev,
+				"AUX Rx Aux hang, need SW reset\n");
+			return -EIO;
+		}
+
+		return -ETIMEDOUT;
+	}
+
+	if (!length) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
+				   0,
+				   AUX_NO_LENGTH_AUX_TX_P0 |
+				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+	} else if (is_read) {
+		int read_delay;
+
+		if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
+		    cmd == DP_AUX_I2C_READ)
+			read_delay = 500;
+		else
+			read_delay = 100;
+
+		mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length, read_delay);
+	}
+
+	return 0;
+}
+
+static void mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int lane_num,
+					  int swing_val, int preemphasis)
+{
+	u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
+
+	dev_dbg(mtk_dp->dev,
+		"link training: swing_val = 0x%x, pre-emphasis = 0x%x\n",
+		swing_val, preemphasis);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+			   swing_val << (DP_TX0_VOLT_SWING_SHIFT + lane_shift),
+			   DP_TX0_VOLT_SWING_MASK << lane_shift);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+			   preemphasis << (DP_TX0_PRE_EMPH_SHIFT + lane_shift),
+			   DP_TX0_PRE_EMPH_MASK << lane_shift);
+}
+
+static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+			   0,
+			   DP_TX0_VOLT_SWING_MASK |
+			   DP_TX1_VOLT_SWING_MASK |
+			   DP_TX2_VOLT_SWING_MASK |
+			   DP_TX3_VOLT_SWING_MASK |
+			   DP_TX0_PRE_EMPH_MASK |
+			   DP_TX1_PRE_EMPH_MASK |
+			   DP_TX2_PRE_EMPH_MASK |
+			   DP_TX3_PRE_EMPH_MASK);
+}
+
+static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
+{
+	u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
+			 SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
+
+	if (irq_status) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
+				   irq_status, SW_IRQ_CLR_DP_TRANS_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
+				   0, SW_IRQ_CLR_DP_TRANS_P0_MASK);
+	}
+
+	return irq_status;
+}
+
+static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
+{
+	u32 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
+			  IRQ_STATUS_DP_TRANS_P0_MASK) >> 12;
+
+	if (irq_status) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
+				   irq_status, IRQ_CLR_DP_TRANS_P0_MASK);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
+				   0, IRQ_CLR_DP_TRANS_P0_MASK);
+	}
+
+	return irq_status;
+}
+
+static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
+			   enable ? 0 :
+			   IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
+			   IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
+			   IRQ_MASK_DP_TRANS_P0_INT_IRQ,
+			   IRQ_MASK_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
+			   XTAL_FREQ_DP_TRANS_P0_DEFAULT,
+			   XTAL_FREQ_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
+			   FEC_CLOCK_EN_MODE_DP_TRANS_P0,
+			   FEC_CLOCK_EN_MODE_DP_TRANS_P0);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
+			   AUDIO_CH_SRC_SEL_DP_ENC0_P0,
+			   AUDIO_CH_SRC_SEL_DP_ENC0_P0);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
+			   0, SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK,
+			   IRQ_MASK_AUX_TOP_IRQ, IRQ_MASK_AUX_TOP_IRQ);
+}
+
+static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp *mtk_dp)
+{
+	u32 val;
+	/* Debounce threshold */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   8, HPD_DEB_THD_DP_TRANS_P0_MASK);
+
+	val = (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
+	       HPD_INT_THD_DP_TRANS_P0_UPPER_1100US) << 4;
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   val, HPD_INT_THD_DP_TRANS_P0_MASK);
+
+	/*
+	 * Connect threshold 1.5ms + 5 x 0.1ms = 2ms
+	 * Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
+	 */
+	val = (5 << 8) | (5 << 12);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   val,
+			   HPD_DISC_THD_DP_TRANS_P0_MASK |
+			   HPD_CONN_THD_DP_TRANS_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
+			   HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
+			   HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
+{
+	/* modify timeout threshold = 0x1595 */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C,
+			   AUX_TIMEOUT_THR_AUX_TX_P0_VAL,
+			   AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658,
+			   0, AUX_TX_OV_EN_AUX_TX_P0_MASK);
+	/* 25 for 26M */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
+			   AUX_TX_OVER_SAMPLE_RATE_FOR_26M << 8,
+			   AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
+	/* 13 for 26M */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
+			   AUX_RX_UI_CNT_THR_AUX_FOR_26M,
+			   AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
+			   MTK_ATOP_EN_AUX_TX_P0,
+			   MTK_ATOP_EN_AUX_TX_P0);
+}
+
+static void mtk_dp_initialize_digital_settings(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
+			   0, VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
+			   BS2BS_MODE_DP_ENC1_P0_VAL << 12,
+			   BS2BS_MODE_DP_ENC1_P0_MASK);
+
+	/* dp tx encoder reset all sw */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
+			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0,
+			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
+
+	/* Wait for sw reset to complete */
+	usleep_range(1000, 5000);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
+			   0, DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
+}
+
+static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
+			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0,
+			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
+
+	/* Wait for sw reset to complete */
+	usleep_range(1000, 5000);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
+			   0, DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
+}
+
+static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
+			   lanes == 0 ? 0 : DP_TRANS_DUMMY_RW_0,
+			   DP_TRANS_DUMMY_RW_0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
+			   lanes, LANE_NUM_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
+			   lanes << 2, LANE_NUM_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
+{
+	const struct mtk_dp_efuse_fmt *fmt;
+	struct device *dev = mtk_dp->dev;
+	struct nvmem_cell *cell;
+	u32 *cal_data = mtk_dp->cal_data;
+	u32 *buf;
+	int i;
+	size_t len;
+
+	cell = nvmem_cell_get(dev, "dp_calibration_data");
+	if (IS_ERR(cell)) {
+		dev_warn(dev, "Failed to get nvmem cell dp_calibration_data\n");
+		goto use_default_val;
+	}
+
+	buf = (u32 *)nvmem_cell_read(cell, &len);
+	nvmem_cell_put(cell);
+
+	if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
+		dev_warn(dev, "Failed to read nvmem_cell_read\n");
+
+		if (!IS_ERR(buf))
+			kfree(buf);
+
+		goto use_default_val;
+	}
+
+	for (i = 0; i < MTK_DP_CAL_MAX; i++) {
+		fmt = &mtk_dp_efuse_data[i];
+		cal_data[i] = (buf[fmt->idx] >> fmt->shift) & fmt->mask;
+
+		if (cal_data[i] < fmt->min_val || cal_data[i] > fmt->max_val) {
+			dev_warn(mtk_dp->dev, "Invalid efuse data, idx = %d\n", i);
+			kfree(buf);
+			goto use_default_val;
+		}
+	}
+	kfree(buf);
+
+	return;
+
+use_default_val:
+	dev_warn(mtk_dp->dev, "Use default calibration data\n");
+	for (i = 0; i < MTK_DP_CAL_MAX; i++)
+		cal_data[i] = mtk_dp_efuse_data[i].default_val;
+}
+
+static void mtk_dp_set_calibration_data(struct mtk_dp *mtk_dp)
+{
+	u32 *cal_data = mtk_dp->cal_data;
+
+	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
+			   cal_data[MTK_DP_CAL_CLKTX_IMPSE] << 20,
+			   RG_CKM_PT0_CKTX_IMPSEL);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
+			   cal_data[MTK_DP_CAL_GLB_BIAS_TRIM] << 16,
+			   RG_XTP_GLB_BIAS_INTR_CTRL);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] << 12,
+			   RG_XTP_LN0_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] << 16,
+			   RG_XTP_LN0_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] << 12,
+			   RG_XTP_LN1_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] << 16,
+			   RG_XTP_LN1_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] << 12,
+			   RG_XTP_LN2_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] << 16,
+			   RG_XTP_LN2_TX_IMPSEL_NMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] << 12,
+			   RG_XTP_LN3_TX_IMPSEL_PMOS);
+	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
+			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] << 16,
+			   RG_XTP_LN3_TX_IMPSEL_NMOS);
+}
+
+static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
+				u32 link_rate, int lane_count)
+{
+	int ret;
+	union phy_configure_opts phy_opts = {
+		.dp = {
+			.link_rate = drm_dp_bw_code_to_link_rate(link_rate) / 100,
+			.set_rate = 1,
+			.lanes = lane_count,
+			.set_lanes = 1,
+			.ssc = mtk_dp->train_info.sink_ssc,
+		}
+	};
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP,
+			   DP_PWR_STATE_MASK);
+
+	ret = phy_configure(mtk_dp->phy, &phy_opts);
+	if (ret)
+		return ret;
+
+	mtk_dp_set_calibration_data(mtk_dp);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL_LANE, DP_PWR_STATE_MASK);
+
+	return 0;
+}
+
+static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool enable)
+{
+	u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
+		  POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
+		  POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
+		  POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580,
+			   enable ? val : 0, val);
+}
+
+static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int pattern)
+{
+	/* TPS1 */
+	if (pattern == 1)
+		mtk_dp_set_idle_pattern(mtk_dp, false);
+
+	mtk_dp_update_bits(mtk_dp,
+			   MTK_DP_TRANS_P0_3400,
+			   pattern ? BIT(pattern - 1) << 12 : 0,
+			   PATTERN1_EN_DP_TRANS_P0_MASK |
+			   PATTERN2_EN_DP_TRANS_P0_MASK |
+			   PATTERN3_EN_DP_TRANS_P0_MASK |
+			   PATTERN4_EN_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
+			   ENHANCED_FRAME_EN_DP_ENC0_P0,
+			   ENHANCED_FRAME_EN_DP_ENC0_P0);
+}
+
+static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool enable)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
+			   enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
+			   DP_SCR_EN_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
+{
+	struct arm_smccc_res res;
+	u32 val = VIDEO_MUTE_SEL_DP_ENC0_P0 |
+		  (enable ? VIDEO_MUTE_SW_DP_ENC0_P0 : 0);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
+			   val,
+			   VIDEO_MUTE_SEL_DP_ENC0_P0 |
+			   VIDEO_MUTE_SW_DP_ENC0_P0);
+
+	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32,
+		      MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable,
+		      0, 0, 0, 0, 0, &res);
+
+	dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
+		MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable, res.a0, res.a1);
+}
+
+static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
+			   0, SW_RST_B_PHYD);
+
+	/* Wait for power enable */
+	usleep_range(10, 200);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
+			   SW_RST_B_PHYD, SW_RST_B_PHYD);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL, DP_PWR_STATE_MASK);
+	mtk_dp_write(mtk_dp, MTK_DP_1040,
+		     RG_DPAUX_RX_VALID_DEGLITCH_EN | RG_XTP_GLB_CKDET_EN |
+		     RG_DPAUX_RX_EN);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_0034, 0, DA_CKM_CKTX0_EN_FORCE_EN);
+}
+
+static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_0034,
+			   DA_CKM_CKTX0_EN_FORCE_EN, DA_CKM_CKTX0_EN_FORCE_EN);
+
+	/* Disable RX */
+	mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
+	mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
+		     0x550 | FUSE_SEL | MEM_ISO_EN);
+}
+
+static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
+{
+	mtk_dp->train_info.link_rate = DP_LINK_BW_5_4;
+	mtk_dp->train_info.lane_count = mtk_dp->max_lanes;
+	mtk_dp->train_info.cable_plugged_in = false;
+
+	mtk_dp->info.format = DP_PIXELFORMAT_RGB;
+	memset(&mtk_dp->info.vm, 0, sizeof(struct videomode));
+}
+
+static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
+{
+	u32 sram_read_start = min_t(u32, MTK_DP_TBC_BUF_READ_START_ADDR,
+				    mtk_dp->info.vm.hactive /
+				    mtk_dp->train_info.lane_count /
+				    MTK_DP_4P1T / MTK_DP_HDE /
+				    MTK_DP_PIX_PER_ADDR);
+	mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
+	mtk_dp_setup_encoder(mtk_dp);
+}
+
+static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_setup_tu(mtk_dp);
+}
+
+static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes,
+					  u8 dpcd_adjust_req[2])
+{
+	int lane;
+
+	for (lane = 0; lane < lanes; ++lane) {
+		u8 val;
+		u8 swing;
+		u8 preemphasis;
+		int index = lane / 2;
+		int shift = lane % 2 ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
+
+		swing = (dpcd_adjust_req[index] >> shift) &
+			DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
+		preemphasis = ((dpcd_adjust_req[index] >> shift) &
+			       DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
+			      DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
+		val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
+		      preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+		if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
+			val |= DP_TRAIN_MAX_SWING_REACHED;
+		if (preemphasis == 3)
+			val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+		mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing, preemphasis);
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET + lane,
+				   val);
+	}
+}
+
+static void mtk_dp_pattern(struct mtk_dp *mtk_dp, bool is_tps1)
+{
+	int pattern;
+	unsigned int aux_offset;
+
+	if (is_tps1) {
+		pattern = 1;
+		aux_offset = DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1;
+	} else {
+		aux_offset = mtk_dp->train_info.channel_eq_pattern;
+
+		switch (mtk_dp->train_info.channel_eq_pattern) {
+		case DP_TRAINING_PATTERN_4:
+			pattern = 4;
+			break;
+		case DP_TRAINING_PATTERN_3:
+			pattern = 3;
+			aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
+			break;
+		case DP_TRAINING_PATTERN_2:
+		default:
+			pattern = 2;
+			aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
+			break;
+		}
+	}
+
+	mtk_dp_train_set_pattern(mtk_dp, pattern);
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET, aux_offset);
+}
+
+static int mtk_dp_train_setting(struct mtk_dp *mtk_dp, u8 target_link_rate,
+				u8 target_lane_count)
+{
+	int ret;
+
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET, target_link_rate);
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
+			   target_lane_count | DP_LANE_COUNT_ENHANCED_FRAME_EN);
+
+	if (mtk_dp->train_info.sink_ssc)
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
+				   DP_SPREAD_AMP_0_5);
+
+	mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
+	ret = mtk_dp_phy_configure(mtk_dp, target_link_rate, target_lane_count);
+	if (ret)
+		return ret;
+
+	dev_dbg(mtk_dp->dev,
+		"Link train target_link_rate = 0x%x, target_lane_count = 0x%x\n",
+		target_link_rate, target_lane_count);
+
+	return 0;
+}
+
+static int mtk_dp_train_cr(struct mtk_dp *mtk_dp, u8 target_lane_count)
+{
+	u8 lane_adjust[2] = {};
+	u8 link_status[DP_LINK_STATUS_SIZE] = {};
+	u8 prev_lane_adjust = 0xff;
+	int train_retries = 0;
+	int voltage_retries = 0;
+
+	mtk_dp_pattern(mtk_dp, true);
+
+	/* In DP spec 1.4, the retry count of CR is defined as 10. */
+	do {
+		train_retries++;
+		if (!mtk_dp->train_info.cable_plugged_in) {
+			mtk_dp_train_set_pattern(mtk_dp, 0);
+			return -ENODEV;
+		}
+
+		drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
+				 lane_adjust, sizeof(lane_adjust));
+		mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
+					      lane_adjust);
+
+		drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
+						       mtk_dp->rx_cap);
+
+		/* check link status from sink device */
+		drm_dp_dpcd_read_link_status(&mtk_dp->aux, link_status);
+		if (drm_dp_clock_recovery_ok(link_status,
+					     target_lane_count)) {
+			dev_dbg(mtk_dp->dev, "Link train CR pass\n");
+			return 0;
+		}
+
+		/*
+		 * In DP spec 1.4, if current voltage level is the same
+		 * with previous voltage level, we need to retry 5 times.
+		 */
+		if (prev_lane_adjust == link_status[4]) {
+			voltage_retries++;
+			/*
+			 * Condition of CR fail:
+			 * 1. Failed to pass CR using the same voltage
+			 *    level over five times.
+			 * 2. Failed to pass CR when the current voltage
+			 *    level is the same with previous voltage
+			 *    level and reach max voltage level (3).
+			 */
+			if (voltage_retries > MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY ||
+			    (prev_lane_adjust & DP_ADJUST_VOLTAGE_SWING_LANE0_MASK) == 3) {
+				dev_dbg(mtk_dp->dev, "Link train CR fail\n");
+				break;
+			}
+		} else {
+			/*
+			 * If the voltage level is changed, we need to
+			 * re-calculate this retry count.
+			 */
+			voltage_retries = 0;
+		}
+		prev_lane_adjust = link_status[4];
+	} while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
+
+	/* Failed to train CR, and disable pattern. */
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+			   DP_TRAINING_PATTERN_DISABLE);
+	mtk_dp_train_set_pattern(mtk_dp, 0);
+
+	return -ETIMEDOUT;
+}
+
+static int mtk_dp_train_eq(struct mtk_dp *mtk_dp, u8 target_lane_count)
+{
+	u8 lane_adjust[2] = {};
+	u8 link_status[DP_LINK_STATUS_SIZE] = {};
+	int train_retries = 0;
+
+	mtk_dp_pattern(mtk_dp, false);
+
+	do {
+		train_retries++;
+		if (!mtk_dp->train_info.cable_plugged_in) {
+			mtk_dp_train_set_pattern(mtk_dp, 0);
+			return -ENODEV;
+		}
+
+		drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
+				 lane_adjust, sizeof(lane_adjust));
+		mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
+					      lane_adjust);
+
+		drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
+						   mtk_dp->rx_cap);
+
+		/* check link status from sink device */
+		drm_dp_dpcd_read_link_status(&mtk_dp->aux, link_status);
+		if (drm_dp_channel_eq_ok(link_status, target_lane_count)) {
+			dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
+
+			/* Training done, and disable pattern. */
+			drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+					   DP_TRAINING_PATTERN_DISABLE);
+			mtk_dp_train_set_pattern(mtk_dp, 0);
+			return 0;
+		}
+		dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
+	} while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
+
+	/* Failed to train EQ, and disable pattern. */
+	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+			   DP_TRAINING_PATTERN_DISABLE);
+	mtk_dp_train_set_pattern(mtk_dp, 0);
+
+	return -ETIMEDOUT;
+}
+
+static int mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
+{
+	u8 val;
+	ssize_t ret;
+
+	drm_dp_read_dpcd_caps(&mtk_dp->aux, mtk_dp->rx_cap);
+
+	if (drm_dp_tps4_supported(mtk_dp->rx_cap))
+		mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_4;
+	else if (drm_dp_tps3_supported(mtk_dp->rx_cap))
+		mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_3;
+	else
+		mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_2;
+
+	mtk_dp->train_info.sink_ssc = drm_dp_max_downspread(mtk_dp->rx_cap);
+
+	ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
+	if (ret < 1) {
+		drm_err(mtk_dp->drm_dev, "Read mstm cap failed\n");
+		return ret == 0 ? -EIO : ret;
+	}
+
+	if (val & DP_MST_CAP) {
+		/* Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 */
+		ret = drm_dp_dpcd_readb(&mtk_dp->aux,
+					DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
+					&val);
+		if (ret < 1) {
+			drm_err(mtk_dp->drm_dev, "Read irq vector failed\n");
+			return ret == 0 ? -EIO : ret;
+		}
+
+		if (val)
+			drm_dp_dpcd_writeb(&mtk_dp->aux,
+					   DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
+					   val);
+	}
+
+	return 0;
+}
+
+static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
+{
+	phy_reset(mtk_dp->phy);
+	mtk_dp_reset_swing_pre_emphasis(mtk_dp);
+}
+
+static int mtk_dp_training(struct mtk_dp *mtk_dp)
+{
+	int ret;
+	u8 lane_count, link_rate, train_limit, max_link_rate;
+
+	link_rate = min_t(u8, mtk_dp->max_linkrate,
+			  mtk_dp->rx_cap[DP_MAX_LINK_RATE]);
+	max_link_rate = link_rate;
+	lane_count = min_t(u8, mtk_dp->max_lanes,
+			   drm_dp_max_lane_count(mtk_dp->rx_cap));
+
+	/*
+	 * TPS are generated by the hardware pattern generator. From the
+	 * hardware setting we need to disable this scramble setting before
+	 * use the TPS pattern generator.
+	 */
+	mtk_dp_training_set_scramble(mtk_dp, false);
+
+	for (train_limit = 6; train_limit > 0; train_limit--) {
+		mtk_dp_train_change_mode(mtk_dp);
+
+		ret = mtk_dp_train_setting(mtk_dp, link_rate, lane_count);
+		if (ret)
+			return ret;
+
+		ret = mtk_dp_train_cr(mtk_dp, lane_count);
+		if (ret == -ENODEV) {
+			return ret;
+		} else if (ret) {
+			/* reduce link rate */
+			switch (link_rate) {
+			case DP_LINK_BW_1_62:
+				lane_count = lane_count / 2;
+				link_rate = max_link_rate;
+				if (lane_count == 0)
+					return -EIO;
+				break;
+			case DP_LINK_BW_2_7:
+				link_rate = DP_LINK_BW_1_62;
+				break;
+			case DP_LINK_BW_5_4:
+				link_rate = DP_LINK_BW_2_7;
+				break;
+			case DP_LINK_BW_8_1:
+				link_rate = DP_LINK_BW_5_4;
+				break;
+			default:
+				return -EINVAL;
+			};
+			continue;
+		}
+
+		ret = mtk_dp_train_eq(mtk_dp, lane_count);
+		if (ret == -ENODEV) {
+			return ret;
+		} else if (ret) {
+			/* reduce lane count */
+			if (lane_count == 0)
+				return -EIO;
+			lane_count /= 2;
+			continue;
+		}
+
+		/* if we can run to this, training is done. */
+		break;
+	}
+
+	if (train_limit == 0)
+		return -ETIMEDOUT;
+
+	mtk_dp->train_info.link_rate = link_rate;
+	mtk_dp->train_info.lane_count = lane_count;
+
+	/*
+	 * After training done, we need to output normal stream instead of TPS,
+	 * so we need to enable scramble.
+	 */
+	mtk_dp_training_set_scramble(mtk_dp, true);
+	mtk_dp_set_enhanced_frame_mode(mtk_dp);
+
+	return 0;
+}
+
+static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+	/* the mute sequence is different between enable and disable */
+	if (enable) {
+		mtk_dp_msa_bypass_enable(mtk_dp, false);
+		mtk_dp_pg_enable(mtk_dp, false);
+		mtk_dp_set_tx_out(mtk_dp);
+		mtk_dp_video_mute(mtk_dp, false);
+	} else {
+		mtk_dp_video_mute(mtk_dp, true);
+		mtk_dp_pg_enable(mtk_dp, true);
+		mtk_dp_msa_bypass_enable(mtk_dp, true);
+	}
+}
+
+static int mtk_dp_video_config(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_config_mn_mode(mtk_dp);
+	mtk_dp_set_msa(mtk_dp);
+	mtk_dp_set_color_depth(mtk_dp);
+	return mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
+}
+
+static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_set_idle_pattern(mtk_dp, true);
+	mtk_dp_initialize_priv_data(mtk_dp);
+
+	mtk_dp_initialize_settings(mtk_dp);
+	mtk_dp_initialize_aux_settings(mtk_dp);
+	mtk_dp_initialize_digital_settings(mtk_dp);
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
+			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0,
+			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0);
+	mtk_dp_initialize_hpd_detect_settings(mtk_dp);
+
+	mtk_dp_digital_sw_reset(mtk_dp);
+}
+
+static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
+{
+	struct mtk_dp *mtk_dp = dev;
+	unsigned long flags;
+	u32 status;
+
+	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
+	status = mtk_dp->irq_thread_handle;
+	mtk_dp->irq_thread_handle = 0;
+	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
+
+	if (status & MTK_DP_THREAD_CABLE_STATE_CHG)
+		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
+
+	if (status & MTK_DP_THREAD_HPD_EVENT)
+		dev_dbg(mtk_dp->dev, "Receive IRQ from sink devices\n");
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
+{
+	struct mtk_dp *mtk_dp = dev;
+	bool cable_sta_chg = false;
+	unsigned long flags;
+	u32 irq_status = mtk_dp_swirq_get_clear(mtk_dp) |
+			 mtk_dp_hwirq_get_clear(mtk_dp);
+
+	if (!irq_status)
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
+
+	if (irq_status & MTK_DP_HPD_INTERRUPT)
+		mtk_dp->irq_thread_handle |= MTK_DP_THREAD_HPD_EVENT;
+
+	/* Cable state is changed. */
+	if (irq_status != MTK_DP_HPD_INTERRUPT) {
+		mtk_dp->irq_thread_handle |= MTK_DP_THREAD_CABLE_STATE_CHG;
+		cable_sta_chg = true;
+	}
+
+	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
+
+	if (cable_sta_chg) {
+		if (!!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
+		       HPD_DB_DP_TRANS_P0_MASK))
+			mtk_dp->train_info.cable_plugged_in = true;
+		else
+			mtk_dp->train_info.cable_plugged_in = false;
+	}
+
+	return IRQ_WAKE_THREAD;
+}
+
+static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
+			   struct platform_device *pdev)
+{
+	struct device_node *endpoint;
+	struct device *dev = &pdev->dev;
+	int ret;
+	void __iomem *base;
+	u32 linkrate;
+	int len;
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	mtk_dp->regs = devm_regmap_init_mmio(dev, base, &mtk_dp_regmap_config);
+	if (IS_ERR(mtk_dp->regs))
+		return PTR_ERR(mtk_dp->regs);
+
+	endpoint = of_graph_get_endpoint_by_regs(pdev->dev.of_node, 1, -1);
+	len = of_property_count_elems_of_size(endpoint,
+					      "data-lanes", sizeof(u32));
+	if (len < 0 || len > 4 || len == 3) {
+		dev_err(dev, "invalid data lane size: %d\n", len);
+		return -EINVAL;
+	}
+
+	mtk_dp->max_lanes = len;
+
+	ret = device_property_read_u32(dev, "max-linkrate-mhz", &linkrate);
+	if (ret) {
+		dev_err(dev, "failed to read max linkrate: %d\n", ret);
+		return ret;
+	}
+
+	mtk_dp->max_linkrate = drm_dp_link_rate_to_bw_code(linkrate * 100);
+
+	return 0;
+}
+
+static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
+				    struct drm_connector *connector)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	bool enabled = mtk_dp->enabled;
+	struct edid *new_edid = NULL;
+
+	if (!enabled) {
+		drm_bridge_chain_pre_enable(bridge);
+
+		/* power on aux */
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+				   DP_PWR_STATE_BANDGAP_TPLL_LANE,
+				   DP_PWR_STATE_MASK);
+
+		/* power on panel */
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+		usleep_range(2000, 5000);
+	}
+
+	new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
+
+	/*
+	 * Parse capability here to let atomic_get_input_bus_fmts and
+	 * mode_valid use the capability to calculate sink bitrates.
+	 */
+	if (mtk_dp_parse_capabilities(mtk_dp)) {
+		drm_err(mtk_dp->drm_dev, "Can't parse capabilities\n");
+		new_edid = NULL;
+	}
+
+	if (!enabled) {
+		/* power off panel */
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
+		usleep_range(2000, 3000);
+
+		/* power off aux */
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+				   DP_PWR_STATE_BANDGAP_TPLL,
+				   DP_PWR_STATE_MASK);
+
+		drm_bridge_chain_post_disable(bridge);
+	}
+
+	return new_edid;
+}
+
+static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
+				   struct drm_dp_aux_msg *msg)
+{
+	struct mtk_dp *mtk_dp;
+	bool is_read;
+	u8 request;
+	size_t accessed_bytes = 0;
+	int ret;
+
+	mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
+
+	if (!mtk_dp->train_info.cable_plugged_in) {
+		ret = -EAGAIN;
+		goto err;
+	}
+
+	switch (msg->request) {
+	case DP_AUX_I2C_MOT:
+	case DP_AUX_I2C_WRITE:
+	case DP_AUX_NATIVE_WRITE:
+	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
+	case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
+		request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
+		is_read = false;
+		break;
+	case DP_AUX_I2C_READ:
+	case DP_AUX_NATIVE_READ:
+	case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
+		request = msg->request;
+		is_read = true;
+		break;
+	default:
+		drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
+			msg->request);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	do {
+		size_t to_access = min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
+					 msg->size - accessed_bytes);
+
+		ret = mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
+					     msg->address + accessed_bytes,
+					     msg->buffer + accessed_bytes,
+					     to_access);
+
+		if (ret) {
+			drm_info(mtk_dp->drm_dev,
+				 "Failed to do AUX transfer: %d\n", ret);
+			goto err;
+		}
+		accessed_bytes += to_access;
+	} while (accessed_bytes < msg->size);
+
+	msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
+	return msg->size;
+err:
+	msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
+	return ret;
+}
+
+static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
+{
+	int ret;
+
+	ret = phy_init(mtk_dp->phy);
+	if (ret) {
+		dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
+		return ret;
+	}
+
+	mtk_dp_init_port(mtk_dp);
+	mtk_dp_power_enable(mtk_dp);
+
+	return 0;
+}
+
+static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_power_disable(mtk_dp);
+	phy_exit(mtk_dp->phy);
+}
+
+static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
+				enum drm_bridge_attach_flags flags)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	int ret;
+
+	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+		dev_err(mtk_dp->dev, "Driver does not provide a connector!");
+		return -EINVAL;
+	}
+
+	mtk_dp->aux.drm_dev = bridge->dev;
+	ret = drm_dp_aux_register(&mtk_dp->aux);
+	if (ret) {
+		dev_err(mtk_dp->dev,
+			"failed to register DP AUX channel: %d\n", ret);
+		return ret;
+	}
+
+	ret = mtk_dp_poweron(mtk_dp);
+	if (ret)
+		goto err_aux_register;
+
+	if (mtk_dp->next_bridge) {
+		ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
+					&mtk_dp->bridge, flags);
+		if (ret) {
+			drm_warn(mtk_dp->drm_dev,
+				 "Failed to attach external bridge: %d\n", ret);
+			goto err_bridge_attach;
+		}
+	}
+
+	mtk_dp->drm_dev = bridge->dev;
+
+	mtk_dp_hwirq_enable(mtk_dp, true);
+
+	return 0;
+
+err_bridge_attach:
+	mtk_dp_poweroff(mtk_dp);
+err_aux_register:
+	drm_dp_aux_unregister(&mtk_dp->aux);
+	return ret;
+}
+
+static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+
+	mtk_dp_hwirq_enable(mtk_dp, false);
+	mtk_dp->drm_dev = NULL;
+	mtk_dp_poweroff(mtk_dp);
+	drm_dp_aux_unregister(&mtk_dp->aux);
+}
+
+static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
+					struct drm_bridge_state *old_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	int ret;
+
+	mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
+								bridge->encoder);
+	if (!mtk_dp->conn) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as connector is missing\n");
+		return;
+	}
+
+	/* power on aux */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL_LANE,
+			   DP_PWR_STATE_MASK);
+
+	if (mtk_dp->train_info.cable_plugged_in) {
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+		usleep_range(2000, 5000);
+	}
+
+	/* Training */
+	ret = mtk_dp_training(mtk_dp);
+	if (ret) {
+		drm_err(mtk_dp->drm_dev, "Training failed, %d\n", ret);
+		goto power_off_aux;
+	}
+
+	ret = mtk_dp_video_config(mtk_dp);
+	if (ret)
+		goto power_off_aux;
+
+	mtk_dp_video_enable(mtk_dp, true);
+
+	mtk_dp->enabled = true;
+
+	return;
+power_off_aux:
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL,
+			   DP_PWR_STATE_MASK);
+}
+
+static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
+					 struct drm_bridge_state *old_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+
+	mtk_dp->enabled = false;
+	mtk_dp_video_enable(mtk_dp, false);
+
+	if (mtk_dp->train_info.cable_plugged_in) {
+		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
+		usleep_range(2000, 3000);
+	}
+
+	/* power off aux */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+			   DP_PWR_STATE_BANDGAP_TPLL,
+			   DP_PWR_STATE_MASK);
+
+	/* Ensure the sink is muted */
+	msleep(20);
+}
+
+static enum drm_mode_status
+mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
+			 const struct drm_display_info *info,
+			 const struct drm_display_mode *mode)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	u32 bpp = info->color_formats & DRM_COLOR_FORMAT_YCBCR422 ? 16 : 24;
+	u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
+			      drm_dp_max_lane_count(mtk_dp->rx_cap),
+			 drm_dp_bw_code_to_link_rate(mtk_dp->max_linkrate) *
+			 mtk_dp->max_lanes);
+
+	if (rate < mode->clock * bpp / 8)
+		return MODE_CLOCK_HIGH;
+
+	return MODE_OK;
+}
+
+static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
+						     struct drm_bridge_state *bridge_state,
+						     struct drm_crtc_state *crtc_state,
+						     struct drm_connector_state *conn_state,
+						     unsigned int *num_output_fmts)
+{
+	u32 *output_fmts;
+
+	*num_output_fmts = 0;
+	output_fmts = kmalloc(sizeof(*output_fmts), GFP_KERNEL);
+	if (!output_fmts)
+		return NULL;
+	*num_output_fmts = 1;
+	output_fmts[0] = MEDIA_BUS_FMT_FIXED;
+	return output_fmts;
+}
+
+static const u32 mt8195_input_fmts[] = {
+	MEDIA_BUS_FMT_RGB888_1X24,
+	MEDIA_BUS_FMT_YUV8_1X24,
+	MEDIA_BUS_FMT_YUYV8_1X16,
+};
+
+static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+						    struct drm_bridge_state *bridge_state,
+						    struct drm_crtc_state *crtc_state,
+						    struct drm_connector_state *conn_state,
+						    u32 output_fmt,
+						    unsigned int *num_input_fmts)
+{
+	u32 *input_fmts;
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+	struct drm_display_info *display_info =
+		&conn_state->connector->display_info;
+	u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
+			      drm_dp_max_lane_count(mtk_dp->rx_cap),
+			 drm_dp_bw_code_to_link_rate(mtk_dp->max_linkrate) *
+			 mtk_dp->max_lanes);
+
+	*num_input_fmts = 0;
+
+	/*
+	 * If the linkrate is smaller than datarate of RGB888, larger than
+	 * datarate of YUV422 and sink device supports YUV422, we output YUV422
+	 * format. Use this condition, we can support more resolution.
+	 */
+	if ((rate < (mode->clock * 24 / 8)) &&
+	    (rate > (mode->clock * 16 / 8)) &&
+	    (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) {
+		input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL);
+		if (!input_fmts)
+			return NULL;
+		*num_input_fmts = 1;
+		input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
+	} else {
+		input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts),
+				     sizeof(*input_fmts),
+				     GFP_KERNEL);
+		if (!input_fmts)
+			return NULL;
+
+		*num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
+		memcpy(input_fmts, mt8195_input_fmts, sizeof(mt8195_input_fmts));
+	}
+
+	return input_fmts;
+}
+
+static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
+				      struct drm_bridge_state *bridge_state,
+				      struct drm_crtc_state *crtc_state,
+				      struct drm_connector_state *conn_state)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	struct drm_crtc *crtc = conn_state->crtc;
+	unsigned int input_bus_format;
+
+	input_bus_format = bridge_state->input_bus_cfg.format;
+
+	dev_dbg(mtk_dp->dev, "input format 0x%04x, output format 0x%04x\n",
+		bridge_state->input_bus_cfg.format,
+		 bridge_state->output_bus_cfg.format);
+
+	if (input_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
+		mtk_dp->info.format = DP_PIXELFORMAT_YUV422;
+	else
+		mtk_dp->info.format = DP_PIXELFORMAT_RGB;
+
+	if (!crtc) {
+		drm_err(mtk_dp->drm_dev,
+			"Can't enable bridge as connector state doesn't have a crtc\n");
+		return -EINVAL;
+	}
+
+	drm_display_mode_to_videomode(&crtc_state->adjusted_mode, &mtk_dp->info.vm);
+
+	return 0;
+}
+
+static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
+	.atomic_check = mtk_dp_bridge_atomic_check,
+	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+	.atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts,
+	.atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts,
+	.atomic_reset = drm_atomic_helper_bridge_reset,
+	.attach = mtk_dp_bridge_attach,
+	.detach = mtk_dp_bridge_detach,
+	.atomic_enable = mtk_dp_bridge_atomic_enable,
+	.atomic_disable = mtk_dp_bridge_atomic_disable,
+	.mode_valid = mtk_dp_bridge_mode_valid,
+	.get_edid = mtk_dp_get_edid,
+};
+
+static int mtk_dp_probe(struct platform_device *pdev)
+{
+	struct mtk_dp *mtk_dp;
+	struct device *dev = &pdev->dev;
+	int ret, irq_num;
+
+	mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
+	if (!mtk_dp)
+		return -ENOMEM;
+
+	mtk_dp->dev = dev;
+
+	irq_num = platform_get_irq(pdev, 0);
+	if (irq_num < 0)
+		return dev_err_probe(dev, irq_num,
+				     "failed to request dp irq resource\n");
+
+	mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
+	if (IS_ERR(mtk_dp->next_bridge))
+		return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge),
+				     "Failed to get bridge\n");
+
+	ret = mtk_dp_dt_parse(mtk_dp, pdev);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to parse dt\n");
+
+	drm_dp_aux_init(&mtk_dp->aux);
+	mtk_dp->aux.name = "aux_mtk_dp";
+	mtk_dp->aux.transfer = mtk_dp_aux_transfer;
+
+	spin_lock_init(&mtk_dp->irq_thread_lock);
+
+	ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
+					mtk_dp_hpd_event_thread,
+					IRQ_TYPE_LEVEL_HIGH, dev_name(dev),
+					mtk_dp);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "failed to request mediatek dptx irq\n");
+
+	platform_set_drvdata(pdev, mtk_dp);
+
+	mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
+							PLATFORM_DEVID_AUTO,
+							&mtk_dp->regs,
+							sizeof(struct regmap *));
+	if (IS_ERR(mtk_dp->phy_dev))
+		return dev_err_probe(dev, PTR_ERR(mtk_dp->phy_dev),
+				     "Failed to create device mediatek-dp-phy\n");
+
+	mtk_dp_get_calibration_data(mtk_dp);
+
+	mtk_dp->phy = devm_phy_get(&mtk_dp->phy_dev->dev, "dp");
+
+	if (IS_ERR(mtk_dp->phy)) {
+		platform_device_unregister(mtk_dp->phy_dev);
+		return dev_err_probe(dev, PTR_ERR(mtk_dp->phy),
+				     "Failed to get phy\n");
+	}
+
+	mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
+	mtk_dp->bridge.of_node = dev->of_node;
+
+	mtk_dp->bridge.ops =
+		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
+	mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
+
+	drm_bridge_add(&mtk_dp->bridge);
+
+	pm_runtime_enable(dev);
+	pm_runtime_get_sync(dev);
+
+	return 0;
+}
+
+static int mtk_dp_remove(struct platform_device *pdev)
+{
+	struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
+
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	drm_bridge_remove(&mtk_dp->bridge);
+	platform_device_unregister(mtk_dp->phy_dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_dp_suspend(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_power_disable(mtk_dp);
+	mtk_dp_hwirq_enable(mtk_dp, false);
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static int mtk_dp_resume(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	pm_runtime_get_sync(dev);
+	mtk_dp_init_port(mtk_dp);
+	mtk_dp_hwirq_enable(mtk_dp, true);
+	mtk_dp_power_enable(mtk_dp);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
+
+static const struct of_device_id mtk_dp_of_match[] = {
+	{ .compatible = "mediatek,mt8195-edp-tx" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
+
+struct platform_driver mtk_dp_driver = {
+	.probe = mtk_dp_probe,
+	.remove = mtk_dp_remove,
+	.driver = {
+		.name = "mediatek-drm-dp",
+		.of_match_table = mtk_dp_of_match,
+		.pm = &mtk_dp_pm_ops,
+	},
+};
+
+module_platform_driver(mtk_dp_driver);
+
+MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
+MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
+MODULE_AUTHOR("Bo-Chen Chen <rex-bc.chen@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
new file mode 100644
index 000000000000..3f01ba44871f
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
@@ -0,0 +1,305 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019-2022 MediaTek Inc.
+ * Copyright (c) 2022 BayLibre
+ */
+#ifndef _MTK_DP_REG_H_
+#define _MTK_DP_REG_H_
+
+#define SEC_OFFSET	0x4000
+
+#define MTK_DP_HPD_DISCONNECT		BIT(1)
+#define MTK_DP_HPD_CONNECT		BIT(2)
+#define MTK_DP_HPD_INTERRUPT		BIT(3)
+
+/* offset: 0x0 */
+#define DP_PHY_GLB_BIAS_GEN_00		0x0
+#define RG_XTP_GLB_BIAS_INTR_CTRL		GENMASK(20, 16)
+#define DP_PHY_GLB_DPAUX_TX		0x8
+#define RG_CKM_PT0_CKTX_IMPSEL			GENMASK(23, 20)
+#define MTK_DP_0034			0x34
+#define DA_XTP_GLB_CKDET_EN_FORCE_VAL		BIT(15)
+#define DA_XTP_GLB_CKDET_EN_FORCE_EN		BIT(14)
+#define DA_CKM_INTCKTX_EN_FORCE_VAL		BIT(13)
+#define DA_CKM_INTCKTX_EN_FORCE_EN		BIT(12)
+#define DA_CKM_CKTX0_EN_FORCE_VAL		BIT(11)
+#define DA_CKM_CKTX0_EN_FORCE_EN		BIT(10)
+#define DA_CKM_XTAL_CK_FORCE_VAL		BIT(9)
+#define DA_CKM_XTAL_CK_FORCE_EN			BIT(8)
+#define DA_CKM_BIAS_LPF_EN_FORCE_VAL		BIT(7)
+#define DA_CKM_BIAS_LPF_EN_FORCE_EN		BIT(6)
+#define DA_CKM_BIAS_EN_FORCE_VAL		BIT(5)
+#define DA_CKM_BIAS_EN_FORCE_EN			BIT(4)
+#define DA_XTP_GLB_AVD10_ON_FORCE_VAL		BIT(3)
+#define DA_XTP_GLB_AVD10_ON_FORCE		BIT(2)
+#define DA_XTP_GLB_LDO_EN_FORCE_VAL		BIT(1)
+#define DA_XTP_GLB_LDO_EN_FORCE_EN		BIT(0)
+#define DP_PHY_LANE_TX_0		0x104
+#define RG_XTP_LN0_TX_IMPSEL_PMOS		GENMASK(15, 12)
+#define RG_XTP_LN0_TX_IMPSEL_NMOS		GENMASK(19, 16)
+#define DP_PHY_LANE_TX_1		0x204
+#define RG_XTP_LN1_TX_IMPSEL_PMOS		GENMASK(15, 12)
+#define RG_XTP_LN1_TX_IMPSEL_NMOS		GENMASK(19, 16)
+#define DP_PHY_LANE_TX_2		0x304
+#define RG_XTP_LN2_TX_IMPSEL_PMOS		GENMASK(15, 12)
+#define RG_XTP_LN2_TX_IMPSEL_NMOS		GENMASK(19, 16)
+#define DP_PHY_LANE_TX_3		0x404
+#define RG_XTP_LN3_TX_IMPSEL_PMOS		GENMASK(15, 12)
+#define RG_XTP_LN3_TX_IMPSEL_NMOS		GENMASK(19, 16)
+#define MTK_DP_1040			0x1040
+#define RG_DPAUX_RX_VALID_DEGLITCH_EN		BIT(2)
+#define RG_XTP_GLB_CKDET_EN			BIT(1)
+#define RG_DPAUX_RX_EN				BIT(0)
+
+/* offset: TOP_OFFSET (0x2000) */
+#define MTK_DP_TOP_PWR_STATE		0x2000
+#define DP_PWR_STATE_MASK			GENMASK(1, 0)
+#define DP_PWR_STATE_BANDGAP			BIT(0)
+#define DP_PWR_STATE_BANDGAP_TPLL		BIT(1)
+#define DP_PWR_STATE_BANDGAP_TPLL_LANE		GENMASK(1, 0)
+#define MTK_DP_TOP_SWING_EMP		0x2004
+#define DP_TX0_VOLT_SWING_MASK			GENMASK(1, 0)
+#define DP_TX0_VOLT_SWING_SHIFT			0
+#define DP_TX0_PRE_EMPH_MASK			GENMASK(3, 2)
+#define DP_TX0_PRE_EMPH_SHIFT			2
+#define DP_TX1_VOLT_SWING_MASK			GENMASK(9, 8)
+#define DP_TX1_VOLT_SWING_SHIFT			8
+#define DP_TX1_PRE_EMPH_MASK			GENMASK(11, 10)
+#define DP_TX2_VOLT_SWING_MASK			GENMASK(17, 16)
+#define DP_TX2_PRE_EMPH_MASK			GENMASK(19, 18)
+#define DP_TX3_VOLT_SWING_MASK			GENMASK(25, 24)
+#define DP_TX3_PRE_EMPH_MASK			GENMASK(27, 26)
+#define MTK_DP_TOP_RESET_AND_PROBE	0x2020
+#define SW_RST_B_PHYD				BIT(4)
+#define MTK_DP_TOP_IRQ_MASK		0x202c
+#define IRQ_MASK_AUX_TOP_IRQ			BIT(2)
+#define MTK_DP_TOP_MEM_PD		0x2038
+#define MEM_ISO_EN				BIT(0)
+#define FUSE_SEL				BIT(2)
+
+/* offset: ENC0_OFFSET (0x3000) */
+#define MTK_DP_ENC0_P0_3000			0x3000
+#define LANE_NUM_DP_ENC0_P0_MASK			GENMASK(1, 0)
+#define VIDEO_MUTE_SW_DP_ENC0_P0			BIT(2)
+#define VIDEO_MUTE_SEL_DP_ENC0_P0			BIT(3)
+#define ENHANCED_FRAME_EN_DP_ENC0_P0			BIT(4)
+#define MTK_DP_ENC0_P0_3004			0x3004
+#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK		BIT(8)
+#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0		BIT(9)
+#define MTK_DP_ENC0_P0_3010			0x3010
+#define HTOTAL_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3014			0x3014
+#define VTOTAL_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3018			0x3018
+#define HSTART_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_301C			0x301c
+#define VSTART_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3020			0x3020
+#define HWIDTH_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3024			0x3024
+#define VHEIGHT_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3028			0x3028
+#define HSW_SW_DP_ENC0_P0_MASK				GENMASK(14, 0)
+#define HSP_SW_DP_ENC0_P0_MASK				BIT(15)
+#define MTK_DP_ENC0_P0_302C			0x302c
+#define VSW_SW_DP_ENC0_P0_MASK				GENMASK(14, 0)
+#define VSP_SW_DP_ENC0_P0_MASK				BIT(15)
+#define MTK_DP_ENC0_P0_3030			0x3030
+#define HTOTAL_SEL_DP_ENC0_P0				BIT(0)
+#define VTOTAL_SEL_DP_ENC0_P0				BIT(1)
+#define HSTART_SEL_DP_ENC0_P0				BIT(2)
+#define VSTART_SEL_DP_ENC0_P0				BIT(3)
+#define HWIDTH_SEL_DP_ENC0_P0				BIT(4)
+#define VHEIGHT_SEL_DP_ENC0_P0				BIT(5)
+#define HSP_SEL_DP_ENC0_P0				BIT(6)
+#define HSW_SEL_DP_ENC0_P0				BIT(7)
+#define VSP_SEL_DP_ENC0_P0				BIT(8)
+#define VSW_SEL_DP_ENC0_P0				BIT(9)
+#define MTK_DP_ENC0_P0_3034			0x3034
+#define MTK_DP_ENC0_P0_3038			0x3038
+#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK		BIT(11)
+#define MTK_DP_ENC0_P0_303C			0x303c
+#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK		GENMASK(5, 0)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK		GENMASK(10, 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT		(0 << 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT		(1 << 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT		(2 << 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT		(3 << 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT		(4 << 8)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK		GENMASK(14, 12)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB		(0 << 12)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422		(1 << 12)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420		(2 << 12)
+#define VIDEO_MN_GEN_EN_DP_ENC0_P0			BIT(15)
+#define MTK_DP_ENC0_P0_3040			0x3040
+#define SDP_DOWN_CNT_DP_ENC0_P0_VAL			0x20
+#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK		GENMASK(11, 0)
+#define MTK_DP_ENC0_P0_304C			0x304c
+#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK			BIT(2)
+#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK		BIT(8)
+#define MTK_DP_ENC0_P0_3064			0x3064
+#define HDE_NUM_LAST_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3154			0x3154
+#define PGEN_HTOTAL_DP_ENC0_P0_MASK			GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_3158			0x3158
+#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK		GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_315C			0x315c
+#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK		GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_3160			0x3160
+#define PGEN_HFDE_START_DP_ENC0_P0_MASK			GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_3164			0x3164
+#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK		GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_3168			0x3168
+#define PGEN_VTOTAL_DP_ENC0_P0_MASK			GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_316C			0x316c
+#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK		GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_3170			0x3170
+#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK		GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_3174			0x3174
+#define PGEN_VFDE_START_DP_ENC0_P0_MASK			GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_3178			0x3178
+#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK		GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_31B0			0x31b0
+#define PGEN_PATTERN_SEL_VAL				4
+#define PGEN_PATTERN_SEL_MASK				GENMASK(6, 4)
+#define MTK_DP_ENC0_P0_31EC			0x31ec
+#define AUDIO_CH_SRC_SEL_DP_ENC0_P0			BIT(4)
+#define ISRC1_HB3_DP_ENC0_P0_MASK			GENMASK(15, 8)
+
+/* offset: ENC1_OFFSET (0x3200) */
+#define MTK_DP_ENC1_P0_3300			0x3300
+#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL		2
+#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK		GENMASK(9, 8)
+#define MTK_DP_ENC1_P0_3364			0x3364
+#define SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL		0x20
+#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK	GENMASK(11, 0)
+#define FIFO_READ_START_POINT_DP_ENC1_P0_VAL		4
+#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK		GENMASK(15, 12)
+#define MTK_DP_ENC1_P0_3368			0x3368
+#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0	BIT(0)
+#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0		BIT(4)
+#define SDP_DP13_EN_DP_ENC1_P0				BIT(8)
+#define BS2BS_MODE_DP_ENC1_P0				BIT(12)
+#define BS2BS_MODE_DP_ENC1_P0_MASK			GENMASK(13, 12)
+#define BS2BS_MODE_DP_ENC1_P0_VAL			1
+#define DP_ENC1_P0_3368_VAL				(VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0 | \
+							 VIDEO_STABLE_CNT_THRD_DP_ENC1_P0 | \
+							 SDP_DP13_EN_DP_ENC1_P0 | \
+							 BS2BS_MODE_DP_ENC1_P0)
+
+/* offset: TRANS_OFFSET (0x3400) */
+#define MTK_DP_TRANS_P0_3400				0x3400
+#define PATTERN1_EN_DP_TRANS_P0_MASK				BIT(12)
+#define PATTERN2_EN_DP_TRANS_P0_MASK				BIT(13)
+#define PATTERN3_EN_DP_TRANS_P0_MASK				BIT(14)
+#define PATTERN4_EN_DP_TRANS_P0_MASK				BIT(15)
+#define MTK_DP_TRANS_P0_3404				0x3404
+#define DP_SCR_EN_DP_TRANS_P0_MASK				BIT(0)
+#define MTK_DP_TRANS_P0_340C				0x340c
+#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0		BIT(13)
+#define MTK_DP_TRANS_P0_3410				0x3410
+#define HPD_DEB_THD_DP_TRANS_P0_MASK				GENMASK(3, 0)
+#define HPD_INT_THD_DP_TRANS_P0_MASK				GENMASK(7, 4)
+#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US			(2 << 4)
+#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US			(2 << 6)
+#define HPD_DISC_THD_DP_TRANS_P0_MASK				GENMASK(11, 8)
+#define HPD_CONN_THD_DP_TRANS_P0_MASK				GENMASK(15, 12)
+#define MTK_DP_TRANS_P0_3414				0x3414
+#define HPD_DB_DP_TRANS_P0_MASK					BIT(2)
+#define MTK_DP_TRANS_P0_3418				0x3418
+#define IRQ_CLR_DP_TRANS_P0_MASK				GENMASK(3, 0)
+#define IRQ_MASK_DP_TRANS_P0_MASK				GENMASK(7, 4)
+#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ				(BIT(1) << 4)
+#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ				(BIT(2) << 4)
+#define IRQ_MASK_DP_TRANS_P0_INT_IRQ				(BIT(3) << 4)
+#define IRQ_STATUS_DP_TRANS_P0_MASK				GENMASK(15, 12)
+#define MTK_DP_TRANS_P0_342C				0x342c
+#define XTAL_FREQ_DP_TRANS_P0_DEFAULT				(BIT(0) | BIT(3) | BIT(5) | BIT(6))
+#define XTAL_FREQ_DP_TRANS_P0_MASK				GENMASK(7, 0)
+#define MTK_DP_TRANS_P0_3430				0x3430
+#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK			GENMASK(1, 0)
+#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT		BIT(1)
+#define MTK_DP_TRANS_P0_34A4				0x34a4
+#define LANE_NUM_DP_TRANS_P0_MASK				GENMASK(3, 2)
+#define MTK_DP_TRANS_P0_3540				0x3540
+#define FEC_EN_DP_TRANS_P0_MASK					BIT(0)
+#define FEC_CLOCK_EN_MODE_DP_TRANS_P0				BIT(3)
+#define MTK_DP_TRANS_P0_3580				0x3580
+#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK		BIT(8)
+#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK		BIT(9)
+#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK		BIT(10)
+#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK		BIT(11)
+#define MTK_DP_TRANS_P0_35C8				0x35c8
+#define SW_IRQ_CLR_DP_TRANS_P0_MASK				GENMASK(15, 0)
+#define SW_IRQ_STATUS_DP_TRANS_P0_MASK				GENMASK(15, 0)
+#define MTK_DP_TRANS_P0_35D0				0x35d0
+#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_TRANS_P0_35F0				0x35f0
+#define DP_TRANS_DUMMY_RW_0					BIT(3)
+#define DP_TRANS_DUMMY_RW_0_MASK				GENMASK(3, 2)
+
+/* offset: AUX_OFFSET (0x3600) */
+#define MTK_DP_AUX_P0_360C			0x360c
+#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK			GENMASK(12, 0)
+#define AUX_TIMEOUT_THR_AUX_TX_P0_VAL			0x1595
+#define MTK_DP_AUX_P0_3614			0x3614
+#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK		GENMASK(6, 0)
+#define AUX_RX_UI_CNT_THR_AUX_FOR_26M			13
+#define MTK_DP_AUX_P0_3618			0x3618
+#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK			BIT(9)
+#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK	GENMASK(3, 0)
+#define MTK_DP_AUX_P0_3620			0x3620
+#define AUX_RD_MODE_AUX_TX_P0_MASK			BIT(9)
+#define AUX_RX_FIFO_READ_PULSE_TX_P0			BIT(8)
+#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK		GENMASK(7, 0)
+#define MTK_DP_AUX_P0_3624			0x3624
+#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK		GENMASK(3, 0)
+#define MTK_DP_AUX_P0_3628			0x3628
+#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK			GENMASK(9, 0)
+#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE		BIT(0)
+#define MTK_DP_AUX_P0_362C			0x362c
+#define AUX_NO_LENGTH_AUX_TX_P0				BIT(0)
+#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK		BIT(1)
+#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK		GENMASK(15, 2)
+#define MTK_DP_AUX_P0_3630			0x3630
+#define AUX_TX_REQUEST_READY_AUX_TX_P0			BIT(3)
+#define MTK_DP_AUX_P0_3634			0x3634
+#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK		GENMASK(15, 8)
+#define AUX_TX_OVER_SAMPLE_RATE_FOR_26M			25
+#define MTK_DP_AUX_P0_3640			0x3640
+#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(6)
+#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(5)
+#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(4)
+#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0			BIT(3)
+#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0			BIT(2)
+#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0			BIT(1)
+#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0			BIT(0)
+#define DP_AUX_P0_3640_VAL				(AUX_400US_TIMEOUT_IRQ_AUX_TX_P0 | \
+							 AUX_RX_DATA_RECV_IRQ_AUX_TX_P0 | \
+							 AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0 | \
+							 AUX_RX_CMD_RECV_IRQ_AUX_TX_P0 | \
+							 AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0 | \
+							 AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0 | \
+							 AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
+#define MTK_DP_AUX_P0_3644			0x3644
+#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK		GENMASK(3, 0)
+#define MTK_DP_AUX_P0_3648			0x3648
+#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK		GENMASK(15, 0)
+#define MTK_DP_AUX_P0_364C			0x364c
+#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK		GENMASK(3, 0)
+#define MTK_DP_AUX_P0_3650			0x3650
+#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK			GENMASK(15, 12)
+#define PHY_FIFO_RST_AUX_TX_P0_MASK			BIT(9)
+#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0			BIT(8)
+#define MTK_DP_AUX_P0_3658			0x3658
+#define AUX_TX_OV_EN_AUX_TX_P0_MASK			BIT(0)
+#define MTK_DP_AUX_P0_3690			0x3690
+#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0		BIT(8)
+#define MTK_DP_AUX_P0_3704			0x3704
+#define AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK	BIT(1)
+#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0		BIT(2)
+#define MTK_DP_AUX_P0_3708			0x3708
+#define MTK_DP_AUX_P0_37C8			0x37c8
+#define MTK_ATOP_EN_AUX_TX_P0				BIT(0)
+
+#endif /*_MTK_DP_REG_H_*/
-- 
2.18.0


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

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

* [PATCH v17 04/10] drm/mediatek: dp: Add multiple bridge types support
  2022-09-01  4:41 ` Bo-Chen Chen
  (?)
@ 2022-09-01  4:41   ` Bo-Chen Chen
  -1 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

The bridge types of eDP and DP are different. We add device data to
this driver and add bridge_type to the device data to define them.

Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index e2ec9b02b1aa..2696c1ac1a47 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -101,6 +101,7 @@ struct mtk_dp {
 	struct drm_device *drm_dev;
 	struct drm_dp_aux aux;
 
+	const struct mtk_dp_data *data;
 	struct mtk_dp_info info;
 	struct mtk_dp_train_info train_info;
 
@@ -109,6 +110,9 @@ struct mtk_dp {
 	struct regmap *regs;
 };
 
+struct mtk_dp_data {
+	int bridge_type;
+};
 static const struct mtk_dp_efuse_fmt mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
 	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
 		.idx = 3,
@@ -1871,6 +1875,7 @@ static int mtk_dp_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	mtk_dp->dev = dev;
+	mtk_dp->data = (struct mtk_dp_data *)of_device_get_match_data(dev);
 
 	irq_num = platform_get_irq(pdev, 0);
 	if (irq_num < 0)
@@ -1925,7 +1930,7 @@ static int mtk_dp_probe(struct platform_device *pdev)
 
 	mtk_dp->bridge.ops =
 		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
-	mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
+	mtk_dp->bridge.type = mtk_dp->data->bridge_type;
 
 	drm_bridge_add(&mtk_dp->bridge);
 
@@ -1974,8 +1979,15 @@ static int mtk_dp_resume(struct device *dev)
 
 static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
 
+static const struct mtk_dp_data mt8195_edp_data = {
+	.bridge_type = DRM_MODE_CONNECTOR_eDP,
+};
+
 static const struct of_device_id mtk_dp_of_match[] = {
-	{ .compatible = "mediatek,mt8195-edp-tx" },
+	{
+		.compatible = "mediatek,mt8195-edp-tx",
+		.data = &mt8195_edp_data,
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
-- 
2.18.0


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

* [PATCH v17 04/10] drm/mediatek: dp: Add multiple bridge types support
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen,
	linux-mediatek, wenst, linux-arm-kernel,
	angelogioacchino.delregno

The bridge types of eDP and DP are different. We add device data to
this driver and add bridge_type to the device data to define them.

Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index e2ec9b02b1aa..2696c1ac1a47 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -101,6 +101,7 @@ struct mtk_dp {
 	struct drm_device *drm_dev;
 	struct drm_dp_aux aux;
 
+	const struct mtk_dp_data *data;
 	struct mtk_dp_info info;
 	struct mtk_dp_train_info train_info;
 
@@ -109,6 +110,9 @@ struct mtk_dp {
 	struct regmap *regs;
 };
 
+struct mtk_dp_data {
+	int bridge_type;
+};
 static const struct mtk_dp_efuse_fmt mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
 	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
 		.idx = 3,
@@ -1871,6 +1875,7 @@ static int mtk_dp_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	mtk_dp->dev = dev;
+	mtk_dp->data = (struct mtk_dp_data *)of_device_get_match_data(dev);
 
 	irq_num = platform_get_irq(pdev, 0);
 	if (irq_num < 0)
@@ -1925,7 +1930,7 @@ static int mtk_dp_probe(struct platform_device *pdev)
 
 	mtk_dp->bridge.ops =
 		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
-	mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
+	mtk_dp->bridge.type = mtk_dp->data->bridge_type;
 
 	drm_bridge_add(&mtk_dp->bridge);
 
@@ -1974,8 +1979,15 @@ static int mtk_dp_resume(struct device *dev)
 
 static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
 
+static const struct mtk_dp_data mt8195_edp_data = {
+	.bridge_type = DRM_MODE_CONNECTOR_eDP,
+};
+
 static const struct of_device_id mtk_dp_of_match[] = {
-	{ .compatible = "mediatek,mt8195-edp-tx" },
+	{
+		.compatible = "mediatek,mt8195-edp-tx",
+		.data = &mt8195_edp_data,
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
-- 
2.18.0


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

* [PATCH v17 04/10] drm/mediatek: dp: Add multiple bridge types support
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

The bridge types of eDP and DP are different. We add device data to
this driver and add bridge_type to the device data to define them.

Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index e2ec9b02b1aa..2696c1ac1a47 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -101,6 +101,7 @@ struct mtk_dp {
 	struct drm_device *drm_dev;
 	struct drm_dp_aux aux;
 
+	const struct mtk_dp_data *data;
 	struct mtk_dp_info info;
 	struct mtk_dp_train_info train_info;
 
@@ -109,6 +110,9 @@ struct mtk_dp {
 	struct regmap *regs;
 };
 
+struct mtk_dp_data {
+	int bridge_type;
+};
 static const struct mtk_dp_efuse_fmt mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
 	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
 		.idx = 3,
@@ -1871,6 +1875,7 @@ static int mtk_dp_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	mtk_dp->dev = dev;
+	mtk_dp->data = (struct mtk_dp_data *)of_device_get_match_data(dev);
 
 	irq_num = platform_get_irq(pdev, 0);
 	if (irq_num < 0)
@@ -1925,7 +1930,7 @@ static int mtk_dp_probe(struct platform_device *pdev)
 
 	mtk_dp->bridge.ops =
 		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
-	mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
+	mtk_dp->bridge.type = mtk_dp->data->bridge_type;
 
 	drm_bridge_add(&mtk_dp->bridge);
 
@@ -1974,8 +1979,15 @@ static int mtk_dp_resume(struct device *dev)
 
 static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
 
+static const struct mtk_dp_data mt8195_edp_data = {
+	.bridge_type = DRM_MODE_CONNECTOR_eDP,
+};
+
 static const struct of_device_id mtk_dp_of_match[] = {
-	{ .compatible = "mediatek,mt8195-edp-tx" },
+	{
+		.compatible = "mediatek,mt8195-edp-tx",
+		.data = &mt8195_edp_data,
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
-- 
2.18.0


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

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

* [PATCH v17 05/10] drm/mediatek: dp: Add multiple smc commands support
  2022-09-01  4:41 ` Bo-Chen Chen
  (?)
@ 2022-09-01  4:41   ` Bo-Chen Chen
  -1 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

The smc commands of eDP and DP are different. We add smc_cmd to the
device data to define them.

Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index 2696c1ac1a47..971bd744cdb2 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -112,6 +112,7 @@ struct mtk_dp {
 
 struct mtk_dp_data {
 	int bridge_type;
+	unsigned int smc_cmd;
 };
 static const struct mtk_dp_efuse_fmt mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
 	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
@@ -945,11 +946,11 @@ static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
 			   VIDEO_MUTE_SW_DP_ENC0_P0);
 
 	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32,
-		      MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable,
+		      mtk_dp->data->smc_cmd, enable,
 		      0, 0, 0, 0, 0, &res);
 
 	dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
-		MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable, res.a0, res.a1);
+		mtk_dp->data->smc_cmd, enable, res.a0, res.a1);
 }
 
 static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
@@ -1981,6 +1982,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
 
 static const struct mtk_dp_data mt8195_edp_data = {
 	.bridge_type = DRM_MODE_CONNECTOR_eDP,
+	.smc_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
 };
 
 static const struct of_device_id mtk_dp_of_match[] = {
-- 
2.18.0


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

* [PATCH v17 05/10] drm/mediatek: dp: Add multiple smc commands support
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen,
	linux-mediatek, wenst, linux-arm-kernel,
	angelogioacchino.delregno

The smc commands of eDP and DP are different. We add smc_cmd to the
device data to define them.

Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index 2696c1ac1a47..971bd744cdb2 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -112,6 +112,7 @@ struct mtk_dp {
 
 struct mtk_dp_data {
 	int bridge_type;
+	unsigned int smc_cmd;
 };
 static const struct mtk_dp_efuse_fmt mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
 	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
@@ -945,11 +946,11 @@ static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
 			   VIDEO_MUTE_SW_DP_ENC0_P0);
 
 	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32,
-		      MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable,
+		      mtk_dp->data->smc_cmd, enable,
 		      0, 0, 0, 0, 0, &res);
 
 	dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
-		MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable, res.a0, res.a1);
+		mtk_dp->data->smc_cmd, enable, res.a0, res.a1);
 }
 
 static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
@@ -1981,6 +1982,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
 
 static const struct mtk_dp_data mt8195_edp_data = {
 	.bridge_type = DRM_MODE_CONNECTOR_eDP,
+	.smc_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
 };
 
 static const struct of_device_id mtk_dp_of_match[] = {
-- 
2.18.0


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

* [PATCH v17 05/10] drm/mediatek: dp: Add multiple smc commands support
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

The smc commands of eDP and DP are different. We add smc_cmd to the
device data to define them.

Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index 2696c1ac1a47..971bd744cdb2 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -112,6 +112,7 @@ struct mtk_dp {
 
 struct mtk_dp_data {
 	int bridge_type;
+	unsigned int smc_cmd;
 };
 static const struct mtk_dp_efuse_fmt mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
 	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
@@ -945,11 +946,11 @@ static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
 			   VIDEO_MUTE_SW_DP_ENC0_P0);
 
 	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32,
-		      MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable,
+		      mtk_dp->data->smc_cmd, enable,
 		      0, 0, 0, 0, 0, &res);
 
 	dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
-		MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable, res.a0, res.a1);
+		mtk_dp->data->smc_cmd, enable, res.a0, res.a1);
 }
 
 static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
@@ -1981,6 +1982,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
 
 static const struct mtk_dp_data mt8195_edp_data = {
 	.bridge_type = DRM_MODE_CONNECTOR_eDP,
+	.smc_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
 };
 
 static const struct of_device_id mtk_dp_of_match[] = {
-- 
2.18.0


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

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

* [PATCH v17 06/10] drm/mediatek: dp: Add multiple calibration data formats support
  2022-09-01  4:41 ` Bo-Chen Chen
  (?)
@ 2022-09-01  4:41   ` Bo-Chen Chen
  -1 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

The calibration data formats of eDP and DP are different. We add
"const struct mtk_dp_efuse_fmt *efuse_fmt" to the device data to
define them.

Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index 971bd744cdb2..136e13150281 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -113,8 +113,10 @@ struct mtk_dp {
 struct mtk_dp_data {
 	int bridge_type;
 	unsigned int smc_cmd;
+	const struct mtk_dp_efuse_fmt *efuse_fmt;
 };
-static const struct mtk_dp_efuse_fmt mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
+
+static const struct mtk_dp_efuse_fmt mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = {
 	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
 		.idx = 3,
 		.shift = 27,
@@ -811,7 +813,7 @@ static void mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
 	}
 
 	for (i = 0; i < MTK_DP_CAL_MAX; i++) {
-		fmt = &mtk_dp_efuse_data[i];
+		fmt = &mtk_dp->data->efuse_fmt[i];
 		cal_data[i] = (buf[fmt->idx] >> fmt->shift) & fmt->mask;
 
 		if (cal_data[i] < fmt->min_val || cal_data[i] > fmt->max_val) {
@@ -827,7 +829,7 @@ static void mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
 use_default_val:
 	dev_warn(mtk_dp->dev, "Use default calibration data\n");
 	for (i = 0; i < MTK_DP_CAL_MAX; i++)
-		cal_data[i] = mtk_dp_efuse_data[i].default_val;
+		cal_data[i] = mtk_dp->data->efuse_fmt[i].default_val;
 }
 
 static void mtk_dp_set_calibration_data(struct mtk_dp *mtk_dp)
@@ -1983,6 +1985,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
 static const struct mtk_dp_data mt8195_edp_data = {
 	.bridge_type = DRM_MODE_CONNECTOR_eDP,
 	.smc_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
+	.efuse_fmt = mt8195_edp_efuse_fmt,
 };
 
 static const struct of_device_id mtk_dp_of_match[] = {
-- 
2.18.0


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

* [PATCH v17 06/10] drm/mediatek: dp: Add multiple calibration data formats support
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen,
	linux-mediatek, wenst, linux-arm-kernel,
	angelogioacchino.delregno

The calibration data formats of eDP and DP are different. We add
"const struct mtk_dp_efuse_fmt *efuse_fmt" to the device data to
define them.

Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index 971bd744cdb2..136e13150281 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -113,8 +113,10 @@ struct mtk_dp {
 struct mtk_dp_data {
 	int bridge_type;
 	unsigned int smc_cmd;
+	const struct mtk_dp_efuse_fmt *efuse_fmt;
 };
-static const struct mtk_dp_efuse_fmt mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
+
+static const struct mtk_dp_efuse_fmt mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = {
 	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
 		.idx = 3,
 		.shift = 27,
@@ -811,7 +813,7 @@ static void mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
 	}
 
 	for (i = 0; i < MTK_DP_CAL_MAX; i++) {
-		fmt = &mtk_dp_efuse_data[i];
+		fmt = &mtk_dp->data->efuse_fmt[i];
 		cal_data[i] = (buf[fmt->idx] >> fmt->shift) & fmt->mask;
 
 		if (cal_data[i] < fmt->min_val || cal_data[i] > fmt->max_val) {
@@ -827,7 +829,7 @@ static void mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
 use_default_val:
 	dev_warn(mtk_dp->dev, "Use default calibration data\n");
 	for (i = 0; i < MTK_DP_CAL_MAX; i++)
-		cal_data[i] = mtk_dp_efuse_data[i].default_val;
+		cal_data[i] = mtk_dp->data->efuse_fmt[i].default_val;
 }
 
 static void mtk_dp_set_calibration_data(struct mtk_dp *mtk_dp)
@@ -1983,6 +1985,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
 static const struct mtk_dp_data mt8195_edp_data = {
 	.bridge_type = DRM_MODE_CONNECTOR_eDP,
 	.smc_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
+	.efuse_fmt = mt8195_edp_efuse_fmt,
 };
 
 static const struct of_device_id mtk_dp_of_match[] = {
-- 
2.18.0


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

* [PATCH v17 06/10] drm/mediatek: dp: Add multiple calibration data formats support
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

The calibration data formats of eDP and DP are different. We add
"const struct mtk_dp_efuse_fmt *efuse_fmt" to the device data to
define them.

Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index 971bd744cdb2..136e13150281 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -113,8 +113,10 @@ struct mtk_dp {
 struct mtk_dp_data {
 	int bridge_type;
 	unsigned int smc_cmd;
+	const struct mtk_dp_efuse_fmt *efuse_fmt;
 };
-static const struct mtk_dp_efuse_fmt mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
+
+static const struct mtk_dp_efuse_fmt mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = {
 	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
 		.idx = 3,
 		.shift = 27,
@@ -811,7 +813,7 @@ static void mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
 	}
 
 	for (i = 0; i < MTK_DP_CAL_MAX; i++) {
-		fmt = &mtk_dp_efuse_data[i];
+		fmt = &mtk_dp->data->efuse_fmt[i];
 		cal_data[i] = (buf[fmt->idx] >> fmt->shift) & fmt->mask;
 
 		if (cal_data[i] < fmt->min_val || cal_data[i] > fmt->max_val) {
@@ -827,7 +829,7 @@ static void mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
 use_default_val:
 	dev_warn(mtk_dp->dev, "Use default calibration data\n");
 	for (i = 0; i < MTK_DP_CAL_MAX; i++)
-		cal_data[i] = mtk_dp_efuse_data[i].default_val;
+		cal_data[i] = mtk_dp->data->efuse_fmt[i].default_val;
 }
 
 static void mtk_dp_set_calibration_data(struct mtk_dp *mtk_dp)
@@ -1983,6 +1985,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
 static const struct mtk_dp_data mt8195_edp_data = {
 	.bridge_type = DRM_MODE_CONNECTOR_eDP,
 	.smc_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
+	.efuse_fmt = mt8195_edp_efuse_fmt,
 };
 
 static const struct of_device_id mtk_dp_of_match[] = {
-- 
2.18.0


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

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

* [PATCH v17 07/10] drm/mediatek: dp: Determine device of next_bridge
  2022-09-01  4:41 ` Bo-Chen Chen
  (?)
@ 2022-09-01  4:41   ` Bo-Chen Chen
  -1 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

It's not necessary to have a next_bridge for DP device, so we add this
patch to judge this.

Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index 136e13150281..e37c9185e4ec 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -1886,7 +1886,10 @@ static int mtk_dp_probe(struct platform_device *pdev)
 				     "failed to request dp irq resource\n");
 
 	mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
-	if (IS_ERR(mtk_dp->next_bridge))
+	if (IS_ERR(mtk_dp->next_bridge) &&
+	    PTR_ERR(mtk_dp->next_bridge) == -ENODEV)
+		mtk_dp->next_bridge = NULL;
+	else if (IS_ERR(mtk_dp->next_bridge))
 		return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge),
 				     "Failed to get bridge\n");
 
-- 
2.18.0


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

* [PATCH v17 07/10] drm/mediatek: dp: Determine device of next_bridge
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen,
	linux-mediatek, wenst, linux-arm-kernel,
	angelogioacchino.delregno

It's not necessary to have a next_bridge for DP device, so we add this
patch to judge this.

Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index 136e13150281..e37c9185e4ec 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -1886,7 +1886,10 @@ static int mtk_dp_probe(struct platform_device *pdev)
 				     "failed to request dp irq resource\n");
 
 	mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
-	if (IS_ERR(mtk_dp->next_bridge))
+	if (IS_ERR(mtk_dp->next_bridge) &&
+	    PTR_ERR(mtk_dp->next_bridge) == -ENODEV)
+		mtk_dp->next_bridge = NULL;
+	else if (IS_ERR(mtk_dp->next_bridge))
 		return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge),
 				     "Failed to get bridge\n");
 
-- 
2.18.0


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

* [PATCH v17 07/10] drm/mediatek: dp: Determine device of next_bridge
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

It's not necessary to have a next_bridge for DP device, so we add this
patch to judge this.

Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index 136e13150281..e37c9185e4ec 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -1886,7 +1886,10 @@ static int mtk_dp_probe(struct platform_device *pdev)
 				     "failed to request dp irq resource\n");
 
 	mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
-	if (IS_ERR(mtk_dp->next_bridge))
+	if (IS_ERR(mtk_dp->next_bridge) &&
+	    PTR_ERR(mtk_dp->next_bridge) == -ENODEV)
+		mtk_dp->next_bridge = NULL;
+	else if (IS_ERR(mtk_dp->next_bridge))
 		return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge),
 				     "Failed to get bridge\n");
 
-- 
2.18.0


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

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

* [PATCH v17 08/10] drm/mediatek: dp: Add MT8195 External DisplayPort support
  2022-09-01  4:41 ` Bo-Chen Chen
  (?)
@ 2022-09-01  4:41   ` Bo-Chen Chen
  -1 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

From: Guillaume Ranquet <granquet@baylibre.com>

Add External DisplayPort support to the MT8195 eDP driver.

Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 139 ++++++++++++++++++++++++++++++
 1 file changed, 139 insertions(+)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index e37c9185e4ec..11a94927c0d0 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -35,6 +35,7 @@
 
 #define MTK_DP_SIP_CONTROL_AARCH32	MTK_SIP_SMC_CMD(0x523)
 #define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE	(BIT(0) | BIT(5))
+#define MTK_DP_SIP_ATF_VIDEO_UNMUTE	BIT(5)
 
 #define MTK_DP_THREAD_CABLE_STATE_CHG	BIT(0)
 #define MTK_DP_THREAD_HPD_EVENT		BIT(1)
@@ -199,6 +200,89 @@ static const struct mtk_dp_efuse_fmt mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = {
 	},
 };
 
+static const struct mtk_dp_efuse_fmt mt8195_dp_efuse_fmt[MTK_DP_CAL_MAX] = {
+	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
+		.idx = 0,
+		.shift = 27,
+		.mask = 0x1f,
+		.min_val = 1,
+		.max_val = 0x1e,
+		.default_val = 0xf,
+	},
+	[MTK_DP_CAL_CLKTX_IMPSE] = {
+		.idx = 0,
+		.shift = 13,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = {
+		.idx = 1,
+		.shift = 28,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = {
+		.idx = 1,
+		.shift = 20,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = {
+		.idx = 1,
+		.shift = 12,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = {
+		.idx = 1,
+		.shift = 4,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = {
+		.idx = 1,
+		.shift = 24,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = {
+		.idx = 1,
+		.shift = 16,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = {
+		.idx = 1,
+		.shift = 8,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = {
+		.idx = 1,
+		.shift = 0,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+};
+
 static struct regmap_config mtk_dp_regmap_config = {
 	.reg_bits = 32,
 	.val_bits = 32,
@@ -1479,6 +1563,50 @@ static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
 	return 0;
 }
 
+static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	enum drm_connector_status ret = connector_status_disconnected;
+	bool enabled = mtk_dp->enabled;
+	u8 sink_count = 0;
+
+	if (mtk_dp->train_info.cable_plugged_in) {
+		if (!enabled) {
+			/* power on aux */
+			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+					   DP_PWR_STATE_BANDGAP_TPLL_LANE,
+					   DP_PWR_STATE_MASK);
+
+			/* power on panel */
+			drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+			usleep_range(2000, 5000);
+		}
+		/*
+		 * Some dongles still source HPD when they do not connect to any
+		 * sink device. To avoid this, we need to read the sink count
+		 * to make sure we do connect to sink devices. After this detect
+		 * function, we just need to check the HPD connection to check
+		 * whether we connect to a sink device.
+		 */
+		drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
+		if (DP_GET_SINK_COUNT(sink_count))
+			ret = connector_status_connected;
+
+		if (!enabled) {
+			/* power off panel */
+			drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
+			usleep_range(2000, 3000);
+
+			/* power off aux */
+			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+					   DP_PWR_STATE_BANDGAP_TPLL,
+					   DP_PWR_STATE_MASK);
+		}
+	}
+
+	return ret;
+}
+
 static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
 				    struct drm_connector *connector)
 {
@@ -1865,6 +1993,7 @@ static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
 	.atomic_disable = mtk_dp_bridge_atomic_disable,
 	.mode_valid = mtk_dp_bridge_mode_valid,
 	.get_edid = mtk_dp_get_edid,
+	.detect = mtk_dp_bdg_detect,
 };
 
 static int mtk_dp_probe(struct platform_device *pdev)
@@ -1991,11 +2120,21 @@ static const struct mtk_dp_data mt8195_edp_data = {
 	.efuse_fmt = mt8195_edp_efuse_fmt,
 };
 
+static const struct mtk_dp_data mt8195_dp_data = {
+	.bridge_type = DRM_MODE_CONNECTOR_DisplayPort,
+	.smc_cmd = MTK_DP_SIP_ATF_VIDEO_UNMUTE,
+	.efuse_fmt = mt8195_dp_efuse_fmt,
+};
+
 static const struct of_device_id mtk_dp_of_match[] = {
 	{
 		.compatible = "mediatek,mt8195-edp-tx",
 		.data = &mt8195_edp_data,
 	},
+	{
+		.compatible = "mediatek,mt8195-dp-tx",
+		.data = &mt8195_dp_data,
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
-- 
2.18.0


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

* [PATCH v17 08/10] drm/mediatek: dp: Add MT8195 External DisplayPort support
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen,
	linux-mediatek, wenst, linux-arm-kernel,
	angelogioacchino.delregno

From: Guillaume Ranquet <granquet@baylibre.com>

Add External DisplayPort support to the MT8195 eDP driver.

Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 139 ++++++++++++++++++++++++++++++
 1 file changed, 139 insertions(+)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index e37c9185e4ec..11a94927c0d0 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -35,6 +35,7 @@
 
 #define MTK_DP_SIP_CONTROL_AARCH32	MTK_SIP_SMC_CMD(0x523)
 #define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE	(BIT(0) | BIT(5))
+#define MTK_DP_SIP_ATF_VIDEO_UNMUTE	BIT(5)
 
 #define MTK_DP_THREAD_CABLE_STATE_CHG	BIT(0)
 #define MTK_DP_THREAD_HPD_EVENT		BIT(1)
@@ -199,6 +200,89 @@ static const struct mtk_dp_efuse_fmt mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = {
 	},
 };
 
+static const struct mtk_dp_efuse_fmt mt8195_dp_efuse_fmt[MTK_DP_CAL_MAX] = {
+	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
+		.idx = 0,
+		.shift = 27,
+		.mask = 0x1f,
+		.min_val = 1,
+		.max_val = 0x1e,
+		.default_val = 0xf,
+	},
+	[MTK_DP_CAL_CLKTX_IMPSE] = {
+		.idx = 0,
+		.shift = 13,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = {
+		.idx = 1,
+		.shift = 28,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = {
+		.idx = 1,
+		.shift = 20,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = {
+		.idx = 1,
+		.shift = 12,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = {
+		.idx = 1,
+		.shift = 4,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = {
+		.idx = 1,
+		.shift = 24,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = {
+		.idx = 1,
+		.shift = 16,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = {
+		.idx = 1,
+		.shift = 8,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = {
+		.idx = 1,
+		.shift = 0,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+};
+
 static struct regmap_config mtk_dp_regmap_config = {
 	.reg_bits = 32,
 	.val_bits = 32,
@@ -1479,6 +1563,50 @@ static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
 	return 0;
 }
 
+static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	enum drm_connector_status ret = connector_status_disconnected;
+	bool enabled = mtk_dp->enabled;
+	u8 sink_count = 0;
+
+	if (mtk_dp->train_info.cable_plugged_in) {
+		if (!enabled) {
+			/* power on aux */
+			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+					   DP_PWR_STATE_BANDGAP_TPLL_LANE,
+					   DP_PWR_STATE_MASK);
+
+			/* power on panel */
+			drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+			usleep_range(2000, 5000);
+		}
+		/*
+		 * Some dongles still source HPD when they do not connect to any
+		 * sink device. To avoid this, we need to read the sink count
+		 * to make sure we do connect to sink devices. After this detect
+		 * function, we just need to check the HPD connection to check
+		 * whether we connect to a sink device.
+		 */
+		drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
+		if (DP_GET_SINK_COUNT(sink_count))
+			ret = connector_status_connected;
+
+		if (!enabled) {
+			/* power off panel */
+			drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
+			usleep_range(2000, 3000);
+
+			/* power off aux */
+			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+					   DP_PWR_STATE_BANDGAP_TPLL,
+					   DP_PWR_STATE_MASK);
+		}
+	}
+
+	return ret;
+}
+
 static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
 				    struct drm_connector *connector)
 {
@@ -1865,6 +1993,7 @@ static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
 	.atomic_disable = mtk_dp_bridge_atomic_disable,
 	.mode_valid = mtk_dp_bridge_mode_valid,
 	.get_edid = mtk_dp_get_edid,
+	.detect = mtk_dp_bdg_detect,
 };
 
 static int mtk_dp_probe(struct platform_device *pdev)
@@ -1991,11 +2120,21 @@ static const struct mtk_dp_data mt8195_edp_data = {
 	.efuse_fmt = mt8195_edp_efuse_fmt,
 };
 
+static const struct mtk_dp_data mt8195_dp_data = {
+	.bridge_type = DRM_MODE_CONNECTOR_DisplayPort,
+	.smc_cmd = MTK_DP_SIP_ATF_VIDEO_UNMUTE,
+	.efuse_fmt = mt8195_dp_efuse_fmt,
+};
+
 static const struct of_device_id mtk_dp_of_match[] = {
 	{
 		.compatible = "mediatek,mt8195-edp-tx",
 		.data = &mt8195_edp_data,
 	},
+	{
+		.compatible = "mediatek,mt8195-dp-tx",
+		.data = &mt8195_dp_data,
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
-- 
2.18.0


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

* [PATCH v17 08/10] drm/mediatek: dp: Add MT8195 External DisplayPort support
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

From: Guillaume Ranquet <granquet@baylibre.com>

Add External DisplayPort support to the MT8195 eDP driver.

Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 139 ++++++++++++++++++++++++++++++
 1 file changed, 139 insertions(+)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index e37c9185e4ec..11a94927c0d0 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -35,6 +35,7 @@
 
 #define MTK_DP_SIP_CONTROL_AARCH32	MTK_SIP_SMC_CMD(0x523)
 #define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE	(BIT(0) | BIT(5))
+#define MTK_DP_SIP_ATF_VIDEO_UNMUTE	BIT(5)
 
 #define MTK_DP_THREAD_CABLE_STATE_CHG	BIT(0)
 #define MTK_DP_THREAD_HPD_EVENT		BIT(1)
@@ -199,6 +200,89 @@ static const struct mtk_dp_efuse_fmt mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = {
 	},
 };
 
+static const struct mtk_dp_efuse_fmt mt8195_dp_efuse_fmt[MTK_DP_CAL_MAX] = {
+	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
+		.idx = 0,
+		.shift = 27,
+		.mask = 0x1f,
+		.min_val = 1,
+		.max_val = 0x1e,
+		.default_val = 0xf,
+	},
+	[MTK_DP_CAL_CLKTX_IMPSE] = {
+		.idx = 0,
+		.shift = 13,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = {
+		.idx = 1,
+		.shift = 28,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = {
+		.idx = 1,
+		.shift = 20,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = {
+		.idx = 1,
+		.shift = 12,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = {
+		.idx = 1,
+		.shift = 4,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = {
+		.idx = 1,
+		.shift = 24,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = {
+		.idx = 1,
+		.shift = 16,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = {
+		.idx = 1,
+		.shift = 8,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = {
+		.idx = 1,
+		.shift = 0,
+		.mask = 0xf,
+		.min_val = 1,
+		.max_val = 0xe,
+		.default_val = 0x8,
+	},
+};
+
 static struct regmap_config mtk_dp_regmap_config = {
 	.reg_bits = 32,
 	.val_bits = 32,
@@ -1479,6 +1563,50 @@ static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
 	return 0;
 }
 
+static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
+{
+	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+	enum drm_connector_status ret = connector_status_disconnected;
+	bool enabled = mtk_dp->enabled;
+	u8 sink_count = 0;
+
+	if (mtk_dp->train_info.cable_plugged_in) {
+		if (!enabled) {
+			/* power on aux */
+			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+					   DP_PWR_STATE_BANDGAP_TPLL_LANE,
+					   DP_PWR_STATE_MASK);
+
+			/* power on panel */
+			drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+			usleep_range(2000, 5000);
+		}
+		/*
+		 * Some dongles still source HPD when they do not connect to any
+		 * sink device. To avoid this, we need to read the sink count
+		 * to make sure we do connect to sink devices. After this detect
+		 * function, we just need to check the HPD connection to check
+		 * whether we connect to a sink device.
+		 */
+		drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
+		if (DP_GET_SINK_COUNT(sink_count))
+			ret = connector_status_connected;
+
+		if (!enabled) {
+			/* power off panel */
+			drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
+			usleep_range(2000, 3000);
+
+			/* power off aux */
+			mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+					   DP_PWR_STATE_BANDGAP_TPLL,
+					   DP_PWR_STATE_MASK);
+		}
+	}
+
+	return ret;
+}
+
 static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
 				    struct drm_connector *connector)
 {
@@ -1865,6 +1993,7 @@ static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
 	.atomic_disable = mtk_dp_bridge_atomic_disable,
 	.mode_valid = mtk_dp_bridge_mode_valid,
 	.get_edid = mtk_dp_get_edid,
+	.detect = mtk_dp_bdg_detect,
 };
 
 static int mtk_dp_probe(struct platform_device *pdev)
@@ -1991,11 +2120,21 @@ static const struct mtk_dp_data mt8195_edp_data = {
 	.efuse_fmt = mt8195_edp_efuse_fmt,
 };
 
+static const struct mtk_dp_data mt8195_dp_data = {
+	.bridge_type = DRM_MODE_CONNECTOR_DisplayPort,
+	.smc_cmd = MTK_DP_SIP_ATF_VIDEO_UNMUTE,
+	.efuse_fmt = mt8195_dp_efuse_fmt,
+};
+
 static const struct of_device_id mtk_dp_of_match[] = {
 	{
 		.compatible = "mediatek,mt8195-edp-tx",
 		.data = &mt8195_edp_data,
 	},
+	{
+		.compatible = "mediatek,mt8195-dp-tx",
+		.data = &mt8195_dp_data,
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
-- 
2.18.0


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

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

* [PATCH v17 09/10] drm/mediatek: dp: Add hpd debounce
  2022-09-01  4:41 ` Bo-Chen Chen
  (?)
@ 2022-09-01  4:41   ` Bo-Chen Chen
  -1 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

From: Jitao Shi <jitao.shi@mediatek.com>

From the DP spec 1.4a chapter 3.3, upstream devices should implement
HPD signal de-bouncing on an external connection.
A period of 100ms should be used to detect an HPD connect event.
To cover these cases, HPD de-bounce should be implemented only after
HPD low has been detected for at least 100ms.

Therefore,
1. If HPD is low (which means plugging out) for longer than 100ms:
   we need to do de-bouncing (which means we need to wait for 100ms).
2. If HPD low is for less than 100ms:
   we don't need to care about the de-bouncing.

In this patch, we start a 100ms timer and use a need_debounce boolean
to implement the feature.

Two cases when HPD is high:
1. If the timer is expired (>100ms):
   - need_debounce is true.
   - When HPD high (plugging event comes), need_debounce will be true
     and then we need to do de-bouncing (wait for 100ms).
2. If the timer is not expired (<100ms):
   - need_debounce is false.
   - When HPD high (plugging event comes), need_debounce will be false
     and no need to do de-bouncing.

HPD_______             __________________
          |            |<-  100ms   ->
          |____________|
          <-  100ms   ->

Without HPD de-bouncing, USB-C to HDMI Adapaters will not be detected.

The change has been successfully tested with the following devices:
- Dell Adapter - USB-C to HDMI
- Acer 1in1 HDMI dongle
- Ugreen 1in1 HDMI dongle
- innowatt HDMI + USB3 hub
- Acer 2in1 HDMI dongle
- Apple 3in1 HDMI dongle (A2119)
- J5Create 3in1 HDMI dongle (JAC379)

Tested-by: Rex-BC Chen <rex-bc.chen@mediatek.com>
Reviewed-by: Rex-BC Chen <rex-bc.chen@mediatek.com>
Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index 11a94927c0d0..dd34dae417e5 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -87,6 +87,7 @@ struct mtk_dp_efuse_fmt {
 
 struct mtk_dp {
 	bool enabled;
+	bool need_debounce;
 	u8 max_lanes;
 	u8 max_linkrate;
 	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
@@ -109,6 +110,7 @@ struct mtk_dp {
 	struct platform_device *phy_dev;
 	struct phy *phy;
 	struct regmap *regs;
+	struct timer_list debounce_timer;
 };
 
 struct mtk_dp_data {
@@ -1475,14 +1477,24 @@ static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
 	unsigned long flags;
 	u32 status;
 
+	if (mtk_dp->need_debounce && mtk_dp->train_info.cable_plugged_in)
+		msleep(100);
+
 	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
 	status = mtk_dp->irq_thread_handle;
 	mtk_dp->irq_thread_handle = 0;
 	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
 
-	if (status & MTK_DP_THREAD_CABLE_STATE_CHG)
+	if (status & MTK_DP_THREAD_CABLE_STATE_CHG) {
 		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
 
+		if (!mtk_dp->train_info.cable_plugged_in) {
+			mtk_dp->need_debounce = false;
+			mod_timer(&mtk_dp->debounce_timer,
+				  jiffies + msecs_to_jiffies(100) - 1);
+		}
+	}
+
 	if (status & MTK_DP_THREAD_HPD_EVENT)
 		dev_dbg(mtk_dp->dev, "Receive IRQ from sink devices\n");
 
@@ -1996,6 +2008,13 @@ static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
 	.detect = mtk_dp_bdg_detect,
 };
 
+static void mtk_dp_debounce_timer(struct timer_list *t)
+{
+	struct mtk_dp *mtk_dp = from_timer(mtk_dp, t, debounce_timer);
+
+	mtk_dp->need_debounce = true;
+}
+
 static int mtk_dp_probe(struct platform_device *pdev)
 {
 	struct mtk_dp *mtk_dp;
@@ -2069,6 +2088,9 @@ static int mtk_dp_probe(struct platform_device *pdev)
 
 	drm_bridge_add(&mtk_dp->bridge);
 
+	mtk_dp->need_debounce = true;
+	timer_setup(&mtk_dp->debounce_timer, mtk_dp_debounce_timer, 0);
+
 	pm_runtime_enable(dev);
 	pm_runtime_get_sync(dev);
 
@@ -2081,6 +2103,7 @@ static int mtk_dp_remove(struct platform_device *pdev)
 
 	pm_runtime_put(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
+	del_timer_sync(&mtk_dp->debounce_timer);
 	drm_bridge_remove(&mtk_dp->bridge);
 	platform_device_unregister(mtk_dp->phy_dev);
 
-- 
2.18.0


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

* [PATCH v17 09/10] drm/mediatek: dp: Add hpd debounce
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen,
	linux-mediatek, wenst, linux-arm-kernel,
	angelogioacchino.delregno

From: Jitao Shi <jitao.shi@mediatek.com>

From the DP spec 1.4a chapter 3.3, upstream devices should implement
HPD signal de-bouncing on an external connection.
A period of 100ms should be used to detect an HPD connect event.
To cover these cases, HPD de-bounce should be implemented only after
HPD low has been detected for at least 100ms.

Therefore,
1. If HPD is low (which means plugging out) for longer than 100ms:
   we need to do de-bouncing (which means we need to wait for 100ms).
2. If HPD low is for less than 100ms:
   we don't need to care about the de-bouncing.

In this patch, we start a 100ms timer and use a need_debounce boolean
to implement the feature.

Two cases when HPD is high:
1. If the timer is expired (>100ms):
   - need_debounce is true.
   - When HPD high (plugging event comes), need_debounce will be true
     and then we need to do de-bouncing (wait for 100ms).
2. If the timer is not expired (<100ms):
   - need_debounce is false.
   - When HPD high (plugging event comes), need_debounce will be false
     and no need to do de-bouncing.

HPD_______             __________________
          |            |<-  100ms   ->
          |____________|
          <-  100ms   ->

Without HPD de-bouncing, USB-C to HDMI Adapaters will not be detected.

The change has been successfully tested with the following devices:
- Dell Adapter - USB-C to HDMI
- Acer 1in1 HDMI dongle
- Ugreen 1in1 HDMI dongle
- innowatt HDMI + USB3 hub
- Acer 2in1 HDMI dongle
- Apple 3in1 HDMI dongle (A2119)
- J5Create 3in1 HDMI dongle (JAC379)

Tested-by: Rex-BC Chen <rex-bc.chen@mediatek.com>
Reviewed-by: Rex-BC Chen <rex-bc.chen@mediatek.com>
Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index 11a94927c0d0..dd34dae417e5 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -87,6 +87,7 @@ struct mtk_dp_efuse_fmt {
 
 struct mtk_dp {
 	bool enabled;
+	bool need_debounce;
 	u8 max_lanes;
 	u8 max_linkrate;
 	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
@@ -109,6 +110,7 @@ struct mtk_dp {
 	struct platform_device *phy_dev;
 	struct phy *phy;
 	struct regmap *regs;
+	struct timer_list debounce_timer;
 };
 
 struct mtk_dp_data {
@@ -1475,14 +1477,24 @@ static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
 	unsigned long flags;
 	u32 status;
 
+	if (mtk_dp->need_debounce && mtk_dp->train_info.cable_plugged_in)
+		msleep(100);
+
 	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
 	status = mtk_dp->irq_thread_handle;
 	mtk_dp->irq_thread_handle = 0;
 	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
 
-	if (status & MTK_DP_THREAD_CABLE_STATE_CHG)
+	if (status & MTK_DP_THREAD_CABLE_STATE_CHG) {
 		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
 
+		if (!mtk_dp->train_info.cable_plugged_in) {
+			mtk_dp->need_debounce = false;
+			mod_timer(&mtk_dp->debounce_timer,
+				  jiffies + msecs_to_jiffies(100) - 1);
+		}
+	}
+
 	if (status & MTK_DP_THREAD_HPD_EVENT)
 		dev_dbg(mtk_dp->dev, "Receive IRQ from sink devices\n");
 
@@ -1996,6 +2008,13 @@ static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
 	.detect = mtk_dp_bdg_detect,
 };
 
+static void mtk_dp_debounce_timer(struct timer_list *t)
+{
+	struct mtk_dp *mtk_dp = from_timer(mtk_dp, t, debounce_timer);
+
+	mtk_dp->need_debounce = true;
+}
+
 static int mtk_dp_probe(struct platform_device *pdev)
 {
 	struct mtk_dp *mtk_dp;
@@ -2069,6 +2088,9 @@ static int mtk_dp_probe(struct platform_device *pdev)
 
 	drm_bridge_add(&mtk_dp->bridge);
 
+	mtk_dp->need_debounce = true;
+	timer_setup(&mtk_dp->debounce_timer, mtk_dp_debounce_timer, 0);
+
 	pm_runtime_enable(dev);
 	pm_runtime_get_sync(dev);
 
@@ -2081,6 +2103,7 @@ static int mtk_dp_remove(struct platform_device *pdev)
 
 	pm_runtime_put(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
+	del_timer_sync(&mtk_dp->debounce_timer);
 	drm_bridge_remove(&mtk_dp->bridge);
 	platform_device_unregister(mtk_dp->phy_dev);
 
-- 
2.18.0


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

* [PATCH v17 09/10] drm/mediatek: dp: Add hpd debounce
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

From: Jitao Shi <jitao.shi@mediatek.com>

From the DP spec 1.4a chapter 3.3, upstream devices should implement
HPD signal de-bouncing on an external connection.
A period of 100ms should be used to detect an HPD connect event.
To cover these cases, HPD de-bounce should be implemented only after
HPD low has been detected for at least 100ms.

Therefore,
1. If HPD is low (which means plugging out) for longer than 100ms:
   we need to do de-bouncing (which means we need to wait for 100ms).
2. If HPD low is for less than 100ms:
   we don't need to care about the de-bouncing.

In this patch, we start a 100ms timer and use a need_debounce boolean
to implement the feature.

Two cases when HPD is high:
1. If the timer is expired (>100ms):
   - need_debounce is true.
   - When HPD high (plugging event comes), need_debounce will be true
     and then we need to do de-bouncing (wait for 100ms).
2. If the timer is not expired (<100ms):
   - need_debounce is false.
   - When HPD high (plugging event comes), need_debounce will be false
     and no need to do de-bouncing.

HPD_______             __________________
          |            |<-  100ms   ->
          |____________|
          <-  100ms   ->

Without HPD de-bouncing, USB-C to HDMI Adapaters will not be detected.

The change has been successfully tested with the following devices:
- Dell Adapter - USB-C to HDMI
- Acer 1in1 HDMI dongle
- Ugreen 1in1 HDMI dongle
- innowatt HDMI + USB3 hub
- Acer 2in1 HDMI dongle
- Apple 3in1 HDMI dongle (A2119)
- J5Create 3in1 HDMI dongle (JAC379)

Tested-by: Rex-BC Chen <rex-bc.chen@mediatek.com>
Reviewed-by: Rex-BC Chen <rex-bc.chen@mediatek.com>
Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index 11a94927c0d0..dd34dae417e5 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -87,6 +87,7 @@ struct mtk_dp_efuse_fmt {
 
 struct mtk_dp {
 	bool enabled;
+	bool need_debounce;
 	u8 max_lanes;
 	u8 max_linkrate;
 	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
@@ -109,6 +110,7 @@ struct mtk_dp {
 	struct platform_device *phy_dev;
 	struct phy *phy;
 	struct regmap *regs;
+	struct timer_list debounce_timer;
 };
 
 struct mtk_dp_data {
@@ -1475,14 +1477,24 @@ static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
 	unsigned long flags;
 	u32 status;
 
+	if (mtk_dp->need_debounce && mtk_dp->train_info.cable_plugged_in)
+		msleep(100);
+
 	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
 	status = mtk_dp->irq_thread_handle;
 	mtk_dp->irq_thread_handle = 0;
 	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
 
-	if (status & MTK_DP_THREAD_CABLE_STATE_CHG)
+	if (status & MTK_DP_THREAD_CABLE_STATE_CHG) {
 		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
 
+		if (!mtk_dp->train_info.cable_plugged_in) {
+			mtk_dp->need_debounce = false;
+			mod_timer(&mtk_dp->debounce_timer,
+				  jiffies + msecs_to_jiffies(100) - 1);
+		}
+	}
+
 	if (status & MTK_DP_THREAD_HPD_EVENT)
 		dev_dbg(mtk_dp->dev, "Receive IRQ from sink devices\n");
 
@@ -1996,6 +2008,13 @@ static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
 	.detect = mtk_dp_bdg_detect,
 };
 
+static void mtk_dp_debounce_timer(struct timer_list *t)
+{
+	struct mtk_dp *mtk_dp = from_timer(mtk_dp, t, debounce_timer);
+
+	mtk_dp->need_debounce = true;
+}
+
 static int mtk_dp_probe(struct platform_device *pdev)
 {
 	struct mtk_dp *mtk_dp;
@@ -2069,6 +2088,9 @@ static int mtk_dp_probe(struct platform_device *pdev)
 
 	drm_bridge_add(&mtk_dp->bridge);
 
+	mtk_dp->need_debounce = true;
+	timer_setup(&mtk_dp->debounce_timer, mtk_dp_debounce_timer, 0);
+
 	pm_runtime_enable(dev);
 	pm_runtime_get_sync(dev);
 
@@ -2081,6 +2103,7 @@ static int mtk_dp_remove(struct platform_device *pdev)
 
 	pm_runtime_put(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
+	del_timer_sync(&mtk_dp->debounce_timer);
 	drm_bridge_remove(&mtk_dp->bridge);
 	platform_device_unregister(mtk_dp->phy_dev);
 
-- 
2.18.0


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

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

* [PATCH v17 10/10] drm/mediatek: dp: Audio support for MT8195
  2022-09-01  4:41 ` Bo-Chen Chen
  (?)
@ 2022-09-01  4:41   ` Bo-Chen Chen
  -1 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

From: Guillaume Ranquet <granquet@baylibre.com>

This patch adds audio support to the DP driver for MT8195 with up to 8
channels.

Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c     | 482 +++++++++++++++++++++++++-
 drivers/gpu/drm/mediatek/mtk_dp_reg.h |  51 +++
 2 files changed, 532 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index dd34dae417e5..dfa942ca62da 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -29,6 +29,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/soc/mediatek/mtk_sip_svc.h>
+#include <sound/hdmi-codec.h>
 #include <video/videomode.h>
 
 #include "mtk_dp_reg.h"
@@ -47,6 +48,8 @@
 #define MTK_DP_TBC_BUF_READ_START_ADDR 0x8
 #define MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY 5
 #define MTK_DP_TRAIN_DOWNSCALE_RETRY 10
+#define MTK_DP_VERSION 0x11
+#define MTK_DP_SDP_AUI 0x4
 
 enum {
 	MTK_DP_CAL_GLB_BIAS_TRIM = 0,
@@ -71,9 +74,18 @@ struct mtk_dp_train_info {
 	unsigned int channel_eq_pattern;
 };
 
+struct mtk_dp_audio_cfg {
+	bool detect_monitor;
+	int sad_count;
+	int sample_rate;
+	int word_length_bits;
+	int channels;
+};
+
 struct mtk_dp_info {
 	enum dp_pixelformat format;
 	struct videomode vm;
+	struct mtk_dp_audio_cfg audio_cur_cfg;
 };
 
 struct mtk_dp_efuse_fmt {
@@ -111,12 +123,22 @@ struct mtk_dp {
 	struct phy *phy;
 	struct regmap *regs;
 	struct timer_list debounce_timer;
+
+	/* For audio */
+	bool audio_enable;
+	hdmi_codec_plugged_cb plugged_cb;
+	struct platform_device *audio_pdev;
+
+	struct device *codec_dev;
+	/* protect the plugged_cb as it's used in both bridge ops and audio */
+	struct mutex update_plugged_status_lock;
 };
 
 struct mtk_dp_data {
 	int bridge_type;
 	unsigned int smc_cmd;
 	const struct mtk_dp_efuse_fmt *efuse_fmt;
+	bool audio_supported;
 };
 
 static const struct mtk_dp_efuse_fmt mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = {
@@ -512,6 +534,169 @@ static void mtk_dp_pg_enable(struct mtk_dp *mtk_dp, bool enable)
 			   PGEN_PATTERN_SEL_VAL << 4, PGEN_PATTERN_SEL_MASK);
 }
 
+static void mtk_dp_audio_setup_channels(struct mtk_dp *mtk_dp,
+					struct mtk_dp_audio_cfg *cfg)
+{
+	u32 channel_enable_bits;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3324,
+			   AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX,
+			   AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK);
+
+	/* audio channel count change reset */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4,
+			   DP_ENC_DUMMY_RW_1, DP_ENC_DUMMY_RW_1);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
+			   AU_PRTY_REGEN_DP_ENC1_P0_MASK |
+			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK |
+			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK,
+			   AU_PRTY_REGEN_DP_ENC1_P0_MASK |
+			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK |
+			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK);
+
+	switch (cfg->channels) {
+	case 2:
+		channel_enable_bits = AUDIO_2CH_SEL_DP_ENC0_P0_MASK |
+				      AUDIO_2CH_EN_DP_ENC0_P0_MASK;
+		break;
+	case 8:
+	default:
+		channel_enable_bits = AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
+				      AUDIO_8CH_EN_DP_ENC0_P0_MASK;
+		break;
+	}
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088,
+			   channel_enable_bits | AU_EN_DP_ENC0_P0,
+			   AUDIO_2CH_SEL_DP_ENC0_P0_MASK |
+			   AUDIO_2CH_EN_DP_ENC0_P0_MASK |
+			   AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
+			   AUDIO_8CH_EN_DP_ENC0_P0_MASK |
+			   AU_EN_DP_ENC0_P0);
+
+	/* audio channel count change reset */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, 0, DP_ENC_DUMMY_RW_1);
+
+	/* enable audio reset */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4,
+			   DP_ENC_DUMMY_RW_1_AUDIO_RST_EN,
+			   DP_ENC_DUMMY_RW_1_AUDIO_RST_EN);
+}
+
+static void mtk_dp_audio_channel_status_set(struct mtk_dp *mtk_dp,
+					    struct mtk_dp_audio_cfg *cfg)
+{
+	struct snd_aes_iec958 iec = { 0 };
+
+	switch (cfg->sample_rate) {
+	case 32000:
+		iec.status[3] = IEC958_AES3_CON_FS_32000;
+		break;
+	case 44100:
+		iec.status[3] = IEC958_AES3_CON_FS_44100;
+		break;
+	case 48000:
+		iec.status[3] = IEC958_AES3_CON_FS_48000;
+		break;
+	case 88200:
+		iec.status[3] = IEC958_AES3_CON_FS_88200;
+		break;
+	case 96000:
+		iec.status[3] = IEC958_AES3_CON_FS_96000;
+		break;
+	case 192000:
+		iec.status[3] = IEC958_AES3_CON_FS_192000;
+		break;
+	default:
+		iec.status[3] = IEC958_AES3_CON_FS_NOTID;
+		break;
+	}
+
+	switch (cfg->word_length_bits) {
+	case 16:
+		iec.status[4] = IEC958_AES4_CON_WORDLEN_20_16;
+		break;
+	case 20:
+		iec.status[4] = IEC958_AES4_CON_WORDLEN_20_16 |
+				IEC958_AES4_CON_MAX_WORDLEN_24;
+		break;
+	case 24:
+		iec.status[4] = IEC958_AES4_CON_WORDLEN_24_20 |
+				IEC958_AES4_CON_MAX_WORDLEN_24;
+		break;
+	default:
+		iec.status[4] = IEC958_AES4_CON_WORDLEN_NOTID;
+	}
+
+	/* IEC 60958 consumer channel status bits */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_308C,
+			   0, CH_STATUS_0_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3090,
+			   iec.status[3] << 8, CH_STATUS_1_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3094,
+			   iec.status[4], CH_STATUS_2_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_audio_sdp_asp_set_channels(struct mtk_dp *mtk_dp,
+					      int channels)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_312C,
+			   (min(8, channels) - 1) << 8,
+			   ASP_HB2_DP_ENC0_P0_MASK | ASP_HB3_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_audio_set_divider(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
+			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2,
+			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_sdp_trigger_aui(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280,
+			   MTK_DP_SDP_AUI, SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280,
+			   SDP_PACKET_W_DP_ENC1_P0, SDP_PACKET_W_DP_ENC1_P0);
+}
+
+static void mtk_dp_sdp_set_data(struct mtk_dp *mtk_dp, u8 *data_bytes)
+{
+	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_ENC1_P0_3200,
+				data_bytes, 0x10);
+}
+
+static void mtk_dp_sdp_set_header_aui(struct mtk_dp *mtk_dp,
+				      struct dp_sdp_header *header)
+{
+	u32 db_addr = MTK_DP_ENC0_P0_30D8 + (MTK_DP_SDP_AUI - 1) * 8;
+
+	mtk_dp_bulk_16bit_write(mtk_dp, db_addr, (u8 *)header, 4);
+}
+
+static void mtk_dp_disable_sdp_aui(struct mtk_dp *mtk_dp)
+{
+	/* Disable periodic send */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A8 & 0xfffc, 0,
+			   0xff << ((MTK_DP_ENC0_P0_30A8 & 3) * 8));
+}
+
+static void mtk_dp_setup_sdp_aui(struct mtk_dp *mtk_dp,
+				 struct dp_sdp *sdp)
+{
+	u32 shift;
+
+	mtk_dp_sdp_set_data(mtk_dp, sdp->db);
+	mtk_dp_sdp_set_header_aui(mtk_dp, &sdp->sdp_header);
+	mtk_dp_disable_sdp_aui(mtk_dp);
+
+	shift = (MTK_DP_ENC0_P0_30A8 & 3) * 8;
+
+	mtk_dp_sdp_trigger_aui(mtk_dp);
+	/* Enable periodic sending */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A8 & 0xfffc,
+			   0x05 << shift, 0xff << shift);
+}
+
 static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
 {
 	mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640, DP_AUX_P0_3640_VAL);
@@ -1041,6 +1226,32 @@ static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
 		mtk_dp->data->smc_cmd, enable, res.a0, res.a1);
 }
 
+static void mtk_dp_audio_mute(struct mtk_dp *mtk_dp, bool mute)
+{
+	u32 val[3];
+
+	if (mute) {
+		val[0] = VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0 |
+			 VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0;
+		val[1] = 0;
+		val[2] = 0;
+	} else {
+		val[0] = 0;
+		val[1] = AU_EN_DP_ENC0_P0;
+		/* Send one every two frames */
+		val[2] = 0x0F;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030,
+			   val[0],
+			   VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0 |
+			   VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088,
+			   val[1], AU_EN_DP_ENC0_P0);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4,
+			   val[2], AU_TS_CFG_DP_ENC0_P0_MASK);
+}
+
 static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
 {
 	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
@@ -1080,6 +1291,76 @@ static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
 
 	mtk_dp->info.format = DP_PIXELFORMAT_RGB;
 	memset(&mtk_dp->info.vm, 0, sizeof(struct videomode));
+	mtk_dp->audio_enable = false;
+}
+
+static void mtk_dp_sdp_set_down_cnt_init(struct mtk_dp *mtk_dp,
+					 u32 sram_read_start)
+{
+	u32 sdp_down_cnt_init = 0;
+	struct drm_display_mode mode;
+	struct videomode *vm = &mtk_dp->info.vm;
+
+	drm_display_mode_from_videomode(vm, &mode);
+
+	if (mode.clock > 0)
+		sdp_down_cnt_init = sram_read_start *
+				    mtk_dp->train_info.link_rate * 2700 * 8 /
+				    (mode.clock * 4);
+
+	switch (mtk_dp->train_info.lane_count) {
+	case 1:
+		sdp_down_cnt_init = max_t(u32, sdp_down_cnt_init, 0x1A);
+		break;
+	case 2:
+		/* case for LowResolution && High Audio Sample Rate */
+		sdp_down_cnt_init = max_t(u32, sdp_down_cnt_init, 0x10);
+		sdp_down_cnt_init += mode.vtotal <= 525 ? 4 : 0;
+		break;
+	case 4:
+	default:
+		sdp_down_cnt_init = max_t(u32, sdp_down_cnt_init, 6);
+		break;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
+			   sdp_down_cnt_init,
+			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_sdp_set_down_cnt_init_in_hblank(struct mtk_dp *mtk_dp)
+{
+	int pix_clk_mhz;
+	u32 dc_offset;
+	u32 spd_down_cnt_init = 0;
+	struct drm_display_mode mode;
+	struct videomode *vm = &mtk_dp->info.vm;
+
+	drm_display_mode_from_videomode(vm, &mode);
+
+	pix_clk_mhz = mtk_dp->info.format == DP_PIXELFORMAT_YUV420 ?
+		      mode.clock / 2000 : mode.clock / 1000;
+
+	switch (mtk_dp->train_info.lane_count) {
+	case 1:
+		spd_down_cnt_init = 0x20;
+		break;
+	case 2:
+		dc_offset = (mode.vtotal <= 525) ? 0x14 : 0x00;
+		spd_down_cnt_init = 0x18 + dc_offset;
+		break;
+	case 4:
+	default:
+		dc_offset = (mode.vtotal <= 525) ? 0x08 : 0x00;
+		if (pix_clk_mhz > mtk_dp->train_info.link_rate * 27)
+			spd_down_cnt_init = 0x8;
+		else
+			spd_down_cnt_init = 0x10 + dc_offset;
+		break;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364, spd_down_cnt_init,
+			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
 }
 
 static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
@@ -1091,6 +1372,8 @@ static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
 				    MTK_DP_PIX_PER_ADDR);
 	mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
 	mtk_dp_setup_encoder(mtk_dp);
+	mtk_dp_sdp_set_down_cnt_init_in_hblank(mtk_dp);
+	mtk_dp_sdp_set_down_cnt_init(mtk_dp, sram_read_start);
 }
 
 static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
@@ -1342,6 +1625,20 @@ static int mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
 	return 0;
 }
 
+static bool mtk_dp_edid_parse_audio_capabilities(struct mtk_dp *mtk_dp,
+						 struct mtk_dp_audio_cfg *cfg)
+{
+	if (!mtk_dp->data->audio_supported)
+		return false;
+
+	if (mtk_dp->info.audio_cur_cfg.sad_count <= 0) {
+		drm_info(mtk_dp->drm_dev, "The SADs is NULL\n");
+		return false;
+	}
+
+	return true;
+}
+
 static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
 {
 	phy_reset(mtk_dp->phy);
@@ -1446,6 +1743,46 @@ static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
 	}
 }
 
+static void mtk_dp_audio_sdp_setup(struct mtk_dp *mtk_dp,
+				   struct mtk_dp_audio_cfg *cfg)
+{
+	struct dp_sdp sdp;
+	struct hdmi_audio_infoframe frame;
+
+	hdmi_audio_infoframe_init(&frame);
+	frame.coding_type = HDMI_AUDIO_CODING_TYPE_PCM;
+	frame.channels = cfg->channels;
+	frame.sample_frequency = cfg->sample_rate;
+
+	switch (cfg->word_length_bits) {
+	case 16:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
+		break;
+	case 20:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_20;
+		break;
+	case 24:
+	default:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_24;
+		break;
+	}
+
+	hdmi_audio_infoframe_pack_for_dp(&frame, &sdp, MTK_DP_VERSION);
+
+	mtk_dp_audio_sdp_asp_set_channels(mtk_dp, cfg->channels);
+	mtk_dp_setup_sdp_aui(mtk_dp, &sdp);
+}
+
+static void mtk_dp_audio_setup(struct mtk_dp *mtk_dp,
+			       struct mtk_dp_audio_cfg *cfg)
+{
+	mtk_dp_audio_sdp_setup(mtk_dp, cfg);
+	mtk_dp_audio_channel_status_set(mtk_dp, cfg);
+
+	mtk_dp_audio_setup_channels(mtk_dp, cfg);
+	mtk_dp_audio_set_divider(mtk_dp);
+}
+
 static int mtk_dp_video_config(struct mtk_dp *mtk_dp)
 {
 	mtk_dp_config_mn_mode(mtk_dp);
@@ -1489,6 +1826,10 @@ static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
 		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
 
 		if (!mtk_dp->train_info.cable_plugged_in) {
+			mtk_dp_disable_sdp_aui(mtk_dp);
+			memset(&mtk_dp->info.audio_cur_cfg, 0,
+			       sizeof(mtk_dp->info.audio_cur_cfg));
+
 			mtk_dp->need_debounce = false;
 			mod_timer(&mtk_dp->debounce_timer,
 				  jiffies + msecs_to_jiffies(100) - 1);
@@ -1575,6 +1916,16 @@ static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
 	return 0;
 }
 
+static void mtk_dp_update_plugged_status(struct mtk_dp *mtk_dp)
+{
+	mutex_lock(&mtk_dp->update_plugged_status_lock);
+	if (mtk_dp->plugged_cb && mtk_dp->codec_dev)
+		mtk_dp->plugged_cb(mtk_dp->codec_dev,
+				   mtk_dp->enabled &
+				   mtk_dp->info.audio_cur_cfg.detect_monitor);
+	mutex_unlock(&mtk_dp->update_plugged_status_lock);
+}
+
 static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
 {
 	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
@@ -1615,7 +1966,6 @@ static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
 					   DP_PWR_STATE_MASK);
 		}
 	}
-
 	return ret;
 }
 
@@ -1625,6 +1975,8 @@ static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
 	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
 	bool enabled = mtk_dp->enabled;
 	struct edid *new_edid = NULL;
+	struct mtk_dp_audio_cfg *audio_caps = &mtk_dp->info.audio_cur_cfg;
+	struct cea_sad *sads;
 
 	if (!enabled) {
 		drm_bridge_chain_pre_enable(bridge);
@@ -1650,6 +2002,11 @@ static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
 		new_edid = NULL;
 	}
 
+	if (new_edid) {
+		audio_caps->sad_count = drm_edid_to_sad(new_edid, &sads);
+		audio_caps->detect_monitor = drm_detect_monitor_audio(new_edid);
+	}
+
 	if (!enabled) {
 		/* power off panel */
 		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
@@ -1843,7 +2200,19 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
 
 	mtk_dp_video_enable(mtk_dp, true);
 
+	mtk_dp->audio_enable =
+		mtk_dp_edid_parse_audio_capabilities(mtk_dp,
+						     &mtk_dp->info.audio_cur_cfg);
+	if (mtk_dp->audio_enable) {
+		mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_cur_cfg);
+		mtk_dp_audio_mute(mtk_dp, false);
+	} else {
+		memset(&mtk_dp->info.audio_cur_cfg, 0,
+		       sizeof(mtk_dp->info.audio_cur_cfg));
+	}
+
 	mtk_dp->enabled = true;
+	mtk_dp_update_plugged_status(mtk_dp);
 
 	return;
 power_off_aux:
@@ -1858,7 +2227,9 @@ static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
 	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
 
 	mtk_dp->enabled = false;
+	mtk_dp_update_plugged_status(mtk_dp);
 	mtk_dp_video_enable(mtk_dp, false);
+	mtk_dp_audio_mute(mtk_dp, true);
 
 	if (mtk_dp->train_info.cable_plugged_in) {
 		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
@@ -2015,6 +2386,100 @@ static void mtk_dp_debounce_timer(struct timer_list *t)
 	mtk_dp->need_debounce = true;
 }
 
+/*
+ * HDMI audio codec callbacks
+ */
+static int mtk_dp_audio_hw_params(struct device *dev, void *data,
+				  struct hdmi_codec_daifmt *daifmt,
+				  struct hdmi_codec_params *params)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	if (!mtk_dp->enabled) {
+		dev_err(mtk_dp->dev, "%s, DP is not ready!\n", __func__);
+		return -ENODEV;
+	}
+
+	mtk_dp->info.audio_cur_cfg.channels = params->cea.channels;
+	mtk_dp->info.audio_cur_cfg.sample_rate = params->sample_rate;
+
+	mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_cur_cfg);
+
+	return 0;
+}
+
+static int mtk_dp_audio_startup(struct device *dev, void *data)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_audio_mute(mtk_dp, false);
+
+	return 0;
+}
+
+static void mtk_dp_audio_shutdown(struct device *dev, void *data)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_audio_mute(mtk_dp, true);
+}
+
+static int mtk_dp_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
+				size_t len)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	if (mtk_dp->enabled)
+		memcpy(buf, mtk_dp->conn->eld, len);
+	else
+		memset(buf, 0, len);
+
+	return 0;
+}
+
+static int mtk_dp_audio_hook_plugged_cb(struct device *dev, void *data,
+					hdmi_codec_plugged_cb fn,
+					struct device *codec_dev)
+{
+	struct mtk_dp *mtk_dp = data;
+
+	mutex_lock(&mtk_dp->update_plugged_status_lock);
+	mtk_dp->plugged_cb = fn;
+	mtk_dp->codec_dev = codec_dev;
+	mutex_unlock(&mtk_dp->update_plugged_status_lock);
+
+	mtk_dp_update_plugged_status(mtk_dp);
+
+	return 0;
+}
+
+static const struct hdmi_codec_ops mtk_dp_audio_codec_ops = {
+	.hw_params = mtk_dp_audio_hw_params,
+	.audio_startup = mtk_dp_audio_startup,
+	.audio_shutdown = mtk_dp_audio_shutdown,
+	.get_eld = mtk_dp_audio_get_eld,
+	.hook_plugged_cb = mtk_dp_audio_hook_plugged_cb,
+	.no_capture_mute = 1,
+};
+
+static int mtk_dp_register_audio_driver(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+	struct hdmi_codec_pdata codec_data = {
+		.ops = &mtk_dp_audio_codec_ops,
+		.max_i2s_channels = 8,
+		.i2s = 1,
+		.data = mtk_dp,
+	};
+
+	mtk_dp->audio_pdev = platform_device_register_data(dev,
+							   HDMI_CODEC_DRV_NAME,
+							   PLATFORM_DEVID_AUTO,
+							   &codec_data,
+							   sizeof(codec_data));
+	return PTR_ERR_OR_ZERO(mtk_dp->audio_pdev);
+}
+
 static int mtk_dp_probe(struct platform_device *pdev)
 {
 	struct mtk_dp *mtk_dp;
@@ -2059,8 +2524,19 @@ static int mtk_dp_probe(struct platform_device *pdev)
 		return dev_err_probe(dev, ret,
 				     "failed to request mediatek dptx irq\n");
 
+	mutex_init(&mtk_dp->update_plugged_status_lock);
+
 	platform_set_drvdata(pdev, mtk_dp);
 
+	if (mtk_dp->data->audio_supported) {
+		ret = mtk_dp_register_audio_driver(dev);
+		if (ret) {
+			dev_err(dev, "Failed to register audio driver: %d\n",
+				ret);
+			return ret;
+		}
+	}
+
 	mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
 							PLATFORM_DEVID_AUTO,
 							&mtk_dp->regs,
@@ -2106,6 +2582,8 @@ static int mtk_dp_remove(struct platform_device *pdev)
 	del_timer_sync(&mtk_dp->debounce_timer);
 	drm_bridge_remove(&mtk_dp->bridge);
 	platform_device_unregister(mtk_dp->phy_dev);
+	if (mtk_dp->audio_pdev)
+		platform_device_unregister(mtk_dp->audio_pdev);
 
 	return 0;
 }
@@ -2141,12 +2619,14 @@ static const struct mtk_dp_data mt8195_edp_data = {
 	.bridge_type = DRM_MODE_CONNECTOR_eDP,
 	.smc_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
 	.efuse_fmt = mt8195_edp_efuse_fmt,
+	.audio_supported = false,
 };
 
 static const struct mtk_dp_data mt8195_dp_data = {
 	.bridge_type = DRM_MODE_CONNECTOR_DisplayPort,
 	.smc_cmd = MTK_DP_SIP_ATF_VIDEO_UNMUTE,
 	.efuse_fmt = mt8195_dp_efuse_fmt,
+	.audio_supported = true,
 };
 
 static const struct of_device_id mtk_dp_of_match[] = {
diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
index 3f01ba44871f..096ad6572a5e 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp_reg.h
+++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
@@ -115,6 +115,8 @@
 #define HSW_SEL_DP_ENC0_P0				BIT(7)
 #define VSP_SEL_DP_ENC0_P0				BIT(8)
 #define VSW_SEL_DP_ENC0_P0				BIT(9)
+#define VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0		BIT(11)
+#define VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0		BIT(12)
 #define MTK_DP_ENC0_P0_3034			0x3034
 #define MTK_DP_ENC0_P0_3038			0x3038
 #define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK		BIT(11)
@@ -139,6 +141,38 @@
 #define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK		BIT(8)
 #define MTK_DP_ENC0_P0_3064			0x3064
 #define HDE_NUM_LAST_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3088			0x3088
+#define AU_EN_DP_ENC0_P0				BIT(6)
+#define AUDIO_8CH_EN_DP_ENC0_P0_MASK			BIT(7)
+#define AUDIO_8CH_SEL_DP_ENC0_P0_MASK			BIT(8)
+#define AUDIO_2CH_EN_DP_ENC0_P0_MASK			BIT(14)
+#define AUDIO_2CH_SEL_DP_ENC0_P0_MASK			BIT(15)
+#define MTK_DP_ENC0_P0_308C			0x308c
+#define CH_STATUS_0_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3090			0x3090
+#define CH_STATUS_1_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3094			0x3094
+#define CH_STATUS_2_DP_ENC0_P0_MASK			GENMASK(7, 0)
+#define MTK_DP_ENC0_P0_30A0			0x30a0
+#define DP_ENC0_30A0_MASK				(BIT(7) | BIT(8) | BIT(12))
+#define MTK_DP_ENC0_P0_30A4			0x30a4
+#define AU_TS_CFG_DP_ENC0_P0_MASK			GENMASK(7, 0)
+#define MTK_DP_ENC0_P0_30A8			0x30a8
+#define MTK_DP_ENC0_P0_30BC			0x30bc
+#define ISRC_CONT_DP_ENC0_P0				BIT(0)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK	GENMASK(10, 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_2	(1 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_4	(2 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_8	(3 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2	(5 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_4	(6 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_8	(7 << 8)
+#define MTK_DP_ENC0_P0_30D8			0x30d8
+#define MTK_DP_ENC0_P0_312C			0x312c
+#define ASP_HB2_DP_ENC0_P0_MASK				GENMASK(7, 0)
+#define ASP_HB3_DP_ENC0_P0_MASK				GENMASK(15, 8)
+#define MTK_DP_ENC0_P0_3130			0x3130
+#define MTK_DP_ENC0_P0_3138			0x3138
 #define MTK_DP_ENC0_P0_3154			0x3154
 #define PGEN_HTOTAL_DP_ENC0_P0_MASK			GENMASK(13, 0)
 #define MTK_DP_ENC0_P0_3158			0x3158
@@ -167,9 +201,23 @@
 #define ISRC1_HB3_DP_ENC0_P0_MASK			GENMASK(15, 8)
 
 /* offset: ENC1_OFFSET (0x3200) */
+#define MTK_DP_ENC1_P0_3200			0x3200
+#define MTK_DP_ENC1_P0_3280			0x3280
+#define SDP_PACKET_TYPE_DP_ENC1_P0_MASK			GENMASK(4, 0)
+#define SDP_PACKET_W_DP_ENC1_P0				BIT(5)
+#define SDP_PACKET_W_DP_ENC1_P0_MASK			BIT(5)
+#define MTK_DP_ENC1_P0_328C			0x328c
+#define VSC_DATA_RDY_VESA_DP_ENC1_P0_MASK		BIT(7)
 #define MTK_DP_ENC1_P0_3300			0x3300
 #define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL		2
 #define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK		GENMASK(9, 8)
+#define MTK_DP_ENC1_P0_3304			0x3304
+#define AU_PRTY_REGEN_DP_ENC1_P0_MASK			BIT(8)
+#define AU_CH_STS_REGEN_DP_ENC1_P0_MASK			BIT(9)
+#define AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK	BIT(12)
+#define MTK_DP_ENC1_P0_3324			0x3324
+#define AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK		GENMASK(9, 8)
+#define AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX		0
 #define MTK_DP_ENC1_P0_3364			0x3364
 #define SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL		0x20
 #define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK	GENMASK(11, 0)
@@ -186,6 +234,9 @@
 							 VIDEO_STABLE_CNT_THRD_DP_ENC1_P0 | \
 							 SDP_DP13_EN_DP_ENC1_P0 | \
 							 BS2BS_MODE_DP_ENC1_P0)
+#define MTK_DP_ENC1_P0_33F4			0x33f4
+#define DP_ENC_DUMMY_RW_1_AUDIO_RST_EN			BIT(0)
+#define DP_ENC_DUMMY_RW_1				BIT(9)
 
 /* offset: TRANS_OFFSET (0x3400) */
 #define MTK_DP_TRANS_P0_3400				0x3400
-- 
2.18.0


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

* [PATCH v17 10/10] drm/mediatek: dp: Audio support for MT8195
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen,
	linux-mediatek, wenst, linux-arm-kernel,
	angelogioacchino.delregno

From: Guillaume Ranquet <granquet@baylibre.com>

This patch adds audio support to the DP driver for MT8195 with up to 8
channels.

Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c     | 482 +++++++++++++++++++++++++-
 drivers/gpu/drm/mediatek/mtk_dp_reg.h |  51 +++
 2 files changed, 532 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index dd34dae417e5..dfa942ca62da 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -29,6 +29,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/soc/mediatek/mtk_sip_svc.h>
+#include <sound/hdmi-codec.h>
 #include <video/videomode.h>
 
 #include "mtk_dp_reg.h"
@@ -47,6 +48,8 @@
 #define MTK_DP_TBC_BUF_READ_START_ADDR 0x8
 #define MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY 5
 #define MTK_DP_TRAIN_DOWNSCALE_RETRY 10
+#define MTK_DP_VERSION 0x11
+#define MTK_DP_SDP_AUI 0x4
 
 enum {
 	MTK_DP_CAL_GLB_BIAS_TRIM = 0,
@@ -71,9 +74,18 @@ struct mtk_dp_train_info {
 	unsigned int channel_eq_pattern;
 };
 
+struct mtk_dp_audio_cfg {
+	bool detect_monitor;
+	int sad_count;
+	int sample_rate;
+	int word_length_bits;
+	int channels;
+};
+
 struct mtk_dp_info {
 	enum dp_pixelformat format;
 	struct videomode vm;
+	struct mtk_dp_audio_cfg audio_cur_cfg;
 };
 
 struct mtk_dp_efuse_fmt {
@@ -111,12 +123,22 @@ struct mtk_dp {
 	struct phy *phy;
 	struct regmap *regs;
 	struct timer_list debounce_timer;
+
+	/* For audio */
+	bool audio_enable;
+	hdmi_codec_plugged_cb plugged_cb;
+	struct platform_device *audio_pdev;
+
+	struct device *codec_dev;
+	/* protect the plugged_cb as it's used in both bridge ops and audio */
+	struct mutex update_plugged_status_lock;
 };
 
 struct mtk_dp_data {
 	int bridge_type;
 	unsigned int smc_cmd;
 	const struct mtk_dp_efuse_fmt *efuse_fmt;
+	bool audio_supported;
 };
 
 static const struct mtk_dp_efuse_fmt mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = {
@@ -512,6 +534,169 @@ static void mtk_dp_pg_enable(struct mtk_dp *mtk_dp, bool enable)
 			   PGEN_PATTERN_SEL_VAL << 4, PGEN_PATTERN_SEL_MASK);
 }
 
+static void mtk_dp_audio_setup_channels(struct mtk_dp *mtk_dp,
+					struct mtk_dp_audio_cfg *cfg)
+{
+	u32 channel_enable_bits;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3324,
+			   AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX,
+			   AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK);
+
+	/* audio channel count change reset */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4,
+			   DP_ENC_DUMMY_RW_1, DP_ENC_DUMMY_RW_1);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
+			   AU_PRTY_REGEN_DP_ENC1_P0_MASK |
+			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK |
+			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK,
+			   AU_PRTY_REGEN_DP_ENC1_P0_MASK |
+			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK |
+			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK);
+
+	switch (cfg->channels) {
+	case 2:
+		channel_enable_bits = AUDIO_2CH_SEL_DP_ENC0_P0_MASK |
+				      AUDIO_2CH_EN_DP_ENC0_P0_MASK;
+		break;
+	case 8:
+	default:
+		channel_enable_bits = AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
+				      AUDIO_8CH_EN_DP_ENC0_P0_MASK;
+		break;
+	}
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088,
+			   channel_enable_bits | AU_EN_DP_ENC0_P0,
+			   AUDIO_2CH_SEL_DP_ENC0_P0_MASK |
+			   AUDIO_2CH_EN_DP_ENC0_P0_MASK |
+			   AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
+			   AUDIO_8CH_EN_DP_ENC0_P0_MASK |
+			   AU_EN_DP_ENC0_P0);
+
+	/* audio channel count change reset */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, 0, DP_ENC_DUMMY_RW_1);
+
+	/* enable audio reset */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4,
+			   DP_ENC_DUMMY_RW_1_AUDIO_RST_EN,
+			   DP_ENC_DUMMY_RW_1_AUDIO_RST_EN);
+}
+
+static void mtk_dp_audio_channel_status_set(struct mtk_dp *mtk_dp,
+					    struct mtk_dp_audio_cfg *cfg)
+{
+	struct snd_aes_iec958 iec = { 0 };
+
+	switch (cfg->sample_rate) {
+	case 32000:
+		iec.status[3] = IEC958_AES3_CON_FS_32000;
+		break;
+	case 44100:
+		iec.status[3] = IEC958_AES3_CON_FS_44100;
+		break;
+	case 48000:
+		iec.status[3] = IEC958_AES3_CON_FS_48000;
+		break;
+	case 88200:
+		iec.status[3] = IEC958_AES3_CON_FS_88200;
+		break;
+	case 96000:
+		iec.status[3] = IEC958_AES3_CON_FS_96000;
+		break;
+	case 192000:
+		iec.status[3] = IEC958_AES3_CON_FS_192000;
+		break;
+	default:
+		iec.status[3] = IEC958_AES3_CON_FS_NOTID;
+		break;
+	}
+
+	switch (cfg->word_length_bits) {
+	case 16:
+		iec.status[4] = IEC958_AES4_CON_WORDLEN_20_16;
+		break;
+	case 20:
+		iec.status[4] = IEC958_AES4_CON_WORDLEN_20_16 |
+				IEC958_AES4_CON_MAX_WORDLEN_24;
+		break;
+	case 24:
+		iec.status[4] = IEC958_AES4_CON_WORDLEN_24_20 |
+				IEC958_AES4_CON_MAX_WORDLEN_24;
+		break;
+	default:
+		iec.status[4] = IEC958_AES4_CON_WORDLEN_NOTID;
+	}
+
+	/* IEC 60958 consumer channel status bits */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_308C,
+			   0, CH_STATUS_0_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3090,
+			   iec.status[3] << 8, CH_STATUS_1_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3094,
+			   iec.status[4], CH_STATUS_2_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_audio_sdp_asp_set_channels(struct mtk_dp *mtk_dp,
+					      int channels)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_312C,
+			   (min(8, channels) - 1) << 8,
+			   ASP_HB2_DP_ENC0_P0_MASK | ASP_HB3_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_audio_set_divider(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
+			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2,
+			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_sdp_trigger_aui(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280,
+			   MTK_DP_SDP_AUI, SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280,
+			   SDP_PACKET_W_DP_ENC1_P0, SDP_PACKET_W_DP_ENC1_P0);
+}
+
+static void mtk_dp_sdp_set_data(struct mtk_dp *mtk_dp, u8 *data_bytes)
+{
+	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_ENC1_P0_3200,
+				data_bytes, 0x10);
+}
+
+static void mtk_dp_sdp_set_header_aui(struct mtk_dp *mtk_dp,
+				      struct dp_sdp_header *header)
+{
+	u32 db_addr = MTK_DP_ENC0_P0_30D8 + (MTK_DP_SDP_AUI - 1) * 8;
+
+	mtk_dp_bulk_16bit_write(mtk_dp, db_addr, (u8 *)header, 4);
+}
+
+static void mtk_dp_disable_sdp_aui(struct mtk_dp *mtk_dp)
+{
+	/* Disable periodic send */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A8 & 0xfffc, 0,
+			   0xff << ((MTK_DP_ENC0_P0_30A8 & 3) * 8));
+}
+
+static void mtk_dp_setup_sdp_aui(struct mtk_dp *mtk_dp,
+				 struct dp_sdp *sdp)
+{
+	u32 shift;
+
+	mtk_dp_sdp_set_data(mtk_dp, sdp->db);
+	mtk_dp_sdp_set_header_aui(mtk_dp, &sdp->sdp_header);
+	mtk_dp_disable_sdp_aui(mtk_dp);
+
+	shift = (MTK_DP_ENC0_P0_30A8 & 3) * 8;
+
+	mtk_dp_sdp_trigger_aui(mtk_dp);
+	/* Enable periodic sending */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A8 & 0xfffc,
+			   0x05 << shift, 0xff << shift);
+}
+
 static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
 {
 	mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640, DP_AUX_P0_3640_VAL);
@@ -1041,6 +1226,32 @@ static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
 		mtk_dp->data->smc_cmd, enable, res.a0, res.a1);
 }
 
+static void mtk_dp_audio_mute(struct mtk_dp *mtk_dp, bool mute)
+{
+	u32 val[3];
+
+	if (mute) {
+		val[0] = VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0 |
+			 VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0;
+		val[1] = 0;
+		val[2] = 0;
+	} else {
+		val[0] = 0;
+		val[1] = AU_EN_DP_ENC0_P0;
+		/* Send one every two frames */
+		val[2] = 0x0F;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030,
+			   val[0],
+			   VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0 |
+			   VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088,
+			   val[1], AU_EN_DP_ENC0_P0);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4,
+			   val[2], AU_TS_CFG_DP_ENC0_P0_MASK);
+}
+
 static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
 {
 	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
@@ -1080,6 +1291,76 @@ static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
 
 	mtk_dp->info.format = DP_PIXELFORMAT_RGB;
 	memset(&mtk_dp->info.vm, 0, sizeof(struct videomode));
+	mtk_dp->audio_enable = false;
+}
+
+static void mtk_dp_sdp_set_down_cnt_init(struct mtk_dp *mtk_dp,
+					 u32 sram_read_start)
+{
+	u32 sdp_down_cnt_init = 0;
+	struct drm_display_mode mode;
+	struct videomode *vm = &mtk_dp->info.vm;
+
+	drm_display_mode_from_videomode(vm, &mode);
+
+	if (mode.clock > 0)
+		sdp_down_cnt_init = sram_read_start *
+				    mtk_dp->train_info.link_rate * 2700 * 8 /
+				    (mode.clock * 4);
+
+	switch (mtk_dp->train_info.lane_count) {
+	case 1:
+		sdp_down_cnt_init = max_t(u32, sdp_down_cnt_init, 0x1A);
+		break;
+	case 2:
+		/* case for LowResolution && High Audio Sample Rate */
+		sdp_down_cnt_init = max_t(u32, sdp_down_cnt_init, 0x10);
+		sdp_down_cnt_init += mode.vtotal <= 525 ? 4 : 0;
+		break;
+	case 4:
+	default:
+		sdp_down_cnt_init = max_t(u32, sdp_down_cnt_init, 6);
+		break;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
+			   sdp_down_cnt_init,
+			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_sdp_set_down_cnt_init_in_hblank(struct mtk_dp *mtk_dp)
+{
+	int pix_clk_mhz;
+	u32 dc_offset;
+	u32 spd_down_cnt_init = 0;
+	struct drm_display_mode mode;
+	struct videomode *vm = &mtk_dp->info.vm;
+
+	drm_display_mode_from_videomode(vm, &mode);
+
+	pix_clk_mhz = mtk_dp->info.format == DP_PIXELFORMAT_YUV420 ?
+		      mode.clock / 2000 : mode.clock / 1000;
+
+	switch (mtk_dp->train_info.lane_count) {
+	case 1:
+		spd_down_cnt_init = 0x20;
+		break;
+	case 2:
+		dc_offset = (mode.vtotal <= 525) ? 0x14 : 0x00;
+		spd_down_cnt_init = 0x18 + dc_offset;
+		break;
+	case 4:
+	default:
+		dc_offset = (mode.vtotal <= 525) ? 0x08 : 0x00;
+		if (pix_clk_mhz > mtk_dp->train_info.link_rate * 27)
+			spd_down_cnt_init = 0x8;
+		else
+			spd_down_cnt_init = 0x10 + dc_offset;
+		break;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364, spd_down_cnt_init,
+			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
 }
 
 static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
@@ -1091,6 +1372,8 @@ static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
 				    MTK_DP_PIX_PER_ADDR);
 	mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
 	mtk_dp_setup_encoder(mtk_dp);
+	mtk_dp_sdp_set_down_cnt_init_in_hblank(mtk_dp);
+	mtk_dp_sdp_set_down_cnt_init(mtk_dp, sram_read_start);
 }
 
 static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
@@ -1342,6 +1625,20 @@ static int mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
 	return 0;
 }
 
+static bool mtk_dp_edid_parse_audio_capabilities(struct mtk_dp *mtk_dp,
+						 struct mtk_dp_audio_cfg *cfg)
+{
+	if (!mtk_dp->data->audio_supported)
+		return false;
+
+	if (mtk_dp->info.audio_cur_cfg.sad_count <= 0) {
+		drm_info(mtk_dp->drm_dev, "The SADs is NULL\n");
+		return false;
+	}
+
+	return true;
+}
+
 static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
 {
 	phy_reset(mtk_dp->phy);
@@ -1446,6 +1743,46 @@ static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
 	}
 }
 
+static void mtk_dp_audio_sdp_setup(struct mtk_dp *mtk_dp,
+				   struct mtk_dp_audio_cfg *cfg)
+{
+	struct dp_sdp sdp;
+	struct hdmi_audio_infoframe frame;
+
+	hdmi_audio_infoframe_init(&frame);
+	frame.coding_type = HDMI_AUDIO_CODING_TYPE_PCM;
+	frame.channels = cfg->channels;
+	frame.sample_frequency = cfg->sample_rate;
+
+	switch (cfg->word_length_bits) {
+	case 16:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
+		break;
+	case 20:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_20;
+		break;
+	case 24:
+	default:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_24;
+		break;
+	}
+
+	hdmi_audio_infoframe_pack_for_dp(&frame, &sdp, MTK_DP_VERSION);
+
+	mtk_dp_audio_sdp_asp_set_channels(mtk_dp, cfg->channels);
+	mtk_dp_setup_sdp_aui(mtk_dp, &sdp);
+}
+
+static void mtk_dp_audio_setup(struct mtk_dp *mtk_dp,
+			       struct mtk_dp_audio_cfg *cfg)
+{
+	mtk_dp_audio_sdp_setup(mtk_dp, cfg);
+	mtk_dp_audio_channel_status_set(mtk_dp, cfg);
+
+	mtk_dp_audio_setup_channels(mtk_dp, cfg);
+	mtk_dp_audio_set_divider(mtk_dp);
+}
+
 static int mtk_dp_video_config(struct mtk_dp *mtk_dp)
 {
 	mtk_dp_config_mn_mode(mtk_dp);
@@ -1489,6 +1826,10 @@ static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
 		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
 
 		if (!mtk_dp->train_info.cable_plugged_in) {
+			mtk_dp_disable_sdp_aui(mtk_dp);
+			memset(&mtk_dp->info.audio_cur_cfg, 0,
+			       sizeof(mtk_dp->info.audio_cur_cfg));
+
 			mtk_dp->need_debounce = false;
 			mod_timer(&mtk_dp->debounce_timer,
 				  jiffies + msecs_to_jiffies(100) - 1);
@@ -1575,6 +1916,16 @@ static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
 	return 0;
 }
 
+static void mtk_dp_update_plugged_status(struct mtk_dp *mtk_dp)
+{
+	mutex_lock(&mtk_dp->update_plugged_status_lock);
+	if (mtk_dp->plugged_cb && mtk_dp->codec_dev)
+		mtk_dp->plugged_cb(mtk_dp->codec_dev,
+				   mtk_dp->enabled &
+				   mtk_dp->info.audio_cur_cfg.detect_monitor);
+	mutex_unlock(&mtk_dp->update_plugged_status_lock);
+}
+
 static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
 {
 	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
@@ -1615,7 +1966,6 @@ static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
 					   DP_PWR_STATE_MASK);
 		}
 	}
-
 	return ret;
 }
 
@@ -1625,6 +1975,8 @@ static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
 	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
 	bool enabled = mtk_dp->enabled;
 	struct edid *new_edid = NULL;
+	struct mtk_dp_audio_cfg *audio_caps = &mtk_dp->info.audio_cur_cfg;
+	struct cea_sad *sads;
 
 	if (!enabled) {
 		drm_bridge_chain_pre_enable(bridge);
@@ -1650,6 +2002,11 @@ static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
 		new_edid = NULL;
 	}
 
+	if (new_edid) {
+		audio_caps->sad_count = drm_edid_to_sad(new_edid, &sads);
+		audio_caps->detect_monitor = drm_detect_monitor_audio(new_edid);
+	}
+
 	if (!enabled) {
 		/* power off panel */
 		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
@@ -1843,7 +2200,19 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
 
 	mtk_dp_video_enable(mtk_dp, true);
 
+	mtk_dp->audio_enable =
+		mtk_dp_edid_parse_audio_capabilities(mtk_dp,
+						     &mtk_dp->info.audio_cur_cfg);
+	if (mtk_dp->audio_enable) {
+		mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_cur_cfg);
+		mtk_dp_audio_mute(mtk_dp, false);
+	} else {
+		memset(&mtk_dp->info.audio_cur_cfg, 0,
+		       sizeof(mtk_dp->info.audio_cur_cfg));
+	}
+
 	mtk_dp->enabled = true;
+	mtk_dp_update_plugged_status(mtk_dp);
 
 	return;
 power_off_aux:
@@ -1858,7 +2227,9 @@ static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
 	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
 
 	mtk_dp->enabled = false;
+	mtk_dp_update_plugged_status(mtk_dp);
 	mtk_dp_video_enable(mtk_dp, false);
+	mtk_dp_audio_mute(mtk_dp, true);
 
 	if (mtk_dp->train_info.cable_plugged_in) {
 		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
@@ -2015,6 +2386,100 @@ static void mtk_dp_debounce_timer(struct timer_list *t)
 	mtk_dp->need_debounce = true;
 }
 
+/*
+ * HDMI audio codec callbacks
+ */
+static int mtk_dp_audio_hw_params(struct device *dev, void *data,
+				  struct hdmi_codec_daifmt *daifmt,
+				  struct hdmi_codec_params *params)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	if (!mtk_dp->enabled) {
+		dev_err(mtk_dp->dev, "%s, DP is not ready!\n", __func__);
+		return -ENODEV;
+	}
+
+	mtk_dp->info.audio_cur_cfg.channels = params->cea.channels;
+	mtk_dp->info.audio_cur_cfg.sample_rate = params->sample_rate;
+
+	mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_cur_cfg);
+
+	return 0;
+}
+
+static int mtk_dp_audio_startup(struct device *dev, void *data)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_audio_mute(mtk_dp, false);
+
+	return 0;
+}
+
+static void mtk_dp_audio_shutdown(struct device *dev, void *data)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_audio_mute(mtk_dp, true);
+}
+
+static int mtk_dp_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
+				size_t len)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	if (mtk_dp->enabled)
+		memcpy(buf, mtk_dp->conn->eld, len);
+	else
+		memset(buf, 0, len);
+
+	return 0;
+}
+
+static int mtk_dp_audio_hook_plugged_cb(struct device *dev, void *data,
+					hdmi_codec_plugged_cb fn,
+					struct device *codec_dev)
+{
+	struct mtk_dp *mtk_dp = data;
+
+	mutex_lock(&mtk_dp->update_plugged_status_lock);
+	mtk_dp->plugged_cb = fn;
+	mtk_dp->codec_dev = codec_dev;
+	mutex_unlock(&mtk_dp->update_plugged_status_lock);
+
+	mtk_dp_update_plugged_status(mtk_dp);
+
+	return 0;
+}
+
+static const struct hdmi_codec_ops mtk_dp_audio_codec_ops = {
+	.hw_params = mtk_dp_audio_hw_params,
+	.audio_startup = mtk_dp_audio_startup,
+	.audio_shutdown = mtk_dp_audio_shutdown,
+	.get_eld = mtk_dp_audio_get_eld,
+	.hook_plugged_cb = mtk_dp_audio_hook_plugged_cb,
+	.no_capture_mute = 1,
+};
+
+static int mtk_dp_register_audio_driver(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+	struct hdmi_codec_pdata codec_data = {
+		.ops = &mtk_dp_audio_codec_ops,
+		.max_i2s_channels = 8,
+		.i2s = 1,
+		.data = mtk_dp,
+	};
+
+	mtk_dp->audio_pdev = platform_device_register_data(dev,
+							   HDMI_CODEC_DRV_NAME,
+							   PLATFORM_DEVID_AUTO,
+							   &codec_data,
+							   sizeof(codec_data));
+	return PTR_ERR_OR_ZERO(mtk_dp->audio_pdev);
+}
+
 static int mtk_dp_probe(struct platform_device *pdev)
 {
 	struct mtk_dp *mtk_dp;
@@ -2059,8 +2524,19 @@ static int mtk_dp_probe(struct platform_device *pdev)
 		return dev_err_probe(dev, ret,
 				     "failed to request mediatek dptx irq\n");
 
+	mutex_init(&mtk_dp->update_plugged_status_lock);
+
 	platform_set_drvdata(pdev, mtk_dp);
 
+	if (mtk_dp->data->audio_supported) {
+		ret = mtk_dp_register_audio_driver(dev);
+		if (ret) {
+			dev_err(dev, "Failed to register audio driver: %d\n",
+				ret);
+			return ret;
+		}
+	}
+
 	mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
 							PLATFORM_DEVID_AUTO,
 							&mtk_dp->regs,
@@ -2106,6 +2582,8 @@ static int mtk_dp_remove(struct platform_device *pdev)
 	del_timer_sync(&mtk_dp->debounce_timer);
 	drm_bridge_remove(&mtk_dp->bridge);
 	platform_device_unregister(mtk_dp->phy_dev);
+	if (mtk_dp->audio_pdev)
+		platform_device_unregister(mtk_dp->audio_pdev);
 
 	return 0;
 }
@@ -2141,12 +2619,14 @@ static const struct mtk_dp_data mt8195_edp_data = {
 	.bridge_type = DRM_MODE_CONNECTOR_eDP,
 	.smc_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
 	.efuse_fmt = mt8195_edp_efuse_fmt,
+	.audio_supported = false,
 };
 
 static const struct mtk_dp_data mt8195_dp_data = {
 	.bridge_type = DRM_MODE_CONNECTOR_DisplayPort,
 	.smc_cmd = MTK_DP_SIP_ATF_VIDEO_UNMUTE,
 	.efuse_fmt = mt8195_dp_efuse_fmt,
+	.audio_supported = true,
 };
 
 static const struct of_device_id mtk_dp_of_match[] = {
diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
index 3f01ba44871f..096ad6572a5e 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp_reg.h
+++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
@@ -115,6 +115,8 @@
 #define HSW_SEL_DP_ENC0_P0				BIT(7)
 #define VSP_SEL_DP_ENC0_P0				BIT(8)
 #define VSW_SEL_DP_ENC0_P0				BIT(9)
+#define VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0		BIT(11)
+#define VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0		BIT(12)
 #define MTK_DP_ENC0_P0_3034			0x3034
 #define MTK_DP_ENC0_P0_3038			0x3038
 #define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK		BIT(11)
@@ -139,6 +141,38 @@
 #define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK		BIT(8)
 #define MTK_DP_ENC0_P0_3064			0x3064
 #define HDE_NUM_LAST_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3088			0x3088
+#define AU_EN_DP_ENC0_P0				BIT(6)
+#define AUDIO_8CH_EN_DP_ENC0_P0_MASK			BIT(7)
+#define AUDIO_8CH_SEL_DP_ENC0_P0_MASK			BIT(8)
+#define AUDIO_2CH_EN_DP_ENC0_P0_MASK			BIT(14)
+#define AUDIO_2CH_SEL_DP_ENC0_P0_MASK			BIT(15)
+#define MTK_DP_ENC0_P0_308C			0x308c
+#define CH_STATUS_0_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3090			0x3090
+#define CH_STATUS_1_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3094			0x3094
+#define CH_STATUS_2_DP_ENC0_P0_MASK			GENMASK(7, 0)
+#define MTK_DP_ENC0_P0_30A0			0x30a0
+#define DP_ENC0_30A0_MASK				(BIT(7) | BIT(8) | BIT(12))
+#define MTK_DP_ENC0_P0_30A4			0x30a4
+#define AU_TS_CFG_DP_ENC0_P0_MASK			GENMASK(7, 0)
+#define MTK_DP_ENC0_P0_30A8			0x30a8
+#define MTK_DP_ENC0_P0_30BC			0x30bc
+#define ISRC_CONT_DP_ENC0_P0				BIT(0)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK	GENMASK(10, 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_2	(1 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_4	(2 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_8	(3 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2	(5 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_4	(6 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_8	(7 << 8)
+#define MTK_DP_ENC0_P0_30D8			0x30d8
+#define MTK_DP_ENC0_P0_312C			0x312c
+#define ASP_HB2_DP_ENC0_P0_MASK				GENMASK(7, 0)
+#define ASP_HB3_DP_ENC0_P0_MASK				GENMASK(15, 8)
+#define MTK_DP_ENC0_P0_3130			0x3130
+#define MTK_DP_ENC0_P0_3138			0x3138
 #define MTK_DP_ENC0_P0_3154			0x3154
 #define PGEN_HTOTAL_DP_ENC0_P0_MASK			GENMASK(13, 0)
 #define MTK_DP_ENC0_P0_3158			0x3158
@@ -167,9 +201,23 @@
 #define ISRC1_HB3_DP_ENC0_P0_MASK			GENMASK(15, 8)
 
 /* offset: ENC1_OFFSET (0x3200) */
+#define MTK_DP_ENC1_P0_3200			0x3200
+#define MTK_DP_ENC1_P0_3280			0x3280
+#define SDP_PACKET_TYPE_DP_ENC1_P0_MASK			GENMASK(4, 0)
+#define SDP_PACKET_W_DP_ENC1_P0				BIT(5)
+#define SDP_PACKET_W_DP_ENC1_P0_MASK			BIT(5)
+#define MTK_DP_ENC1_P0_328C			0x328c
+#define VSC_DATA_RDY_VESA_DP_ENC1_P0_MASK		BIT(7)
 #define MTK_DP_ENC1_P0_3300			0x3300
 #define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL		2
 #define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK		GENMASK(9, 8)
+#define MTK_DP_ENC1_P0_3304			0x3304
+#define AU_PRTY_REGEN_DP_ENC1_P0_MASK			BIT(8)
+#define AU_CH_STS_REGEN_DP_ENC1_P0_MASK			BIT(9)
+#define AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK	BIT(12)
+#define MTK_DP_ENC1_P0_3324			0x3324
+#define AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK		GENMASK(9, 8)
+#define AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX		0
 #define MTK_DP_ENC1_P0_3364			0x3364
 #define SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL		0x20
 #define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK	GENMASK(11, 0)
@@ -186,6 +234,9 @@
 							 VIDEO_STABLE_CNT_THRD_DP_ENC1_P0 | \
 							 SDP_DP13_EN_DP_ENC1_P0 | \
 							 BS2BS_MODE_DP_ENC1_P0)
+#define MTK_DP_ENC1_P0_33F4			0x33f4
+#define DP_ENC_DUMMY_RW_1_AUDIO_RST_EN			BIT(0)
+#define DP_ENC_DUMMY_RW_1				BIT(9)
 
 /* offset: TRANS_OFFSET (0x3400) */
 #define MTK_DP_TRANS_P0_3400				0x3400
-- 
2.18.0


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

* [PATCH v17 10/10] drm/mediatek: dp: Audio support for MT8195
@ 2022-09-01  4:41   ` Bo-Chen Chen
  0 siblings, 0 replies; 73+ messages in thread
From: Bo-Chen Chen @ 2022-09-01  4:41 UTC (permalink / raw)
  To: chunkuang.hu, p.zabel, daniel, robh+dt, krzysztof.kozlowski+dt,
	mripard, tzimmermann, matthias.bgg, deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	ck.hu, liangxu.xu, dri-devel, linux-mediatek, devicetree,
	linux-kernel, linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group, Bo-Chen Chen

From: Guillaume Ranquet <granquet@baylibre.com>

This patch adds audio support to the DP driver for MT8195 with up to 8
channels.

Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c     | 482 +++++++++++++++++++++++++-
 drivers/gpu/drm/mediatek/mtk_dp_reg.h |  51 +++
 2 files changed, 532 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index dd34dae417e5..dfa942ca62da 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -29,6 +29,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/soc/mediatek/mtk_sip_svc.h>
+#include <sound/hdmi-codec.h>
 #include <video/videomode.h>
 
 #include "mtk_dp_reg.h"
@@ -47,6 +48,8 @@
 #define MTK_DP_TBC_BUF_READ_START_ADDR 0x8
 #define MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY 5
 #define MTK_DP_TRAIN_DOWNSCALE_RETRY 10
+#define MTK_DP_VERSION 0x11
+#define MTK_DP_SDP_AUI 0x4
 
 enum {
 	MTK_DP_CAL_GLB_BIAS_TRIM = 0,
@@ -71,9 +74,18 @@ struct mtk_dp_train_info {
 	unsigned int channel_eq_pattern;
 };
 
+struct mtk_dp_audio_cfg {
+	bool detect_monitor;
+	int sad_count;
+	int sample_rate;
+	int word_length_bits;
+	int channels;
+};
+
 struct mtk_dp_info {
 	enum dp_pixelformat format;
 	struct videomode vm;
+	struct mtk_dp_audio_cfg audio_cur_cfg;
 };
 
 struct mtk_dp_efuse_fmt {
@@ -111,12 +123,22 @@ struct mtk_dp {
 	struct phy *phy;
 	struct regmap *regs;
 	struct timer_list debounce_timer;
+
+	/* For audio */
+	bool audio_enable;
+	hdmi_codec_plugged_cb plugged_cb;
+	struct platform_device *audio_pdev;
+
+	struct device *codec_dev;
+	/* protect the plugged_cb as it's used in both bridge ops and audio */
+	struct mutex update_plugged_status_lock;
 };
 
 struct mtk_dp_data {
 	int bridge_type;
 	unsigned int smc_cmd;
 	const struct mtk_dp_efuse_fmt *efuse_fmt;
+	bool audio_supported;
 };
 
 static const struct mtk_dp_efuse_fmt mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = {
@@ -512,6 +534,169 @@ static void mtk_dp_pg_enable(struct mtk_dp *mtk_dp, bool enable)
 			   PGEN_PATTERN_SEL_VAL << 4, PGEN_PATTERN_SEL_MASK);
 }
 
+static void mtk_dp_audio_setup_channels(struct mtk_dp *mtk_dp,
+					struct mtk_dp_audio_cfg *cfg)
+{
+	u32 channel_enable_bits;
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3324,
+			   AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX,
+			   AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK);
+
+	/* audio channel count change reset */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4,
+			   DP_ENC_DUMMY_RW_1, DP_ENC_DUMMY_RW_1);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
+			   AU_PRTY_REGEN_DP_ENC1_P0_MASK |
+			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK |
+			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK,
+			   AU_PRTY_REGEN_DP_ENC1_P0_MASK |
+			   AU_CH_STS_REGEN_DP_ENC1_P0_MASK |
+			   AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK);
+
+	switch (cfg->channels) {
+	case 2:
+		channel_enable_bits = AUDIO_2CH_SEL_DP_ENC0_P0_MASK |
+				      AUDIO_2CH_EN_DP_ENC0_P0_MASK;
+		break;
+	case 8:
+	default:
+		channel_enable_bits = AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
+				      AUDIO_8CH_EN_DP_ENC0_P0_MASK;
+		break;
+	}
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088,
+			   channel_enable_bits | AU_EN_DP_ENC0_P0,
+			   AUDIO_2CH_SEL_DP_ENC0_P0_MASK |
+			   AUDIO_2CH_EN_DP_ENC0_P0_MASK |
+			   AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
+			   AUDIO_8CH_EN_DP_ENC0_P0_MASK |
+			   AU_EN_DP_ENC0_P0);
+
+	/* audio channel count change reset */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, 0, DP_ENC_DUMMY_RW_1);
+
+	/* enable audio reset */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4,
+			   DP_ENC_DUMMY_RW_1_AUDIO_RST_EN,
+			   DP_ENC_DUMMY_RW_1_AUDIO_RST_EN);
+}
+
+static void mtk_dp_audio_channel_status_set(struct mtk_dp *mtk_dp,
+					    struct mtk_dp_audio_cfg *cfg)
+{
+	struct snd_aes_iec958 iec = { 0 };
+
+	switch (cfg->sample_rate) {
+	case 32000:
+		iec.status[3] = IEC958_AES3_CON_FS_32000;
+		break;
+	case 44100:
+		iec.status[3] = IEC958_AES3_CON_FS_44100;
+		break;
+	case 48000:
+		iec.status[3] = IEC958_AES3_CON_FS_48000;
+		break;
+	case 88200:
+		iec.status[3] = IEC958_AES3_CON_FS_88200;
+		break;
+	case 96000:
+		iec.status[3] = IEC958_AES3_CON_FS_96000;
+		break;
+	case 192000:
+		iec.status[3] = IEC958_AES3_CON_FS_192000;
+		break;
+	default:
+		iec.status[3] = IEC958_AES3_CON_FS_NOTID;
+		break;
+	}
+
+	switch (cfg->word_length_bits) {
+	case 16:
+		iec.status[4] = IEC958_AES4_CON_WORDLEN_20_16;
+		break;
+	case 20:
+		iec.status[4] = IEC958_AES4_CON_WORDLEN_20_16 |
+				IEC958_AES4_CON_MAX_WORDLEN_24;
+		break;
+	case 24:
+		iec.status[4] = IEC958_AES4_CON_WORDLEN_24_20 |
+				IEC958_AES4_CON_MAX_WORDLEN_24;
+		break;
+	default:
+		iec.status[4] = IEC958_AES4_CON_WORDLEN_NOTID;
+	}
+
+	/* IEC 60958 consumer channel status bits */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_308C,
+			   0, CH_STATUS_0_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3090,
+			   iec.status[3] << 8, CH_STATUS_1_DP_ENC0_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3094,
+			   iec.status[4], CH_STATUS_2_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_audio_sdp_asp_set_channels(struct mtk_dp *mtk_dp,
+					      int channels)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_312C,
+			   (min(8, channels) - 1) << 8,
+			   ASP_HB2_DP_ENC0_P0_MASK | ASP_HB3_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_audio_set_divider(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
+			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2,
+			   AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_sdp_trigger_aui(struct mtk_dp *mtk_dp)
+{
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280,
+			   MTK_DP_SDP_AUI, SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280,
+			   SDP_PACKET_W_DP_ENC1_P0, SDP_PACKET_W_DP_ENC1_P0);
+}
+
+static void mtk_dp_sdp_set_data(struct mtk_dp *mtk_dp, u8 *data_bytes)
+{
+	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_ENC1_P0_3200,
+				data_bytes, 0x10);
+}
+
+static void mtk_dp_sdp_set_header_aui(struct mtk_dp *mtk_dp,
+				      struct dp_sdp_header *header)
+{
+	u32 db_addr = MTK_DP_ENC0_P0_30D8 + (MTK_DP_SDP_AUI - 1) * 8;
+
+	mtk_dp_bulk_16bit_write(mtk_dp, db_addr, (u8 *)header, 4);
+}
+
+static void mtk_dp_disable_sdp_aui(struct mtk_dp *mtk_dp)
+{
+	/* Disable periodic send */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A8 & 0xfffc, 0,
+			   0xff << ((MTK_DP_ENC0_P0_30A8 & 3) * 8));
+}
+
+static void mtk_dp_setup_sdp_aui(struct mtk_dp *mtk_dp,
+				 struct dp_sdp *sdp)
+{
+	u32 shift;
+
+	mtk_dp_sdp_set_data(mtk_dp, sdp->db);
+	mtk_dp_sdp_set_header_aui(mtk_dp, &sdp->sdp_header);
+	mtk_dp_disable_sdp_aui(mtk_dp);
+
+	shift = (MTK_DP_ENC0_P0_30A8 & 3) * 8;
+
+	mtk_dp_sdp_trigger_aui(mtk_dp);
+	/* Enable periodic sending */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A8 & 0xfffc,
+			   0x05 << shift, 0xff << shift);
+}
+
 static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
 {
 	mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640, DP_AUX_P0_3640_VAL);
@@ -1041,6 +1226,32 @@ static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
 		mtk_dp->data->smc_cmd, enable, res.a0, res.a1);
 }
 
+static void mtk_dp_audio_mute(struct mtk_dp *mtk_dp, bool mute)
+{
+	u32 val[3];
+
+	if (mute) {
+		val[0] = VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0 |
+			 VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0;
+		val[1] = 0;
+		val[2] = 0;
+	} else {
+		val[0] = 0;
+		val[1] = AU_EN_DP_ENC0_P0;
+		/* Send one every two frames */
+		val[2] = 0x0F;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030,
+			   val[0],
+			   VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0 |
+			   VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088,
+			   val[1], AU_EN_DP_ENC0_P0);
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4,
+			   val[2], AU_TS_CFG_DP_ENC0_P0_MASK);
+}
+
 static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
 {
 	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
@@ -1080,6 +1291,76 @@ static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
 
 	mtk_dp->info.format = DP_PIXELFORMAT_RGB;
 	memset(&mtk_dp->info.vm, 0, sizeof(struct videomode));
+	mtk_dp->audio_enable = false;
+}
+
+static void mtk_dp_sdp_set_down_cnt_init(struct mtk_dp *mtk_dp,
+					 u32 sram_read_start)
+{
+	u32 sdp_down_cnt_init = 0;
+	struct drm_display_mode mode;
+	struct videomode *vm = &mtk_dp->info.vm;
+
+	drm_display_mode_from_videomode(vm, &mode);
+
+	if (mode.clock > 0)
+		sdp_down_cnt_init = sram_read_start *
+				    mtk_dp->train_info.link_rate * 2700 * 8 /
+				    (mode.clock * 4);
+
+	switch (mtk_dp->train_info.lane_count) {
+	case 1:
+		sdp_down_cnt_init = max_t(u32, sdp_down_cnt_init, 0x1A);
+		break;
+	case 2:
+		/* case for LowResolution && High Audio Sample Rate */
+		sdp_down_cnt_init = max_t(u32, sdp_down_cnt_init, 0x10);
+		sdp_down_cnt_init += mode.vtotal <= 525 ? 4 : 0;
+		break;
+	case 4:
+	default:
+		sdp_down_cnt_init = max_t(u32, sdp_down_cnt_init, 6);
+		break;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
+			   sdp_down_cnt_init,
+			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_sdp_set_down_cnt_init_in_hblank(struct mtk_dp *mtk_dp)
+{
+	int pix_clk_mhz;
+	u32 dc_offset;
+	u32 spd_down_cnt_init = 0;
+	struct drm_display_mode mode;
+	struct videomode *vm = &mtk_dp->info.vm;
+
+	drm_display_mode_from_videomode(vm, &mode);
+
+	pix_clk_mhz = mtk_dp->info.format == DP_PIXELFORMAT_YUV420 ?
+		      mode.clock / 2000 : mode.clock / 1000;
+
+	switch (mtk_dp->train_info.lane_count) {
+	case 1:
+		spd_down_cnt_init = 0x20;
+		break;
+	case 2:
+		dc_offset = (mode.vtotal <= 525) ? 0x14 : 0x00;
+		spd_down_cnt_init = 0x18 + dc_offset;
+		break;
+	case 4:
+	default:
+		dc_offset = (mode.vtotal <= 525) ? 0x08 : 0x00;
+		if (pix_clk_mhz > mtk_dp->train_info.link_rate * 27)
+			spd_down_cnt_init = 0x8;
+		else
+			spd_down_cnt_init = 0x10 + dc_offset;
+		break;
+	}
+
+	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364, spd_down_cnt_init,
+			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
 }
 
 static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
@@ -1091,6 +1372,8 @@ static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
 				    MTK_DP_PIX_PER_ADDR);
 	mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
 	mtk_dp_setup_encoder(mtk_dp);
+	mtk_dp_sdp_set_down_cnt_init_in_hblank(mtk_dp);
+	mtk_dp_sdp_set_down_cnt_init(mtk_dp, sram_read_start);
 }
 
 static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
@@ -1342,6 +1625,20 @@ static int mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
 	return 0;
 }
 
+static bool mtk_dp_edid_parse_audio_capabilities(struct mtk_dp *mtk_dp,
+						 struct mtk_dp_audio_cfg *cfg)
+{
+	if (!mtk_dp->data->audio_supported)
+		return false;
+
+	if (mtk_dp->info.audio_cur_cfg.sad_count <= 0) {
+		drm_info(mtk_dp->drm_dev, "The SADs is NULL\n");
+		return false;
+	}
+
+	return true;
+}
+
 static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
 {
 	phy_reset(mtk_dp->phy);
@@ -1446,6 +1743,46 @@ static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
 	}
 }
 
+static void mtk_dp_audio_sdp_setup(struct mtk_dp *mtk_dp,
+				   struct mtk_dp_audio_cfg *cfg)
+{
+	struct dp_sdp sdp;
+	struct hdmi_audio_infoframe frame;
+
+	hdmi_audio_infoframe_init(&frame);
+	frame.coding_type = HDMI_AUDIO_CODING_TYPE_PCM;
+	frame.channels = cfg->channels;
+	frame.sample_frequency = cfg->sample_rate;
+
+	switch (cfg->word_length_bits) {
+	case 16:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
+		break;
+	case 20:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_20;
+		break;
+	case 24:
+	default:
+		frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_24;
+		break;
+	}
+
+	hdmi_audio_infoframe_pack_for_dp(&frame, &sdp, MTK_DP_VERSION);
+
+	mtk_dp_audio_sdp_asp_set_channels(mtk_dp, cfg->channels);
+	mtk_dp_setup_sdp_aui(mtk_dp, &sdp);
+}
+
+static void mtk_dp_audio_setup(struct mtk_dp *mtk_dp,
+			       struct mtk_dp_audio_cfg *cfg)
+{
+	mtk_dp_audio_sdp_setup(mtk_dp, cfg);
+	mtk_dp_audio_channel_status_set(mtk_dp, cfg);
+
+	mtk_dp_audio_setup_channels(mtk_dp, cfg);
+	mtk_dp_audio_set_divider(mtk_dp);
+}
+
 static int mtk_dp_video_config(struct mtk_dp *mtk_dp)
 {
 	mtk_dp_config_mn_mode(mtk_dp);
@@ -1489,6 +1826,10 @@ static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
 		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
 
 		if (!mtk_dp->train_info.cable_plugged_in) {
+			mtk_dp_disable_sdp_aui(mtk_dp);
+			memset(&mtk_dp->info.audio_cur_cfg, 0,
+			       sizeof(mtk_dp->info.audio_cur_cfg));
+
 			mtk_dp->need_debounce = false;
 			mod_timer(&mtk_dp->debounce_timer,
 				  jiffies + msecs_to_jiffies(100) - 1);
@@ -1575,6 +1916,16 @@ static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
 	return 0;
 }
 
+static void mtk_dp_update_plugged_status(struct mtk_dp *mtk_dp)
+{
+	mutex_lock(&mtk_dp->update_plugged_status_lock);
+	if (mtk_dp->plugged_cb && mtk_dp->codec_dev)
+		mtk_dp->plugged_cb(mtk_dp->codec_dev,
+				   mtk_dp->enabled &
+				   mtk_dp->info.audio_cur_cfg.detect_monitor);
+	mutex_unlock(&mtk_dp->update_plugged_status_lock);
+}
+
 static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
 {
 	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
@@ -1615,7 +1966,6 @@ static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
 					   DP_PWR_STATE_MASK);
 		}
 	}
-
 	return ret;
 }
 
@@ -1625,6 +1975,8 @@ static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
 	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
 	bool enabled = mtk_dp->enabled;
 	struct edid *new_edid = NULL;
+	struct mtk_dp_audio_cfg *audio_caps = &mtk_dp->info.audio_cur_cfg;
+	struct cea_sad *sads;
 
 	if (!enabled) {
 		drm_bridge_chain_pre_enable(bridge);
@@ -1650,6 +2002,11 @@ static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
 		new_edid = NULL;
 	}
 
+	if (new_edid) {
+		audio_caps->sad_count = drm_edid_to_sad(new_edid, &sads);
+		audio_caps->detect_monitor = drm_detect_monitor_audio(new_edid);
+	}
+
 	if (!enabled) {
 		/* power off panel */
 		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
@@ -1843,7 +2200,19 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
 
 	mtk_dp_video_enable(mtk_dp, true);
 
+	mtk_dp->audio_enable =
+		mtk_dp_edid_parse_audio_capabilities(mtk_dp,
+						     &mtk_dp->info.audio_cur_cfg);
+	if (mtk_dp->audio_enable) {
+		mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_cur_cfg);
+		mtk_dp_audio_mute(mtk_dp, false);
+	} else {
+		memset(&mtk_dp->info.audio_cur_cfg, 0,
+		       sizeof(mtk_dp->info.audio_cur_cfg));
+	}
+
 	mtk_dp->enabled = true;
+	mtk_dp_update_plugged_status(mtk_dp);
 
 	return;
 power_off_aux:
@@ -1858,7 +2227,9 @@ static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
 	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
 
 	mtk_dp->enabled = false;
+	mtk_dp_update_plugged_status(mtk_dp);
 	mtk_dp_video_enable(mtk_dp, false);
+	mtk_dp_audio_mute(mtk_dp, true);
 
 	if (mtk_dp->train_info.cable_plugged_in) {
 		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
@@ -2015,6 +2386,100 @@ static void mtk_dp_debounce_timer(struct timer_list *t)
 	mtk_dp->need_debounce = true;
 }
 
+/*
+ * HDMI audio codec callbacks
+ */
+static int mtk_dp_audio_hw_params(struct device *dev, void *data,
+				  struct hdmi_codec_daifmt *daifmt,
+				  struct hdmi_codec_params *params)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	if (!mtk_dp->enabled) {
+		dev_err(mtk_dp->dev, "%s, DP is not ready!\n", __func__);
+		return -ENODEV;
+	}
+
+	mtk_dp->info.audio_cur_cfg.channels = params->cea.channels;
+	mtk_dp->info.audio_cur_cfg.sample_rate = params->sample_rate;
+
+	mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_cur_cfg);
+
+	return 0;
+}
+
+static int mtk_dp_audio_startup(struct device *dev, void *data)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_audio_mute(mtk_dp, false);
+
+	return 0;
+}
+
+static void mtk_dp_audio_shutdown(struct device *dev, void *data)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	mtk_dp_audio_mute(mtk_dp, true);
+}
+
+static int mtk_dp_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
+				size_t len)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+	if (mtk_dp->enabled)
+		memcpy(buf, mtk_dp->conn->eld, len);
+	else
+		memset(buf, 0, len);
+
+	return 0;
+}
+
+static int mtk_dp_audio_hook_plugged_cb(struct device *dev, void *data,
+					hdmi_codec_plugged_cb fn,
+					struct device *codec_dev)
+{
+	struct mtk_dp *mtk_dp = data;
+
+	mutex_lock(&mtk_dp->update_plugged_status_lock);
+	mtk_dp->plugged_cb = fn;
+	mtk_dp->codec_dev = codec_dev;
+	mutex_unlock(&mtk_dp->update_plugged_status_lock);
+
+	mtk_dp_update_plugged_status(mtk_dp);
+
+	return 0;
+}
+
+static const struct hdmi_codec_ops mtk_dp_audio_codec_ops = {
+	.hw_params = mtk_dp_audio_hw_params,
+	.audio_startup = mtk_dp_audio_startup,
+	.audio_shutdown = mtk_dp_audio_shutdown,
+	.get_eld = mtk_dp_audio_get_eld,
+	.hook_plugged_cb = mtk_dp_audio_hook_plugged_cb,
+	.no_capture_mute = 1,
+};
+
+static int mtk_dp_register_audio_driver(struct device *dev)
+{
+	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+	struct hdmi_codec_pdata codec_data = {
+		.ops = &mtk_dp_audio_codec_ops,
+		.max_i2s_channels = 8,
+		.i2s = 1,
+		.data = mtk_dp,
+	};
+
+	mtk_dp->audio_pdev = platform_device_register_data(dev,
+							   HDMI_CODEC_DRV_NAME,
+							   PLATFORM_DEVID_AUTO,
+							   &codec_data,
+							   sizeof(codec_data));
+	return PTR_ERR_OR_ZERO(mtk_dp->audio_pdev);
+}
+
 static int mtk_dp_probe(struct platform_device *pdev)
 {
 	struct mtk_dp *mtk_dp;
@@ -2059,8 +2524,19 @@ static int mtk_dp_probe(struct platform_device *pdev)
 		return dev_err_probe(dev, ret,
 				     "failed to request mediatek dptx irq\n");
 
+	mutex_init(&mtk_dp->update_plugged_status_lock);
+
 	platform_set_drvdata(pdev, mtk_dp);
 
+	if (mtk_dp->data->audio_supported) {
+		ret = mtk_dp_register_audio_driver(dev);
+		if (ret) {
+			dev_err(dev, "Failed to register audio driver: %d\n",
+				ret);
+			return ret;
+		}
+	}
+
 	mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
 							PLATFORM_DEVID_AUTO,
 							&mtk_dp->regs,
@@ -2106,6 +2582,8 @@ static int mtk_dp_remove(struct platform_device *pdev)
 	del_timer_sync(&mtk_dp->debounce_timer);
 	drm_bridge_remove(&mtk_dp->bridge);
 	platform_device_unregister(mtk_dp->phy_dev);
+	if (mtk_dp->audio_pdev)
+		platform_device_unregister(mtk_dp->audio_pdev);
 
 	return 0;
 }
@@ -2141,12 +2619,14 @@ static const struct mtk_dp_data mt8195_edp_data = {
 	.bridge_type = DRM_MODE_CONNECTOR_eDP,
 	.smc_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
 	.efuse_fmt = mt8195_edp_efuse_fmt,
+	.audio_supported = false,
 };
 
 static const struct mtk_dp_data mt8195_dp_data = {
 	.bridge_type = DRM_MODE_CONNECTOR_DisplayPort,
 	.smc_cmd = MTK_DP_SIP_ATF_VIDEO_UNMUTE,
 	.efuse_fmt = mt8195_dp_efuse_fmt,
+	.audio_supported = true,
 };
 
 static const struct of_device_id mtk_dp_of_match[] = {
diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
index 3f01ba44871f..096ad6572a5e 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp_reg.h
+++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
@@ -115,6 +115,8 @@
 #define HSW_SEL_DP_ENC0_P0				BIT(7)
 #define VSP_SEL_DP_ENC0_P0				BIT(8)
 #define VSW_SEL_DP_ENC0_P0				BIT(9)
+#define VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0		BIT(11)
+#define VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0		BIT(12)
 #define MTK_DP_ENC0_P0_3034			0x3034
 #define MTK_DP_ENC0_P0_3038			0x3038
 #define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK		BIT(11)
@@ -139,6 +141,38 @@
 #define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK		BIT(8)
 #define MTK_DP_ENC0_P0_3064			0x3064
 #define HDE_NUM_LAST_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3088			0x3088
+#define AU_EN_DP_ENC0_P0				BIT(6)
+#define AUDIO_8CH_EN_DP_ENC0_P0_MASK			BIT(7)
+#define AUDIO_8CH_SEL_DP_ENC0_P0_MASK			BIT(8)
+#define AUDIO_2CH_EN_DP_ENC0_P0_MASK			BIT(14)
+#define AUDIO_2CH_SEL_DP_ENC0_P0_MASK			BIT(15)
+#define MTK_DP_ENC0_P0_308C			0x308c
+#define CH_STATUS_0_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3090			0x3090
+#define CH_STATUS_1_DP_ENC0_P0_MASK			GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3094			0x3094
+#define CH_STATUS_2_DP_ENC0_P0_MASK			GENMASK(7, 0)
+#define MTK_DP_ENC0_P0_30A0			0x30a0
+#define DP_ENC0_30A0_MASK				(BIT(7) | BIT(8) | BIT(12))
+#define MTK_DP_ENC0_P0_30A4			0x30a4
+#define AU_TS_CFG_DP_ENC0_P0_MASK			GENMASK(7, 0)
+#define MTK_DP_ENC0_P0_30A8			0x30a8
+#define MTK_DP_ENC0_P0_30BC			0x30bc
+#define ISRC_CONT_DP_ENC0_P0				BIT(0)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK	GENMASK(10, 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_2	(1 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_4	(2 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_8	(3 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2	(5 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_4	(6 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_8	(7 << 8)
+#define MTK_DP_ENC0_P0_30D8			0x30d8
+#define MTK_DP_ENC0_P0_312C			0x312c
+#define ASP_HB2_DP_ENC0_P0_MASK				GENMASK(7, 0)
+#define ASP_HB3_DP_ENC0_P0_MASK				GENMASK(15, 8)
+#define MTK_DP_ENC0_P0_3130			0x3130
+#define MTK_DP_ENC0_P0_3138			0x3138
 #define MTK_DP_ENC0_P0_3154			0x3154
 #define PGEN_HTOTAL_DP_ENC0_P0_MASK			GENMASK(13, 0)
 #define MTK_DP_ENC0_P0_3158			0x3158
@@ -167,9 +201,23 @@
 #define ISRC1_HB3_DP_ENC0_P0_MASK			GENMASK(15, 8)
 
 /* offset: ENC1_OFFSET (0x3200) */
+#define MTK_DP_ENC1_P0_3200			0x3200
+#define MTK_DP_ENC1_P0_3280			0x3280
+#define SDP_PACKET_TYPE_DP_ENC1_P0_MASK			GENMASK(4, 0)
+#define SDP_PACKET_W_DP_ENC1_P0				BIT(5)
+#define SDP_PACKET_W_DP_ENC1_P0_MASK			BIT(5)
+#define MTK_DP_ENC1_P0_328C			0x328c
+#define VSC_DATA_RDY_VESA_DP_ENC1_P0_MASK		BIT(7)
 #define MTK_DP_ENC1_P0_3300			0x3300
 #define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL		2
 #define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK		GENMASK(9, 8)
+#define MTK_DP_ENC1_P0_3304			0x3304
+#define AU_PRTY_REGEN_DP_ENC1_P0_MASK			BIT(8)
+#define AU_CH_STS_REGEN_DP_ENC1_P0_MASK			BIT(9)
+#define AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK	BIT(12)
+#define MTK_DP_ENC1_P0_3324			0x3324
+#define AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK		GENMASK(9, 8)
+#define AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX		0
 #define MTK_DP_ENC1_P0_3364			0x3364
 #define SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL		0x20
 #define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK	GENMASK(11, 0)
@@ -186,6 +234,9 @@
 							 VIDEO_STABLE_CNT_THRD_DP_ENC1_P0 | \
 							 SDP_DP13_EN_DP_ENC1_P0 | \
 							 BS2BS_MODE_DP_ENC1_P0)
+#define MTK_DP_ENC1_P0_33F4			0x33f4
+#define DP_ENC_DUMMY_RW_1_AUDIO_RST_EN			BIT(0)
+#define DP_ENC_DUMMY_RW_1				BIT(9)
 
 /* offset: TRANS_OFFSET (0x3400) */
 #define MTK_DP_TRANS_P0_3400				0x3400
-- 
2.18.0


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

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

* Re: [PATCH v17 00/10] Add MT8195 DisplayPort driver
  2022-09-01  4:41 ` Bo-Chen Chen
  (?)
@ 2022-09-01 10:58   ` AngeloGioacchino Del Regno
  -1 siblings, 0 replies; 73+ messages in thread
From: AngeloGioacchino Del Regno @ 2022-09-01 10:58 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, ck.hu, liangxu.xu, dri-devel,
	linux-mediatek, devicetree, linux-kernel, linux-arm-kernel,
	linux-fbdev, Project_Global_Chrome_Upstream_Group

Il 01/09/22 06:41, Bo-Chen Chen ha scritto:
> This patch is separated from v10 which is including dp driver, phy driver
> and dpintf driver. This series is only contained the DisplayPort driver.

For the entire series:

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

Thanks!
Angelo

> 
> Bo-Chen Chen (4):
>    drm/mediatek: dp: Add multiple bridge types support
>    drm/mediatek: dp: Add multiple smc commands support
>    drm/mediatek: dp: Add multiple calibration data formats support
>    drm/mediatek: dp: Determine device of next_bridge
> 
> Guillaume Ranquet (2):
>    drm/mediatek: dp: Add MT8195 External DisplayPort support
>    drm/mediatek: dp: Audio support for MT8195
> 
> Jitao Shi (1):
>    drm/mediatek: dp: Add hpd debounce
> 
> Markus Schneider-Pargmann (3):
>    dt-bindings: mediatek,dp: Add Display Port binding
>    video/hdmi: Add audio_infoframe packing for DP
>    drm/mediatek: Add MT8195 Embedded DisplayPort driver
> 
>   .../display/mediatek/mediatek,dp.yaml         |  116 +
>   drivers/gpu/drm/mediatek/Kconfig              |    9 +
>   drivers/gpu/drm/mediatek/Makefile             |    2 +
>   drivers/gpu/drm/mediatek/mtk_dp.c             | 2661 +++++++++++++++++
>   drivers/gpu/drm/mediatek/mtk_dp_reg.h         |  356 +++
>   drivers/video/hdmi.c                          |   82 +-
>   include/drm/display/drm_dp.h                  |    2 +
>   include/linux/hdmi.h                          |    7 +-
>   8 files changed, 3215 insertions(+), 20 deletions(-)
>   create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
>   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> 




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

* Re: [PATCH v17 00/10] Add MT8195 DisplayPort driver
@ 2022-09-01 10:58   ` AngeloGioacchino Del Regno
  0 siblings, 0 replies; 73+ messages in thread
From: AngeloGioacchino Del Regno @ 2022-09-01 10:58 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, linux-mediatek, wenst,
	linux-arm-kernel

Il 01/09/22 06:41, Bo-Chen Chen ha scritto:
> This patch is separated from v10 which is including dp driver, phy driver
> and dpintf driver. This series is only contained the DisplayPort driver.

For the entire series:

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

Thanks!
Angelo

> 
> Bo-Chen Chen (4):
>    drm/mediatek: dp: Add multiple bridge types support
>    drm/mediatek: dp: Add multiple smc commands support
>    drm/mediatek: dp: Add multiple calibration data formats support
>    drm/mediatek: dp: Determine device of next_bridge
> 
> Guillaume Ranquet (2):
>    drm/mediatek: dp: Add MT8195 External DisplayPort support
>    drm/mediatek: dp: Audio support for MT8195
> 
> Jitao Shi (1):
>    drm/mediatek: dp: Add hpd debounce
> 
> Markus Schneider-Pargmann (3):
>    dt-bindings: mediatek,dp: Add Display Port binding
>    video/hdmi: Add audio_infoframe packing for DP
>    drm/mediatek: Add MT8195 Embedded DisplayPort driver
> 
>   .../display/mediatek/mediatek,dp.yaml         |  116 +
>   drivers/gpu/drm/mediatek/Kconfig              |    9 +
>   drivers/gpu/drm/mediatek/Makefile             |    2 +
>   drivers/gpu/drm/mediatek/mtk_dp.c             | 2661 +++++++++++++++++
>   drivers/gpu/drm/mediatek/mtk_dp_reg.h         |  356 +++
>   drivers/video/hdmi.c                          |   82 +-
>   include/drm/display/drm_dp.h                  |    2 +
>   include/linux/hdmi.h                          |    7 +-
>   8 files changed, 3215 insertions(+), 20 deletions(-)
>   create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
>   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> 




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

* Re: [PATCH v17 00/10] Add MT8195 DisplayPort driver
@ 2022-09-01 10:58   ` AngeloGioacchino Del Regno
  0 siblings, 0 replies; 73+ messages in thread
From: AngeloGioacchino Del Regno @ 2022-09-01 10:58 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, ck.hu, liangxu.xu, dri-devel,
	linux-mediatek, devicetree, linux-kernel, linux-arm-kernel,
	linux-fbdev, Project_Global_Chrome_Upstream_Group

Il 01/09/22 06:41, Bo-Chen Chen ha scritto:
> This patch is separated from v10 which is including dp driver, phy driver
> and dpintf driver. This series is only contained the DisplayPort driver.

For the entire series:

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

Thanks!
Angelo

> 
> Bo-Chen Chen (4):
>    drm/mediatek: dp: Add multiple bridge types support
>    drm/mediatek: dp: Add multiple smc commands support
>    drm/mediatek: dp: Add multiple calibration data formats support
>    drm/mediatek: dp: Determine device of next_bridge
> 
> Guillaume Ranquet (2):
>    drm/mediatek: dp: Add MT8195 External DisplayPort support
>    drm/mediatek: dp: Audio support for MT8195
> 
> Jitao Shi (1):
>    drm/mediatek: dp: Add hpd debounce
> 
> Markus Schneider-Pargmann (3):
>    dt-bindings: mediatek,dp: Add Display Port binding
>    video/hdmi: Add audio_infoframe packing for DP
>    drm/mediatek: Add MT8195 Embedded DisplayPort driver
> 
>   .../display/mediatek/mediatek,dp.yaml         |  116 +
>   drivers/gpu/drm/mediatek/Kconfig              |    9 +
>   drivers/gpu/drm/mediatek/Makefile             |    2 +
>   drivers/gpu/drm/mediatek/mtk_dp.c             | 2661 +++++++++++++++++
>   drivers/gpu/drm/mediatek/mtk_dp_reg.h         |  356 +++
>   drivers/video/hdmi.c                          |   82 +-
>   include/drm/display/drm_dp.h                  |    2 +
>   include/linux/hdmi.h                          |    7 +-
>   8 files changed, 3215 insertions(+), 20 deletions(-)
>   create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
>   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>   create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> 




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

* Re: [PATCH v17 03/10] drm/mediatek: Add MT8195 Embedded DisplayPort driver
  2022-09-01  4:41   ` Bo-Chen Chen
  (?)
@ 2022-09-02  7:17     ` CK Hu
  -1 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  7:17 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	liangxu.xu, dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This patch adds a embedded displayport driver for the MediaTek mt8195
> SoC.
> 
> It supports the MT8195, the embedded DisplayPort units. It offers
> DisplayPort 1.4 with up to 4 lanes.
> 
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so
> that
> both can work with the same register range. The phy driver sets
> device
> data that is read by the parent to get the phy device that can be
> used
> to control the phy properties.

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> This driver is based on an initial version by
> Jitao shi <jitao.shi@mediatek.com>
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/Kconfig      |    9 +
>  drivers/gpu/drm/mediatek/Makefile     |    2 +
>  drivers/gpu/drm/mediatek/mtk_dp.c     | 1999
> +++++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_dp_reg.h |  305 ++++
>  4 files changed, 2315 insertions(+)
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> 
> diff --git a/drivers/gpu/drm/mediatek/Kconfig
> b/drivers/gpu/drm/mediatek/Kconfig
> index 2976d21e9a34..e66f4a3b6be0 100644
> --- a/drivers/gpu/drm/mediatek/Kconfig
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -21,6 +21,15 @@ config DRM_MEDIATEK
>  	  This driver provides kernel mode setting and
>  	  buffer management to userspace.
>  
> +config DRM_MEDIATEK_DP
> +	tristate "DRM DPTX Support for MediaTek SoCs"
> +	depends on DRM_MEDIATEK
> +	select PHY_MTK_DP
> +	select DRM_DISPLAY_HELPER
> +	select DRM_DISPLAY_DP_HELPER
> +	help
> +	  DRM/KMS Display Port driver for MediaTek SoCs.
> +
>  config DRM_MEDIATEK_HDMI
>  	tristate "DRM HDMI Support for Mediatek SoCs"
>  	depends on DRM_MEDIATEK
> diff --git a/drivers/gpu/drm/mediatek/Makefile
> b/drivers/gpu/drm/mediatek/Makefile
> index 6e604a933ed0..3517d1c65cd7 100644
> --- a/drivers/gpu/drm/mediatek/Makefile
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -23,3 +23,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
>  			  mtk_hdmi_ddc.o
>  
>  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> +
> +obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> new file mode 100644
> index 000000000000..e2ec9b02b1aa
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -0,0 +1,1999 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019-2022 MediaTek Inc.
> + * Copyright (c) 2022 BayLibre
> + */
> +
> +#include <drm/display/drm_dp.h>
> +#include <drm/display/drm_dp_helper.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <linux/arm-smccc.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/media-bus-format.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/soc/mediatek/mtk_sip_svc.h>
> +#include <video/videomode.h>
> +
> +#include "mtk_dp_reg.h"
> +
> +#define MTK_DP_SIP_CONTROL_AARCH32	MTK_SIP_SMC_CMD(0x523)
> +#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE	(BIT(0) | BIT(5))
> +
> +#define MTK_DP_THREAD_CABLE_STATE_CHG	BIT(0)
> +#define MTK_DP_THREAD_HPD_EVENT		BIT(1)
> +
> +#define MTK_DP_4P1T 4
> +#define MTK_DP_HDE 2
> +#define MTK_DP_PIX_PER_ADDR 2
> +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x8
> +#define MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY 5
> +#define MTK_DP_TRAIN_DOWNSCALE_RETRY 10
> +
> +enum {
> +	MTK_DP_CAL_GLB_BIAS_TRIM = 0,
> +	MTK_DP_CAL_CLKTX_IMPSE,
> +	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0,
> +	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1,
> +	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2,
> +	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3,
> +	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0,
> +	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1,
> +	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2,
> +	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3,
> +	MTK_DP_CAL_MAX,
> +};
> +
> +struct mtk_dp_train_info {
> +	bool sink_ssc;
> +	bool cable_plugged_in;
> +	/* link_rate is in multiple of 0.27Gbps */
> +	int link_rate;
> +	int lane_count;
> +	unsigned int channel_eq_pattern;
> +};
> +
> +struct mtk_dp_info {
> +	enum dp_pixelformat format;
> +	struct videomode vm;
> +};
> +
> +struct mtk_dp_efuse_fmt {
> +	unsigned short idx;
> +	unsigned short shift;
> +	unsigned short mask;
> +	unsigned short min_val;
> +	unsigned short max_val;
> +	unsigned short default_val;
> +};
> +
> +struct mtk_dp {
> +	bool enabled;
> +	u8 max_lanes;
> +	u8 max_linkrate;
> +	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> +	u32 cal_data[MTK_DP_CAL_MAX];
> +	u32 irq_thread_handle;
> +	/* irq_thread_lock is used to protect irq_thread_handle */
> +	spinlock_t irq_thread_lock;
> +
> +	struct device *dev;
> +	struct drm_bridge bridge;
> +	struct drm_bridge *next_bridge;
> +	struct drm_connector *conn;
> +	struct drm_device *drm_dev;
> +	struct drm_dp_aux aux;
> +
> +	struct mtk_dp_info info;
> +	struct mtk_dp_train_info train_info;
> +
> +	struct platform_device *phy_dev;
> +	struct phy *phy;
> +	struct regmap *regs;
> +};
> +
> +static const struct mtk_dp_efuse_fmt
> mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
> +	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
> +		.idx = 3,
> +		.shift = 27,
> +		.mask = 0x1f,
> +		.min_val = 1,
> +		.max_val = 0x1e,
> +		.default_val = 0xf,
> +	},
> +	[MTK_DP_CAL_CLKTX_IMPSE] = {
> +		.idx = 0,
> +		.shift = 9,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = {
> +		.idx = 2,
> +		.shift = 28,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = {
> +		.idx = 2,
> +		.shift = 20,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = {
> +		.idx = 2,
> +		.shift = 12,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = {
> +		.idx = 2,
> +		.shift = 4,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = {
> +		.idx = 2,
> +		.shift = 24,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = {
> +		.idx = 2,
> +		.shift = 16,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = {
> +		.idx = 2,
> +		.shift = 8,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = {
> +		.idx = 2,
> +		.shift = 0,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +};
> +
> +static struct regmap_config mtk_dp_regmap_config = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +	.max_register = SEC_OFFSET + 0x90,
> +	.name = "mtk-dp-registers",
> +};
> +
> +static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
> +{
> +	return container_of(b, struct mtk_dp, bridge);
> +}
> +
> +static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
> +{
> +	u32 read_val;
> +	int ret;
> +
> +	ret = regmap_read(mtk_dp->regs, offset, &read_val);
> +	if (ret) {
> +		dev_err(mtk_dp->dev, "Failed to read register 0x%x:
> %d\n",
> +			offset, ret);
> +		return 0;
> +	}
> +
> +	return read_val;
> +}
> +
> +static int mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
> +{
> +	int ret = regmap_write(mtk_dp->regs, offset, val);
> +
> +	if (ret)
> +		dev_err(mtk_dp->dev,
> +			"Failed to write register 0x%x with value
> 0x%x\n",
> +			offset, val);
> +	return ret;
> +}
> +
> +static int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset,
> +			      u32 val, u32 mask)
> +{
> +	int ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
> +
> +	if (ret)
> +		dev_err(mtk_dp->dev,
> +			"Failed to update register 0x%x with value
> 0x%x, mask 0x%x\n",
> +			offset, val, mask);
> +	return ret;
> +}
> +
> +static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32
> offset, u8 *buf,
> +				    size_t length)
> +{
> +	int i;
> +
> +	/* 2 bytes per register */
> +	for (i = 0; i < length; i += 2) {
> +		u32 val = buf[i] | (i + 1 < length ? buf[i + 1] << 8 :
> 0);
> +
> +		if (mtk_dp_write(mtk_dp, offset + i * 2, val))
> +			return;
> +	}
> +}
> +
> +static void mtk_dp_msa_bypass_enable(struct mtk_dp *mtk_dp, bool
> enable)
> +{
> +	u32 mask = HTOTAL_SEL_DP_ENC0_P0 | VTOTAL_SEL_DP_ENC0_P0 |
> +		   HSTART_SEL_DP_ENC0_P0 | VSTART_SEL_DP_ENC0_P0 |
> +		   HWIDTH_SEL_DP_ENC0_P0 | VHEIGHT_SEL_DP_ENC0_P0 |
> +		   HSP_SEL_DP_ENC0_P0 | HSW_SEL_DP_ENC0_P0 |
> +		   VSP_SEL_DP_ENC0_P0 | VSW_SEL_DP_ENC0_P0;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, enable ? 0 :
> mask, mask);
> +}
> +
> +static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
> +{
> +	struct drm_display_mode mode;
> +	struct videomode *vm = &mtk_dp->info.vm;
> +
> +	drm_display_mode_from_videomode(vm, &mode);
> +
> +	/* horizontal */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010,
> +			   mode.htotal, HTOTAL_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
> +			   vm->hsync_len + vm->hback_porch,
> +			   HSTART_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> +			   vm->hsync_len, HSW_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> +			   0, HSP_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020,
> +			   vm->hactive, HWIDTH_SW_DP_ENC0_P0_MASK);
> +
> +	/* vertical */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014,
> +			   mode.vtotal, VTOTAL_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
> +			   vm->vsync_len + vm->vback_porch,
> +			   VSTART_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> +			   vm->vsync_len, VSW_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> +			   0, VSP_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024,
> +			   vm->vactive, VHEIGHT_SW_DP_ENC0_P0_MASK);
> +
> +	/* horizontal */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064,
> +			   vm->hactive, HDE_NUM_LAST_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154,
> +			   mode.htotal, PGEN_HTOTAL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
> +			   vm->hfront_porch,
> +			   PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C,
> +			   vm->hsync_len,
> +			   PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
> +			   vm->hback_porch + vm->hsync_len,
> +			   PGEN_HFDE_START_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164,
> +			   vm->hactive,
> +			   PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +
> +	/* vertical */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168,
> +			   mode.vtotal,
> +			   PGEN_VTOTAL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
> +			   vm->vfront_porch,
> +			   PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170,
> +			   vm->vsync_len,
> +			   PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
> +			   vm->vback_porch + vm->vsync_len,
> +			   PGEN_VFDE_START_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178,
> +			   vm->vactive,
> +			   PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +}
> +
> +static int mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
> +				   enum dp_pixelformat color_format)
> +{
> +	u32 val;
> +
> +	/* update MISC0 */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +			   color_format << DP_TEST_COLOR_FORMAT_SHIFT,
> +			   DP_TEST_COLOR_FORMAT_MASK);
> +
> +	switch (color_format) {
> +	case DP_PIXELFORMAT_YUV422:
> +		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
> +		break;
> +	case DP_PIXELFORMAT_RGB:
> +		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
> +		break;
> +	default:
> +		drm_warn(mtk_dp->drm_dev, "Unsupported color format:
> %d\n",
> +			 color_format);
> +		return -EINVAL;
> +	}
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   val, PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
> +	return 0;
> +}
> +
> +static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp)
> +{
> +	/* Only support 8 bits currently */
> +	/* Update MISC0 */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +			   DP_MSA_MISC_8_BPC, DP_TEST_BIT_DEPTH_MASK);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT,
> +			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_config_mn_mode(struct mtk_dp *mtk_dp)
> +{
> +	/* 0: hw mode, 1: sw mode */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +			   0, VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32
> val)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   val, SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   VIDEO_MN_GEN_EN_DP_ENC0_P0,
> +			   VIDEO_MN_GEN_EN_DP_ENC0_P0);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> +			   SDP_DOWN_CNT_DP_ENC0_P0_VAL,
> +			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +			   SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL,
> +			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK)
> ;
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
> +			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL << 8,
> +			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +			   FIFO_READ_START_POINT_DP_ENC1_P0_VAL << 12,
> +			   FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
> +	mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368, DP_ENC1_P0_3368_VAL);
> +}
> +
> +static void mtk_dp_pg_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038,
> +			   enable ? VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK :
> 0,
> +			   VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
> +			   PGEN_PATTERN_SEL_VAL << 4,
> PGEN_PATTERN_SEL_MASK);
> +}
> +
> +static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640, DP_AUX_P0_3640_VAL);
> +}
> +
> +static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32
> addr)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644,
> +			   cmd, MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648,
> +			   addr,
> MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C,
> +			   addr >> 16,
> MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_clear_fifo(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0,
> +			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0 |
> +			   PHY_FIFO_RST_AUX_TX_P0_MASK |
> +			   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
> +			   AUX_TX_REQUEST_READY_AUX_TX_P0,
> +			   AUX_TX_REQUEST_READY_AUX_TX_P0);
> +}
> +
> +static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8
> *buf,
> +				       size_t length)
> +{
> +	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf,
> length);
> +}
> +
> +static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> +				    size_t length, int read_delay)
> +{
> +	int read_pos;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> +			   0, AUX_RD_MODE_AUX_TX_P0_MASK);
> +
> +	for (read_pos = 0; read_pos < length; read_pos++) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> +				   AUX_RX_FIFO_READ_PULSE_TX_P0,
> +				   AUX_RX_FIFO_READ_PULSE_TX_P0);
> +
> +		/* Hardware needs time to update the data */
> +		usleep_range(read_delay, read_delay * 2);
> +		buf[read_pos] = (u8)(mtk_dp_read(mtk_dp,
> MTK_DP_AUX_P0_3620) &
> +				     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MA
> SK);
> +	}
> +}
> +
> +static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t
> length)
> +{
> +	if (length > 0) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +				   (length - 1) << 12,
> +				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +				   0,
> +				   AUX_NO_LENGTH_AUX_TX_P0 |
> +				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	} else {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +				   AUX_NO_LENGTH_AUX_TX_P0,
> +				   AUX_NO_LENGTH_AUX_TX_P0 |
> +				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	}
> +}
> +
> +static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp,
> bool is_read)
> +{
> +	int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
> +
> +	while (--wait_reply) {
> +		u32 aux_irq_status;
> +
> +		if (is_read) {
> +			u32 fifo_status = mtk_dp_read(mtk_dp,
> MTK_DP_AUX_P0_3618);
> +
> +			if (fifo_status &
> +			    (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
> +			     AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
> +				return 0;
> +			}
> +		}
> +
> +		aux_irq_status = mtk_dp_read(mtk_dp,
> MTK_DP_AUX_P0_3640);
> +		if (aux_irq_status &
> AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
> +			return 0;
> +
> +		if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0)
> +			return -ETIMEDOUT;
> +
> +		/* Give the hardware a chance to reach completion
> before retrying */
> +		usleep_range(100, 500);
> +	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool
> is_read, u8 cmd,
> +				  u32 addr, u8 *buf, size_t length)
> +{
> +	int ret;
> +	u32 reply_cmd;
> +
> +	if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
> +			(cmd == DP_AUX_NATIVE_READ && !length)))
> +		return -EINVAL;
> +
> +	if (!is_read)
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> +				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0,
> +				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0);
> +
> +	/* We need to clear fifo and irq before sending commands to the
> sink device. */
> +	mtk_dp_aux_clear_fifo(mtk_dp);
> +	mtk_dp_aux_irq_clear(mtk_dp);
> +
> +	mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
> +	mtk_dp_aux_set_length(mtk_dp, length);
> +
> +	if (!is_read) {
> +		if (length)
> +			mtk_dp_aux_fill_write_fifo(mtk_dp, buf,
> length);
> +
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> +				   AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_
> P0_MASK,
> +				   AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_
> P0_MASK);
> +	}
> +
> +	mtk_dp_aux_request_ready(mtk_dp);
> +
> +	/* Wait for feedback from sink device. */
> +	ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
> +
> +	reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
> +		    AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
> +
> +	if (ret || reply_cmd) {
> +		u32 phy_status = mtk_dp_read(mtk_dp,
> MTK_DP_AUX_P0_3628) &
> +				 AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
> +		if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
> +			drm_err(mtk_dp->drm_dev,
> +				"AUX Rx Aux hang, need SW reset\n");
> +			return -EIO;
> +		}
> +
> +		return -ETIMEDOUT;
> +	}
> +
> +	if (!length) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +				   0,
> +				   AUX_NO_LENGTH_AUX_TX_P0 |
> +				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	} else if (is_read) {
> +		int read_delay;
> +
> +		if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
> +		    cmd == DP_AUX_I2C_READ)
> +			read_delay = 500;
> +		else
> +			read_delay = 100;
> +
> +		mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length,
> read_delay);
> +	}
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int
> lane_num,
> +					  int swing_val, int
> preemphasis)
> +{
> +	u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
> +
> +	dev_dbg(mtk_dp->dev,
> +		"link training: swing_val = 0x%x, pre-emphasis =
> 0x%x\n",
> +		swing_val, preemphasis);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +			   swing_val << (DP_TX0_VOLT_SWING_SHIFT +
> lane_shift),
> +			   DP_TX0_VOLT_SWING_MASK << lane_shift);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +			   preemphasis << (DP_TX0_PRE_EMPH_SHIFT +
> lane_shift),
> +			   DP_TX0_PRE_EMPH_MASK << lane_shift);
> +}
> +
> +static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +			   0,
> +			   DP_TX0_VOLT_SWING_MASK |
> +			   DP_TX1_VOLT_SWING_MASK |
> +			   DP_TX2_VOLT_SWING_MASK |
> +			   DP_TX3_VOLT_SWING_MASK |
> +			   DP_TX0_PRE_EMPH_MASK |
> +			   DP_TX1_PRE_EMPH_MASK |
> +			   DP_TX2_PRE_EMPH_MASK |
> +			   DP_TX3_PRE_EMPH_MASK);
> +}
> +
> +static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +	u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
> +			 SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
> +
> +	if (irq_status) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
> +				   irq_status,
> SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
> +				   0, SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +	}
> +
> +	return irq_status;
> +}
> +
> +static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +	u32 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
> +			  IRQ_STATUS_DP_TRANS_P0_MASK) >> 12;
> +
> +	if (irq_status) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +				   irq_status,
> IRQ_CLR_DP_TRANS_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +				   0, IRQ_CLR_DP_TRANS_P0_MASK);
> +	}
> +
> +	return irq_status;
> +}
> +
> +static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +			   enable ? 0 :
> +			   IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
> +			   IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
> +			   IRQ_MASK_DP_TRANS_P0_INT_IRQ,
> +			   IRQ_MASK_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
> +			   XTAL_FREQ_DP_TRANS_P0_DEFAULT,
> +			   XTAL_FREQ_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> +			   FEC_CLOCK_EN_MODE_DP_TRANS_P0,
> +			   FEC_CLOCK_EN_MODE_DP_TRANS_P0);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> +			   AUDIO_CH_SRC_SEL_DP_ENC0_P0,
> +			   AUDIO_CH_SRC_SEL_DP_ENC0_P0);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
> +			   0, SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK,
> +			   IRQ_MASK_AUX_TOP_IRQ, IRQ_MASK_AUX_TOP_IRQ);
> +}
> +
> +static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp
> *mtk_dp)
> +{
> +	u32 val;
> +	/* Debounce threshold */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   8, HPD_DEB_THD_DP_TRANS_P0_MASK);
> +
> +	val = (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
> +	       HPD_INT_THD_DP_TRANS_P0_UPPER_1100US) << 4;
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   val, HPD_INT_THD_DP_TRANS_P0_MASK);
> +
> +	/*
> +	 * Connect threshold 1.5ms + 5 x 0.1ms = 2ms
> +	 * Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
> +	 */
> +	val = (5 << 8) | (5 << 12);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   val,
> +			   HPD_DISC_THD_DP_TRANS_P0_MASK |
> +			   HPD_CONN_THD_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
> +			   HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
> +			   HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
> +{
> +	/* modify timeout threshold = 0x1595 */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C,
> +			   AUX_TIMEOUT_THR_AUX_TX_P0_VAL,
> +			   AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658,
> +			   0, AUX_TX_OV_EN_AUX_TX_P0_MASK);
> +	/* 25 for 26M */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
> +			   AUX_TX_OVER_SAMPLE_RATE_FOR_26M << 8,
> +			   AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
> +	/* 13 for 26M */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
> +			   AUX_RX_UI_CNT_THR_AUX_FOR_26M,
> +			   AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
> +			   MTK_ATOP_EN_AUX_TX_P0,
> +			   MTK_ATOP_EN_AUX_TX_P0);
> +}
> +
> +static void mtk_dp_initialize_digital_settings(struct mtk_dp
> *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
> +			   0, VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
> +			   BS2BS_MODE_DP_ENC1_P0_VAL << 12,
> +			   BS2BS_MODE_DP_ENC1_P0_MASK);
> +
> +	/* dp tx encoder reset all sw */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0,
> +			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
> +
> +	/* Wait for sw reset to complete */
> +	usleep_range(1000, 5000);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +			   0, DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
> +}
> +
> +static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> +			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0,
> +			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
> +
> +	/* Wait for sw reset to complete */
> +	usleep_range(1000, 5000);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> +			   0,
> DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
> +}
> +
> +static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
> +			   lanes == 0 ? 0 : DP_TRANS_DUMMY_RW_0,
> +			   DP_TRANS_DUMMY_RW_0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +			   lanes, LANE_NUM_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
> +			   lanes << 2, LANE_NUM_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
> +{
> +	const struct mtk_dp_efuse_fmt *fmt;
> +	struct device *dev = mtk_dp->dev;
> +	struct nvmem_cell *cell;
> +	u32 *cal_data = mtk_dp->cal_data;
> +	u32 *buf;
> +	int i;
> +	size_t len;
> +
> +	cell = nvmem_cell_get(dev, "dp_calibration_data");
> +	if (IS_ERR(cell)) {
> +		dev_warn(dev, "Failed to get nvmem cell
> dp_calibration_data\n");
> +		goto use_default_val;
> +	}
> +
> +	buf = (u32 *)nvmem_cell_read(cell, &len);
> +	nvmem_cell_put(cell);
> +
> +	if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
> +		dev_warn(dev, "Failed to read nvmem_cell_read\n");
> +
> +		if (!IS_ERR(buf))
> +			kfree(buf);
> +
> +		goto use_default_val;
> +	}
> +
> +	for (i = 0; i < MTK_DP_CAL_MAX; i++) {
> +		fmt = &mtk_dp_efuse_data[i];
> +		cal_data[i] = (buf[fmt->idx] >> fmt->shift) & fmt-
> >mask;
> +
> +		if (cal_data[i] < fmt->min_val || cal_data[i] > fmt-
> >max_val) {
> +			dev_warn(mtk_dp->dev, "Invalid efuse data, idx
> = %d\n", i);
> +			kfree(buf);
> +			goto use_default_val;
> +		}
> +	}
> +	kfree(buf);
> +
> +	return;
> +
> +use_default_val:
> +	dev_warn(mtk_dp->dev, "Use default calibration data\n");
> +	for (i = 0; i < MTK_DP_CAL_MAX; i++)
> +		cal_data[i] = mtk_dp_efuse_data[i].default_val;
> +}
> +
> +static void mtk_dp_set_calibration_data(struct mtk_dp *mtk_dp)
> +{
> +	u32 *cal_data = mtk_dp->cal_data;
> +
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
> +			   cal_data[MTK_DP_CAL_CLKTX_IMPSE] << 20,
> +			   RG_CKM_PT0_CKTX_IMPSEL);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
> +			   cal_data[MTK_DP_CAL_GLB_BIAS_TRIM] << 16,
> +			   RG_XTP_GLB_BIAS_INTR_CTRL);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] <<
> 12,
> +			   RG_XTP_LN0_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] <<
> 16,
> +			   RG_XTP_LN0_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] <<
> 12,
> +			   RG_XTP_LN1_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] <<
> 16,
> +			   RG_XTP_LN1_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] <<
> 12,
> +			   RG_XTP_LN2_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] <<
> 16,
> +			   RG_XTP_LN2_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] <<
> 12,
> +			   RG_XTP_LN3_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] <<
> 16,
> +			   RG_XTP_LN3_TX_IMPSEL_NMOS);
> +}
> +
> +static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
> +				u32 link_rate, int lane_count)
> +{
> +	int ret;
> +	union phy_configure_opts phy_opts = {
> +		.dp = {
> +			.link_rate =
> drm_dp_bw_code_to_link_rate(link_rate) / 100,
> +			.set_rate = 1,
> +			.lanes = lane_count,
> +			.set_lanes = 1,
> +			.ssc = mtk_dp->train_info.sink_ssc,
> +		}
> +	};
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> DP_PWR_STATE_BANDGAP,
> +			   DP_PWR_STATE_MASK);
> +
> +	ret = phy_configure(mtk_dp->phy, &phy_opts);
> +	if (ret)
> +		return ret;
> +
> +	mtk_dp_set_calibration_data(mtk_dp);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL_LANE,
> DP_PWR_STATE_MASK);
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool
> enable)
> +{
> +	u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
> +		  POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
> +		  POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
> +		  POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580,
> +			   enable ? val : 0, val);
> +}
> +
> +static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int
> pattern)
> +{
> +	/* TPS1 */
> +	if (pattern == 1)
> +		mtk_dp_set_idle_pattern(mtk_dp, false);
> +
> +	mtk_dp_update_bits(mtk_dp,
> +			   MTK_DP_TRANS_P0_3400,
> +			   pattern ? BIT(pattern - 1) << 12 : 0,
> +			   PATTERN1_EN_DP_TRANS_P0_MASK |
> +			   PATTERN2_EN_DP_TRANS_P0_MASK |
> +			   PATTERN3_EN_DP_TRANS_P0_MASK |
> +			   PATTERN4_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +			   ENHANCED_FRAME_EN_DP_ENC0_P0,
> +			   ENHANCED_FRAME_EN_DP_ENC0_P0);
> +}
> +
> +static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool
> enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
> +			   enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
> +			   DP_SCR_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	struct arm_smccc_res res;
> +	u32 val = VIDEO_MUTE_SEL_DP_ENC0_P0 |
> +		  (enable ? VIDEO_MUTE_SW_DP_ENC0_P0 : 0);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +			   val,
> +			   VIDEO_MUTE_SEL_DP_ENC0_P0 |
> +			   VIDEO_MUTE_SW_DP_ENC0_P0);
> +
> +	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32,
> +		      MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable,
> +		      0, 0, 0, 0, 0, &res);
> +
> +	dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-
> 0x%lx\n",
> +		MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable, res.a0,
> res.a1);
> +}
> +
> +static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
> +			   0, SW_RST_B_PHYD);
> +
> +	/* Wait for power enable */
> +	usleep_range(10, 200);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
> +			   SW_RST_B_PHYD, SW_RST_B_PHYD);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL,
> DP_PWR_STATE_MASK);
> +	mtk_dp_write(mtk_dp, MTK_DP_1040,
> +		     RG_DPAUX_RX_VALID_DEGLITCH_EN |
> RG_XTP_GLB_CKDET_EN |
> +		     RG_DPAUX_RX_EN);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_0034, 0,
> DA_CKM_CKTX0_EN_FORCE_EN);
> +}
> +
> +static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_0034,
> +			   DA_CKM_CKTX0_EN_FORCE_EN,
> DA_CKM_CKTX0_EN_FORCE_EN);
> +
> +	/* Disable RX */
> +	mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
> +	mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
> +		     0x550 | FUSE_SEL | MEM_ISO_EN);
> +}
> +
> +static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp->train_info.link_rate = DP_LINK_BW_5_4;
> +	mtk_dp->train_info.lane_count = mtk_dp->max_lanes;
> +	mtk_dp->train_info.cable_plugged_in = false;
> +
> +	mtk_dp->info.format = DP_PIXELFORMAT_RGB;
> +	memset(&mtk_dp->info.vm, 0, sizeof(struct videomode));
> +}
> +
> +static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
> +{
> +	u32 sram_read_start = min_t(u32,
> MTK_DP_TBC_BUF_READ_START_ADDR,
> +				    mtk_dp->info.vm.hactive /
> +				    mtk_dp->train_info.lane_count /
> +				    MTK_DP_4P1T / MTK_DP_HDE /
> +				    MTK_DP_PIX_PER_ADDR);
> +	mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
> +	mtk_dp_setup_encoder(mtk_dp);
> +}
> +
> +static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_setup_tu(mtk_dp);
> +}
> +
> +static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int
> lanes,
> +					  u8 dpcd_adjust_req[2])
> +{
> +	int lane;
> +
> +	for (lane = 0; lane < lanes; ++lane) {
> +		u8 val;
> +		u8 swing;
> +		u8 preemphasis;
> +		int index = lane / 2;
> +		int shift = lane % 2 ?
> DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
> +
> +		swing = (dpcd_adjust_req[index] >> shift) &
> +			DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
> +		preemphasis = ((dpcd_adjust_req[index] >> shift) &
> +			       DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
> +			      DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
> +		val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
> +		      preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +
> +		if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
> +			val |= DP_TRAIN_MAX_SWING_REACHED;
> +		if (preemphasis == 3)
> +			val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> +
> +		mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing,
> preemphasis);
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET
> + lane,
> +				   val);
> +	}
> +}
> +
> +static void mtk_dp_pattern(struct mtk_dp *mtk_dp, bool is_tps1)
> +{
> +	int pattern;
> +	unsigned int aux_offset;
> +
> +	if (is_tps1) {
> +		pattern = 1;
> +		aux_offset = DP_LINK_SCRAMBLING_DISABLE |
> DP_TRAINING_PATTERN_1;
> +	} else {
> +		aux_offset = mtk_dp->train_info.channel_eq_pattern;
> +
> +		switch (mtk_dp->train_info.channel_eq_pattern) {
> +		case DP_TRAINING_PATTERN_4:
> +			pattern = 4;
> +			break;
> +		case DP_TRAINING_PATTERN_3:
> +			pattern = 3;
> +			aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
> +			break;
> +		case DP_TRAINING_PATTERN_2:
> +		default:
> +			pattern = 2;
> +			aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
> +			break;
> +		}
> +	}
> +
> +	mtk_dp_train_set_pattern(mtk_dp, pattern);
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> aux_offset);
> +}
> +
> +static int mtk_dp_train_setting(struct mtk_dp *mtk_dp, u8
> target_link_rate,
> +				u8 target_lane_count)
> +{
> +	int ret;
> +
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET,
> target_link_rate);
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> +			   target_lane_count |
> DP_LANE_COUNT_ENHANCED_FRAME_EN);
> +
> +	if (mtk_dp->train_info.sink_ssc)
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
> +				   DP_SPREAD_AMP_0_5);
> +
> +	mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
> +	ret = mtk_dp_phy_configure(mtk_dp, target_link_rate,
> target_lane_count);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(mtk_dp->dev,
> +		"Link train target_link_rate = 0x%x, target_lane_count
> = 0x%x\n",
> +		target_link_rate, target_lane_count);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_train_cr(struct mtk_dp *mtk_dp, u8
> target_lane_count)
> +{
> +	u8 lane_adjust[2] = {};
> +	u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +	u8 prev_lane_adjust = 0xff;
> +	int train_retries = 0;
> +	int voltage_retries = 0;
> +
> +	mtk_dp_pattern(mtk_dp, true);
> +
> +	/* In DP spec 1.4, the retry count of CR is defined as 10. */
> +	do {
> +		train_retries++;
> +		if (!mtk_dp->train_info.cable_plugged_in) {
> +			mtk_dp_train_set_pattern(mtk_dp, 0);
> +			return -ENODEV;
> +		}
> +
> +		drm_dp_dpcd_read(&mtk_dp->aux,
> DP_ADJUST_REQUEST_LANE0_1,
> +				 lane_adjust, sizeof(lane_adjust));
> +		mtk_dp_train_update_swing_pre(mtk_dp,
> target_lane_count,
> +					      lane_adjust);
> +
> +		drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
> +						       mtk_dp->rx_cap);
> +
> +		/* check link status from sink device */
> +		drm_dp_dpcd_read_link_status(&mtk_dp->aux,
> link_status);
> +		if (drm_dp_clock_recovery_ok(link_status,
> +					     target_lane_count)) {
> +			dev_dbg(mtk_dp->dev, "Link train CR pass\n");
> +			return 0;
> +		}
> +
> +		/*
> +		 * In DP spec 1.4, if current voltage level is the same
> +		 * with previous voltage level, we need to retry 5
> times.
> +		 */
> +		if (prev_lane_adjust == link_status[4]) {
> +			voltage_retries++;
> +			/*
> +			 * Condition of CR fail:
> +			 * 1. Failed to pass CR using the same voltage
> +			 *    level over five times.
> +			 * 2. Failed to pass CR when the current
> voltage
> +			 *    level is the same with previous voltage
> +			 *    level and reach max voltage level (3).
> +			 */
> +			if (voltage_retries >
> MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY ||
> +			    (prev_lane_adjust &
> DP_ADJUST_VOLTAGE_SWING_LANE0_MASK) == 3) {
> +				dev_dbg(mtk_dp->dev, "Link train CR
> fail\n");
> +				break;
> +			}
> +		} else {
> +			/*
> +			 * If the voltage level is changed, we need to
> +			 * re-calculate this retry count.
> +			 */
> +			voltage_retries = 0;
> +		}
> +		prev_lane_adjust = link_status[4];
> +	} while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
> +
> +	/* Failed to train CR, and disable pattern. */
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +			   DP_TRAINING_PATTERN_DISABLE);
> +	mtk_dp_train_set_pattern(mtk_dp, 0);
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_train_eq(struct mtk_dp *mtk_dp, u8
> target_lane_count)
> +{
> +	u8 lane_adjust[2] = {};
> +	u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +	int train_retries = 0;
> +
> +	mtk_dp_pattern(mtk_dp, false);
> +
> +	do {
> +		train_retries++;
> +		if (!mtk_dp->train_info.cable_plugged_in) {
> +			mtk_dp_train_set_pattern(mtk_dp, 0);
> +			return -ENODEV;
> +		}
> +
> +		drm_dp_dpcd_read(&mtk_dp->aux,
> DP_ADJUST_REQUEST_LANE0_1,
> +				 lane_adjust, sizeof(lane_adjust));
> +		mtk_dp_train_update_swing_pre(mtk_dp,
> target_lane_count,
> +					      lane_adjust);
> +
> +		drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
> +						   mtk_dp->rx_cap);
> +
> +		/* check link status from sink device */
> +		drm_dp_dpcd_read_link_status(&mtk_dp->aux,
> link_status);
> +		if (drm_dp_channel_eq_ok(link_status,
> target_lane_count)) {
> +			dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
> +
> +			/* Training done, and disable pattern. */
> +			drm_dp_dpcd_writeb(&mtk_dp->aux,
> DP_TRAINING_PATTERN_SET,
> +					   DP_TRAINING_PATTERN_DISABLE)
> ;
> +			mtk_dp_train_set_pattern(mtk_dp, 0);
> +			return 0;
> +		}
> +		dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
> +	} while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
> +
> +	/* Failed to train EQ, and disable pattern. */
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +			   DP_TRAINING_PATTERN_DISABLE);
> +	mtk_dp_train_set_pattern(mtk_dp, 0);
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
> +{
> +	u8 val;
> +	ssize_t ret;
> +
> +	drm_dp_read_dpcd_caps(&mtk_dp->aux, mtk_dp->rx_cap);
> +
> +	if (drm_dp_tps4_supported(mtk_dp->rx_cap))
> +		mtk_dp->train_info.channel_eq_pattern =
> DP_TRAINING_PATTERN_4;
> +	else if (drm_dp_tps3_supported(mtk_dp->rx_cap))
> +		mtk_dp->train_info.channel_eq_pattern =
> DP_TRAINING_PATTERN_3;
> +	else
> +		mtk_dp->train_info.channel_eq_pattern =
> DP_TRAINING_PATTERN_2;
> +
> +	mtk_dp->train_info.sink_ssc = drm_dp_max_downspread(mtk_dp-
> >rx_cap);
> +
> +	ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
> +	if (ret < 1) {
> +		drm_err(mtk_dp->drm_dev, "Read mstm cap failed\n");
> +		return ret == 0 ? -EIO : ret;
> +	}
> +
> +	if (val & DP_MST_CAP) {
> +		/* Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 */
> +		ret = drm_dp_dpcd_readb(&mtk_dp->aux,
> +					DP_DEVICE_SERVICE_IRQ_VECTOR_ES
> I0,
> +					&val);
> +		if (ret < 1) {
> +			drm_err(mtk_dp->drm_dev, "Read irq vector
> failed\n");
> +			return ret == 0 ? -EIO : ret;
> +		}
> +
> +		if (val)
> +			drm_dp_dpcd_writeb(&mtk_dp->aux,
> +					   DP_DEVICE_SERVICE_IRQ_VECTOR
> _ESI0,
> +					   val);
> +	}
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
> +{
> +	phy_reset(mtk_dp->phy);
> +	mtk_dp_reset_swing_pre_emphasis(mtk_dp);
> +}
> +
> +static int mtk_dp_training(struct mtk_dp *mtk_dp)
> +{
> +	int ret;
> +	u8 lane_count, link_rate, train_limit, max_link_rate;
> +
> +	link_rate = min_t(u8, mtk_dp->max_linkrate,
> +			  mtk_dp->rx_cap[DP_MAX_LINK_RATE]);
> +	max_link_rate = link_rate;
> +	lane_count = min_t(u8, mtk_dp->max_lanes,
> +			   drm_dp_max_lane_count(mtk_dp->rx_cap));
> +
> +	/*
> +	 * TPS are generated by the hardware pattern generator. From
> the
> +	 * hardware setting we need to disable this scramble setting
> before
> +	 * use the TPS pattern generator.
> +	 */
> +	mtk_dp_training_set_scramble(mtk_dp, false);
> +
> +	for (train_limit = 6; train_limit > 0; train_limit--) {
> +		mtk_dp_train_change_mode(mtk_dp);
> +
> +		ret = mtk_dp_train_setting(mtk_dp, link_rate,
> lane_count);
> +		if (ret)
> +			return ret;
> +
> +		ret = mtk_dp_train_cr(mtk_dp, lane_count);
> +		if (ret == -ENODEV) {
> +			return ret;
> +		} else if (ret) {
> +			/* reduce link rate */
> +			switch (link_rate) {
> +			case DP_LINK_BW_1_62:
> +				lane_count = lane_count / 2;
> +				link_rate = max_link_rate;
> +				if (lane_count == 0)
> +					return -EIO;
> +				break;
> +			case DP_LINK_BW_2_7:
> +				link_rate = DP_LINK_BW_1_62;
> +				break;
> +			case DP_LINK_BW_5_4:
> +				link_rate = DP_LINK_BW_2_7;
> +				break;
> +			case DP_LINK_BW_8_1:
> +				link_rate = DP_LINK_BW_5_4;
> +				break;
> +			default:
> +				return -EINVAL;
> +			};
> +			continue;
> +		}
> +
> +		ret = mtk_dp_train_eq(mtk_dp, lane_count);
> +		if (ret == -ENODEV) {
> +			return ret;
> +		} else if (ret) {
> +			/* reduce lane count */
> +			if (lane_count == 0)
> +				return -EIO;
> +			lane_count /= 2;
> +			continue;
> +		}
> +
> +		/* if we can run to this, training is done. */
> +		break;
> +	}
> +
> +	if (train_limit == 0)
> +		return -ETIMEDOUT;
> +
> +	mtk_dp->train_info.link_rate = link_rate;
> +	mtk_dp->train_info.lane_count = lane_count;
> +
> +	/*
> +	 * After training done, we need to output normal stream instead
> of TPS,
> +	 * so we need to enable scramble.
> +	 */
> +	mtk_dp_training_set_scramble(mtk_dp, true);
> +	mtk_dp_set_enhanced_frame_mode(mtk_dp);
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	/* the mute sequence is different between enable and disable */
> +	if (enable) {
> +		mtk_dp_msa_bypass_enable(mtk_dp, false);
> +		mtk_dp_pg_enable(mtk_dp, false);
> +		mtk_dp_set_tx_out(mtk_dp);
> +		mtk_dp_video_mute(mtk_dp, false);
> +	} else {
> +		mtk_dp_video_mute(mtk_dp, true);
> +		mtk_dp_pg_enable(mtk_dp, true);
> +		mtk_dp_msa_bypass_enable(mtk_dp, true);
> +	}
> +}
> +
> +static int mtk_dp_video_config(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_config_mn_mode(mtk_dp);
> +	mtk_dp_set_msa(mtk_dp);
> +	mtk_dp_set_color_depth(mtk_dp);
> +	return mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
> +}
> +
> +static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_set_idle_pattern(mtk_dp, true);
> +	mtk_dp_initialize_priv_data(mtk_dp);
> +
> +	mtk_dp_initialize_settings(mtk_dp);
> +	mtk_dp_initialize_aux_settings(mtk_dp);
> +	mtk_dp_initialize_digital_settings(mtk_dp);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
> +			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0,
> +			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0);
> +	mtk_dp_initialize_hpd_detect_settings(mtk_dp);
> +
> +	mtk_dp_digital_sw_reset(mtk_dp);
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev;
> +	unsigned long flags;
> +	u32 status;
> +
> +	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
> +	status = mtk_dp->irq_thread_handle;
> +	mtk_dp->irq_thread_handle = 0;
> +	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
> +
> +	if (status & MTK_DP_THREAD_CABLE_STATE_CHG)
> +		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
> +
> +	if (status & MTK_DP_THREAD_HPD_EVENT)
> +		dev_dbg(mtk_dp->dev, "Receive IRQ from sink
> devices\n");
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev;
> +	bool cable_sta_chg = false;
> +	unsigned long flags;
> +	u32 irq_status = mtk_dp_swirq_get_clear(mtk_dp) |
> +			 mtk_dp_hwirq_get_clear(mtk_dp);
> +
> +	if (!irq_status)
> +		return IRQ_HANDLED;
> +
> +	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
> +
> +	if (irq_status & MTK_DP_HPD_INTERRUPT)
> +		mtk_dp->irq_thread_handle |= MTK_DP_THREAD_HPD_EVENT;
> +
> +	/* Cable state is changed. */
> +	if (irq_status != MTK_DP_HPD_INTERRUPT) {
> +		mtk_dp->irq_thread_handle |=
> MTK_DP_THREAD_CABLE_STATE_CHG;
> +		cable_sta_chg = true;
> +	}
> +
> +	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
> +
> +	if (cable_sta_chg) {
> +		if (!!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
> +		       HPD_DB_DP_TRANS_P0_MASK))
> +			mtk_dp->train_info.cable_plugged_in = true;
> +		else
> +			mtk_dp->train_info.cable_plugged_in = false;
> +	}
> +
> +	return IRQ_WAKE_THREAD;
> +}
> +
> +static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
> +			   struct platform_device *pdev)
> +{
> +	struct device_node *endpoint;
> +	struct device *dev = &pdev->dev;
> +	int ret;
> +	void __iomem *base;
> +	u32 linkrate;
> +	int len;
> +
> +	base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(base))
> +		return PTR_ERR(base);
> +
> +	mtk_dp->regs = devm_regmap_init_mmio(dev, base,
> &mtk_dp_regmap_config);
> +	if (IS_ERR(mtk_dp->regs))
> +		return PTR_ERR(mtk_dp->regs);
> +
> +	endpoint = of_graph_get_endpoint_by_regs(pdev->dev.of_node, 1,
> -1);
> +	len = of_property_count_elems_of_size(endpoint,
> +					      "data-lanes",
> sizeof(u32));
> +	if (len < 0 || len > 4 || len == 3) {
> +		dev_err(dev, "invalid data lane size: %d\n", len);
> +		return -EINVAL;
> +	}
> +
> +	mtk_dp->max_lanes = len;
> +
> +	ret = device_property_read_u32(dev, "max-linkrate-mhz",
> &linkrate);
> +	if (ret) {
> +		dev_err(dev, "failed to read max linkrate: %d\n", ret);
> +		return ret;
> +	}
> +
> +	mtk_dp->max_linkrate = drm_dp_link_rate_to_bw_code(linkrate *
> 100);
> +
> +	return 0;
> +}
> +
> +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> +				    struct drm_connector *connector)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	bool enabled = mtk_dp->enabled;
> +	struct edid *new_edid = NULL;
> +
> +	if (!enabled) {
> +		drm_bridge_chain_pre_enable(bridge);
> +
> +		/* power on aux */
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +				   DP_PWR_STATE_BANDGAP_TPLL_LANE,
> +				   DP_PWR_STATE_MASK);
> +
> +		/* power on panel */
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D0);
> +		usleep_range(2000, 5000);
> +	}
> +
> +	new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> +
> +	/*
> +	 * Parse capability here to let atomic_get_input_bus_fmts and
> +	 * mode_valid use the capability to calculate sink bitrates.
> +	 */
> +	if (mtk_dp_parse_capabilities(mtk_dp)) {
> +		drm_err(mtk_dp->drm_dev, "Can't parse capabilities\n");
> +		new_edid = NULL;
> +	}
> +
> +	if (!enabled) {
> +		/* power off panel */
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D3);
> +		usleep_range(2000, 3000);
> +
> +		/* power off aux */
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +				   DP_PWR_STATE_BANDGAP_TPLL,
> +				   DP_PWR_STATE_MASK);
> +
> +		drm_bridge_chain_post_disable(bridge);
> +	}
> +
> +	return new_edid;
> +}
> +
> +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> +				   struct drm_dp_aux_msg *msg)
> +{
> +	struct mtk_dp *mtk_dp;
> +	bool is_read;
> +	u8 request;
> +	size_t accessed_bytes = 0;
> +	int ret;
> +
> +	mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> +
> +	if (!mtk_dp->train_info.cable_plugged_in) {
> +		ret = -EAGAIN;
> +		goto err;
> +	}
> +
> +	switch (msg->request) {
> +	case DP_AUX_I2C_MOT:
> +	case DP_AUX_I2C_WRITE:
> +	case DP_AUX_NATIVE_WRITE:
> +	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> +	case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> +		request = msg->request &
> ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> +		is_read = false;
> +		break;
> +	case DP_AUX_I2C_READ:
> +	case DP_AUX_NATIVE_READ:
> +	case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> +		request = msg->request;
> +		is_read = true;
> +		break;
> +	default:
> +		drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> +			msg->request);
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	do {
> +		size_t to_access = min_t(size_t,
> DP_AUX_MAX_PAYLOAD_BYTES,
> +					 msg->size - accessed_bytes);
> +
> +		ret = mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> +					     msg->address +
> accessed_bytes,
> +					     msg->buffer +
> accessed_bytes,
> +					     to_access);
> +
> +		if (ret) {
> +			drm_info(mtk_dp->drm_dev,
> +				 "Failed to do AUX transfer: %d\n",
> ret);
> +			goto err;
> +		}
> +		accessed_bytes += to_access;
> +	} while (accessed_bytes < msg->size);
> +
> +	msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +	return msg->size;
> +err:
> +	msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> +	return ret;
> +}
> +
> +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> +{
> +	int ret;
> +
> +	ret = phy_init(mtk_dp->phy);
> +	if (ret) {
> +		dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n",
> ret);
> +		return ret;
> +	}
> +
> +	mtk_dp_init_port(mtk_dp);
> +	mtk_dp_power_enable(mtk_dp);
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_power_disable(mtk_dp);
> +	phy_exit(mtk_dp->phy);
> +}
> +
> +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> +				enum drm_bridge_attach_flags flags)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	int ret;
> +
> +	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> +		dev_err(mtk_dp->dev, "Driver does not provide a
> connector!");
> +		return -EINVAL;
> +	}
> +
> +	mtk_dp->aux.drm_dev = bridge->dev;
> +	ret = drm_dp_aux_register(&mtk_dp->aux);
> +	if (ret) {
> +		dev_err(mtk_dp->dev,
> +			"failed to register DP AUX channel: %d\n",
> ret);
> +		return ret;
> +	}
> +
> +	ret = mtk_dp_poweron(mtk_dp);
> +	if (ret)
> +		goto err_aux_register;
> +
> +	if (mtk_dp->next_bridge) {
> +		ret = drm_bridge_attach(bridge->encoder, mtk_dp-
> >next_bridge,
> +					&mtk_dp->bridge, flags);
> +		if (ret) {
> +			drm_warn(mtk_dp->drm_dev,
> +				 "Failed to attach external bridge:
> %d\n", ret);
> +			goto err_bridge_attach;
> +		}
> +	}
> +
> +	mtk_dp->drm_dev = bridge->dev;
> +
> +	mtk_dp_hwirq_enable(mtk_dp, true);
> +
> +	return 0;
> +
> +err_bridge_attach:
> +	mtk_dp_poweroff(mtk_dp);
> +err_aux_register:
> +	drm_dp_aux_unregister(&mtk_dp->aux);
> +	return ret;
> +}
> +
> +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +	mtk_dp_hwirq_enable(mtk_dp, false);
> +	mtk_dp->drm_dev = NULL;
> +	mtk_dp_poweroff(mtk_dp);
> +	drm_dp_aux_unregister(&mtk_dp->aux);
> +}
> +
> +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> +					struct drm_bridge_state
> *old_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	int ret;
> +
> +	mtk_dp->conn =
> drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> +								bridge-
> >encoder);
> +	if (!mtk_dp->conn) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as connector is
> missing\n");
> +		return;
> +	}
> +
> +	/* power on aux */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL_LANE,
> +			   DP_PWR_STATE_MASK);
> +
> +	if (mtk_dp->train_info.cable_plugged_in) {
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D0);
> +		usleep_range(2000, 5000);
> +	}
> +
> +	/* Training */
> +	ret = mtk_dp_training(mtk_dp);
> +	if (ret) {
> +		drm_err(mtk_dp->drm_dev, "Training failed, %d\n", ret);
> +		goto power_off_aux;
> +	}
> +
> +	ret = mtk_dp_video_config(mtk_dp);
> +	if (ret)
> +		goto power_off_aux;
> +
> +	mtk_dp_video_enable(mtk_dp, true);
> +
> +	mtk_dp->enabled = true;
> +
> +	return;
> +power_off_aux:
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL,
> +			   DP_PWR_STATE_MASK);
> +}
> +
> +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> +					 struct drm_bridge_state
> *old_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +	mtk_dp->enabled = false;
> +	mtk_dp_video_enable(mtk_dp, false);
> +
> +	if (mtk_dp->train_info.cable_plugged_in) {
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D3);
> +		usleep_range(2000, 3000);
> +	}
> +
> +	/* power off aux */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL,
> +			   DP_PWR_STATE_MASK);
> +
> +	/* Ensure the sink is muted */
> +	msleep(20);
> +}
> +
> +static enum drm_mode_status
> +mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
> +			 const struct drm_display_info *info,
> +			 const struct drm_display_mode *mode)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	u32 bpp = info->color_formats & DRM_COLOR_FORMAT_YCBCR422 ? 16
> : 24;
> +	u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
> +			      drm_dp_max_lane_count(mtk_dp->rx_cap),
> +			 drm_dp_bw_code_to_link_rate(mtk_dp-
> >max_linkrate) *
> +			 mtk_dp->max_lanes);
> +
> +	if (rate < mode->clock * bpp / 8)
> +		return MODE_CLOCK_HIGH;
> +
> +	return MODE_OK;
> +}
> +
> +static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(struct
> drm_bridge *bridge,
> +						     struct
> drm_bridge_state *bridge_state,
> +						     struct
> drm_crtc_state *crtc_state,
> +						     struct
> drm_connector_state *conn_state,
> +						     unsigned int
> *num_output_fmts)
> +{
> +	u32 *output_fmts;
> +
> +	*num_output_fmts = 0;
> +	output_fmts = kmalloc(sizeof(*output_fmts), GFP_KERNEL);
> +	if (!output_fmts)
> +		return NULL;
> +	*num_output_fmts = 1;
> +	output_fmts[0] = MEDIA_BUS_FMT_FIXED;
> +	return output_fmts;
> +}
> +
> +static const u32 mt8195_input_fmts[] = {
> +	MEDIA_BUS_FMT_RGB888_1X24,
> +	MEDIA_BUS_FMT_YUV8_1X24,
> +	MEDIA_BUS_FMT_YUYV8_1X16,
> +};
> +
> +static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(struct
> drm_bridge *bridge,
> +						    struct
> drm_bridge_state *bridge_state,
> +						    struct
> drm_crtc_state *crtc_state,
> +						    struct
> drm_connector_state *conn_state,
> +						    u32 output_fmt,
> +						    unsigned int
> *num_input_fmts)
> +{
> +	u32 *input_fmts;
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> +	struct drm_display_info *display_info =
> +		&conn_state->connector->display_info;
> +	u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
> +			      drm_dp_max_lane_count(mtk_dp->rx_cap),
> +			 drm_dp_bw_code_to_link_rate(mtk_dp-
> >max_linkrate) *
> +			 mtk_dp->max_lanes);
> +
> +	*num_input_fmts = 0;
> +
> +	/*
> +	 * If the linkrate is smaller than datarate of RGB888, larger
> than
> +	 * datarate of YUV422 and sink device supports YUV422, we
> output YUV422
> +	 * format. Use this condition, we can support more resolution.
> +	 */
> +	if ((rate < (mode->clock * 24 / 8)) &&
> +	    (rate > (mode->clock * 16 / 8)) &&
> +	    (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR422))
> {
> +		input_fmts = kcalloc(1, sizeof(*input_fmts),
> GFP_KERNEL);
> +		if (!input_fmts)
> +			return NULL;
> +		*num_input_fmts = 1;
> +		input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
> +	} else {
> +		input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts),
> +				     sizeof(*input_fmts),
> +				     GFP_KERNEL);
> +		if (!input_fmts)
> +			return NULL;
> +
> +		*num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
> +		memcpy(input_fmts, mt8195_input_fmts,
> sizeof(mt8195_input_fmts));
> +	}
> +
> +	return input_fmts;
> +}
> +
> +static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
> +				      struct drm_bridge_state
> *bridge_state,
> +				      struct drm_crtc_state
> *crtc_state,
> +				      struct drm_connector_state
> *conn_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	struct drm_crtc *crtc = conn_state->crtc;
> +	unsigned int input_bus_format;
> +
> +	input_bus_format = bridge_state->input_bus_cfg.format;
> +
> +	dev_dbg(mtk_dp->dev, "input format 0x%04x, output format
> 0x%04x\n",
> +		bridge_state->input_bus_cfg.format,
> +		 bridge_state->output_bus_cfg.format);
> +
> +	if (input_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> +		mtk_dp->info.format = DP_PIXELFORMAT_YUV422;
> +	else
> +		mtk_dp->info.format = DP_PIXELFORMAT_RGB;
> +
> +	if (!crtc) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as connector state doesn't
> have a crtc\n");
> +		return -EINVAL;
> +	}
> +
> +	drm_display_mode_to_videomode(&crtc_state->adjusted_mode,
> &mtk_dp->info.vm);
> +
> +	return 0;
> +}
> +
> +static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
> +	.atomic_check = mtk_dp_bridge_atomic_check,
> +	.atomic_duplicate_state =
> drm_atomic_helper_bridge_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> +	.atomic_get_output_bus_fmts =
> mtk_dp_bridge_atomic_get_output_bus_fmts,
> +	.atomic_get_input_bus_fmts =
> mtk_dp_bridge_atomic_get_input_bus_fmts,
> +	.atomic_reset = drm_atomic_helper_bridge_reset,
> +	.attach = mtk_dp_bridge_attach,
> +	.detach = mtk_dp_bridge_detach,
> +	.atomic_enable = mtk_dp_bridge_atomic_enable,
> +	.atomic_disable = mtk_dp_bridge_atomic_disable,
> +	.mode_valid = mtk_dp_bridge_mode_valid,
> +	.get_edid = mtk_dp_get_edid,
> +};
> +
> +static int mtk_dp_probe(struct platform_device *pdev)
> +{
> +	struct mtk_dp *mtk_dp;
> +	struct device *dev = &pdev->dev;
> +	int ret, irq_num;
> +
> +	mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> +	if (!mtk_dp)
> +		return -ENOMEM;
> +
> +	mtk_dp->dev = dev;
> +
> +	irq_num = platform_get_irq(pdev, 0);
> +	if (irq_num < 0)
> +		return dev_err_probe(dev, irq_num,
> +				     "failed to request dp irq
> resource\n");
> +
> +	mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 
> 1, 0);
> +	if (IS_ERR(mtk_dp->next_bridge))
> +		return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge),
> +				     "Failed to get bridge\n");
> +
> +	ret = mtk_dp_dt_parse(mtk_dp, pdev);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to parse dt\n");
> +
> +	drm_dp_aux_init(&mtk_dp->aux);
> +	mtk_dp->aux.name = "aux_mtk_dp";
> +	mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> +
> +	spin_lock_init(&mtk_dp->irq_thread_lock);
> +
> +	ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
> +					mtk_dp_hpd_event_thread,
> +					IRQ_TYPE_LEVEL_HIGH,
> dev_name(dev),
> +					mtk_dp);
> +	if (ret)
> +		return dev_err_probe(dev, ret,
> +				     "failed to request mediatek dptx
> irq\n");
> +
> +	platform_set_drvdata(pdev, mtk_dp);
> +
> +	mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-
> dp-phy",
> +							PLATFORM_DEVID_
> AUTO,
> +							&mtk_dp->regs,
> +							sizeof(struct
> regmap *));
> +	if (IS_ERR(mtk_dp->phy_dev))
> +		return dev_err_probe(dev, PTR_ERR(mtk_dp->phy_dev),
> +				     "Failed to create device mediatek-
> dp-phy\n");
> +
> +	mtk_dp_get_calibration_data(mtk_dp);
> +
> +	mtk_dp->phy = devm_phy_get(&mtk_dp->phy_dev->dev, "dp");
> +
> +	if (IS_ERR(mtk_dp->phy)) {
> +		platform_device_unregister(mtk_dp->phy_dev);
> +		return dev_err_probe(dev, PTR_ERR(mtk_dp->phy),
> +				     "Failed to get phy\n");
> +	}
> +
> +	mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
> +	mtk_dp->bridge.of_node = dev->of_node;
> +
> +	mtk_dp->bridge.ops =
> +		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
> DRM_BRIDGE_OP_HPD;
> +	mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> +
> +	drm_bridge_add(&mtk_dp->bridge);
> +
> +	pm_runtime_enable(dev);
> +	pm_runtime_get_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_remove(struct platform_device *pdev)
> +{
> +	struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
> +
> +	pm_runtime_put(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +	drm_bridge_remove(&mtk_dp->bridge);
> +	platform_device_unregister(mtk_dp->phy_dev);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int mtk_dp_suspend(struct device *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	mtk_dp_power_disable(mtk_dp);
> +	mtk_dp_hwirq_enable(mtk_dp, false);
> +	pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_resume(struct device *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	pm_runtime_get_sync(dev);
> +	mtk_dp_init_port(mtk_dp);
> +	mtk_dp_hwirq_enable(mtk_dp, true);
> +	mtk_dp_power_enable(mtk_dp);
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend,
> mtk_dp_resume);
> +
> +static const struct of_device_id mtk_dp_of_match[] = {
> +	{ .compatible = "mediatek,mt8195-edp-tx" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
> +
> +struct platform_driver mtk_dp_driver = {
> +	.probe = mtk_dp_probe,
> +	.remove = mtk_dp_remove,
> +	.driver = {
> +		.name = "mediatek-drm-dp",
> +		.of_match_table = mtk_dp_of_match,
> +		.pm = &mtk_dp_pm_ops,
> +	},
> +};
> +
> +module_platform_driver(mtk_dp_driver);
> +
> +MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
> +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> +MODULE_AUTHOR("Bo-Chen Chen <rex-bc.chen@mediatek.com>");
> +MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> new file mode 100644
> index 000000000000..3f01ba44871f
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> @@ -0,0 +1,305 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019-2022 MediaTek Inc.
> + * Copyright (c) 2022 BayLibre
> + */
> +#ifndef _MTK_DP_REG_H_
> +#define _MTK_DP_REG_H_
> +
> +#define SEC_OFFSET	0x4000
> +
> +#define MTK_DP_HPD_DISCONNECT		BIT(1)
> +#define MTK_DP_HPD_CONNECT		BIT(2)
> +#define MTK_DP_HPD_INTERRUPT		BIT(3)
> +
> +/* offset: 0x0 */
> +#define DP_PHY_GLB_BIAS_GEN_00		0x0
> +#define RG_XTP_GLB_BIAS_INTR_CTRL		GENMASK(20, 16)
> +#define DP_PHY_GLB_DPAUX_TX		0x8
> +#define RG_CKM_PT0_CKTX_IMPSEL			GENMASK(23, 20)
> +#define MTK_DP_0034			0x34
> +#define DA_XTP_GLB_CKDET_EN_FORCE_VAL		BIT(15)
> +#define DA_XTP_GLB_CKDET_EN_FORCE_EN		BIT(14)
> +#define DA_CKM_INTCKTX_EN_FORCE_VAL		BIT(13)
> +#define DA_CKM_INTCKTX_EN_FORCE_EN		BIT(12)
> +#define DA_CKM_CKTX0_EN_FORCE_VAL		BIT(11)
> +#define DA_CKM_CKTX0_EN_FORCE_EN		BIT(10)
> +#define DA_CKM_XTAL_CK_FORCE_VAL		BIT(9)
> +#define DA_CKM_XTAL_CK_FORCE_EN			BIT(8)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_VAL		BIT(7)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_EN		BIT(6)
> +#define DA_CKM_BIAS_EN_FORCE_VAL		BIT(5)
> +#define DA_CKM_BIAS_EN_FORCE_EN			BIT(4)
> +#define DA_XTP_GLB_AVD10_ON_FORCE_VAL		BIT(3)
> +#define DA_XTP_GLB_AVD10_ON_FORCE		BIT(2)
> +#define DA_XTP_GLB_LDO_EN_FORCE_VAL		BIT(1)
> +#define DA_XTP_GLB_LDO_EN_FORCE_EN		BIT(0)
> +#define DP_PHY_LANE_TX_0		0x104
> +#define RG_XTP_LN0_TX_IMPSEL_PMOS		GENMASK(15, 12)
> +#define RG_XTP_LN0_TX_IMPSEL_NMOS		GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_1		0x204
> +#define RG_XTP_LN1_TX_IMPSEL_PMOS		GENMASK(15, 12)
> +#define RG_XTP_LN1_TX_IMPSEL_NMOS		GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_2		0x304
> +#define RG_XTP_LN2_TX_IMPSEL_PMOS		GENMASK(15, 12)
> +#define RG_XTP_LN2_TX_IMPSEL_NMOS		GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_3		0x404
> +#define RG_XTP_LN3_TX_IMPSEL_PMOS		GENMASK(15, 12)
> +#define RG_XTP_LN3_TX_IMPSEL_NMOS		GENMASK(19, 16)
> +#define MTK_DP_1040			0x1040
> +#define RG_DPAUX_RX_VALID_DEGLITCH_EN		BIT(2)
> +#define RG_XTP_GLB_CKDET_EN			BIT(1)
> +#define RG_DPAUX_RX_EN				BIT(0)
> +
> +/* offset: TOP_OFFSET (0x2000) */
> +#define MTK_DP_TOP_PWR_STATE		0x2000
> +#define DP_PWR_STATE_MASK			GENMASK(1, 0)
> +#define DP_PWR_STATE_BANDGAP			BIT(0)
> +#define DP_PWR_STATE_BANDGAP_TPLL		BIT(1)
> +#define DP_PWR_STATE_BANDGAP_TPLL_LANE		GENMASK(1, 0)
> +#define MTK_DP_TOP_SWING_EMP		0x2004
> +#define DP_TX0_VOLT_SWING_MASK			GENMASK(1, 0)
> +#define DP_TX0_VOLT_SWING_SHIFT			0
> +#define DP_TX0_PRE_EMPH_MASK			GENMASK(3, 2)
> +#define DP_TX0_PRE_EMPH_SHIFT			2
> +#define DP_TX1_VOLT_SWING_MASK			GENMASK(9, 8)
> +#define DP_TX1_VOLT_SWING_SHIFT			8
> +#define DP_TX1_PRE_EMPH_MASK			GENMASK(11, 10)
> +#define DP_TX2_VOLT_SWING_MASK			GENMASK(17, 16)
> +#define DP_TX2_PRE_EMPH_MASK			GENMASK(19, 18)
> +#define DP_TX3_VOLT_SWING_MASK			GENMASK(25, 24)
> +#define DP_TX3_PRE_EMPH_MASK			GENMASK(27, 26)
> +#define MTK_DP_TOP_RESET_AND_PROBE	0x2020
> +#define SW_RST_B_PHYD				BIT(4)
> +#define MTK_DP_TOP_IRQ_MASK		0x202c
> +#define IRQ_MASK_AUX_TOP_IRQ			BIT(2)
> +#define MTK_DP_TOP_MEM_PD		0x2038
> +#define MEM_ISO_EN				BIT(0)
> +#define FUSE_SEL				BIT(2)
> +
> +/* offset: ENC0_OFFSET (0x3000) */
> +#define MTK_DP_ENC0_P0_3000			0x3000
> +#define LANE_NUM_DP_ENC0_P0_MASK			GENMASK(1, 0)
> +#define VIDEO_MUTE_SW_DP_ENC0_P0			BIT(2)
> +#define VIDEO_MUTE_SEL_DP_ENC0_P0			BIT(3)
> +#define ENHANCED_FRAME_EN_DP_ENC0_P0			BIT(4)
> +#define MTK_DP_ENC0_P0_3004			0x3004
> +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK		BIT(8)
> +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0		BIT(9)
> +#define MTK_DP_ENC0_P0_3010			0x3010
> +#define HTOTAL_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3014			0x3014
> +#define VTOTAL_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3018			0x3018
> +#define HSTART_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_301C			0x301c
> +#define VSTART_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3020			0x3020
> +#define HWIDTH_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3024			0x3024
> +#define VHEIGHT_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3028			0x3028
> +#define HSW_SW_DP_ENC0_P0_MASK				GENMASK
> (14, 0)
> +#define HSP_SW_DP_ENC0_P0_MASK				BIT(15)
> +#define MTK_DP_ENC0_P0_302C			0x302c
> +#define VSW_SW_DP_ENC0_P0_MASK				GENMASK
> (14, 0)
> +#define VSP_SW_DP_ENC0_P0_MASK				BIT(15)
> +#define MTK_DP_ENC0_P0_3030			0x3030
> +#define HTOTAL_SEL_DP_ENC0_P0				BIT(0)
> +#define VTOTAL_SEL_DP_ENC0_P0				BIT(1)
> +#define HSTART_SEL_DP_ENC0_P0				BIT(2)
> +#define VSTART_SEL_DP_ENC0_P0				BIT(3)
> +#define HWIDTH_SEL_DP_ENC0_P0				BIT(4)
> +#define VHEIGHT_SEL_DP_ENC0_P0				BIT(5)
> +#define HSP_SEL_DP_ENC0_P0				BIT(6)
> +#define HSW_SEL_DP_ENC0_P0				BIT(7)
> +#define VSP_SEL_DP_ENC0_P0				BIT(8)
> +#define VSW_SEL_DP_ENC0_P0				BIT(9)
> +#define MTK_DP_ENC0_P0_3034			0x3034
> +#define MTK_DP_ENC0_P0_3038			0x3038
> +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK		BIT(11)
> +#define MTK_DP_ENC0_P0_303C			0x303c
> +#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK		GENMASK(5, 0)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK		GENMASK(10, 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT		(0 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT		(1 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT		(2 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT		(3 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT		(4 << 8)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK		GENMASK(14, 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB		(0 << 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422		(1 <<
> 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420		(2 <<
> 12)
> +#define VIDEO_MN_GEN_EN_DP_ENC0_P0			BIT(15)
> +#define MTK_DP_ENC0_P0_3040			0x3040
> +#define SDP_DOWN_CNT_DP_ENC0_P0_VAL			0x20
> +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK		GENMASK(11, 0)
> +#define MTK_DP_ENC0_P0_304C			0x304c
> +#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK			BIT(2)
> +#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK		BIT(8)
> +#define MTK_DP_ENC0_P0_3064			0x3064
> +#define HDE_NUM_LAST_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3154			0x3154
> +#define PGEN_HTOTAL_DP_ENC0_P0_MASK			GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_3158			0x3158
> +#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK		GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_315C			0x315c
> +#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK		GENMASK
> (13, 0)
> +#define MTK_DP_ENC0_P0_3160			0x3160
> +#define PGEN_HFDE_START_DP_ENC0_P0_MASK			GENMASK
> (13, 0)
> +#define MTK_DP_ENC0_P0_3164			0x3164
> +#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK		GENMASK
> (13, 0)
> +#define MTK_DP_ENC0_P0_3168			0x3168
> +#define PGEN_VTOTAL_DP_ENC0_P0_MASK			GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_316C			0x316c
> +#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK		GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_3170			0x3170
> +#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK		GENMASK
> (12, 0)
> +#define MTK_DP_ENC0_P0_3174			0x3174
> +#define PGEN_VFDE_START_DP_ENC0_P0_MASK			GENMASK
> (12, 0)
> +#define MTK_DP_ENC0_P0_3178			0x3178
> +#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK		GENMASK
> (12, 0)
> +#define MTK_DP_ENC0_P0_31B0			0x31b0
> +#define PGEN_PATTERN_SEL_VAL				4
> +#define PGEN_PATTERN_SEL_MASK				GENMASK
> (6, 4)
> +#define MTK_DP_ENC0_P0_31EC			0x31ec
> +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0			BIT(4)
> +#define ISRC1_HB3_DP_ENC0_P0_MASK			GENMASK(15, 8)
> +
> +/* offset: ENC1_OFFSET (0x3200) */
> +#define MTK_DP_ENC1_P0_3300			0x3300
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL		2
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK		GENMASK(9, 8)
> +#define MTK_DP_ENC1_P0_3364			0x3364
> +#define SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL		0x20
> +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK	GENMASK(11, 0)
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_VAL		4
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK		GENMASK
> (15, 12)
> +#define MTK_DP_ENC1_P0_3368			0x3368
> +#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0	BIT(0)
> +#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0		BIT(4)
> +#define SDP_DP13_EN_DP_ENC1_P0				BIT(8)
> +#define BS2BS_MODE_DP_ENC1_P0				BIT(12)
> +#define BS2BS_MODE_DP_ENC1_P0_MASK			GENMASK(13, 12)
> +#define BS2BS_MODE_DP_ENC1_P0_VAL			1
> +#define DP_ENC1_P0_3368_VAL				(VIDEO_SRAM_FIF
> O_CNT_RESET_SEL_DP_ENC1_P0 | \
> +							 VIDEO_STABLE_C
> NT_THRD_DP_ENC1_P0 | \
> +							 SDP_DP13_EN_DP
> _ENC1_P0 | \
> +							 BS2BS_MODE_DP_
> ENC1_P0)
> +
> +/* offset: TRANS_OFFSET (0x3400) */
> +#define MTK_DP_TRANS_P0_3400				0x3400
> +#define PATTERN1_EN_DP_TRANS_P0_MASK				BIT(12)
> +#define PATTERN2_EN_DP_TRANS_P0_MASK				BIT(13)
> +#define PATTERN3_EN_DP_TRANS_P0_MASK				BIT(14)
> +#define PATTERN4_EN_DP_TRANS_P0_MASK				BIT(15)
> +#define MTK_DP_TRANS_P0_3404				0x3404
> +#define DP_SCR_EN_DP_TRANS_P0_MASK				BIT(0)
> +#define MTK_DP_TRANS_P0_340C				0x340c
> +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0		BIT(13)
> +#define MTK_DP_TRANS_P0_3410				0x3410
> +#define HPD_DEB_THD_DP_TRANS_P0_MASK				GENMASK
> (3, 0)
> +#define HPD_INT_THD_DP_TRANS_P0_MASK				GENMASK
> (7, 4)
> +#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US			(2 <<
> 4)
> +#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US			(2 <<
> 6)
> +#define HPD_DISC_THD_DP_TRANS_P0_MASK				
> GENMASK(11, 8)
> +#define HPD_CONN_THD_DP_TRANS_P0_MASK				
> GENMASK(15, 12)
> +#define MTK_DP_TRANS_P0_3414				0x3414
> +#define HPD_DB_DP_TRANS_P0_MASK					
> BIT(2)
> +#define MTK_DP_TRANS_P0_3418				0x3418
> +#define IRQ_CLR_DP_TRANS_P0_MASK				GENMASK
> (3, 0)
> +#define IRQ_MASK_DP_TRANS_P0_MASK				GENMASK
> (7, 4)
> +#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ				
> (BIT(1) << 4)
> +#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ				
> (BIT(2) << 4)
> +#define IRQ_MASK_DP_TRANS_P0_INT_IRQ				(BIT(3)
> << 4)
> +#define IRQ_STATUS_DP_TRANS_P0_MASK				GENMASK
> (15, 12)
> +#define MTK_DP_TRANS_P0_342C				0x342c
> +#define XTAL_FREQ_DP_TRANS_P0_DEFAULT				
> (BIT(0) | BIT(3) | BIT(5) | BIT(6))
> +#define XTAL_FREQ_DP_TRANS_P0_MASK				GENMASK
> (7, 0)
> +#define MTK_DP_TRANS_P0_3430				0x3430
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK			GENMASK
> (1, 0)
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT		BIT(1)
> +#define MTK_DP_TRANS_P0_34A4				0x34a4
> +#define LANE_NUM_DP_TRANS_P0_MASK				GENMASK
> (3, 2)
> +#define MTK_DP_TRANS_P0_3540				0x3540
> +#define FEC_EN_DP_TRANS_P0_MASK					
> BIT(0)
> +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0				
> BIT(3)
> +#define MTK_DP_TRANS_P0_3580				0x3580
> +#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK		BIT(8)
> +#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK		BIT(9)
> +#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK		BIT(10)
> +#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK		BIT(11)
> +#define MTK_DP_TRANS_P0_35C8				0x35c8
> +#define SW_IRQ_CLR_DP_TRANS_P0_MASK				GENMASK
> (15, 0)
> +#define SW_IRQ_STATUS_DP_TRANS_P0_MASK				
> GENMASK(15, 0)
> +#define MTK_DP_TRANS_P0_35D0				0x35d0
> +#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK			GENMASK
> (15, 0)
> +#define MTK_DP_TRANS_P0_35F0				0x35f0
> +#define DP_TRANS_DUMMY_RW_0					BIT(3)
> +#define DP_TRANS_DUMMY_RW_0_MASK				GENMASK
> (3, 2)
> +
> +/* offset: AUX_OFFSET (0x3600) */
> +#define MTK_DP_AUX_P0_360C			0x360c
> +#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK			GENMASK
> (12, 0)
> +#define AUX_TIMEOUT_THR_AUX_TX_P0_VAL			0x1595
> +#define MTK_DP_AUX_P0_3614			0x3614
> +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK		GENMASK(6, 0)
> +#define AUX_RX_UI_CNT_THR_AUX_FOR_26M			13
> +#define MTK_DP_AUX_P0_3618			0x3618
> +#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK			BIT(9)
> +#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK	GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3620			0x3620
> +#define AUX_RD_MODE_AUX_TX_P0_MASK			BIT(9)
> +#define AUX_RX_FIFO_READ_PULSE_TX_P0			BIT(8)
> +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK		GENMASK(7, 0)
> +#define MTK_DP_AUX_P0_3624			0x3624
> +#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK		GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3628			0x3628
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK			GENMASK
> (9, 0)
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE		BIT(0)
> +#define MTK_DP_AUX_P0_362C			0x362c
> +#define AUX_NO_LENGTH_AUX_TX_P0				BIT(0)
> +#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK		BIT(1)
> +#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK		GENMASK(15, 2)
> +#define MTK_DP_AUX_P0_3630			0x3630
> +#define AUX_TX_REQUEST_READY_AUX_TX_P0			BIT(3)
> +#define MTK_DP_AUX_P0_3634			0x3634
> +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK		GENMASK
> (15, 8)
> +#define AUX_TX_OVER_SAMPLE_RATE_FOR_26M			25
> +#define MTK_DP_AUX_P0_3640			0x3640
> +#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(6)
> +#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(5)
> +#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(4)
> +#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0			BIT(3)
> +#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0			BIT(2)
> +#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0			BIT(1)
> +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0			BIT(0)
> +#define DP_AUX_P0_3640_VAL				(AUX_400US_TIME
> OUT_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_DATA_RE
> CV_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_ADDR_RE
> CV_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_CMD_REC
> V_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_MCCS_RE
> CV_COMPLETE_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_EDID_RE
> CV_COMPLETE_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_AUX_REC
> V_COMPLETE_IRQ_AUX_TX_P0)
> +#define MTK_DP_AUX_P0_3644			0x3644
> +#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK		GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3648			0x3648
> +#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK		GENMASK
> (15, 0)
> +#define MTK_DP_AUX_P0_364C			0x364c
> +#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK		GENMASK
> (3, 0)
> +#define MTK_DP_AUX_P0_3650			0x3650
> +#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK			GENMASK
> (15, 12)
> +#define PHY_FIFO_RST_AUX_TX_P0_MASK			BIT(9)
> +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0			BIT(8)
> +#define MTK_DP_AUX_P0_3658			0x3658
> +#define AUX_TX_OV_EN_AUX_TX_P0_MASK			BIT(0)
> +#define MTK_DP_AUX_P0_3690			0x3690
> +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0		BIT(8)
> +#define MTK_DP_AUX_P0_3704			0x3704
> +#define AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK	BIT(1)
> +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0		BIT(2)
> +#define MTK_DP_AUX_P0_3708			0x3708
> +#define MTK_DP_AUX_P0_37C8			0x37c8
> +#define MTK_ATOP_EN_AUX_TX_P0				BIT(0)
> +
> +#endif /*_MTK_DP_REG_H_*/


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

* Re: [PATCH v17 03/10] drm/mediatek: Add MT8195 Embedded DisplayPort driver
@ 2022-09-02  7:17     ` CK Hu
  0 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  7:17 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, linux-mediatek, wenst,
	linux-arm-kernel, angelogioacchino.delregno

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This patch adds a embedded displayport driver for the MediaTek mt8195
> SoC.
> 
> It supports the MT8195, the embedded DisplayPort units. It offers
> DisplayPort 1.4 with up to 4 lanes.
> 
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so
> that
> both can work with the same register range. The phy driver sets
> device
> data that is read by the parent to get the phy device that can be
> used
> to control the phy properties.

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> This driver is based on an initial version by
> Jitao shi <jitao.shi@mediatek.com>
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/Kconfig      |    9 +
>  drivers/gpu/drm/mediatek/Makefile     |    2 +
>  drivers/gpu/drm/mediatek/mtk_dp.c     | 1999
> +++++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_dp_reg.h |  305 ++++
>  4 files changed, 2315 insertions(+)
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> 
> diff --git a/drivers/gpu/drm/mediatek/Kconfig
> b/drivers/gpu/drm/mediatek/Kconfig
> index 2976d21e9a34..e66f4a3b6be0 100644
> --- a/drivers/gpu/drm/mediatek/Kconfig
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -21,6 +21,15 @@ config DRM_MEDIATEK
>  	  This driver provides kernel mode setting and
>  	  buffer management to userspace.
>  
> +config DRM_MEDIATEK_DP
> +	tristate "DRM DPTX Support for MediaTek SoCs"
> +	depends on DRM_MEDIATEK
> +	select PHY_MTK_DP
> +	select DRM_DISPLAY_HELPER
> +	select DRM_DISPLAY_DP_HELPER
> +	help
> +	  DRM/KMS Display Port driver for MediaTek SoCs.
> +
>  config DRM_MEDIATEK_HDMI
>  	tristate "DRM HDMI Support for Mediatek SoCs"
>  	depends on DRM_MEDIATEK
> diff --git a/drivers/gpu/drm/mediatek/Makefile
> b/drivers/gpu/drm/mediatek/Makefile
> index 6e604a933ed0..3517d1c65cd7 100644
> --- a/drivers/gpu/drm/mediatek/Makefile
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -23,3 +23,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
>  			  mtk_hdmi_ddc.o
>  
>  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> +
> +obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> new file mode 100644
> index 000000000000..e2ec9b02b1aa
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -0,0 +1,1999 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019-2022 MediaTek Inc.
> + * Copyright (c) 2022 BayLibre
> + */
> +
> +#include <drm/display/drm_dp.h>
> +#include <drm/display/drm_dp_helper.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <linux/arm-smccc.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/media-bus-format.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/soc/mediatek/mtk_sip_svc.h>
> +#include <video/videomode.h>
> +
> +#include "mtk_dp_reg.h"
> +
> +#define MTK_DP_SIP_CONTROL_AARCH32	MTK_SIP_SMC_CMD(0x523)
> +#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE	(BIT(0) | BIT(5))
> +
> +#define MTK_DP_THREAD_CABLE_STATE_CHG	BIT(0)
> +#define MTK_DP_THREAD_HPD_EVENT		BIT(1)
> +
> +#define MTK_DP_4P1T 4
> +#define MTK_DP_HDE 2
> +#define MTK_DP_PIX_PER_ADDR 2
> +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x8
> +#define MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY 5
> +#define MTK_DP_TRAIN_DOWNSCALE_RETRY 10
> +
> +enum {
> +	MTK_DP_CAL_GLB_BIAS_TRIM = 0,
> +	MTK_DP_CAL_CLKTX_IMPSE,
> +	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0,
> +	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1,
> +	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2,
> +	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3,
> +	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0,
> +	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1,
> +	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2,
> +	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3,
> +	MTK_DP_CAL_MAX,
> +};
> +
> +struct mtk_dp_train_info {
> +	bool sink_ssc;
> +	bool cable_plugged_in;
> +	/* link_rate is in multiple of 0.27Gbps */
> +	int link_rate;
> +	int lane_count;
> +	unsigned int channel_eq_pattern;
> +};
> +
> +struct mtk_dp_info {
> +	enum dp_pixelformat format;
> +	struct videomode vm;
> +};
> +
> +struct mtk_dp_efuse_fmt {
> +	unsigned short idx;
> +	unsigned short shift;
> +	unsigned short mask;
> +	unsigned short min_val;
> +	unsigned short max_val;
> +	unsigned short default_val;
> +};
> +
> +struct mtk_dp {
> +	bool enabled;
> +	u8 max_lanes;
> +	u8 max_linkrate;
> +	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> +	u32 cal_data[MTK_DP_CAL_MAX];
> +	u32 irq_thread_handle;
> +	/* irq_thread_lock is used to protect irq_thread_handle */
> +	spinlock_t irq_thread_lock;
> +
> +	struct device *dev;
> +	struct drm_bridge bridge;
> +	struct drm_bridge *next_bridge;
> +	struct drm_connector *conn;
> +	struct drm_device *drm_dev;
> +	struct drm_dp_aux aux;
> +
> +	struct mtk_dp_info info;
> +	struct mtk_dp_train_info train_info;
> +
> +	struct platform_device *phy_dev;
> +	struct phy *phy;
> +	struct regmap *regs;
> +};
> +
> +static const struct mtk_dp_efuse_fmt
> mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
> +	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
> +		.idx = 3,
> +		.shift = 27,
> +		.mask = 0x1f,
> +		.min_val = 1,
> +		.max_val = 0x1e,
> +		.default_val = 0xf,
> +	},
> +	[MTK_DP_CAL_CLKTX_IMPSE] = {
> +		.idx = 0,
> +		.shift = 9,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = {
> +		.idx = 2,
> +		.shift = 28,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = {
> +		.idx = 2,
> +		.shift = 20,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = {
> +		.idx = 2,
> +		.shift = 12,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = {
> +		.idx = 2,
> +		.shift = 4,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = {
> +		.idx = 2,
> +		.shift = 24,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = {
> +		.idx = 2,
> +		.shift = 16,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = {
> +		.idx = 2,
> +		.shift = 8,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = {
> +		.idx = 2,
> +		.shift = 0,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +};
> +
> +static struct regmap_config mtk_dp_regmap_config = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +	.max_register = SEC_OFFSET + 0x90,
> +	.name = "mtk-dp-registers",
> +};
> +
> +static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
> +{
> +	return container_of(b, struct mtk_dp, bridge);
> +}
> +
> +static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
> +{
> +	u32 read_val;
> +	int ret;
> +
> +	ret = regmap_read(mtk_dp->regs, offset, &read_val);
> +	if (ret) {
> +		dev_err(mtk_dp->dev, "Failed to read register 0x%x:
> %d\n",
> +			offset, ret);
> +		return 0;
> +	}
> +
> +	return read_val;
> +}
> +
> +static int mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
> +{
> +	int ret = regmap_write(mtk_dp->regs, offset, val);
> +
> +	if (ret)
> +		dev_err(mtk_dp->dev,
> +			"Failed to write register 0x%x with value
> 0x%x\n",
> +			offset, val);
> +	return ret;
> +}
> +
> +static int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset,
> +			      u32 val, u32 mask)
> +{
> +	int ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
> +
> +	if (ret)
> +		dev_err(mtk_dp->dev,
> +			"Failed to update register 0x%x with value
> 0x%x, mask 0x%x\n",
> +			offset, val, mask);
> +	return ret;
> +}
> +
> +static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32
> offset, u8 *buf,
> +				    size_t length)
> +{
> +	int i;
> +
> +	/* 2 bytes per register */
> +	for (i = 0; i < length; i += 2) {
> +		u32 val = buf[i] | (i + 1 < length ? buf[i + 1] << 8 :
> 0);
> +
> +		if (mtk_dp_write(mtk_dp, offset + i * 2, val))
> +			return;
> +	}
> +}
> +
> +static void mtk_dp_msa_bypass_enable(struct mtk_dp *mtk_dp, bool
> enable)
> +{
> +	u32 mask = HTOTAL_SEL_DP_ENC0_P0 | VTOTAL_SEL_DP_ENC0_P0 |
> +		   HSTART_SEL_DP_ENC0_P0 | VSTART_SEL_DP_ENC0_P0 |
> +		   HWIDTH_SEL_DP_ENC0_P0 | VHEIGHT_SEL_DP_ENC0_P0 |
> +		   HSP_SEL_DP_ENC0_P0 | HSW_SEL_DP_ENC0_P0 |
> +		   VSP_SEL_DP_ENC0_P0 | VSW_SEL_DP_ENC0_P0;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, enable ? 0 :
> mask, mask);
> +}
> +
> +static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
> +{
> +	struct drm_display_mode mode;
> +	struct videomode *vm = &mtk_dp->info.vm;
> +
> +	drm_display_mode_from_videomode(vm, &mode);
> +
> +	/* horizontal */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010,
> +			   mode.htotal, HTOTAL_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
> +			   vm->hsync_len + vm->hback_porch,
> +			   HSTART_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> +			   vm->hsync_len, HSW_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> +			   0, HSP_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020,
> +			   vm->hactive, HWIDTH_SW_DP_ENC0_P0_MASK);
> +
> +	/* vertical */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014,
> +			   mode.vtotal, VTOTAL_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
> +			   vm->vsync_len + vm->vback_porch,
> +			   VSTART_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> +			   vm->vsync_len, VSW_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> +			   0, VSP_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024,
> +			   vm->vactive, VHEIGHT_SW_DP_ENC0_P0_MASK);
> +
> +	/* horizontal */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064,
> +			   vm->hactive, HDE_NUM_LAST_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154,
> +			   mode.htotal, PGEN_HTOTAL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
> +			   vm->hfront_porch,
> +			   PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C,
> +			   vm->hsync_len,
> +			   PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
> +			   vm->hback_porch + vm->hsync_len,
> +			   PGEN_HFDE_START_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164,
> +			   vm->hactive,
> +			   PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +
> +	/* vertical */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168,
> +			   mode.vtotal,
> +			   PGEN_VTOTAL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
> +			   vm->vfront_porch,
> +			   PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170,
> +			   vm->vsync_len,
> +			   PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
> +			   vm->vback_porch + vm->vsync_len,
> +			   PGEN_VFDE_START_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178,
> +			   vm->vactive,
> +			   PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +}
> +
> +static int mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
> +				   enum dp_pixelformat color_format)
> +{
> +	u32 val;
> +
> +	/* update MISC0 */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +			   color_format << DP_TEST_COLOR_FORMAT_SHIFT,
> +			   DP_TEST_COLOR_FORMAT_MASK);
> +
> +	switch (color_format) {
> +	case DP_PIXELFORMAT_YUV422:
> +		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
> +		break;
> +	case DP_PIXELFORMAT_RGB:
> +		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
> +		break;
> +	default:
> +		drm_warn(mtk_dp->drm_dev, "Unsupported color format:
> %d\n",
> +			 color_format);
> +		return -EINVAL;
> +	}
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   val, PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
> +	return 0;
> +}
> +
> +static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp)
> +{
> +	/* Only support 8 bits currently */
> +	/* Update MISC0 */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +			   DP_MSA_MISC_8_BPC, DP_TEST_BIT_DEPTH_MASK);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT,
> +			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_config_mn_mode(struct mtk_dp *mtk_dp)
> +{
> +	/* 0: hw mode, 1: sw mode */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +			   0, VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32
> val)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   val, SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   VIDEO_MN_GEN_EN_DP_ENC0_P0,
> +			   VIDEO_MN_GEN_EN_DP_ENC0_P0);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> +			   SDP_DOWN_CNT_DP_ENC0_P0_VAL,
> +			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +			   SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL,
> +			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK)
> ;
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
> +			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL << 8,
> +			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +			   FIFO_READ_START_POINT_DP_ENC1_P0_VAL << 12,
> +			   FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
> +	mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368, DP_ENC1_P0_3368_VAL);
> +}
> +
> +static void mtk_dp_pg_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038,
> +			   enable ? VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK :
> 0,
> +			   VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
> +			   PGEN_PATTERN_SEL_VAL << 4,
> PGEN_PATTERN_SEL_MASK);
> +}
> +
> +static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640, DP_AUX_P0_3640_VAL);
> +}
> +
> +static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32
> addr)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644,
> +			   cmd, MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648,
> +			   addr,
> MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C,
> +			   addr >> 16,
> MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_clear_fifo(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0,
> +			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0 |
> +			   PHY_FIFO_RST_AUX_TX_P0_MASK |
> +			   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
> +			   AUX_TX_REQUEST_READY_AUX_TX_P0,
> +			   AUX_TX_REQUEST_READY_AUX_TX_P0);
> +}
> +
> +static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8
> *buf,
> +				       size_t length)
> +{
> +	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf,
> length);
> +}
> +
> +static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> +				    size_t length, int read_delay)
> +{
> +	int read_pos;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> +			   0, AUX_RD_MODE_AUX_TX_P0_MASK);
> +
> +	for (read_pos = 0; read_pos < length; read_pos++) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> +				   AUX_RX_FIFO_READ_PULSE_TX_P0,
> +				   AUX_RX_FIFO_READ_PULSE_TX_P0);
> +
> +		/* Hardware needs time to update the data */
> +		usleep_range(read_delay, read_delay * 2);
> +		buf[read_pos] = (u8)(mtk_dp_read(mtk_dp,
> MTK_DP_AUX_P0_3620) &
> +				     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MA
> SK);
> +	}
> +}
> +
> +static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t
> length)
> +{
> +	if (length > 0) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +				   (length - 1) << 12,
> +				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +				   0,
> +				   AUX_NO_LENGTH_AUX_TX_P0 |
> +				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	} else {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +				   AUX_NO_LENGTH_AUX_TX_P0,
> +				   AUX_NO_LENGTH_AUX_TX_P0 |
> +				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	}
> +}
> +
> +static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp,
> bool is_read)
> +{
> +	int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
> +
> +	while (--wait_reply) {
> +		u32 aux_irq_status;
> +
> +		if (is_read) {
> +			u32 fifo_status = mtk_dp_read(mtk_dp,
> MTK_DP_AUX_P0_3618);
> +
> +			if (fifo_status &
> +			    (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
> +			     AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
> +				return 0;
> +			}
> +		}
> +
> +		aux_irq_status = mtk_dp_read(mtk_dp,
> MTK_DP_AUX_P0_3640);
> +		if (aux_irq_status &
> AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
> +			return 0;
> +
> +		if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0)
> +			return -ETIMEDOUT;
> +
> +		/* Give the hardware a chance to reach completion
> before retrying */
> +		usleep_range(100, 500);
> +	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool
> is_read, u8 cmd,
> +				  u32 addr, u8 *buf, size_t length)
> +{
> +	int ret;
> +	u32 reply_cmd;
> +
> +	if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
> +			(cmd == DP_AUX_NATIVE_READ && !length)))
> +		return -EINVAL;
> +
> +	if (!is_read)
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> +				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0,
> +				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0);
> +
> +	/* We need to clear fifo and irq before sending commands to the
> sink device. */
> +	mtk_dp_aux_clear_fifo(mtk_dp);
> +	mtk_dp_aux_irq_clear(mtk_dp);
> +
> +	mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
> +	mtk_dp_aux_set_length(mtk_dp, length);
> +
> +	if (!is_read) {
> +		if (length)
> +			mtk_dp_aux_fill_write_fifo(mtk_dp, buf,
> length);
> +
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> +				   AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_
> P0_MASK,
> +				   AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_
> P0_MASK);
> +	}
> +
> +	mtk_dp_aux_request_ready(mtk_dp);
> +
> +	/* Wait for feedback from sink device. */
> +	ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
> +
> +	reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
> +		    AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
> +
> +	if (ret || reply_cmd) {
> +		u32 phy_status = mtk_dp_read(mtk_dp,
> MTK_DP_AUX_P0_3628) &
> +				 AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
> +		if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
> +			drm_err(mtk_dp->drm_dev,
> +				"AUX Rx Aux hang, need SW reset\n");
> +			return -EIO;
> +		}
> +
> +		return -ETIMEDOUT;
> +	}
> +
> +	if (!length) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +				   0,
> +				   AUX_NO_LENGTH_AUX_TX_P0 |
> +				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	} else if (is_read) {
> +		int read_delay;
> +
> +		if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
> +		    cmd == DP_AUX_I2C_READ)
> +			read_delay = 500;
> +		else
> +			read_delay = 100;
> +
> +		mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length,
> read_delay);
> +	}
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int
> lane_num,
> +					  int swing_val, int
> preemphasis)
> +{
> +	u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
> +
> +	dev_dbg(mtk_dp->dev,
> +		"link training: swing_val = 0x%x, pre-emphasis =
> 0x%x\n",
> +		swing_val, preemphasis);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +			   swing_val << (DP_TX0_VOLT_SWING_SHIFT +
> lane_shift),
> +			   DP_TX0_VOLT_SWING_MASK << lane_shift);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +			   preemphasis << (DP_TX0_PRE_EMPH_SHIFT +
> lane_shift),
> +			   DP_TX0_PRE_EMPH_MASK << lane_shift);
> +}
> +
> +static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +			   0,
> +			   DP_TX0_VOLT_SWING_MASK |
> +			   DP_TX1_VOLT_SWING_MASK |
> +			   DP_TX2_VOLT_SWING_MASK |
> +			   DP_TX3_VOLT_SWING_MASK |
> +			   DP_TX0_PRE_EMPH_MASK |
> +			   DP_TX1_PRE_EMPH_MASK |
> +			   DP_TX2_PRE_EMPH_MASK |
> +			   DP_TX3_PRE_EMPH_MASK);
> +}
> +
> +static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +	u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
> +			 SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
> +
> +	if (irq_status) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
> +				   irq_status,
> SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
> +				   0, SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +	}
> +
> +	return irq_status;
> +}
> +
> +static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +	u32 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
> +			  IRQ_STATUS_DP_TRANS_P0_MASK) >> 12;
> +
> +	if (irq_status) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +				   irq_status,
> IRQ_CLR_DP_TRANS_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +				   0, IRQ_CLR_DP_TRANS_P0_MASK);
> +	}
> +
> +	return irq_status;
> +}
> +
> +static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +			   enable ? 0 :
> +			   IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
> +			   IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
> +			   IRQ_MASK_DP_TRANS_P0_INT_IRQ,
> +			   IRQ_MASK_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
> +			   XTAL_FREQ_DP_TRANS_P0_DEFAULT,
> +			   XTAL_FREQ_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> +			   FEC_CLOCK_EN_MODE_DP_TRANS_P0,
> +			   FEC_CLOCK_EN_MODE_DP_TRANS_P0);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> +			   AUDIO_CH_SRC_SEL_DP_ENC0_P0,
> +			   AUDIO_CH_SRC_SEL_DP_ENC0_P0);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
> +			   0, SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK,
> +			   IRQ_MASK_AUX_TOP_IRQ, IRQ_MASK_AUX_TOP_IRQ);
> +}
> +
> +static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp
> *mtk_dp)
> +{
> +	u32 val;
> +	/* Debounce threshold */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   8, HPD_DEB_THD_DP_TRANS_P0_MASK);
> +
> +	val = (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
> +	       HPD_INT_THD_DP_TRANS_P0_UPPER_1100US) << 4;
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   val, HPD_INT_THD_DP_TRANS_P0_MASK);
> +
> +	/*
> +	 * Connect threshold 1.5ms + 5 x 0.1ms = 2ms
> +	 * Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
> +	 */
> +	val = (5 << 8) | (5 << 12);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   val,
> +			   HPD_DISC_THD_DP_TRANS_P0_MASK |
> +			   HPD_CONN_THD_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
> +			   HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
> +			   HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
> +{
> +	/* modify timeout threshold = 0x1595 */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C,
> +			   AUX_TIMEOUT_THR_AUX_TX_P0_VAL,
> +			   AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658,
> +			   0, AUX_TX_OV_EN_AUX_TX_P0_MASK);
> +	/* 25 for 26M */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
> +			   AUX_TX_OVER_SAMPLE_RATE_FOR_26M << 8,
> +			   AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
> +	/* 13 for 26M */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
> +			   AUX_RX_UI_CNT_THR_AUX_FOR_26M,
> +			   AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
> +			   MTK_ATOP_EN_AUX_TX_P0,
> +			   MTK_ATOP_EN_AUX_TX_P0);
> +}
> +
> +static void mtk_dp_initialize_digital_settings(struct mtk_dp
> *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
> +			   0, VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
> +			   BS2BS_MODE_DP_ENC1_P0_VAL << 12,
> +			   BS2BS_MODE_DP_ENC1_P0_MASK);
> +
> +	/* dp tx encoder reset all sw */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0,
> +			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
> +
> +	/* Wait for sw reset to complete */
> +	usleep_range(1000, 5000);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +			   0, DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
> +}
> +
> +static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> +			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0,
> +			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
> +
> +	/* Wait for sw reset to complete */
> +	usleep_range(1000, 5000);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> +			   0,
> DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
> +}
> +
> +static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
> +			   lanes == 0 ? 0 : DP_TRANS_DUMMY_RW_0,
> +			   DP_TRANS_DUMMY_RW_0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +			   lanes, LANE_NUM_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
> +			   lanes << 2, LANE_NUM_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
> +{
> +	const struct mtk_dp_efuse_fmt *fmt;
> +	struct device *dev = mtk_dp->dev;
> +	struct nvmem_cell *cell;
> +	u32 *cal_data = mtk_dp->cal_data;
> +	u32 *buf;
> +	int i;
> +	size_t len;
> +
> +	cell = nvmem_cell_get(dev, "dp_calibration_data");
> +	if (IS_ERR(cell)) {
> +		dev_warn(dev, "Failed to get nvmem cell
> dp_calibration_data\n");
> +		goto use_default_val;
> +	}
> +
> +	buf = (u32 *)nvmem_cell_read(cell, &len);
> +	nvmem_cell_put(cell);
> +
> +	if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
> +		dev_warn(dev, "Failed to read nvmem_cell_read\n");
> +
> +		if (!IS_ERR(buf))
> +			kfree(buf);
> +
> +		goto use_default_val;
> +	}
> +
> +	for (i = 0; i < MTK_DP_CAL_MAX; i++) {
> +		fmt = &mtk_dp_efuse_data[i];
> +		cal_data[i] = (buf[fmt->idx] >> fmt->shift) & fmt-
> >mask;
> +
> +		if (cal_data[i] < fmt->min_val || cal_data[i] > fmt-
> >max_val) {
> +			dev_warn(mtk_dp->dev, "Invalid efuse data, idx
> = %d\n", i);
> +			kfree(buf);
> +			goto use_default_val;
> +		}
> +	}
> +	kfree(buf);
> +
> +	return;
> +
> +use_default_val:
> +	dev_warn(mtk_dp->dev, "Use default calibration data\n");
> +	for (i = 0; i < MTK_DP_CAL_MAX; i++)
> +		cal_data[i] = mtk_dp_efuse_data[i].default_val;
> +}
> +
> +static void mtk_dp_set_calibration_data(struct mtk_dp *mtk_dp)
> +{
> +	u32 *cal_data = mtk_dp->cal_data;
> +
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
> +			   cal_data[MTK_DP_CAL_CLKTX_IMPSE] << 20,
> +			   RG_CKM_PT0_CKTX_IMPSEL);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
> +			   cal_data[MTK_DP_CAL_GLB_BIAS_TRIM] << 16,
> +			   RG_XTP_GLB_BIAS_INTR_CTRL);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] <<
> 12,
> +			   RG_XTP_LN0_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] <<
> 16,
> +			   RG_XTP_LN0_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] <<
> 12,
> +			   RG_XTP_LN1_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] <<
> 16,
> +			   RG_XTP_LN1_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] <<
> 12,
> +			   RG_XTP_LN2_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] <<
> 16,
> +			   RG_XTP_LN2_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] <<
> 12,
> +			   RG_XTP_LN3_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] <<
> 16,
> +			   RG_XTP_LN3_TX_IMPSEL_NMOS);
> +}
> +
> +static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
> +				u32 link_rate, int lane_count)
> +{
> +	int ret;
> +	union phy_configure_opts phy_opts = {
> +		.dp = {
> +			.link_rate =
> drm_dp_bw_code_to_link_rate(link_rate) / 100,
> +			.set_rate = 1,
> +			.lanes = lane_count,
> +			.set_lanes = 1,
> +			.ssc = mtk_dp->train_info.sink_ssc,
> +		}
> +	};
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> DP_PWR_STATE_BANDGAP,
> +			   DP_PWR_STATE_MASK);
> +
> +	ret = phy_configure(mtk_dp->phy, &phy_opts);
> +	if (ret)
> +		return ret;
> +
> +	mtk_dp_set_calibration_data(mtk_dp);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL_LANE,
> DP_PWR_STATE_MASK);
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool
> enable)
> +{
> +	u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
> +		  POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
> +		  POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
> +		  POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580,
> +			   enable ? val : 0, val);
> +}
> +
> +static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int
> pattern)
> +{
> +	/* TPS1 */
> +	if (pattern == 1)
> +		mtk_dp_set_idle_pattern(mtk_dp, false);
> +
> +	mtk_dp_update_bits(mtk_dp,
> +			   MTK_DP_TRANS_P0_3400,
> +			   pattern ? BIT(pattern - 1) << 12 : 0,
> +			   PATTERN1_EN_DP_TRANS_P0_MASK |
> +			   PATTERN2_EN_DP_TRANS_P0_MASK |
> +			   PATTERN3_EN_DP_TRANS_P0_MASK |
> +			   PATTERN4_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +			   ENHANCED_FRAME_EN_DP_ENC0_P0,
> +			   ENHANCED_FRAME_EN_DP_ENC0_P0);
> +}
> +
> +static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool
> enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
> +			   enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
> +			   DP_SCR_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	struct arm_smccc_res res;
> +	u32 val = VIDEO_MUTE_SEL_DP_ENC0_P0 |
> +		  (enable ? VIDEO_MUTE_SW_DP_ENC0_P0 : 0);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +			   val,
> +			   VIDEO_MUTE_SEL_DP_ENC0_P0 |
> +			   VIDEO_MUTE_SW_DP_ENC0_P0);
> +
> +	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32,
> +		      MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable,
> +		      0, 0, 0, 0, 0, &res);
> +
> +	dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-
> 0x%lx\n",
> +		MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable, res.a0,
> res.a1);
> +}
> +
> +static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
> +			   0, SW_RST_B_PHYD);
> +
> +	/* Wait for power enable */
> +	usleep_range(10, 200);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
> +			   SW_RST_B_PHYD, SW_RST_B_PHYD);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL,
> DP_PWR_STATE_MASK);
> +	mtk_dp_write(mtk_dp, MTK_DP_1040,
> +		     RG_DPAUX_RX_VALID_DEGLITCH_EN |
> RG_XTP_GLB_CKDET_EN |
> +		     RG_DPAUX_RX_EN);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_0034, 0,
> DA_CKM_CKTX0_EN_FORCE_EN);
> +}
> +
> +static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_0034,
> +			   DA_CKM_CKTX0_EN_FORCE_EN,
> DA_CKM_CKTX0_EN_FORCE_EN);
> +
> +	/* Disable RX */
> +	mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
> +	mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
> +		     0x550 | FUSE_SEL | MEM_ISO_EN);
> +}
> +
> +static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp->train_info.link_rate = DP_LINK_BW_5_4;
> +	mtk_dp->train_info.lane_count = mtk_dp->max_lanes;
> +	mtk_dp->train_info.cable_plugged_in = false;
> +
> +	mtk_dp->info.format = DP_PIXELFORMAT_RGB;
> +	memset(&mtk_dp->info.vm, 0, sizeof(struct videomode));
> +}
> +
> +static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
> +{
> +	u32 sram_read_start = min_t(u32,
> MTK_DP_TBC_BUF_READ_START_ADDR,
> +				    mtk_dp->info.vm.hactive /
> +				    mtk_dp->train_info.lane_count /
> +				    MTK_DP_4P1T / MTK_DP_HDE /
> +				    MTK_DP_PIX_PER_ADDR);
> +	mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
> +	mtk_dp_setup_encoder(mtk_dp);
> +}
> +
> +static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_setup_tu(mtk_dp);
> +}
> +
> +static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int
> lanes,
> +					  u8 dpcd_adjust_req[2])
> +{
> +	int lane;
> +
> +	for (lane = 0; lane < lanes; ++lane) {
> +		u8 val;
> +		u8 swing;
> +		u8 preemphasis;
> +		int index = lane / 2;
> +		int shift = lane % 2 ?
> DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
> +
> +		swing = (dpcd_adjust_req[index] >> shift) &
> +			DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
> +		preemphasis = ((dpcd_adjust_req[index] >> shift) &
> +			       DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
> +			      DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
> +		val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
> +		      preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +
> +		if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
> +			val |= DP_TRAIN_MAX_SWING_REACHED;
> +		if (preemphasis == 3)
> +			val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> +
> +		mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing,
> preemphasis);
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET
> + lane,
> +				   val);
> +	}
> +}
> +
> +static void mtk_dp_pattern(struct mtk_dp *mtk_dp, bool is_tps1)
> +{
> +	int pattern;
> +	unsigned int aux_offset;
> +
> +	if (is_tps1) {
> +		pattern = 1;
> +		aux_offset = DP_LINK_SCRAMBLING_DISABLE |
> DP_TRAINING_PATTERN_1;
> +	} else {
> +		aux_offset = mtk_dp->train_info.channel_eq_pattern;
> +
> +		switch (mtk_dp->train_info.channel_eq_pattern) {
> +		case DP_TRAINING_PATTERN_4:
> +			pattern = 4;
> +			break;
> +		case DP_TRAINING_PATTERN_3:
> +			pattern = 3;
> +			aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
> +			break;
> +		case DP_TRAINING_PATTERN_2:
> +		default:
> +			pattern = 2;
> +			aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
> +			break;
> +		}
> +	}
> +
> +	mtk_dp_train_set_pattern(mtk_dp, pattern);
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> aux_offset);
> +}
> +
> +static int mtk_dp_train_setting(struct mtk_dp *mtk_dp, u8
> target_link_rate,
> +				u8 target_lane_count)
> +{
> +	int ret;
> +
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET,
> target_link_rate);
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> +			   target_lane_count |
> DP_LANE_COUNT_ENHANCED_FRAME_EN);
> +
> +	if (mtk_dp->train_info.sink_ssc)
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
> +				   DP_SPREAD_AMP_0_5);
> +
> +	mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
> +	ret = mtk_dp_phy_configure(mtk_dp, target_link_rate,
> target_lane_count);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(mtk_dp->dev,
> +		"Link train target_link_rate = 0x%x, target_lane_count
> = 0x%x\n",
> +		target_link_rate, target_lane_count);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_train_cr(struct mtk_dp *mtk_dp, u8
> target_lane_count)
> +{
> +	u8 lane_adjust[2] = {};
> +	u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +	u8 prev_lane_adjust = 0xff;
> +	int train_retries = 0;
> +	int voltage_retries = 0;
> +
> +	mtk_dp_pattern(mtk_dp, true);
> +
> +	/* In DP spec 1.4, the retry count of CR is defined as 10. */
> +	do {
> +		train_retries++;
> +		if (!mtk_dp->train_info.cable_plugged_in) {
> +			mtk_dp_train_set_pattern(mtk_dp, 0);
> +			return -ENODEV;
> +		}
> +
> +		drm_dp_dpcd_read(&mtk_dp->aux,
> DP_ADJUST_REQUEST_LANE0_1,
> +				 lane_adjust, sizeof(lane_adjust));
> +		mtk_dp_train_update_swing_pre(mtk_dp,
> target_lane_count,
> +					      lane_adjust);
> +
> +		drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
> +						       mtk_dp->rx_cap);
> +
> +		/* check link status from sink device */
> +		drm_dp_dpcd_read_link_status(&mtk_dp->aux,
> link_status);
> +		if (drm_dp_clock_recovery_ok(link_status,
> +					     target_lane_count)) {
> +			dev_dbg(mtk_dp->dev, "Link train CR pass\n");
> +			return 0;
> +		}
> +
> +		/*
> +		 * In DP spec 1.4, if current voltage level is the same
> +		 * with previous voltage level, we need to retry 5
> times.
> +		 */
> +		if (prev_lane_adjust == link_status[4]) {
> +			voltage_retries++;
> +			/*
> +			 * Condition of CR fail:
> +			 * 1. Failed to pass CR using the same voltage
> +			 *    level over five times.
> +			 * 2. Failed to pass CR when the current
> voltage
> +			 *    level is the same with previous voltage
> +			 *    level and reach max voltage level (3).
> +			 */
> +			if (voltage_retries >
> MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY ||
> +			    (prev_lane_adjust &
> DP_ADJUST_VOLTAGE_SWING_LANE0_MASK) == 3) {
> +				dev_dbg(mtk_dp->dev, "Link train CR
> fail\n");
> +				break;
> +			}
> +		} else {
> +			/*
> +			 * If the voltage level is changed, we need to
> +			 * re-calculate this retry count.
> +			 */
> +			voltage_retries = 0;
> +		}
> +		prev_lane_adjust = link_status[4];
> +	} while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
> +
> +	/* Failed to train CR, and disable pattern. */
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +			   DP_TRAINING_PATTERN_DISABLE);
> +	mtk_dp_train_set_pattern(mtk_dp, 0);
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_train_eq(struct mtk_dp *mtk_dp, u8
> target_lane_count)
> +{
> +	u8 lane_adjust[2] = {};
> +	u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +	int train_retries = 0;
> +
> +	mtk_dp_pattern(mtk_dp, false);
> +
> +	do {
> +		train_retries++;
> +		if (!mtk_dp->train_info.cable_plugged_in) {
> +			mtk_dp_train_set_pattern(mtk_dp, 0);
> +			return -ENODEV;
> +		}
> +
> +		drm_dp_dpcd_read(&mtk_dp->aux,
> DP_ADJUST_REQUEST_LANE0_1,
> +				 lane_adjust, sizeof(lane_adjust));
> +		mtk_dp_train_update_swing_pre(mtk_dp,
> target_lane_count,
> +					      lane_adjust);
> +
> +		drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
> +						   mtk_dp->rx_cap);
> +
> +		/* check link status from sink device */
> +		drm_dp_dpcd_read_link_status(&mtk_dp->aux,
> link_status);
> +		if (drm_dp_channel_eq_ok(link_status,
> target_lane_count)) {
> +			dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
> +
> +			/* Training done, and disable pattern. */
> +			drm_dp_dpcd_writeb(&mtk_dp->aux,
> DP_TRAINING_PATTERN_SET,
> +					   DP_TRAINING_PATTERN_DISABLE)
> ;
> +			mtk_dp_train_set_pattern(mtk_dp, 0);
> +			return 0;
> +		}
> +		dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
> +	} while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
> +
> +	/* Failed to train EQ, and disable pattern. */
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +			   DP_TRAINING_PATTERN_DISABLE);
> +	mtk_dp_train_set_pattern(mtk_dp, 0);
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
> +{
> +	u8 val;
> +	ssize_t ret;
> +
> +	drm_dp_read_dpcd_caps(&mtk_dp->aux, mtk_dp->rx_cap);
> +
> +	if (drm_dp_tps4_supported(mtk_dp->rx_cap))
> +		mtk_dp->train_info.channel_eq_pattern =
> DP_TRAINING_PATTERN_4;
> +	else if (drm_dp_tps3_supported(mtk_dp->rx_cap))
> +		mtk_dp->train_info.channel_eq_pattern =
> DP_TRAINING_PATTERN_3;
> +	else
> +		mtk_dp->train_info.channel_eq_pattern =
> DP_TRAINING_PATTERN_2;
> +
> +	mtk_dp->train_info.sink_ssc = drm_dp_max_downspread(mtk_dp-
> >rx_cap);
> +
> +	ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
> +	if (ret < 1) {
> +		drm_err(mtk_dp->drm_dev, "Read mstm cap failed\n");
> +		return ret == 0 ? -EIO : ret;
> +	}
> +
> +	if (val & DP_MST_CAP) {
> +		/* Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 */
> +		ret = drm_dp_dpcd_readb(&mtk_dp->aux,
> +					DP_DEVICE_SERVICE_IRQ_VECTOR_ES
> I0,
> +					&val);
> +		if (ret < 1) {
> +			drm_err(mtk_dp->drm_dev, "Read irq vector
> failed\n");
> +			return ret == 0 ? -EIO : ret;
> +		}
> +
> +		if (val)
> +			drm_dp_dpcd_writeb(&mtk_dp->aux,
> +					   DP_DEVICE_SERVICE_IRQ_VECTOR
> _ESI0,
> +					   val);
> +	}
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
> +{
> +	phy_reset(mtk_dp->phy);
> +	mtk_dp_reset_swing_pre_emphasis(mtk_dp);
> +}
> +
> +static int mtk_dp_training(struct mtk_dp *mtk_dp)
> +{
> +	int ret;
> +	u8 lane_count, link_rate, train_limit, max_link_rate;
> +
> +	link_rate = min_t(u8, mtk_dp->max_linkrate,
> +			  mtk_dp->rx_cap[DP_MAX_LINK_RATE]);
> +	max_link_rate = link_rate;
> +	lane_count = min_t(u8, mtk_dp->max_lanes,
> +			   drm_dp_max_lane_count(mtk_dp->rx_cap));
> +
> +	/*
> +	 * TPS are generated by the hardware pattern generator. From
> the
> +	 * hardware setting we need to disable this scramble setting
> before
> +	 * use the TPS pattern generator.
> +	 */
> +	mtk_dp_training_set_scramble(mtk_dp, false);
> +
> +	for (train_limit = 6; train_limit > 0; train_limit--) {
> +		mtk_dp_train_change_mode(mtk_dp);
> +
> +		ret = mtk_dp_train_setting(mtk_dp, link_rate,
> lane_count);
> +		if (ret)
> +			return ret;
> +
> +		ret = mtk_dp_train_cr(mtk_dp, lane_count);
> +		if (ret == -ENODEV) {
> +			return ret;
> +		} else if (ret) {
> +			/* reduce link rate */
> +			switch (link_rate) {
> +			case DP_LINK_BW_1_62:
> +				lane_count = lane_count / 2;
> +				link_rate = max_link_rate;
> +				if (lane_count == 0)
> +					return -EIO;
> +				break;
> +			case DP_LINK_BW_2_7:
> +				link_rate = DP_LINK_BW_1_62;
> +				break;
> +			case DP_LINK_BW_5_4:
> +				link_rate = DP_LINK_BW_2_7;
> +				break;
> +			case DP_LINK_BW_8_1:
> +				link_rate = DP_LINK_BW_5_4;
> +				break;
> +			default:
> +				return -EINVAL;
> +			};
> +			continue;
> +		}
> +
> +		ret = mtk_dp_train_eq(mtk_dp, lane_count);
> +		if (ret == -ENODEV) {
> +			return ret;
> +		} else if (ret) {
> +			/* reduce lane count */
> +			if (lane_count == 0)
> +				return -EIO;
> +			lane_count /= 2;
> +			continue;
> +		}
> +
> +		/* if we can run to this, training is done. */
> +		break;
> +	}
> +
> +	if (train_limit == 0)
> +		return -ETIMEDOUT;
> +
> +	mtk_dp->train_info.link_rate = link_rate;
> +	mtk_dp->train_info.lane_count = lane_count;
> +
> +	/*
> +	 * After training done, we need to output normal stream instead
> of TPS,
> +	 * so we need to enable scramble.
> +	 */
> +	mtk_dp_training_set_scramble(mtk_dp, true);
> +	mtk_dp_set_enhanced_frame_mode(mtk_dp);
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	/* the mute sequence is different between enable and disable */
> +	if (enable) {
> +		mtk_dp_msa_bypass_enable(mtk_dp, false);
> +		mtk_dp_pg_enable(mtk_dp, false);
> +		mtk_dp_set_tx_out(mtk_dp);
> +		mtk_dp_video_mute(mtk_dp, false);
> +	} else {
> +		mtk_dp_video_mute(mtk_dp, true);
> +		mtk_dp_pg_enable(mtk_dp, true);
> +		mtk_dp_msa_bypass_enable(mtk_dp, true);
> +	}
> +}
> +
> +static int mtk_dp_video_config(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_config_mn_mode(mtk_dp);
> +	mtk_dp_set_msa(mtk_dp);
> +	mtk_dp_set_color_depth(mtk_dp);
> +	return mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
> +}
> +
> +static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_set_idle_pattern(mtk_dp, true);
> +	mtk_dp_initialize_priv_data(mtk_dp);
> +
> +	mtk_dp_initialize_settings(mtk_dp);
> +	mtk_dp_initialize_aux_settings(mtk_dp);
> +	mtk_dp_initialize_digital_settings(mtk_dp);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
> +			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0,
> +			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0);
> +	mtk_dp_initialize_hpd_detect_settings(mtk_dp);
> +
> +	mtk_dp_digital_sw_reset(mtk_dp);
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev;
> +	unsigned long flags;
> +	u32 status;
> +
> +	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
> +	status = mtk_dp->irq_thread_handle;
> +	mtk_dp->irq_thread_handle = 0;
> +	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
> +
> +	if (status & MTK_DP_THREAD_CABLE_STATE_CHG)
> +		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
> +
> +	if (status & MTK_DP_THREAD_HPD_EVENT)
> +		dev_dbg(mtk_dp->dev, "Receive IRQ from sink
> devices\n");
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev;
> +	bool cable_sta_chg = false;
> +	unsigned long flags;
> +	u32 irq_status = mtk_dp_swirq_get_clear(mtk_dp) |
> +			 mtk_dp_hwirq_get_clear(mtk_dp);
> +
> +	if (!irq_status)
> +		return IRQ_HANDLED;
> +
> +	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
> +
> +	if (irq_status & MTK_DP_HPD_INTERRUPT)
> +		mtk_dp->irq_thread_handle |= MTK_DP_THREAD_HPD_EVENT;
> +
> +	/* Cable state is changed. */
> +	if (irq_status != MTK_DP_HPD_INTERRUPT) {
> +		mtk_dp->irq_thread_handle |=
> MTK_DP_THREAD_CABLE_STATE_CHG;
> +		cable_sta_chg = true;
> +	}
> +
> +	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
> +
> +	if (cable_sta_chg) {
> +		if (!!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
> +		       HPD_DB_DP_TRANS_P0_MASK))
> +			mtk_dp->train_info.cable_plugged_in = true;
> +		else
> +			mtk_dp->train_info.cable_plugged_in = false;
> +	}
> +
> +	return IRQ_WAKE_THREAD;
> +}
> +
> +static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
> +			   struct platform_device *pdev)
> +{
> +	struct device_node *endpoint;
> +	struct device *dev = &pdev->dev;
> +	int ret;
> +	void __iomem *base;
> +	u32 linkrate;
> +	int len;
> +
> +	base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(base))
> +		return PTR_ERR(base);
> +
> +	mtk_dp->regs = devm_regmap_init_mmio(dev, base,
> &mtk_dp_regmap_config);
> +	if (IS_ERR(mtk_dp->regs))
> +		return PTR_ERR(mtk_dp->regs);
> +
> +	endpoint = of_graph_get_endpoint_by_regs(pdev->dev.of_node, 1,
> -1);
> +	len = of_property_count_elems_of_size(endpoint,
> +					      "data-lanes",
> sizeof(u32));
> +	if (len < 0 || len > 4 || len == 3) {
> +		dev_err(dev, "invalid data lane size: %d\n", len);
> +		return -EINVAL;
> +	}
> +
> +	mtk_dp->max_lanes = len;
> +
> +	ret = device_property_read_u32(dev, "max-linkrate-mhz",
> &linkrate);
> +	if (ret) {
> +		dev_err(dev, "failed to read max linkrate: %d\n", ret);
> +		return ret;
> +	}
> +
> +	mtk_dp->max_linkrate = drm_dp_link_rate_to_bw_code(linkrate *
> 100);
> +
> +	return 0;
> +}
> +
> +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> +				    struct drm_connector *connector)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	bool enabled = mtk_dp->enabled;
> +	struct edid *new_edid = NULL;
> +
> +	if (!enabled) {
> +		drm_bridge_chain_pre_enable(bridge);
> +
> +		/* power on aux */
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +				   DP_PWR_STATE_BANDGAP_TPLL_LANE,
> +				   DP_PWR_STATE_MASK);
> +
> +		/* power on panel */
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D0);
> +		usleep_range(2000, 5000);
> +	}
> +
> +	new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> +
> +	/*
> +	 * Parse capability here to let atomic_get_input_bus_fmts and
> +	 * mode_valid use the capability to calculate sink bitrates.
> +	 */
> +	if (mtk_dp_parse_capabilities(mtk_dp)) {
> +		drm_err(mtk_dp->drm_dev, "Can't parse capabilities\n");
> +		new_edid = NULL;
> +	}
> +
> +	if (!enabled) {
> +		/* power off panel */
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D3);
> +		usleep_range(2000, 3000);
> +
> +		/* power off aux */
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +				   DP_PWR_STATE_BANDGAP_TPLL,
> +				   DP_PWR_STATE_MASK);
> +
> +		drm_bridge_chain_post_disable(bridge);
> +	}
> +
> +	return new_edid;
> +}
> +
> +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> +				   struct drm_dp_aux_msg *msg)
> +{
> +	struct mtk_dp *mtk_dp;
> +	bool is_read;
> +	u8 request;
> +	size_t accessed_bytes = 0;
> +	int ret;
> +
> +	mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> +
> +	if (!mtk_dp->train_info.cable_plugged_in) {
> +		ret = -EAGAIN;
> +		goto err;
> +	}
> +
> +	switch (msg->request) {
> +	case DP_AUX_I2C_MOT:
> +	case DP_AUX_I2C_WRITE:
> +	case DP_AUX_NATIVE_WRITE:
> +	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> +	case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> +		request = msg->request &
> ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> +		is_read = false;
> +		break;
> +	case DP_AUX_I2C_READ:
> +	case DP_AUX_NATIVE_READ:
> +	case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> +		request = msg->request;
> +		is_read = true;
> +		break;
> +	default:
> +		drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> +			msg->request);
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	do {
> +		size_t to_access = min_t(size_t,
> DP_AUX_MAX_PAYLOAD_BYTES,
> +					 msg->size - accessed_bytes);
> +
> +		ret = mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> +					     msg->address +
> accessed_bytes,
> +					     msg->buffer +
> accessed_bytes,
> +					     to_access);
> +
> +		if (ret) {
> +			drm_info(mtk_dp->drm_dev,
> +				 "Failed to do AUX transfer: %d\n",
> ret);
> +			goto err;
> +		}
> +		accessed_bytes += to_access;
> +	} while (accessed_bytes < msg->size);
> +
> +	msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +	return msg->size;
> +err:
> +	msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> +	return ret;
> +}
> +
> +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> +{
> +	int ret;
> +
> +	ret = phy_init(mtk_dp->phy);
> +	if (ret) {
> +		dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n",
> ret);
> +		return ret;
> +	}
> +
> +	mtk_dp_init_port(mtk_dp);
> +	mtk_dp_power_enable(mtk_dp);
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_power_disable(mtk_dp);
> +	phy_exit(mtk_dp->phy);
> +}
> +
> +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> +				enum drm_bridge_attach_flags flags)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	int ret;
> +
> +	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> +		dev_err(mtk_dp->dev, "Driver does not provide a
> connector!");
> +		return -EINVAL;
> +	}
> +
> +	mtk_dp->aux.drm_dev = bridge->dev;
> +	ret = drm_dp_aux_register(&mtk_dp->aux);
> +	if (ret) {
> +		dev_err(mtk_dp->dev,
> +			"failed to register DP AUX channel: %d\n",
> ret);
> +		return ret;
> +	}
> +
> +	ret = mtk_dp_poweron(mtk_dp);
> +	if (ret)
> +		goto err_aux_register;
> +
> +	if (mtk_dp->next_bridge) {
> +		ret = drm_bridge_attach(bridge->encoder, mtk_dp-
> >next_bridge,
> +					&mtk_dp->bridge, flags);
> +		if (ret) {
> +			drm_warn(mtk_dp->drm_dev,
> +				 "Failed to attach external bridge:
> %d\n", ret);
> +			goto err_bridge_attach;
> +		}
> +	}
> +
> +	mtk_dp->drm_dev = bridge->dev;
> +
> +	mtk_dp_hwirq_enable(mtk_dp, true);
> +
> +	return 0;
> +
> +err_bridge_attach:
> +	mtk_dp_poweroff(mtk_dp);
> +err_aux_register:
> +	drm_dp_aux_unregister(&mtk_dp->aux);
> +	return ret;
> +}
> +
> +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +	mtk_dp_hwirq_enable(mtk_dp, false);
> +	mtk_dp->drm_dev = NULL;
> +	mtk_dp_poweroff(mtk_dp);
> +	drm_dp_aux_unregister(&mtk_dp->aux);
> +}
> +
> +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> +					struct drm_bridge_state
> *old_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	int ret;
> +
> +	mtk_dp->conn =
> drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> +								bridge-
> >encoder);
> +	if (!mtk_dp->conn) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as connector is
> missing\n");
> +		return;
> +	}
> +
> +	/* power on aux */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL_LANE,
> +			   DP_PWR_STATE_MASK);
> +
> +	if (mtk_dp->train_info.cable_plugged_in) {
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D0);
> +		usleep_range(2000, 5000);
> +	}
> +
> +	/* Training */
> +	ret = mtk_dp_training(mtk_dp);
> +	if (ret) {
> +		drm_err(mtk_dp->drm_dev, "Training failed, %d\n", ret);
> +		goto power_off_aux;
> +	}
> +
> +	ret = mtk_dp_video_config(mtk_dp);
> +	if (ret)
> +		goto power_off_aux;
> +
> +	mtk_dp_video_enable(mtk_dp, true);
> +
> +	mtk_dp->enabled = true;
> +
> +	return;
> +power_off_aux:
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL,
> +			   DP_PWR_STATE_MASK);
> +}
> +
> +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> +					 struct drm_bridge_state
> *old_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +	mtk_dp->enabled = false;
> +	mtk_dp_video_enable(mtk_dp, false);
> +
> +	if (mtk_dp->train_info.cable_plugged_in) {
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D3);
> +		usleep_range(2000, 3000);
> +	}
> +
> +	/* power off aux */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL,
> +			   DP_PWR_STATE_MASK);
> +
> +	/* Ensure the sink is muted */
> +	msleep(20);
> +}
> +
> +static enum drm_mode_status
> +mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
> +			 const struct drm_display_info *info,
> +			 const struct drm_display_mode *mode)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	u32 bpp = info->color_formats & DRM_COLOR_FORMAT_YCBCR422 ? 16
> : 24;
> +	u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
> +			      drm_dp_max_lane_count(mtk_dp->rx_cap),
> +			 drm_dp_bw_code_to_link_rate(mtk_dp-
> >max_linkrate) *
> +			 mtk_dp->max_lanes);
> +
> +	if (rate < mode->clock * bpp / 8)
> +		return MODE_CLOCK_HIGH;
> +
> +	return MODE_OK;
> +}
> +
> +static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(struct
> drm_bridge *bridge,
> +						     struct
> drm_bridge_state *bridge_state,
> +						     struct
> drm_crtc_state *crtc_state,
> +						     struct
> drm_connector_state *conn_state,
> +						     unsigned int
> *num_output_fmts)
> +{
> +	u32 *output_fmts;
> +
> +	*num_output_fmts = 0;
> +	output_fmts = kmalloc(sizeof(*output_fmts), GFP_KERNEL);
> +	if (!output_fmts)
> +		return NULL;
> +	*num_output_fmts = 1;
> +	output_fmts[0] = MEDIA_BUS_FMT_FIXED;
> +	return output_fmts;
> +}
> +
> +static const u32 mt8195_input_fmts[] = {
> +	MEDIA_BUS_FMT_RGB888_1X24,
> +	MEDIA_BUS_FMT_YUV8_1X24,
> +	MEDIA_BUS_FMT_YUYV8_1X16,
> +};
> +
> +static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(struct
> drm_bridge *bridge,
> +						    struct
> drm_bridge_state *bridge_state,
> +						    struct
> drm_crtc_state *crtc_state,
> +						    struct
> drm_connector_state *conn_state,
> +						    u32 output_fmt,
> +						    unsigned int
> *num_input_fmts)
> +{
> +	u32 *input_fmts;
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> +	struct drm_display_info *display_info =
> +		&conn_state->connector->display_info;
> +	u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
> +			      drm_dp_max_lane_count(mtk_dp->rx_cap),
> +			 drm_dp_bw_code_to_link_rate(mtk_dp-
> >max_linkrate) *
> +			 mtk_dp->max_lanes);
> +
> +	*num_input_fmts = 0;
> +
> +	/*
> +	 * If the linkrate is smaller than datarate of RGB888, larger
> than
> +	 * datarate of YUV422 and sink device supports YUV422, we
> output YUV422
> +	 * format. Use this condition, we can support more resolution.
> +	 */
> +	if ((rate < (mode->clock * 24 / 8)) &&
> +	    (rate > (mode->clock * 16 / 8)) &&
> +	    (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR422))
> {
> +		input_fmts = kcalloc(1, sizeof(*input_fmts),
> GFP_KERNEL);
> +		if (!input_fmts)
> +			return NULL;
> +		*num_input_fmts = 1;
> +		input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
> +	} else {
> +		input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts),
> +				     sizeof(*input_fmts),
> +				     GFP_KERNEL);
> +		if (!input_fmts)
> +			return NULL;
> +
> +		*num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
> +		memcpy(input_fmts, mt8195_input_fmts,
> sizeof(mt8195_input_fmts));
> +	}
> +
> +	return input_fmts;
> +}
> +
> +static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
> +				      struct drm_bridge_state
> *bridge_state,
> +				      struct drm_crtc_state
> *crtc_state,
> +				      struct drm_connector_state
> *conn_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	struct drm_crtc *crtc = conn_state->crtc;
> +	unsigned int input_bus_format;
> +
> +	input_bus_format = bridge_state->input_bus_cfg.format;
> +
> +	dev_dbg(mtk_dp->dev, "input format 0x%04x, output format
> 0x%04x\n",
> +		bridge_state->input_bus_cfg.format,
> +		 bridge_state->output_bus_cfg.format);
> +
> +	if (input_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> +		mtk_dp->info.format = DP_PIXELFORMAT_YUV422;
> +	else
> +		mtk_dp->info.format = DP_PIXELFORMAT_RGB;
> +
> +	if (!crtc) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as connector state doesn't
> have a crtc\n");
> +		return -EINVAL;
> +	}
> +
> +	drm_display_mode_to_videomode(&crtc_state->adjusted_mode,
> &mtk_dp->info.vm);
> +
> +	return 0;
> +}
> +
> +static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
> +	.atomic_check = mtk_dp_bridge_atomic_check,
> +	.atomic_duplicate_state =
> drm_atomic_helper_bridge_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> +	.atomic_get_output_bus_fmts =
> mtk_dp_bridge_atomic_get_output_bus_fmts,
> +	.atomic_get_input_bus_fmts =
> mtk_dp_bridge_atomic_get_input_bus_fmts,
> +	.atomic_reset = drm_atomic_helper_bridge_reset,
> +	.attach = mtk_dp_bridge_attach,
> +	.detach = mtk_dp_bridge_detach,
> +	.atomic_enable = mtk_dp_bridge_atomic_enable,
> +	.atomic_disable = mtk_dp_bridge_atomic_disable,
> +	.mode_valid = mtk_dp_bridge_mode_valid,
> +	.get_edid = mtk_dp_get_edid,
> +};
> +
> +static int mtk_dp_probe(struct platform_device *pdev)
> +{
> +	struct mtk_dp *mtk_dp;
> +	struct device *dev = &pdev->dev;
> +	int ret, irq_num;
> +
> +	mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> +	if (!mtk_dp)
> +		return -ENOMEM;
> +
> +	mtk_dp->dev = dev;
> +
> +	irq_num = platform_get_irq(pdev, 0);
> +	if (irq_num < 0)
> +		return dev_err_probe(dev, irq_num,
> +				     "failed to request dp irq
> resource\n");
> +
> +	mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 
> 1, 0);
> +	if (IS_ERR(mtk_dp->next_bridge))
> +		return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge),
> +				     "Failed to get bridge\n");
> +
> +	ret = mtk_dp_dt_parse(mtk_dp, pdev);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to parse dt\n");
> +
> +	drm_dp_aux_init(&mtk_dp->aux);
> +	mtk_dp->aux.name = "aux_mtk_dp";
> +	mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> +
> +	spin_lock_init(&mtk_dp->irq_thread_lock);
> +
> +	ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
> +					mtk_dp_hpd_event_thread,
> +					IRQ_TYPE_LEVEL_HIGH,
> dev_name(dev),
> +					mtk_dp);
> +	if (ret)
> +		return dev_err_probe(dev, ret,
> +				     "failed to request mediatek dptx
> irq\n");
> +
> +	platform_set_drvdata(pdev, mtk_dp);
> +
> +	mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-
> dp-phy",
> +							PLATFORM_DEVID_
> AUTO,
> +							&mtk_dp->regs,
> +							sizeof(struct
> regmap *));
> +	if (IS_ERR(mtk_dp->phy_dev))
> +		return dev_err_probe(dev, PTR_ERR(mtk_dp->phy_dev),
> +				     "Failed to create device mediatek-
> dp-phy\n");
> +
> +	mtk_dp_get_calibration_data(mtk_dp);
> +
> +	mtk_dp->phy = devm_phy_get(&mtk_dp->phy_dev->dev, "dp");
> +
> +	if (IS_ERR(mtk_dp->phy)) {
> +		platform_device_unregister(mtk_dp->phy_dev);
> +		return dev_err_probe(dev, PTR_ERR(mtk_dp->phy),
> +				     "Failed to get phy\n");
> +	}
> +
> +	mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
> +	mtk_dp->bridge.of_node = dev->of_node;
> +
> +	mtk_dp->bridge.ops =
> +		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
> DRM_BRIDGE_OP_HPD;
> +	mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> +
> +	drm_bridge_add(&mtk_dp->bridge);
> +
> +	pm_runtime_enable(dev);
> +	pm_runtime_get_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_remove(struct platform_device *pdev)
> +{
> +	struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
> +
> +	pm_runtime_put(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +	drm_bridge_remove(&mtk_dp->bridge);
> +	platform_device_unregister(mtk_dp->phy_dev);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int mtk_dp_suspend(struct device *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	mtk_dp_power_disable(mtk_dp);
> +	mtk_dp_hwirq_enable(mtk_dp, false);
> +	pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_resume(struct device *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	pm_runtime_get_sync(dev);
> +	mtk_dp_init_port(mtk_dp);
> +	mtk_dp_hwirq_enable(mtk_dp, true);
> +	mtk_dp_power_enable(mtk_dp);
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend,
> mtk_dp_resume);
> +
> +static const struct of_device_id mtk_dp_of_match[] = {
> +	{ .compatible = "mediatek,mt8195-edp-tx" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
> +
> +struct platform_driver mtk_dp_driver = {
> +	.probe = mtk_dp_probe,
> +	.remove = mtk_dp_remove,
> +	.driver = {
> +		.name = "mediatek-drm-dp",
> +		.of_match_table = mtk_dp_of_match,
> +		.pm = &mtk_dp_pm_ops,
> +	},
> +};
> +
> +module_platform_driver(mtk_dp_driver);
> +
> +MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
> +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> +MODULE_AUTHOR("Bo-Chen Chen <rex-bc.chen@mediatek.com>");
> +MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> new file mode 100644
> index 000000000000..3f01ba44871f
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> @@ -0,0 +1,305 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019-2022 MediaTek Inc.
> + * Copyright (c) 2022 BayLibre
> + */
> +#ifndef _MTK_DP_REG_H_
> +#define _MTK_DP_REG_H_
> +
> +#define SEC_OFFSET	0x4000
> +
> +#define MTK_DP_HPD_DISCONNECT		BIT(1)
> +#define MTK_DP_HPD_CONNECT		BIT(2)
> +#define MTK_DP_HPD_INTERRUPT		BIT(3)
> +
> +/* offset: 0x0 */
> +#define DP_PHY_GLB_BIAS_GEN_00		0x0
> +#define RG_XTP_GLB_BIAS_INTR_CTRL		GENMASK(20, 16)
> +#define DP_PHY_GLB_DPAUX_TX		0x8
> +#define RG_CKM_PT0_CKTX_IMPSEL			GENMASK(23, 20)
> +#define MTK_DP_0034			0x34
> +#define DA_XTP_GLB_CKDET_EN_FORCE_VAL		BIT(15)
> +#define DA_XTP_GLB_CKDET_EN_FORCE_EN		BIT(14)
> +#define DA_CKM_INTCKTX_EN_FORCE_VAL		BIT(13)
> +#define DA_CKM_INTCKTX_EN_FORCE_EN		BIT(12)
> +#define DA_CKM_CKTX0_EN_FORCE_VAL		BIT(11)
> +#define DA_CKM_CKTX0_EN_FORCE_EN		BIT(10)
> +#define DA_CKM_XTAL_CK_FORCE_VAL		BIT(9)
> +#define DA_CKM_XTAL_CK_FORCE_EN			BIT(8)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_VAL		BIT(7)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_EN		BIT(6)
> +#define DA_CKM_BIAS_EN_FORCE_VAL		BIT(5)
> +#define DA_CKM_BIAS_EN_FORCE_EN			BIT(4)
> +#define DA_XTP_GLB_AVD10_ON_FORCE_VAL		BIT(3)
> +#define DA_XTP_GLB_AVD10_ON_FORCE		BIT(2)
> +#define DA_XTP_GLB_LDO_EN_FORCE_VAL		BIT(1)
> +#define DA_XTP_GLB_LDO_EN_FORCE_EN		BIT(0)
> +#define DP_PHY_LANE_TX_0		0x104
> +#define RG_XTP_LN0_TX_IMPSEL_PMOS		GENMASK(15, 12)
> +#define RG_XTP_LN0_TX_IMPSEL_NMOS		GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_1		0x204
> +#define RG_XTP_LN1_TX_IMPSEL_PMOS		GENMASK(15, 12)
> +#define RG_XTP_LN1_TX_IMPSEL_NMOS		GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_2		0x304
> +#define RG_XTP_LN2_TX_IMPSEL_PMOS		GENMASK(15, 12)
> +#define RG_XTP_LN2_TX_IMPSEL_NMOS		GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_3		0x404
> +#define RG_XTP_LN3_TX_IMPSEL_PMOS		GENMASK(15, 12)
> +#define RG_XTP_LN3_TX_IMPSEL_NMOS		GENMASK(19, 16)
> +#define MTK_DP_1040			0x1040
> +#define RG_DPAUX_RX_VALID_DEGLITCH_EN		BIT(2)
> +#define RG_XTP_GLB_CKDET_EN			BIT(1)
> +#define RG_DPAUX_RX_EN				BIT(0)
> +
> +/* offset: TOP_OFFSET (0x2000) */
> +#define MTK_DP_TOP_PWR_STATE		0x2000
> +#define DP_PWR_STATE_MASK			GENMASK(1, 0)
> +#define DP_PWR_STATE_BANDGAP			BIT(0)
> +#define DP_PWR_STATE_BANDGAP_TPLL		BIT(1)
> +#define DP_PWR_STATE_BANDGAP_TPLL_LANE		GENMASK(1, 0)
> +#define MTK_DP_TOP_SWING_EMP		0x2004
> +#define DP_TX0_VOLT_SWING_MASK			GENMASK(1, 0)
> +#define DP_TX0_VOLT_SWING_SHIFT			0
> +#define DP_TX0_PRE_EMPH_MASK			GENMASK(3, 2)
> +#define DP_TX0_PRE_EMPH_SHIFT			2
> +#define DP_TX1_VOLT_SWING_MASK			GENMASK(9, 8)
> +#define DP_TX1_VOLT_SWING_SHIFT			8
> +#define DP_TX1_PRE_EMPH_MASK			GENMASK(11, 10)
> +#define DP_TX2_VOLT_SWING_MASK			GENMASK(17, 16)
> +#define DP_TX2_PRE_EMPH_MASK			GENMASK(19, 18)
> +#define DP_TX3_VOLT_SWING_MASK			GENMASK(25, 24)
> +#define DP_TX3_PRE_EMPH_MASK			GENMASK(27, 26)
> +#define MTK_DP_TOP_RESET_AND_PROBE	0x2020
> +#define SW_RST_B_PHYD				BIT(4)
> +#define MTK_DP_TOP_IRQ_MASK		0x202c
> +#define IRQ_MASK_AUX_TOP_IRQ			BIT(2)
> +#define MTK_DP_TOP_MEM_PD		0x2038
> +#define MEM_ISO_EN				BIT(0)
> +#define FUSE_SEL				BIT(2)
> +
> +/* offset: ENC0_OFFSET (0x3000) */
> +#define MTK_DP_ENC0_P0_3000			0x3000
> +#define LANE_NUM_DP_ENC0_P0_MASK			GENMASK(1, 0)
> +#define VIDEO_MUTE_SW_DP_ENC0_P0			BIT(2)
> +#define VIDEO_MUTE_SEL_DP_ENC0_P0			BIT(3)
> +#define ENHANCED_FRAME_EN_DP_ENC0_P0			BIT(4)
> +#define MTK_DP_ENC0_P0_3004			0x3004
> +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK		BIT(8)
> +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0		BIT(9)
> +#define MTK_DP_ENC0_P0_3010			0x3010
> +#define HTOTAL_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3014			0x3014
> +#define VTOTAL_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3018			0x3018
> +#define HSTART_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_301C			0x301c
> +#define VSTART_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3020			0x3020
> +#define HWIDTH_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3024			0x3024
> +#define VHEIGHT_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3028			0x3028
> +#define HSW_SW_DP_ENC0_P0_MASK				GENMASK
> (14, 0)
> +#define HSP_SW_DP_ENC0_P0_MASK				BIT(15)
> +#define MTK_DP_ENC0_P0_302C			0x302c
> +#define VSW_SW_DP_ENC0_P0_MASK				GENMASK
> (14, 0)
> +#define VSP_SW_DP_ENC0_P0_MASK				BIT(15)
> +#define MTK_DP_ENC0_P0_3030			0x3030
> +#define HTOTAL_SEL_DP_ENC0_P0				BIT(0)
> +#define VTOTAL_SEL_DP_ENC0_P0				BIT(1)
> +#define HSTART_SEL_DP_ENC0_P0				BIT(2)
> +#define VSTART_SEL_DP_ENC0_P0				BIT(3)
> +#define HWIDTH_SEL_DP_ENC0_P0				BIT(4)
> +#define VHEIGHT_SEL_DP_ENC0_P0				BIT(5)
> +#define HSP_SEL_DP_ENC0_P0				BIT(6)
> +#define HSW_SEL_DP_ENC0_P0				BIT(7)
> +#define VSP_SEL_DP_ENC0_P0				BIT(8)
> +#define VSW_SEL_DP_ENC0_P0				BIT(9)
> +#define MTK_DP_ENC0_P0_3034			0x3034
> +#define MTK_DP_ENC0_P0_3038			0x3038
> +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK		BIT(11)
> +#define MTK_DP_ENC0_P0_303C			0x303c
> +#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK		GENMASK(5, 0)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK		GENMASK(10, 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT		(0 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT		(1 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT		(2 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT		(3 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT		(4 << 8)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK		GENMASK(14, 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB		(0 << 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422		(1 <<
> 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420		(2 <<
> 12)
> +#define VIDEO_MN_GEN_EN_DP_ENC0_P0			BIT(15)
> +#define MTK_DP_ENC0_P0_3040			0x3040
> +#define SDP_DOWN_CNT_DP_ENC0_P0_VAL			0x20
> +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK		GENMASK(11, 0)
> +#define MTK_DP_ENC0_P0_304C			0x304c
> +#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK			BIT(2)
> +#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK		BIT(8)
> +#define MTK_DP_ENC0_P0_3064			0x3064
> +#define HDE_NUM_LAST_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3154			0x3154
> +#define PGEN_HTOTAL_DP_ENC0_P0_MASK			GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_3158			0x3158
> +#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK		GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_315C			0x315c
> +#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK		GENMASK
> (13, 0)
> +#define MTK_DP_ENC0_P0_3160			0x3160
> +#define PGEN_HFDE_START_DP_ENC0_P0_MASK			GENMASK
> (13, 0)
> +#define MTK_DP_ENC0_P0_3164			0x3164
> +#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK		GENMASK
> (13, 0)
> +#define MTK_DP_ENC0_P0_3168			0x3168
> +#define PGEN_VTOTAL_DP_ENC0_P0_MASK			GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_316C			0x316c
> +#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK		GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_3170			0x3170
> +#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK		GENMASK
> (12, 0)
> +#define MTK_DP_ENC0_P0_3174			0x3174
> +#define PGEN_VFDE_START_DP_ENC0_P0_MASK			GENMASK
> (12, 0)
> +#define MTK_DP_ENC0_P0_3178			0x3178
> +#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK		GENMASK
> (12, 0)
> +#define MTK_DP_ENC0_P0_31B0			0x31b0
> +#define PGEN_PATTERN_SEL_VAL				4
> +#define PGEN_PATTERN_SEL_MASK				GENMASK
> (6, 4)
> +#define MTK_DP_ENC0_P0_31EC			0x31ec
> +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0			BIT(4)
> +#define ISRC1_HB3_DP_ENC0_P0_MASK			GENMASK(15, 8)
> +
> +/* offset: ENC1_OFFSET (0x3200) */
> +#define MTK_DP_ENC1_P0_3300			0x3300
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL		2
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK		GENMASK(9, 8)
> +#define MTK_DP_ENC1_P0_3364			0x3364
> +#define SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL		0x20
> +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK	GENMASK(11, 0)
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_VAL		4
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK		GENMASK
> (15, 12)
> +#define MTK_DP_ENC1_P0_3368			0x3368
> +#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0	BIT(0)
> +#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0		BIT(4)
> +#define SDP_DP13_EN_DP_ENC1_P0				BIT(8)
> +#define BS2BS_MODE_DP_ENC1_P0				BIT(12)
> +#define BS2BS_MODE_DP_ENC1_P0_MASK			GENMASK(13, 12)
> +#define BS2BS_MODE_DP_ENC1_P0_VAL			1
> +#define DP_ENC1_P0_3368_VAL				(VIDEO_SRAM_FIF
> O_CNT_RESET_SEL_DP_ENC1_P0 | \
> +							 VIDEO_STABLE_C
> NT_THRD_DP_ENC1_P0 | \
> +							 SDP_DP13_EN_DP
> _ENC1_P0 | \
> +							 BS2BS_MODE_DP_
> ENC1_P0)
> +
> +/* offset: TRANS_OFFSET (0x3400) */
> +#define MTK_DP_TRANS_P0_3400				0x3400
> +#define PATTERN1_EN_DP_TRANS_P0_MASK				BIT(12)
> +#define PATTERN2_EN_DP_TRANS_P0_MASK				BIT(13)
> +#define PATTERN3_EN_DP_TRANS_P0_MASK				BIT(14)
> +#define PATTERN4_EN_DP_TRANS_P0_MASK				BIT(15)
> +#define MTK_DP_TRANS_P0_3404				0x3404
> +#define DP_SCR_EN_DP_TRANS_P0_MASK				BIT(0)
> +#define MTK_DP_TRANS_P0_340C				0x340c
> +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0		BIT(13)
> +#define MTK_DP_TRANS_P0_3410				0x3410
> +#define HPD_DEB_THD_DP_TRANS_P0_MASK				GENMASK
> (3, 0)
> +#define HPD_INT_THD_DP_TRANS_P0_MASK				GENMASK
> (7, 4)
> +#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US			(2 <<
> 4)
> +#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US			(2 <<
> 6)
> +#define HPD_DISC_THD_DP_TRANS_P0_MASK				
> GENMASK(11, 8)
> +#define HPD_CONN_THD_DP_TRANS_P0_MASK				
> GENMASK(15, 12)
> +#define MTK_DP_TRANS_P0_3414				0x3414
> +#define HPD_DB_DP_TRANS_P0_MASK					
> BIT(2)
> +#define MTK_DP_TRANS_P0_3418				0x3418
> +#define IRQ_CLR_DP_TRANS_P0_MASK				GENMASK
> (3, 0)
> +#define IRQ_MASK_DP_TRANS_P0_MASK				GENMASK
> (7, 4)
> +#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ				
> (BIT(1) << 4)
> +#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ				
> (BIT(2) << 4)
> +#define IRQ_MASK_DP_TRANS_P0_INT_IRQ				(BIT(3)
> << 4)
> +#define IRQ_STATUS_DP_TRANS_P0_MASK				GENMASK
> (15, 12)
> +#define MTK_DP_TRANS_P0_342C				0x342c
> +#define XTAL_FREQ_DP_TRANS_P0_DEFAULT				
> (BIT(0) | BIT(3) | BIT(5) | BIT(6))
> +#define XTAL_FREQ_DP_TRANS_P0_MASK				GENMASK
> (7, 0)
> +#define MTK_DP_TRANS_P0_3430				0x3430
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK			GENMASK
> (1, 0)
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT		BIT(1)
> +#define MTK_DP_TRANS_P0_34A4				0x34a4
> +#define LANE_NUM_DP_TRANS_P0_MASK				GENMASK
> (3, 2)
> +#define MTK_DP_TRANS_P0_3540				0x3540
> +#define FEC_EN_DP_TRANS_P0_MASK					
> BIT(0)
> +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0				
> BIT(3)
> +#define MTK_DP_TRANS_P0_3580				0x3580
> +#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK		BIT(8)
> +#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK		BIT(9)
> +#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK		BIT(10)
> +#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK		BIT(11)
> +#define MTK_DP_TRANS_P0_35C8				0x35c8
> +#define SW_IRQ_CLR_DP_TRANS_P0_MASK				GENMASK
> (15, 0)
> +#define SW_IRQ_STATUS_DP_TRANS_P0_MASK				
> GENMASK(15, 0)
> +#define MTK_DP_TRANS_P0_35D0				0x35d0
> +#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK			GENMASK
> (15, 0)
> +#define MTK_DP_TRANS_P0_35F0				0x35f0
> +#define DP_TRANS_DUMMY_RW_0					BIT(3)
> +#define DP_TRANS_DUMMY_RW_0_MASK				GENMASK
> (3, 2)
> +
> +/* offset: AUX_OFFSET (0x3600) */
> +#define MTK_DP_AUX_P0_360C			0x360c
> +#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK			GENMASK
> (12, 0)
> +#define AUX_TIMEOUT_THR_AUX_TX_P0_VAL			0x1595
> +#define MTK_DP_AUX_P0_3614			0x3614
> +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK		GENMASK(6, 0)
> +#define AUX_RX_UI_CNT_THR_AUX_FOR_26M			13
> +#define MTK_DP_AUX_P0_3618			0x3618
> +#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK			BIT(9)
> +#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK	GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3620			0x3620
> +#define AUX_RD_MODE_AUX_TX_P0_MASK			BIT(9)
> +#define AUX_RX_FIFO_READ_PULSE_TX_P0			BIT(8)
> +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK		GENMASK(7, 0)
> +#define MTK_DP_AUX_P0_3624			0x3624
> +#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK		GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3628			0x3628
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK			GENMASK
> (9, 0)
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE		BIT(0)
> +#define MTK_DP_AUX_P0_362C			0x362c
> +#define AUX_NO_LENGTH_AUX_TX_P0				BIT(0)
> +#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK		BIT(1)
> +#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK		GENMASK(15, 2)
> +#define MTK_DP_AUX_P0_3630			0x3630
> +#define AUX_TX_REQUEST_READY_AUX_TX_P0			BIT(3)
> +#define MTK_DP_AUX_P0_3634			0x3634
> +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK		GENMASK
> (15, 8)
> +#define AUX_TX_OVER_SAMPLE_RATE_FOR_26M			25
> +#define MTK_DP_AUX_P0_3640			0x3640
> +#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(6)
> +#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(5)
> +#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(4)
> +#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0			BIT(3)
> +#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0			BIT(2)
> +#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0			BIT(1)
> +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0			BIT(0)
> +#define DP_AUX_P0_3640_VAL				(AUX_400US_TIME
> OUT_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_DATA_RE
> CV_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_ADDR_RE
> CV_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_CMD_REC
> V_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_MCCS_RE
> CV_COMPLETE_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_EDID_RE
> CV_COMPLETE_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_AUX_REC
> V_COMPLETE_IRQ_AUX_TX_P0)
> +#define MTK_DP_AUX_P0_3644			0x3644
> +#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK		GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3648			0x3648
> +#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK		GENMASK
> (15, 0)
> +#define MTK_DP_AUX_P0_364C			0x364c
> +#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK		GENMASK
> (3, 0)
> +#define MTK_DP_AUX_P0_3650			0x3650
> +#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK			GENMASK
> (15, 12)
> +#define PHY_FIFO_RST_AUX_TX_P0_MASK			BIT(9)
> +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0			BIT(8)
> +#define MTK_DP_AUX_P0_3658			0x3658
> +#define AUX_TX_OV_EN_AUX_TX_P0_MASK			BIT(0)
> +#define MTK_DP_AUX_P0_3690			0x3690
> +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0		BIT(8)
> +#define MTK_DP_AUX_P0_3704			0x3704
> +#define AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK	BIT(1)
> +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0		BIT(2)
> +#define MTK_DP_AUX_P0_3708			0x3708
> +#define MTK_DP_AUX_P0_37C8			0x37c8
> +#define MTK_ATOP_EN_AUX_TX_P0				BIT(0)
> +
> +#endif /*_MTK_DP_REG_H_*/


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

* Re: [PATCH v17 03/10] drm/mediatek: Add MT8195 Embedded DisplayPort driver
@ 2022-09-02  7:17     ` CK Hu
  0 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  7:17 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	liangxu.xu, dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> From: Markus Schneider-Pargmann <msp@baylibre.com>
> 
> This patch adds a embedded displayport driver for the MediaTek mt8195
> SoC.
> 
> It supports the MT8195, the embedded DisplayPort units. It offers
> DisplayPort 1.4 with up to 4 lanes.
> 
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so
> that
> both can work with the same register range. The phy driver sets
> device
> data that is read by the parent to get the phy device that can be
> used
> to control the phy properties.

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> This driver is based on an initial version by
> Jitao shi <jitao.shi@mediatek.com>
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/Kconfig      |    9 +
>  drivers/gpu/drm/mediatek/Makefile     |    2 +
>  drivers/gpu/drm/mediatek/mtk_dp.c     | 1999
> +++++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_dp_reg.h |  305 ++++
>  4 files changed, 2315 insertions(+)
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
> 
> diff --git a/drivers/gpu/drm/mediatek/Kconfig
> b/drivers/gpu/drm/mediatek/Kconfig
> index 2976d21e9a34..e66f4a3b6be0 100644
> --- a/drivers/gpu/drm/mediatek/Kconfig
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -21,6 +21,15 @@ config DRM_MEDIATEK
>  	  This driver provides kernel mode setting and
>  	  buffer management to userspace.
>  
> +config DRM_MEDIATEK_DP
> +	tristate "DRM DPTX Support for MediaTek SoCs"
> +	depends on DRM_MEDIATEK
> +	select PHY_MTK_DP
> +	select DRM_DISPLAY_HELPER
> +	select DRM_DISPLAY_DP_HELPER
> +	help
> +	  DRM/KMS Display Port driver for MediaTek SoCs.
> +
>  config DRM_MEDIATEK_HDMI
>  	tristate "DRM HDMI Support for Mediatek SoCs"
>  	depends on DRM_MEDIATEK
> diff --git a/drivers/gpu/drm/mediatek/Makefile
> b/drivers/gpu/drm/mediatek/Makefile
> index 6e604a933ed0..3517d1c65cd7 100644
> --- a/drivers/gpu/drm/mediatek/Makefile
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -23,3 +23,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
>  			  mtk_hdmi_ddc.o
>  
>  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> +
> +obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> new file mode 100644
> index 000000000000..e2ec9b02b1aa
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -0,0 +1,1999 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019-2022 MediaTek Inc.
> + * Copyright (c) 2022 BayLibre
> + */
> +
> +#include <drm/display/drm_dp.h>
> +#include <drm/display/drm_dp_helper.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <linux/arm-smccc.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/media-bus-format.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/soc/mediatek/mtk_sip_svc.h>
> +#include <video/videomode.h>
> +
> +#include "mtk_dp_reg.h"
> +
> +#define MTK_DP_SIP_CONTROL_AARCH32	MTK_SIP_SMC_CMD(0x523)
> +#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE	(BIT(0) | BIT(5))
> +
> +#define MTK_DP_THREAD_CABLE_STATE_CHG	BIT(0)
> +#define MTK_DP_THREAD_HPD_EVENT		BIT(1)
> +
> +#define MTK_DP_4P1T 4
> +#define MTK_DP_HDE 2
> +#define MTK_DP_PIX_PER_ADDR 2
> +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x8
> +#define MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY 5
> +#define MTK_DP_TRAIN_DOWNSCALE_RETRY 10
> +
> +enum {
> +	MTK_DP_CAL_GLB_BIAS_TRIM = 0,
> +	MTK_DP_CAL_CLKTX_IMPSE,
> +	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0,
> +	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1,
> +	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2,
> +	MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3,
> +	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0,
> +	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1,
> +	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2,
> +	MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3,
> +	MTK_DP_CAL_MAX,
> +};
> +
> +struct mtk_dp_train_info {
> +	bool sink_ssc;
> +	bool cable_plugged_in;
> +	/* link_rate is in multiple of 0.27Gbps */
> +	int link_rate;
> +	int lane_count;
> +	unsigned int channel_eq_pattern;
> +};
> +
> +struct mtk_dp_info {
> +	enum dp_pixelformat format;
> +	struct videomode vm;
> +};
> +
> +struct mtk_dp_efuse_fmt {
> +	unsigned short idx;
> +	unsigned short shift;
> +	unsigned short mask;
> +	unsigned short min_val;
> +	unsigned short max_val;
> +	unsigned short default_val;
> +};
> +
> +struct mtk_dp {
> +	bool enabled;
> +	u8 max_lanes;
> +	u8 max_linkrate;
> +	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> +	u32 cal_data[MTK_DP_CAL_MAX];
> +	u32 irq_thread_handle;
> +	/* irq_thread_lock is used to protect irq_thread_handle */
> +	spinlock_t irq_thread_lock;
> +
> +	struct device *dev;
> +	struct drm_bridge bridge;
> +	struct drm_bridge *next_bridge;
> +	struct drm_connector *conn;
> +	struct drm_device *drm_dev;
> +	struct drm_dp_aux aux;
> +
> +	struct mtk_dp_info info;
> +	struct mtk_dp_train_info train_info;
> +
> +	struct platform_device *phy_dev;
> +	struct phy *phy;
> +	struct regmap *regs;
> +};
> +
> +static const struct mtk_dp_efuse_fmt
> mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
> +	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
> +		.idx = 3,
> +		.shift = 27,
> +		.mask = 0x1f,
> +		.min_val = 1,
> +		.max_val = 0x1e,
> +		.default_val = 0xf,
> +	},
> +	[MTK_DP_CAL_CLKTX_IMPSE] = {
> +		.idx = 0,
> +		.shift = 9,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = {
> +		.idx = 2,
> +		.shift = 28,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = {
> +		.idx = 2,
> +		.shift = 20,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = {
> +		.idx = 2,
> +		.shift = 12,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = {
> +		.idx = 2,
> +		.shift = 4,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = {
> +		.idx = 2,
> +		.shift = 24,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = {
> +		.idx = 2,
> +		.shift = 16,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = {
> +		.idx = 2,
> +		.shift = 8,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = {
> +		.idx = 2,
> +		.shift = 0,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +};
> +
> +static struct regmap_config mtk_dp_regmap_config = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +	.max_register = SEC_OFFSET + 0x90,
> +	.name = "mtk-dp-registers",
> +};
> +
> +static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
> +{
> +	return container_of(b, struct mtk_dp, bridge);
> +}
> +
> +static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
> +{
> +	u32 read_val;
> +	int ret;
> +
> +	ret = regmap_read(mtk_dp->regs, offset, &read_val);
> +	if (ret) {
> +		dev_err(mtk_dp->dev, "Failed to read register 0x%x:
> %d\n",
> +			offset, ret);
> +		return 0;
> +	}
> +
> +	return read_val;
> +}
> +
> +static int mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
> +{
> +	int ret = regmap_write(mtk_dp->regs, offset, val);
> +
> +	if (ret)
> +		dev_err(mtk_dp->dev,
> +			"Failed to write register 0x%x with value
> 0x%x\n",
> +			offset, val);
> +	return ret;
> +}
> +
> +static int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset,
> +			      u32 val, u32 mask)
> +{
> +	int ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
> +
> +	if (ret)
> +		dev_err(mtk_dp->dev,
> +			"Failed to update register 0x%x with value
> 0x%x, mask 0x%x\n",
> +			offset, val, mask);
> +	return ret;
> +}
> +
> +static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32
> offset, u8 *buf,
> +				    size_t length)
> +{
> +	int i;
> +
> +	/* 2 bytes per register */
> +	for (i = 0; i < length; i += 2) {
> +		u32 val = buf[i] | (i + 1 < length ? buf[i + 1] << 8 :
> 0);
> +
> +		if (mtk_dp_write(mtk_dp, offset + i * 2, val))
> +			return;
> +	}
> +}
> +
> +static void mtk_dp_msa_bypass_enable(struct mtk_dp *mtk_dp, bool
> enable)
> +{
> +	u32 mask = HTOTAL_SEL_DP_ENC0_P0 | VTOTAL_SEL_DP_ENC0_P0 |
> +		   HSTART_SEL_DP_ENC0_P0 | VSTART_SEL_DP_ENC0_P0 |
> +		   HWIDTH_SEL_DP_ENC0_P0 | VHEIGHT_SEL_DP_ENC0_P0 |
> +		   HSP_SEL_DP_ENC0_P0 | HSW_SEL_DP_ENC0_P0 |
> +		   VSP_SEL_DP_ENC0_P0 | VSW_SEL_DP_ENC0_P0;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, enable ? 0 :
> mask, mask);
> +}
> +
> +static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
> +{
> +	struct drm_display_mode mode;
> +	struct videomode *vm = &mtk_dp->info.vm;
> +
> +	drm_display_mode_from_videomode(vm, &mode);
> +
> +	/* horizontal */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010,
> +			   mode.htotal, HTOTAL_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
> +			   vm->hsync_len + vm->hback_porch,
> +			   HSTART_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> +			   vm->hsync_len, HSW_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> +			   0, HSP_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020,
> +			   vm->hactive, HWIDTH_SW_DP_ENC0_P0_MASK);
> +
> +	/* vertical */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014,
> +			   mode.vtotal, VTOTAL_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
> +			   vm->vsync_len + vm->vback_porch,
> +			   VSTART_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> +			   vm->vsync_len, VSW_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> +			   0, VSP_SW_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024,
> +			   vm->vactive, VHEIGHT_SW_DP_ENC0_P0_MASK);
> +
> +	/* horizontal */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064,
> +			   vm->hactive, HDE_NUM_LAST_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154,
> +			   mode.htotal, PGEN_HTOTAL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
> +			   vm->hfront_porch,
> +			   PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C,
> +			   vm->hsync_len,
> +			   PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
> +			   vm->hback_porch + vm->hsync_len,
> +			   PGEN_HFDE_START_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164,
> +			   vm->hactive,
> +			   PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +
> +	/* vertical */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168,
> +			   mode.vtotal,
> +			   PGEN_VTOTAL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
> +			   vm->vfront_porch,
> +			   PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170,
> +			   vm->vsync_len,
> +			   PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
> +			   vm->vback_porch + vm->vsync_len,
> +			   PGEN_VFDE_START_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178,
> +			   vm->vactive,
> +			   PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +}
> +
> +static int mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
> +				   enum dp_pixelformat color_format)
> +{
> +	u32 val;
> +
> +	/* update MISC0 */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +			   color_format << DP_TEST_COLOR_FORMAT_SHIFT,
> +			   DP_TEST_COLOR_FORMAT_MASK);
> +
> +	switch (color_format) {
> +	case DP_PIXELFORMAT_YUV422:
> +		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
> +		break;
> +	case DP_PIXELFORMAT_RGB:
> +		val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
> +		break;
> +	default:
> +		drm_warn(mtk_dp->drm_dev, "Unsupported color format:
> %d\n",
> +			 color_format);
> +		return -EINVAL;
> +	}
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   val, PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
> +	return 0;
> +}
> +
> +static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp)
> +{
> +	/* Only support 8 bits currently */
> +	/* Update MISC0 */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +			   DP_MSA_MISC_8_BPC, DP_TEST_BIT_DEPTH_MASK);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT,
> +			   VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_config_mn_mode(struct mtk_dp *mtk_dp)
> +{
> +	/* 0: hw mode, 1: sw mode */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +			   0, VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32
> val)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   val, SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +			   VIDEO_MN_GEN_EN_DP_ENC0_P0,
> +			   VIDEO_MN_GEN_EN_DP_ENC0_P0);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> +			   SDP_DOWN_CNT_DP_ENC0_P0_VAL,
> +			   SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +			   SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL,
> +			   SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK)
> ;
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
> +			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL << 8,
> +			   VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +			   FIFO_READ_START_POINT_DP_ENC1_P0_VAL << 12,
> +			   FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
> +	mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368, DP_ENC1_P0_3368_VAL);
> +}
> +
> +static void mtk_dp_pg_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038,
> +			   enable ? VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK :
> 0,
> +			   VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
> +			   PGEN_PATTERN_SEL_VAL << 4,
> PGEN_PATTERN_SEL_MASK);
> +}
> +
> +static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640, DP_AUX_P0_3640_VAL);
> +}
> +
> +static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32
> addr)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644,
> +			   cmd, MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648,
> +			   addr,
> MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C,
> +			   addr >> 16,
> MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_clear_fifo(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0,
> +			   MCU_ACK_TRAN_COMPLETE_AUX_TX_P0 |
> +			   PHY_FIFO_RST_AUX_TX_P0_MASK |
> +			   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
> +			   AUX_TX_REQUEST_READY_AUX_TX_P0,
> +			   AUX_TX_REQUEST_READY_AUX_TX_P0);
> +}
> +
> +static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8
> *buf,
> +				       size_t length)
> +{
> +	mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf,
> length);
> +}
> +
> +static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> +				    size_t length, int read_delay)
> +{
> +	int read_pos;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> +			   0, AUX_RD_MODE_AUX_TX_P0_MASK);
> +
> +	for (read_pos = 0; read_pos < length; read_pos++) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> +				   AUX_RX_FIFO_READ_PULSE_TX_P0,
> +				   AUX_RX_FIFO_READ_PULSE_TX_P0);
> +
> +		/* Hardware needs time to update the data */
> +		usleep_range(read_delay, read_delay * 2);
> +		buf[read_pos] = (u8)(mtk_dp_read(mtk_dp,
> MTK_DP_AUX_P0_3620) &
> +				     AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MA
> SK);
> +	}
> +}
> +
> +static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t
> length)
> +{
> +	if (length > 0) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +				   (length - 1) << 12,
> +				   MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +				   0,
> +				   AUX_NO_LENGTH_AUX_TX_P0 |
> +				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	} else {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +				   AUX_NO_LENGTH_AUX_TX_P0,
> +				   AUX_NO_LENGTH_AUX_TX_P0 |
> +				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	}
> +}
> +
> +static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp,
> bool is_read)
> +{
> +	int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
> +
> +	while (--wait_reply) {
> +		u32 aux_irq_status;
> +
> +		if (is_read) {
> +			u32 fifo_status = mtk_dp_read(mtk_dp,
> MTK_DP_AUX_P0_3618);
> +
> +			if (fifo_status &
> +			    (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
> +			     AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
> +				return 0;
> +			}
> +		}
> +
> +		aux_irq_status = mtk_dp_read(mtk_dp,
> MTK_DP_AUX_P0_3640);
> +		if (aux_irq_status &
> AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
> +			return 0;
> +
> +		if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0)
> +			return -ETIMEDOUT;
> +
> +		/* Give the hardware a chance to reach completion
> before retrying */
> +		usleep_range(100, 500);
> +	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool
> is_read, u8 cmd,
> +				  u32 addr, u8 *buf, size_t length)
> +{
> +	int ret;
> +	u32 reply_cmd;
> +
> +	if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
> +			(cmd == DP_AUX_NATIVE_READ && !length)))
> +		return -EINVAL;
> +
> +	if (!is_read)
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> +				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0,
> +				   AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0);
> +
> +	/* We need to clear fifo and irq before sending commands to the
> sink device. */
> +	mtk_dp_aux_clear_fifo(mtk_dp);
> +	mtk_dp_aux_irq_clear(mtk_dp);
> +
> +	mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
> +	mtk_dp_aux_set_length(mtk_dp, length);
> +
> +	if (!is_read) {
> +		if (length)
> +			mtk_dp_aux_fill_write_fifo(mtk_dp, buf,
> length);
> +
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> +				   AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_
> P0_MASK,
> +				   AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_
> P0_MASK);
> +	}
> +
> +	mtk_dp_aux_request_ready(mtk_dp);
> +
> +	/* Wait for feedback from sink device. */
> +	ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
> +
> +	reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
> +		    AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
> +
> +	if (ret || reply_cmd) {
> +		u32 phy_status = mtk_dp_read(mtk_dp,
> MTK_DP_AUX_P0_3628) &
> +				 AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
> +		if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
> +			drm_err(mtk_dp->drm_dev,
> +				"AUX Rx Aux hang, need SW reset\n");
> +			return -EIO;
> +		}
> +
> +		return -ETIMEDOUT;
> +	}
> +
> +	if (!length) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +				   0,
> +				   AUX_NO_LENGTH_AUX_TX_P0 |
> +				   AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +				   AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +	} else if (is_read) {
> +		int read_delay;
> +
> +		if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
> +		    cmd == DP_AUX_I2C_READ)
> +			read_delay = 500;
> +		else
> +			read_delay = 100;
> +
> +		mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length,
> read_delay);
> +	}
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int
> lane_num,
> +					  int swing_val, int
> preemphasis)
> +{
> +	u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
> +
> +	dev_dbg(mtk_dp->dev,
> +		"link training: swing_val = 0x%x, pre-emphasis =
> 0x%x\n",
> +		swing_val, preemphasis);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +			   swing_val << (DP_TX0_VOLT_SWING_SHIFT +
> lane_shift),
> +			   DP_TX0_VOLT_SWING_MASK << lane_shift);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +			   preemphasis << (DP_TX0_PRE_EMPH_SHIFT +
> lane_shift),
> +			   DP_TX0_PRE_EMPH_MASK << lane_shift);
> +}
> +
> +static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +			   0,
> +			   DP_TX0_VOLT_SWING_MASK |
> +			   DP_TX1_VOLT_SWING_MASK |
> +			   DP_TX2_VOLT_SWING_MASK |
> +			   DP_TX3_VOLT_SWING_MASK |
> +			   DP_TX0_PRE_EMPH_MASK |
> +			   DP_TX1_PRE_EMPH_MASK |
> +			   DP_TX2_PRE_EMPH_MASK |
> +			   DP_TX3_PRE_EMPH_MASK);
> +}
> +
> +static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +	u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
> +			 SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
> +
> +	if (irq_status) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
> +				   irq_status,
> SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
> +				   0, SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +	}
> +
> +	return irq_status;
> +}
> +
> +static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +	u32 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
> +			  IRQ_STATUS_DP_TRANS_P0_MASK) >> 12;
> +
> +	if (irq_status) {
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +				   irq_status,
> IRQ_CLR_DP_TRANS_P0_MASK);
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +				   0, IRQ_CLR_DP_TRANS_P0_MASK);
> +	}
> +
> +	return irq_status;
> +}
> +
> +static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +			   enable ? 0 :
> +			   IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
> +			   IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
> +			   IRQ_MASK_DP_TRANS_P0_INT_IRQ,
> +			   IRQ_MASK_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
> +			   XTAL_FREQ_DP_TRANS_P0_DEFAULT,
> +			   XTAL_FREQ_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> +			   FEC_CLOCK_EN_MODE_DP_TRANS_P0,
> +			   FEC_CLOCK_EN_MODE_DP_TRANS_P0);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> +			   AUDIO_CH_SRC_SEL_DP_ENC0_P0,
> +			   AUDIO_CH_SRC_SEL_DP_ENC0_P0);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
> +			   0, SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK,
> +			   IRQ_MASK_AUX_TOP_IRQ, IRQ_MASK_AUX_TOP_IRQ);
> +}
> +
> +static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp
> *mtk_dp)
> +{
> +	u32 val;
> +	/* Debounce threshold */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   8, HPD_DEB_THD_DP_TRANS_P0_MASK);
> +
> +	val = (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
> +	       HPD_INT_THD_DP_TRANS_P0_UPPER_1100US) << 4;
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   val, HPD_INT_THD_DP_TRANS_P0_MASK);
> +
> +	/*
> +	 * Connect threshold 1.5ms + 5 x 0.1ms = 2ms
> +	 * Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
> +	 */
> +	val = (5 << 8) | (5 << 12);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +			   val,
> +			   HPD_DISC_THD_DP_TRANS_P0_MASK |
> +			   HPD_CONN_THD_DP_TRANS_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
> +			   HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
> +			   HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
> +{
> +	/* modify timeout threshold = 0x1595 */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C,
> +			   AUX_TIMEOUT_THR_AUX_TX_P0_VAL,
> +			   AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658,
> +			   0, AUX_TX_OV_EN_AUX_TX_P0_MASK);
> +	/* 25 for 26M */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
> +			   AUX_TX_OVER_SAMPLE_RATE_FOR_26M << 8,
> +			   AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
> +	/* 13 for 26M */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
> +			   AUX_RX_UI_CNT_THR_AUX_FOR_26M,
> +			   AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
> +			   MTK_ATOP_EN_AUX_TX_P0,
> +			   MTK_ATOP_EN_AUX_TX_P0);
> +}
> +
> +static void mtk_dp_initialize_digital_settings(struct mtk_dp
> *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
> +			   0, VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
> +			   BS2BS_MODE_DP_ENC1_P0_VAL << 12,
> +			   BS2BS_MODE_DP_ENC1_P0_MASK);
> +
> +	/* dp tx encoder reset all sw */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0,
> +			   DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
> +
> +	/* Wait for sw reset to complete */
> +	usleep_range(1000, 5000);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +			   0, DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
> +}
> +
> +static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> +			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0,
> +			   DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
> +
> +	/* Wait for sw reset to complete */
> +	usleep_range(1000, 5000);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> +			   0,
> DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
> +}
> +
> +static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
> +			   lanes == 0 ? 0 : DP_TRANS_DUMMY_RW_0,
> +			   DP_TRANS_DUMMY_RW_0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +			   lanes, LANE_NUM_DP_ENC0_P0_MASK);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
> +			   lanes << 2, LANE_NUM_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
> +{
> +	const struct mtk_dp_efuse_fmt *fmt;
> +	struct device *dev = mtk_dp->dev;
> +	struct nvmem_cell *cell;
> +	u32 *cal_data = mtk_dp->cal_data;
> +	u32 *buf;
> +	int i;
> +	size_t len;
> +
> +	cell = nvmem_cell_get(dev, "dp_calibration_data");
> +	if (IS_ERR(cell)) {
> +		dev_warn(dev, "Failed to get nvmem cell
> dp_calibration_data\n");
> +		goto use_default_val;
> +	}
> +
> +	buf = (u32 *)nvmem_cell_read(cell, &len);
> +	nvmem_cell_put(cell);
> +
> +	if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
> +		dev_warn(dev, "Failed to read nvmem_cell_read\n");
> +
> +		if (!IS_ERR(buf))
> +			kfree(buf);
> +
> +		goto use_default_val;
> +	}
> +
> +	for (i = 0; i < MTK_DP_CAL_MAX; i++) {
> +		fmt = &mtk_dp_efuse_data[i];
> +		cal_data[i] = (buf[fmt->idx] >> fmt->shift) & fmt-
> >mask;
> +
> +		if (cal_data[i] < fmt->min_val || cal_data[i] > fmt-
> >max_val) {
> +			dev_warn(mtk_dp->dev, "Invalid efuse data, idx
> = %d\n", i);
> +			kfree(buf);
> +			goto use_default_val;
> +		}
> +	}
> +	kfree(buf);
> +
> +	return;
> +
> +use_default_val:
> +	dev_warn(mtk_dp->dev, "Use default calibration data\n");
> +	for (i = 0; i < MTK_DP_CAL_MAX; i++)
> +		cal_data[i] = mtk_dp_efuse_data[i].default_val;
> +}
> +
> +static void mtk_dp_set_calibration_data(struct mtk_dp *mtk_dp)
> +{
> +	u32 *cal_data = mtk_dp->cal_data;
> +
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
> +			   cal_data[MTK_DP_CAL_CLKTX_IMPSE] << 20,
> +			   RG_CKM_PT0_CKTX_IMPSEL);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
> +			   cal_data[MTK_DP_CAL_GLB_BIAS_TRIM] << 16,
> +			   RG_XTP_GLB_BIAS_INTR_CTRL);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] <<
> 12,
> +			   RG_XTP_LN0_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] <<
> 16,
> +			   RG_XTP_LN0_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] <<
> 12,
> +			   RG_XTP_LN1_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] <<
> 16,
> +			   RG_XTP_LN1_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] <<
> 12,
> +			   RG_XTP_LN2_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] <<
> 16,
> +			   RG_XTP_LN2_TX_IMPSEL_NMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] <<
> 12,
> +			   RG_XTP_LN3_TX_IMPSEL_PMOS);
> +	mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +			   cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] <<
> 16,
> +			   RG_XTP_LN3_TX_IMPSEL_NMOS);
> +}
> +
> +static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
> +				u32 link_rate, int lane_count)
> +{
> +	int ret;
> +	union phy_configure_opts phy_opts = {
> +		.dp = {
> +			.link_rate =
> drm_dp_bw_code_to_link_rate(link_rate) / 100,
> +			.set_rate = 1,
> +			.lanes = lane_count,
> +			.set_lanes = 1,
> +			.ssc = mtk_dp->train_info.sink_ssc,
> +		}
> +	};
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> DP_PWR_STATE_BANDGAP,
> +			   DP_PWR_STATE_MASK);
> +
> +	ret = phy_configure(mtk_dp->phy, &phy_opts);
> +	if (ret)
> +		return ret;
> +
> +	mtk_dp_set_calibration_data(mtk_dp);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL_LANE,
> DP_PWR_STATE_MASK);
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool
> enable)
> +{
> +	u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
> +		  POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
> +		  POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
> +		  POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580,
> +			   enable ? val : 0, val);
> +}
> +
> +static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int
> pattern)
> +{
> +	/* TPS1 */
> +	if (pattern == 1)
> +		mtk_dp_set_idle_pattern(mtk_dp, false);
> +
> +	mtk_dp_update_bits(mtk_dp,
> +			   MTK_DP_TRANS_P0_3400,
> +			   pattern ? BIT(pattern - 1) << 12 : 0,
> +			   PATTERN1_EN_DP_TRANS_P0_MASK |
> +			   PATTERN2_EN_DP_TRANS_P0_MASK |
> +			   PATTERN3_EN_DP_TRANS_P0_MASK |
> +			   PATTERN4_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +			   ENHANCED_FRAME_EN_DP_ENC0_P0,
> +			   ENHANCED_FRAME_EN_DP_ENC0_P0);
> +}
> +
> +static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool
> enable)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
> +			   enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
> +			   DP_SCR_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	struct arm_smccc_res res;
> +	u32 val = VIDEO_MUTE_SEL_DP_ENC0_P0 |
> +		  (enable ? VIDEO_MUTE_SW_DP_ENC0_P0 : 0);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +			   val,
> +			   VIDEO_MUTE_SEL_DP_ENC0_P0 |
> +			   VIDEO_MUTE_SW_DP_ENC0_P0);
> +
> +	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32,
> +		      MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable,
> +		      0, 0, 0, 0, 0, &res);
> +
> +	dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-
> 0x%lx\n",
> +		MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable, res.a0,
> res.a1);
> +}
> +
> +static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
> +			   0, SW_RST_B_PHYD);
> +
> +	/* Wait for power enable */
> +	usleep_range(10, 200);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
> +			   SW_RST_B_PHYD, SW_RST_B_PHYD);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL,
> DP_PWR_STATE_MASK);
> +	mtk_dp_write(mtk_dp, MTK_DP_1040,
> +		     RG_DPAUX_RX_VALID_DEGLITCH_EN |
> RG_XTP_GLB_CKDET_EN |
> +		     RG_DPAUX_RX_EN);
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_0034, 0,
> DA_CKM_CKTX0_EN_FORCE_EN);
> +}
> +
> +static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_0034,
> +			   DA_CKM_CKTX0_EN_FORCE_EN,
> DA_CKM_CKTX0_EN_FORCE_EN);
> +
> +	/* Disable RX */
> +	mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
> +	mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
> +		     0x550 | FUSE_SEL | MEM_ISO_EN);
> +}
> +
> +static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp->train_info.link_rate = DP_LINK_BW_5_4;
> +	mtk_dp->train_info.lane_count = mtk_dp->max_lanes;
> +	mtk_dp->train_info.cable_plugged_in = false;
> +
> +	mtk_dp->info.format = DP_PIXELFORMAT_RGB;
> +	memset(&mtk_dp->info.vm, 0, sizeof(struct videomode));
> +}
> +
> +static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
> +{
> +	u32 sram_read_start = min_t(u32,
> MTK_DP_TBC_BUF_READ_START_ADDR,
> +				    mtk_dp->info.vm.hactive /
> +				    mtk_dp->train_info.lane_count /
> +				    MTK_DP_4P1T / MTK_DP_HDE /
> +				    MTK_DP_PIX_PER_ADDR);
> +	mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
> +	mtk_dp_setup_encoder(mtk_dp);
> +}
> +
> +static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_setup_tu(mtk_dp);
> +}
> +
> +static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int
> lanes,
> +					  u8 dpcd_adjust_req[2])
> +{
> +	int lane;
> +
> +	for (lane = 0; lane < lanes; ++lane) {
> +		u8 val;
> +		u8 swing;
> +		u8 preemphasis;
> +		int index = lane / 2;
> +		int shift = lane % 2 ?
> DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
> +
> +		swing = (dpcd_adjust_req[index] >> shift) &
> +			DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
> +		preemphasis = ((dpcd_adjust_req[index] >> shift) &
> +			       DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
> +			      DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
> +		val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
> +		      preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +
> +		if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
> +			val |= DP_TRAIN_MAX_SWING_REACHED;
> +		if (preemphasis == 3)
> +			val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> +
> +		mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing,
> preemphasis);
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET
> + lane,
> +				   val);
> +	}
> +}
> +
> +static void mtk_dp_pattern(struct mtk_dp *mtk_dp, bool is_tps1)
> +{
> +	int pattern;
> +	unsigned int aux_offset;
> +
> +	if (is_tps1) {
> +		pattern = 1;
> +		aux_offset = DP_LINK_SCRAMBLING_DISABLE |
> DP_TRAINING_PATTERN_1;
> +	} else {
> +		aux_offset = mtk_dp->train_info.channel_eq_pattern;
> +
> +		switch (mtk_dp->train_info.channel_eq_pattern) {
> +		case DP_TRAINING_PATTERN_4:
> +			pattern = 4;
> +			break;
> +		case DP_TRAINING_PATTERN_3:
> +			pattern = 3;
> +			aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
> +			break;
> +		case DP_TRAINING_PATTERN_2:
> +		default:
> +			pattern = 2;
> +			aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
> +			break;
> +		}
> +	}
> +
> +	mtk_dp_train_set_pattern(mtk_dp, pattern);
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> aux_offset);
> +}
> +
> +static int mtk_dp_train_setting(struct mtk_dp *mtk_dp, u8
> target_link_rate,
> +				u8 target_lane_count)
> +{
> +	int ret;
> +
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET,
> target_link_rate);
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> +			   target_lane_count |
> DP_LANE_COUNT_ENHANCED_FRAME_EN);
> +
> +	if (mtk_dp->train_info.sink_ssc)
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
> +				   DP_SPREAD_AMP_0_5);
> +
> +	mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
> +	ret = mtk_dp_phy_configure(mtk_dp, target_link_rate,
> target_lane_count);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(mtk_dp->dev,
> +		"Link train target_link_rate = 0x%x, target_lane_count
> = 0x%x\n",
> +		target_link_rate, target_lane_count);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_train_cr(struct mtk_dp *mtk_dp, u8
> target_lane_count)
> +{
> +	u8 lane_adjust[2] = {};
> +	u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +	u8 prev_lane_adjust = 0xff;
> +	int train_retries = 0;
> +	int voltage_retries = 0;
> +
> +	mtk_dp_pattern(mtk_dp, true);
> +
> +	/* In DP spec 1.4, the retry count of CR is defined as 10. */
> +	do {
> +		train_retries++;
> +		if (!mtk_dp->train_info.cable_plugged_in) {
> +			mtk_dp_train_set_pattern(mtk_dp, 0);
> +			return -ENODEV;
> +		}
> +
> +		drm_dp_dpcd_read(&mtk_dp->aux,
> DP_ADJUST_REQUEST_LANE0_1,
> +				 lane_adjust, sizeof(lane_adjust));
> +		mtk_dp_train_update_swing_pre(mtk_dp,
> target_lane_count,
> +					      lane_adjust);
> +
> +		drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
> +						       mtk_dp->rx_cap);
> +
> +		/* check link status from sink device */
> +		drm_dp_dpcd_read_link_status(&mtk_dp->aux,
> link_status);
> +		if (drm_dp_clock_recovery_ok(link_status,
> +					     target_lane_count)) {
> +			dev_dbg(mtk_dp->dev, "Link train CR pass\n");
> +			return 0;
> +		}
> +
> +		/*
> +		 * In DP spec 1.4, if current voltage level is the same
> +		 * with previous voltage level, we need to retry 5
> times.
> +		 */
> +		if (prev_lane_adjust == link_status[4]) {
> +			voltage_retries++;
> +			/*
> +			 * Condition of CR fail:
> +			 * 1. Failed to pass CR using the same voltage
> +			 *    level over five times.
> +			 * 2. Failed to pass CR when the current
> voltage
> +			 *    level is the same with previous voltage
> +			 *    level and reach max voltage level (3).
> +			 */
> +			if (voltage_retries >
> MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY ||
> +			    (prev_lane_adjust &
> DP_ADJUST_VOLTAGE_SWING_LANE0_MASK) == 3) {
> +				dev_dbg(mtk_dp->dev, "Link train CR
> fail\n");
> +				break;
> +			}
> +		} else {
> +			/*
> +			 * If the voltage level is changed, we need to
> +			 * re-calculate this retry count.
> +			 */
> +			voltage_retries = 0;
> +		}
> +		prev_lane_adjust = link_status[4];
> +	} while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
> +
> +	/* Failed to train CR, and disable pattern. */
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +			   DP_TRAINING_PATTERN_DISABLE);
> +	mtk_dp_train_set_pattern(mtk_dp, 0);
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_train_eq(struct mtk_dp *mtk_dp, u8
> target_lane_count)
> +{
> +	u8 lane_adjust[2] = {};
> +	u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +	int train_retries = 0;
> +
> +	mtk_dp_pattern(mtk_dp, false);
> +
> +	do {
> +		train_retries++;
> +		if (!mtk_dp->train_info.cable_plugged_in) {
> +			mtk_dp_train_set_pattern(mtk_dp, 0);
> +			return -ENODEV;
> +		}
> +
> +		drm_dp_dpcd_read(&mtk_dp->aux,
> DP_ADJUST_REQUEST_LANE0_1,
> +				 lane_adjust, sizeof(lane_adjust));
> +		mtk_dp_train_update_swing_pre(mtk_dp,
> target_lane_count,
> +					      lane_adjust);
> +
> +		drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
> +						   mtk_dp->rx_cap);
> +
> +		/* check link status from sink device */
> +		drm_dp_dpcd_read_link_status(&mtk_dp->aux,
> link_status);
> +		if (drm_dp_channel_eq_ok(link_status,
> target_lane_count)) {
> +			dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
> +
> +			/* Training done, and disable pattern. */
> +			drm_dp_dpcd_writeb(&mtk_dp->aux,
> DP_TRAINING_PATTERN_SET,
> +					   DP_TRAINING_PATTERN_DISABLE)
> ;
> +			mtk_dp_train_set_pattern(mtk_dp, 0);
> +			return 0;
> +		}
> +		dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
> +	} while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
> +
> +	/* Failed to train EQ, and disable pattern. */
> +	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +			   DP_TRAINING_PATTERN_DISABLE);
> +	mtk_dp_train_set_pattern(mtk_dp, 0);
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
> +{
> +	u8 val;
> +	ssize_t ret;
> +
> +	drm_dp_read_dpcd_caps(&mtk_dp->aux, mtk_dp->rx_cap);
> +
> +	if (drm_dp_tps4_supported(mtk_dp->rx_cap))
> +		mtk_dp->train_info.channel_eq_pattern =
> DP_TRAINING_PATTERN_4;
> +	else if (drm_dp_tps3_supported(mtk_dp->rx_cap))
> +		mtk_dp->train_info.channel_eq_pattern =
> DP_TRAINING_PATTERN_3;
> +	else
> +		mtk_dp->train_info.channel_eq_pattern =
> DP_TRAINING_PATTERN_2;
> +
> +	mtk_dp->train_info.sink_ssc = drm_dp_max_downspread(mtk_dp-
> >rx_cap);
> +
> +	ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
> +	if (ret < 1) {
> +		drm_err(mtk_dp->drm_dev, "Read mstm cap failed\n");
> +		return ret == 0 ? -EIO : ret;
> +	}
> +
> +	if (val & DP_MST_CAP) {
> +		/* Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 */
> +		ret = drm_dp_dpcd_readb(&mtk_dp->aux,
> +					DP_DEVICE_SERVICE_IRQ_VECTOR_ES
> I0,
> +					&val);
> +		if (ret < 1) {
> +			drm_err(mtk_dp->drm_dev, "Read irq vector
> failed\n");
> +			return ret == 0 ? -EIO : ret;
> +		}
> +
> +		if (val)
> +			drm_dp_dpcd_writeb(&mtk_dp->aux,
> +					   DP_DEVICE_SERVICE_IRQ_VECTOR
> _ESI0,
> +					   val);
> +	}
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
> +{
> +	phy_reset(mtk_dp->phy);
> +	mtk_dp_reset_swing_pre_emphasis(mtk_dp);
> +}
> +
> +static int mtk_dp_training(struct mtk_dp *mtk_dp)
> +{
> +	int ret;
> +	u8 lane_count, link_rate, train_limit, max_link_rate;
> +
> +	link_rate = min_t(u8, mtk_dp->max_linkrate,
> +			  mtk_dp->rx_cap[DP_MAX_LINK_RATE]);
> +	max_link_rate = link_rate;
> +	lane_count = min_t(u8, mtk_dp->max_lanes,
> +			   drm_dp_max_lane_count(mtk_dp->rx_cap));
> +
> +	/*
> +	 * TPS are generated by the hardware pattern generator. From
> the
> +	 * hardware setting we need to disable this scramble setting
> before
> +	 * use the TPS pattern generator.
> +	 */
> +	mtk_dp_training_set_scramble(mtk_dp, false);
> +
> +	for (train_limit = 6; train_limit > 0; train_limit--) {
> +		mtk_dp_train_change_mode(mtk_dp);
> +
> +		ret = mtk_dp_train_setting(mtk_dp, link_rate,
> lane_count);
> +		if (ret)
> +			return ret;
> +
> +		ret = mtk_dp_train_cr(mtk_dp, lane_count);
> +		if (ret == -ENODEV) {
> +			return ret;
> +		} else if (ret) {
> +			/* reduce link rate */
> +			switch (link_rate) {
> +			case DP_LINK_BW_1_62:
> +				lane_count = lane_count / 2;
> +				link_rate = max_link_rate;
> +				if (lane_count == 0)
> +					return -EIO;
> +				break;
> +			case DP_LINK_BW_2_7:
> +				link_rate = DP_LINK_BW_1_62;
> +				break;
> +			case DP_LINK_BW_5_4:
> +				link_rate = DP_LINK_BW_2_7;
> +				break;
> +			case DP_LINK_BW_8_1:
> +				link_rate = DP_LINK_BW_5_4;
> +				break;
> +			default:
> +				return -EINVAL;
> +			};
> +			continue;
> +		}
> +
> +		ret = mtk_dp_train_eq(mtk_dp, lane_count);
> +		if (ret == -ENODEV) {
> +			return ret;
> +		} else if (ret) {
> +			/* reduce lane count */
> +			if (lane_count == 0)
> +				return -EIO;
> +			lane_count /= 2;
> +			continue;
> +		}
> +
> +		/* if we can run to this, training is done. */
> +		break;
> +	}
> +
> +	if (train_limit == 0)
> +		return -ETIMEDOUT;
> +
> +	mtk_dp->train_info.link_rate = link_rate;
> +	mtk_dp->train_info.lane_count = lane_count;
> +
> +	/*
> +	 * After training done, we need to output normal stream instead
> of TPS,
> +	 * so we need to enable scramble.
> +	 */
> +	mtk_dp_training_set_scramble(mtk_dp, true);
> +	mtk_dp_set_enhanced_frame_mode(mtk_dp);
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +	/* the mute sequence is different between enable and disable */
> +	if (enable) {
> +		mtk_dp_msa_bypass_enable(mtk_dp, false);
> +		mtk_dp_pg_enable(mtk_dp, false);
> +		mtk_dp_set_tx_out(mtk_dp);
> +		mtk_dp_video_mute(mtk_dp, false);
> +	} else {
> +		mtk_dp_video_mute(mtk_dp, true);
> +		mtk_dp_pg_enable(mtk_dp, true);
> +		mtk_dp_msa_bypass_enable(mtk_dp, true);
> +	}
> +}
> +
> +static int mtk_dp_video_config(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_config_mn_mode(mtk_dp);
> +	mtk_dp_set_msa(mtk_dp);
> +	mtk_dp_set_color_depth(mtk_dp);
> +	return mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
> +}
> +
> +static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_set_idle_pattern(mtk_dp, true);
> +	mtk_dp_initialize_priv_data(mtk_dp);
> +
> +	mtk_dp_initialize_settings(mtk_dp);
> +	mtk_dp_initialize_aux_settings(mtk_dp);
> +	mtk_dp_initialize_digital_settings(mtk_dp);
> +
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
> +			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0,
> +			   RX_REPLY_COMPLETE_MODE_AUX_TX_P0);
> +	mtk_dp_initialize_hpd_detect_settings(mtk_dp);
> +
> +	mtk_dp_digital_sw_reset(mtk_dp);
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev;
> +	unsigned long flags;
> +	u32 status;
> +
> +	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
> +	status = mtk_dp->irq_thread_handle;
> +	mtk_dp->irq_thread_handle = 0;
> +	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
> +
> +	if (status & MTK_DP_THREAD_CABLE_STATE_CHG)
> +		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
> +
> +	if (status & MTK_DP_THREAD_HPD_EVENT)
> +		dev_dbg(mtk_dp->dev, "Receive IRQ from sink
> devices\n");
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev;
> +	bool cable_sta_chg = false;
> +	unsigned long flags;
> +	u32 irq_status = mtk_dp_swirq_get_clear(mtk_dp) |
> +			 mtk_dp_hwirq_get_clear(mtk_dp);
> +
> +	if (!irq_status)
> +		return IRQ_HANDLED;
> +
> +	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
> +
> +	if (irq_status & MTK_DP_HPD_INTERRUPT)
> +		mtk_dp->irq_thread_handle |= MTK_DP_THREAD_HPD_EVENT;
> +
> +	/* Cable state is changed. */
> +	if (irq_status != MTK_DP_HPD_INTERRUPT) {
> +		mtk_dp->irq_thread_handle |=
> MTK_DP_THREAD_CABLE_STATE_CHG;
> +		cable_sta_chg = true;
> +	}
> +
> +	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
> +
> +	if (cable_sta_chg) {
> +		if (!!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
> +		       HPD_DB_DP_TRANS_P0_MASK))
> +			mtk_dp->train_info.cable_plugged_in = true;
> +		else
> +			mtk_dp->train_info.cable_plugged_in = false;
> +	}
> +
> +	return IRQ_WAKE_THREAD;
> +}
> +
> +static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
> +			   struct platform_device *pdev)
> +{
> +	struct device_node *endpoint;
> +	struct device *dev = &pdev->dev;
> +	int ret;
> +	void __iomem *base;
> +	u32 linkrate;
> +	int len;
> +
> +	base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(base))
> +		return PTR_ERR(base);
> +
> +	mtk_dp->regs = devm_regmap_init_mmio(dev, base,
> &mtk_dp_regmap_config);
> +	if (IS_ERR(mtk_dp->regs))
> +		return PTR_ERR(mtk_dp->regs);
> +
> +	endpoint = of_graph_get_endpoint_by_regs(pdev->dev.of_node, 1,
> -1);
> +	len = of_property_count_elems_of_size(endpoint,
> +					      "data-lanes",
> sizeof(u32));
> +	if (len < 0 || len > 4 || len == 3) {
> +		dev_err(dev, "invalid data lane size: %d\n", len);
> +		return -EINVAL;
> +	}
> +
> +	mtk_dp->max_lanes = len;
> +
> +	ret = device_property_read_u32(dev, "max-linkrate-mhz",
> &linkrate);
> +	if (ret) {
> +		dev_err(dev, "failed to read max linkrate: %d\n", ret);
> +		return ret;
> +	}
> +
> +	mtk_dp->max_linkrate = drm_dp_link_rate_to_bw_code(linkrate *
> 100);
> +
> +	return 0;
> +}
> +
> +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> +				    struct drm_connector *connector)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	bool enabled = mtk_dp->enabled;
> +	struct edid *new_edid = NULL;
> +
> +	if (!enabled) {
> +		drm_bridge_chain_pre_enable(bridge);
> +
> +		/* power on aux */
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +				   DP_PWR_STATE_BANDGAP_TPLL_LANE,
> +				   DP_PWR_STATE_MASK);
> +
> +		/* power on panel */
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D0);
> +		usleep_range(2000, 5000);
> +	}
> +
> +	new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> +
> +	/*
> +	 * Parse capability here to let atomic_get_input_bus_fmts and
> +	 * mode_valid use the capability to calculate sink bitrates.
> +	 */
> +	if (mtk_dp_parse_capabilities(mtk_dp)) {
> +		drm_err(mtk_dp->drm_dev, "Can't parse capabilities\n");
> +		new_edid = NULL;
> +	}
> +
> +	if (!enabled) {
> +		/* power off panel */
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D3);
> +		usleep_range(2000, 3000);
> +
> +		/* power off aux */
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +				   DP_PWR_STATE_BANDGAP_TPLL,
> +				   DP_PWR_STATE_MASK);
> +
> +		drm_bridge_chain_post_disable(bridge);
> +	}
> +
> +	return new_edid;
> +}
> +
> +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> +				   struct drm_dp_aux_msg *msg)
> +{
> +	struct mtk_dp *mtk_dp;
> +	bool is_read;
> +	u8 request;
> +	size_t accessed_bytes = 0;
> +	int ret;
> +
> +	mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> +
> +	if (!mtk_dp->train_info.cable_plugged_in) {
> +		ret = -EAGAIN;
> +		goto err;
> +	}
> +
> +	switch (msg->request) {
> +	case DP_AUX_I2C_MOT:
> +	case DP_AUX_I2C_WRITE:
> +	case DP_AUX_NATIVE_WRITE:
> +	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> +	case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> +		request = msg->request &
> ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> +		is_read = false;
> +		break;
> +	case DP_AUX_I2C_READ:
> +	case DP_AUX_NATIVE_READ:
> +	case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> +		request = msg->request;
> +		is_read = true;
> +		break;
> +	default:
> +		drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> +			msg->request);
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	do {
> +		size_t to_access = min_t(size_t,
> DP_AUX_MAX_PAYLOAD_BYTES,
> +					 msg->size - accessed_bytes);
> +
> +		ret = mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> +					     msg->address +
> accessed_bytes,
> +					     msg->buffer +
> accessed_bytes,
> +					     to_access);
> +
> +		if (ret) {
> +			drm_info(mtk_dp->drm_dev,
> +				 "Failed to do AUX transfer: %d\n",
> ret);
> +			goto err;
> +		}
> +		accessed_bytes += to_access;
> +	} while (accessed_bytes < msg->size);
> +
> +	msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +	return msg->size;
> +err:
> +	msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> +	return ret;
> +}
> +
> +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> +{
> +	int ret;
> +
> +	ret = phy_init(mtk_dp->phy);
> +	if (ret) {
> +		dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n",
> ret);
> +		return ret;
> +	}
> +
> +	mtk_dp_init_port(mtk_dp);
> +	mtk_dp_power_enable(mtk_dp);
> +
> +	return 0;
> +}
> +
> +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> +{
> +	mtk_dp_power_disable(mtk_dp);
> +	phy_exit(mtk_dp->phy);
> +}
> +
> +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> +				enum drm_bridge_attach_flags flags)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	int ret;
> +
> +	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> +		dev_err(mtk_dp->dev, "Driver does not provide a
> connector!");
> +		return -EINVAL;
> +	}
> +
> +	mtk_dp->aux.drm_dev = bridge->dev;
> +	ret = drm_dp_aux_register(&mtk_dp->aux);
> +	if (ret) {
> +		dev_err(mtk_dp->dev,
> +			"failed to register DP AUX channel: %d\n",
> ret);
> +		return ret;
> +	}
> +
> +	ret = mtk_dp_poweron(mtk_dp);
> +	if (ret)
> +		goto err_aux_register;
> +
> +	if (mtk_dp->next_bridge) {
> +		ret = drm_bridge_attach(bridge->encoder, mtk_dp-
> >next_bridge,
> +					&mtk_dp->bridge, flags);
> +		if (ret) {
> +			drm_warn(mtk_dp->drm_dev,
> +				 "Failed to attach external bridge:
> %d\n", ret);
> +			goto err_bridge_attach;
> +		}
> +	}
> +
> +	mtk_dp->drm_dev = bridge->dev;
> +
> +	mtk_dp_hwirq_enable(mtk_dp, true);
> +
> +	return 0;
> +
> +err_bridge_attach:
> +	mtk_dp_poweroff(mtk_dp);
> +err_aux_register:
> +	drm_dp_aux_unregister(&mtk_dp->aux);
> +	return ret;
> +}
> +
> +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +	mtk_dp_hwirq_enable(mtk_dp, false);
> +	mtk_dp->drm_dev = NULL;
> +	mtk_dp_poweroff(mtk_dp);
> +	drm_dp_aux_unregister(&mtk_dp->aux);
> +}
> +
> +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> +					struct drm_bridge_state
> *old_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	int ret;
> +
> +	mtk_dp->conn =
> drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> +								bridge-
> >encoder);
> +	if (!mtk_dp->conn) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as connector is
> missing\n");
> +		return;
> +	}
> +
> +	/* power on aux */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL_LANE,
> +			   DP_PWR_STATE_MASK);
> +
> +	if (mtk_dp->train_info.cable_plugged_in) {
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D0);
> +		usleep_range(2000, 5000);
> +	}
> +
> +	/* Training */
> +	ret = mtk_dp_training(mtk_dp);
> +	if (ret) {
> +		drm_err(mtk_dp->drm_dev, "Training failed, %d\n", ret);
> +		goto power_off_aux;
> +	}
> +
> +	ret = mtk_dp_video_config(mtk_dp);
> +	if (ret)
> +		goto power_off_aux;
> +
> +	mtk_dp_video_enable(mtk_dp, true);
> +
> +	mtk_dp->enabled = true;
> +
> +	return;
> +power_off_aux:
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL,
> +			   DP_PWR_STATE_MASK);
> +}
> +
> +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> +					 struct drm_bridge_state
> *old_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +	mtk_dp->enabled = false;
> +	mtk_dp_video_enable(mtk_dp, false);
> +
> +	if (mtk_dp->train_info.cable_plugged_in) {
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D3);
> +		usleep_range(2000, 3000);
> +	}
> +
> +	/* power off aux */
> +	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +			   DP_PWR_STATE_BANDGAP_TPLL,
> +			   DP_PWR_STATE_MASK);
> +
> +	/* Ensure the sink is muted */
> +	msleep(20);
> +}
> +
> +static enum drm_mode_status
> +mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
> +			 const struct drm_display_info *info,
> +			 const struct drm_display_mode *mode)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	u32 bpp = info->color_formats & DRM_COLOR_FORMAT_YCBCR422 ? 16
> : 24;
> +	u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
> +			      drm_dp_max_lane_count(mtk_dp->rx_cap),
> +			 drm_dp_bw_code_to_link_rate(mtk_dp-
> >max_linkrate) *
> +			 mtk_dp->max_lanes);
> +
> +	if (rate < mode->clock * bpp / 8)
> +		return MODE_CLOCK_HIGH;
> +
> +	return MODE_OK;
> +}
> +
> +static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(struct
> drm_bridge *bridge,
> +						     struct
> drm_bridge_state *bridge_state,
> +						     struct
> drm_crtc_state *crtc_state,
> +						     struct
> drm_connector_state *conn_state,
> +						     unsigned int
> *num_output_fmts)
> +{
> +	u32 *output_fmts;
> +
> +	*num_output_fmts = 0;
> +	output_fmts = kmalloc(sizeof(*output_fmts), GFP_KERNEL);
> +	if (!output_fmts)
> +		return NULL;
> +	*num_output_fmts = 1;
> +	output_fmts[0] = MEDIA_BUS_FMT_FIXED;
> +	return output_fmts;
> +}
> +
> +static const u32 mt8195_input_fmts[] = {
> +	MEDIA_BUS_FMT_RGB888_1X24,
> +	MEDIA_BUS_FMT_YUV8_1X24,
> +	MEDIA_BUS_FMT_YUYV8_1X16,
> +};
> +
> +static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(struct
> drm_bridge *bridge,
> +						    struct
> drm_bridge_state *bridge_state,
> +						    struct
> drm_crtc_state *crtc_state,
> +						    struct
> drm_connector_state *conn_state,
> +						    u32 output_fmt,
> +						    unsigned int
> *num_input_fmts)
> +{
> +	u32 *input_fmts;
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> +	struct drm_display_info *display_info =
> +		&conn_state->connector->display_info;
> +	u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
> +			      drm_dp_max_lane_count(mtk_dp->rx_cap),
> +			 drm_dp_bw_code_to_link_rate(mtk_dp-
> >max_linkrate) *
> +			 mtk_dp->max_lanes);
> +
> +	*num_input_fmts = 0;
> +
> +	/*
> +	 * If the linkrate is smaller than datarate of RGB888, larger
> than
> +	 * datarate of YUV422 and sink device supports YUV422, we
> output YUV422
> +	 * format. Use this condition, we can support more resolution.
> +	 */
> +	if ((rate < (mode->clock * 24 / 8)) &&
> +	    (rate > (mode->clock * 16 / 8)) &&
> +	    (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR422))
> {
> +		input_fmts = kcalloc(1, sizeof(*input_fmts),
> GFP_KERNEL);
> +		if (!input_fmts)
> +			return NULL;
> +		*num_input_fmts = 1;
> +		input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
> +	} else {
> +		input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts),
> +				     sizeof(*input_fmts),
> +				     GFP_KERNEL);
> +		if (!input_fmts)
> +			return NULL;
> +
> +		*num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
> +		memcpy(input_fmts, mt8195_input_fmts,
> sizeof(mt8195_input_fmts));
> +	}
> +
> +	return input_fmts;
> +}
> +
> +static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
> +				      struct drm_bridge_state
> *bridge_state,
> +				      struct drm_crtc_state
> *crtc_state,
> +				      struct drm_connector_state
> *conn_state)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	struct drm_crtc *crtc = conn_state->crtc;
> +	unsigned int input_bus_format;
> +
> +	input_bus_format = bridge_state->input_bus_cfg.format;
> +
> +	dev_dbg(mtk_dp->dev, "input format 0x%04x, output format
> 0x%04x\n",
> +		bridge_state->input_bus_cfg.format,
> +		 bridge_state->output_bus_cfg.format);
> +
> +	if (input_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> +		mtk_dp->info.format = DP_PIXELFORMAT_YUV422;
> +	else
> +		mtk_dp->info.format = DP_PIXELFORMAT_RGB;
> +
> +	if (!crtc) {
> +		drm_err(mtk_dp->drm_dev,
> +			"Can't enable bridge as connector state doesn't
> have a crtc\n");
> +		return -EINVAL;
> +	}
> +
> +	drm_display_mode_to_videomode(&crtc_state->adjusted_mode,
> &mtk_dp->info.vm);
> +
> +	return 0;
> +}
> +
> +static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
> +	.atomic_check = mtk_dp_bridge_atomic_check,
> +	.atomic_duplicate_state =
> drm_atomic_helper_bridge_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> +	.atomic_get_output_bus_fmts =
> mtk_dp_bridge_atomic_get_output_bus_fmts,
> +	.atomic_get_input_bus_fmts =
> mtk_dp_bridge_atomic_get_input_bus_fmts,
> +	.atomic_reset = drm_atomic_helper_bridge_reset,
> +	.attach = mtk_dp_bridge_attach,
> +	.detach = mtk_dp_bridge_detach,
> +	.atomic_enable = mtk_dp_bridge_atomic_enable,
> +	.atomic_disable = mtk_dp_bridge_atomic_disable,
> +	.mode_valid = mtk_dp_bridge_mode_valid,
> +	.get_edid = mtk_dp_get_edid,
> +};
> +
> +static int mtk_dp_probe(struct platform_device *pdev)
> +{
> +	struct mtk_dp *mtk_dp;
> +	struct device *dev = &pdev->dev;
> +	int ret, irq_num;
> +
> +	mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> +	if (!mtk_dp)
> +		return -ENOMEM;
> +
> +	mtk_dp->dev = dev;
> +
> +	irq_num = platform_get_irq(pdev, 0);
> +	if (irq_num < 0)
> +		return dev_err_probe(dev, irq_num,
> +				     "failed to request dp irq
> resource\n");
> +
> +	mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 
> 1, 0);
> +	if (IS_ERR(mtk_dp->next_bridge))
> +		return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge),
> +				     "Failed to get bridge\n");
> +
> +	ret = mtk_dp_dt_parse(mtk_dp, pdev);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to parse dt\n");
> +
> +	drm_dp_aux_init(&mtk_dp->aux);
> +	mtk_dp->aux.name = "aux_mtk_dp";
> +	mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> +
> +	spin_lock_init(&mtk_dp->irq_thread_lock);
> +
> +	ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
> +					mtk_dp_hpd_event_thread,
> +					IRQ_TYPE_LEVEL_HIGH,
> dev_name(dev),
> +					mtk_dp);
> +	if (ret)
> +		return dev_err_probe(dev, ret,
> +				     "failed to request mediatek dptx
> irq\n");
> +
> +	platform_set_drvdata(pdev, mtk_dp);
> +
> +	mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-
> dp-phy",
> +							PLATFORM_DEVID_
> AUTO,
> +							&mtk_dp->regs,
> +							sizeof(struct
> regmap *));
> +	if (IS_ERR(mtk_dp->phy_dev))
> +		return dev_err_probe(dev, PTR_ERR(mtk_dp->phy_dev),
> +				     "Failed to create device mediatek-
> dp-phy\n");
> +
> +	mtk_dp_get_calibration_data(mtk_dp);
> +
> +	mtk_dp->phy = devm_phy_get(&mtk_dp->phy_dev->dev, "dp");
> +
> +	if (IS_ERR(mtk_dp->phy)) {
> +		platform_device_unregister(mtk_dp->phy_dev);
> +		return dev_err_probe(dev, PTR_ERR(mtk_dp->phy),
> +				     "Failed to get phy\n");
> +	}
> +
> +	mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
> +	mtk_dp->bridge.of_node = dev->of_node;
> +
> +	mtk_dp->bridge.ops =
> +		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
> DRM_BRIDGE_OP_HPD;
> +	mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> +
> +	drm_bridge_add(&mtk_dp->bridge);
> +
> +	pm_runtime_enable(dev);
> +	pm_runtime_get_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_remove(struct platform_device *pdev)
> +{
> +	struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
> +
> +	pm_runtime_put(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +	drm_bridge_remove(&mtk_dp->bridge);
> +	platform_device_unregister(mtk_dp->phy_dev);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int mtk_dp_suspend(struct device *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	mtk_dp_power_disable(mtk_dp);
> +	mtk_dp_hwirq_enable(mtk_dp, false);
> +	pm_runtime_put_sync(dev);
> +
> +	return 0;
> +}
> +
> +static int mtk_dp_resume(struct device *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +	pm_runtime_get_sync(dev);
> +	mtk_dp_init_port(mtk_dp);
> +	mtk_dp_hwirq_enable(mtk_dp, true);
> +	mtk_dp_power_enable(mtk_dp);
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend,
> mtk_dp_resume);
> +
> +static const struct of_device_id mtk_dp_of_match[] = {
> +	{ .compatible = "mediatek,mt8195-edp-tx" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
> +
> +struct platform_driver mtk_dp_driver = {
> +	.probe = mtk_dp_probe,
> +	.remove = mtk_dp_remove,
> +	.driver = {
> +		.name = "mediatek-drm-dp",
> +		.of_match_table = mtk_dp_of_match,
> +		.pm = &mtk_dp_pm_ops,
> +	},
> +};
> +
> +module_platform_driver(mtk_dp_driver);
> +
> +MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
> +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> +MODULE_AUTHOR("Bo-Chen Chen <rex-bc.chen@mediatek.com>");
> +MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> new file mode 100644
> index 000000000000..3f01ba44871f
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> @@ -0,0 +1,305 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019-2022 MediaTek Inc.
> + * Copyright (c) 2022 BayLibre
> + */
> +#ifndef _MTK_DP_REG_H_
> +#define _MTK_DP_REG_H_
> +
> +#define SEC_OFFSET	0x4000
> +
> +#define MTK_DP_HPD_DISCONNECT		BIT(1)
> +#define MTK_DP_HPD_CONNECT		BIT(2)
> +#define MTK_DP_HPD_INTERRUPT		BIT(3)
> +
> +/* offset: 0x0 */
> +#define DP_PHY_GLB_BIAS_GEN_00		0x0
> +#define RG_XTP_GLB_BIAS_INTR_CTRL		GENMASK(20, 16)
> +#define DP_PHY_GLB_DPAUX_TX		0x8
> +#define RG_CKM_PT0_CKTX_IMPSEL			GENMASK(23, 20)
> +#define MTK_DP_0034			0x34
> +#define DA_XTP_GLB_CKDET_EN_FORCE_VAL		BIT(15)
> +#define DA_XTP_GLB_CKDET_EN_FORCE_EN		BIT(14)
> +#define DA_CKM_INTCKTX_EN_FORCE_VAL		BIT(13)
> +#define DA_CKM_INTCKTX_EN_FORCE_EN		BIT(12)
> +#define DA_CKM_CKTX0_EN_FORCE_VAL		BIT(11)
> +#define DA_CKM_CKTX0_EN_FORCE_EN		BIT(10)
> +#define DA_CKM_XTAL_CK_FORCE_VAL		BIT(9)
> +#define DA_CKM_XTAL_CK_FORCE_EN			BIT(8)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_VAL		BIT(7)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_EN		BIT(6)
> +#define DA_CKM_BIAS_EN_FORCE_VAL		BIT(5)
> +#define DA_CKM_BIAS_EN_FORCE_EN			BIT(4)
> +#define DA_XTP_GLB_AVD10_ON_FORCE_VAL		BIT(3)
> +#define DA_XTP_GLB_AVD10_ON_FORCE		BIT(2)
> +#define DA_XTP_GLB_LDO_EN_FORCE_VAL		BIT(1)
> +#define DA_XTP_GLB_LDO_EN_FORCE_EN		BIT(0)
> +#define DP_PHY_LANE_TX_0		0x104
> +#define RG_XTP_LN0_TX_IMPSEL_PMOS		GENMASK(15, 12)
> +#define RG_XTP_LN0_TX_IMPSEL_NMOS		GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_1		0x204
> +#define RG_XTP_LN1_TX_IMPSEL_PMOS		GENMASK(15, 12)
> +#define RG_XTP_LN1_TX_IMPSEL_NMOS		GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_2		0x304
> +#define RG_XTP_LN2_TX_IMPSEL_PMOS		GENMASK(15, 12)
> +#define RG_XTP_LN2_TX_IMPSEL_NMOS		GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_3		0x404
> +#define RG_XTP_LN3_TX_IMPSEL_PMOS		GENMASK(15, 12)
> +#define RG_XTP_LN3_TX_IMPSEL_NMOS		GENMASK(19, 16)
> +#define MTK_DP_1040			0x1040
> +#define RG_DPAUX_RX_VALID_DEGLITCH_EN		BIT(2)
> +#define RG_XTP_GLB_CKDET_EN			BIT(1)
> +#define RG_DPAUX_RX_EN				BIT(0)
> +
> +/* offset: TOP_OFFSET (0x2000) */
> +#define MTK_DP_TOP_PWR_STATE		0x2000
> +#define DP_PWR_STATE_MASK			GENMASK(1, 0)
> +#define DP_PWR_STATE_BANDGAP			BIT(0)
> +#define DP_PWR_STATE_BANDGAP_TPLL		BIT(1)
> +#define DP_PWR_STATE_BANDGAP_TPLL_LANE		GENMASK(1, 0)
> +#define MTK_DP_TOP_SWING_EMP		0x2004
> +#define DP_TX0_VOLT_SWING_MASK			GENMASK(1, 0)
> +#define DP_TX0_VOLT_SWING_SHIFT			0
> +#define DP_TX0_PRE_EMPH_MASK			GENMASK(3, 2)
> +#define DP_TX0_PRE_EMPH_SHIFT			2
> +#define DP_TX1_VOLT_SWING_MASK			GENMASK(9, 8)
> +#define DP_TX1_VOLT_SWING_SHIFT			8
> +#define DP_TX1_PRE_EMPH_MASK			GENMASK(11, 10)
> +#define DP_TX2_VOLT_SWING_MASK			GENMASK(17, 16)
> +#define DP_TX2_PRE_EMPH_MASK			GENMASK(19, 18)
> +#define DP_TX3_VOLT_SWING_MASK			GENMASK(25, 24)
> +#define DP_TX3_PRE_EMPH_MASK			GENMASK(27, 26)
> +#define MTK_DP_TOP_RESET_AND_PROBE	0x2020
> +#define SW_RST_B_PHYD				BIT(4)
> +#define MTK_DP_TOP_IRQ_MASK		0x202c
> +#define IRQ_MASK_AUX_TOP_IRQ			BIT(2)
> +#define MTK_DP_TOP_MEM_PD		0x2038
> +#define MEM_ISO_EN				BIT(0)
> +#define FUSE_SEL				BIT(2)
> +
> +/* offset: ENC0_OFFSET (0x3000) */
> +#define MTK_DP_ENC0_P0_3000			0x3000
> +#define LANE_NUM_DP_ENC0_P0_MASK			GENMASK(1, 0)
> +#define VIDEO_MUTE_SW_DP_ENC0_P0			BIT(2)
> +#define VIDEO_MUTE_SEL_DP_ENC0_P0			BIT(3)
> +#define ENHANCED_FRAME_EN_DP_ENC0_P0			BIT(4)
> +#define MTK_DP_ENC0_P0_3004			0x3004
> +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK		BIT(8)
> +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0		BIT(9)
> +#define MTK_DP_ENC0_P0_3010			0x3010
> +#define HTOTAL_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3014			0x3014
> +#define VTOTAL_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3018			0x3018
> +#define HSTART_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_301C			0x301c
> +#define VSTART_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3020			0x3020
> +#define HWIDTH_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3024			0x3024
> +#define VHEIGHT_SW_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3028			0x3028
> +#define HSW_SW_DP_ENC0_P0_MASK				GENMASK
> (14, 0)
> +#define HSP_SW_DP_ENC0_P0_MASK				BIT(15)
> +#define MTK_DP_ENC0_P0_302C			0x302c
> +#define VSW_SW_DP_ENC0_P0_MASK				GENMASK
> (14, 0)
> +#define VSP_SW_DP_ENC0_P0_MASK				BIT(15)
> +#define MTK_DP_ENC0_P0_3030			0x3030
> +#define HTOTAL_SEL_DP_ENC0_P0				BIT(0)
> +#define VTOTAL_SEL_DP_ENC0_P0				BIT(1)
> +#define HSTART_SEL_DP_ENC0_P0				BIT(2)
> +#define VSTART_SEL_DP_ENC0_P0				BIT(3)
> +#define HWIDTH_SEL_DP_ENC0_P0				BIT(4)
> +#define VHEIGHT_SEL_DP_ENC0_P0				BIT(5)
> +#define HSP_SEL_DP_ENC0_P0				BIT(6)
> +#define HSW_SEL_DP_ENC0_P0				BIT(7)
> +#define VSP_SEL_DP_ENC0_P0				BIT(8)
> +#define VSW_SEL_DP_ENC0_P0				BIT(9)
> +#define MTK_DP_ENC0_P0_3034			0x3034
> +#define MTK_DP_ENC0_P0_3038			0x3038
> +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK		BIT(11)
> +#define MTK_DP_ENC0_P0_303C			0x303c
> +#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK		GENMASK(5, 0)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK		GENMASK(10, 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT		(0 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT		(1 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT		(2 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT		(3 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT		(4 << 8)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK		GENMASK(14, 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB		(0 << 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422		(1 <<
> 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420		(2 <<
> 12)
> +#define VIDEO_MN_GEN_EN_DP_ENC0_P0			BIT(15)
> +#define MTK_DP_ENC0_P0_3040			0x3040
> +#define SDP_DOWN_CNT_DP_ENC0_P0_VAL			0x20
> +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK		GENMASK(11, 0)
> +#define MTK_DP_ENC0_P0_304C			0x304c
> +#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK			BIT(2)
> +#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK		BIT(8)
> +#define MTK_DP_ENC0_P0_3064			0x3064
> +#define HDE_NUM_LAST_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3154			0x3154
> +#define PGEN_HTOTAL_DP_ENC0_P0_MASK			GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_3158			0x3158
> +#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK		GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_315C			0x315c
> +#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK		GENMASK
> (13, 0)
> +#define MTK_DP_ENC0_P0_3160			0x3160
> +#define PGEN_HFDE_START_DP_ENC0_P0_MASK			GENMASK
> (13, 0)
> +#define MTK_DP_ENC0_P0_3164			0x3164
> +#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK		GENMASK
> (13, 0)
> +#define MTK_DP_ENC0_P0_3168			0x3168
> +#define PGEN_VTOTAL_DP_ENC0_P0_MASK			GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_316C			0x316c
> +#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK		GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_3170			0x3170
> +#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK		GENMASK
> (12, 0)
> +#define MTK_DP_ENC0_P0_3174			0x3174
> +#define PGEN_VFDE_START_DP_ENC0_P0_MASK			GENMASK
> (12, 0)
> +#define MTK_DP_ENC0_P0_3178			0x3178
> +#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK		GENMASK
> (12, 0)
> +#define MTK_DP_ENC0_P0_31B0			0x31b0
> +#define PGEN_PATTERN_SEL_VAL				4
> +#define PGEN_PATTERN_SEL_MASK				GENMASK
> (6, 4)
> +#define MTK_DP_ENC0_P0_31EC			0x31ec
> +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0			BIT(4)
> +#define ISRC1_HB3_DP_ENC0_P0_MASK			GENMASK(15, 8)
> +
> +/* offset: ENC1_OFFSET (0x3200) */
> +#define MTK_DP_ENC1_P0_3300			0x3300
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL		2
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK		GENMASK(9, 8)
> +#define MTK_DP_ENC1_P0_3364			0x3364
> +#define SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL		0x20
> +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK	GENMASK(11, 0)
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_VAL		4
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK		GENMASK
> (15, 12)
> +#define MTK_DP_ENC1_P0_3368			0x3368
> +#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0	BIT(0)
> +#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0		BIT(4)
> +#define SDP_DP13_EN_DP_ENC1_P0				BIT(8)
> +#define BS2BS_MODE_DP_ENC1_P0				BIT(12)
> +#define BS2BS_MODE_DP_ENC1_P0_MASK			GENMASK(13, 12)
> +#define BS2BS_MODE_DP_ENC1_P0_VAL			1
> +#define DP_ENC1_P0_3368_VAL				(VIDEO_SRAM_FIF
> O_CNT_RESET_SEL_DP_ENC1_P0 | \
> +							 VIDEO_STABLE_C
> NT_THRD_DP_ENC1_P0 | \
> +							 SDP_DP13_EN_DP
> _ENC1_P0 | \
> +							 BS2BS_MODE_DP_
> ENC1_P0)
> +
> +/* offset: TRANS_OFFSET (0x3400) */
> +#define MTK_DP_TRANS_P0_3400				0x3400
> +#define PATTERN1_EN_DP_TRANS_P0_MASK				BIT(12)
> +#define PATTERN2_EN_DP_TRANS_P0_MASK				BIT(13)
> +#define PATTERN3_EN_DP_TRANS_P0_MASK				BIT(14)
> +#define PATTERN4_EN_DP_TRANS_P0_MASK				BIT(15)
> +#define MTK_DP_TRANS_P0_3404				0x3404
> +#define DP_SCR_EN_DP_TRANS_P0_MASK				BIT(0)
> +#define MTK_DP_TRANS_P0_340C				0x340c
> +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0		BIT(13)
> +#define MTK_DP_TRANS_P0_3410				0x3410
> +#define HPD_DEB_THD_DP_TRANS_P0_MASK				GENMASK
> (3, 0)
> +#define HPD_INT_THD_DP_TRANS_P0_MASK				GENMASK
> (7, 4)
> +#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US			(2 <<
> 4)
> +#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US			(2 <<
> 6)
> +#define HPD_DISC_THD_DP_TRANS_P0_MASK				
> GENMASK(11, 8)
> +#define HPD_CONN_THD_DP_TRANS_P0_MASK				
> GENMASK(15, 12)
> +#define MTK_DP_TRANS_P0_3414				0x3414
> +#define HPD_DB_DP_TRANS_P0_MASK					
> BIT(2)
> +#define MTK_DP_TRANS_P0_3418				0x3418
> +#define IRQ_CLR_DP_TRANS_P0_MASK				GENMASK
> (3, 0)
> +#define IRQ_MASK_DP_TRANS_P0_MASK				GENMASK
> (7, 4)
> +#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ				
> (BIT(1) << 4)
> +#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ				
> (BIT(2) << 4)
> +#define IRQ_MASK_DP_TRANS_P0_INT_IRQ				(BIT(3)
> << 4)
> +#define IRQ_STATUS_DP_TRANS_P0_MASK				GENMASK
> (15, 12)
> +#define MTK_DP_TRANS_P0_342C				0x342c
> +#define XTAL_FREQ_DP_TRANS_P0_DEFAULT				
> (BIT(0) | BIT(3) | BIT(5) | BIT(6))
> +#define XTAL_FREQ_DP_TRANS_P0_MASK				GENMASK
> (7, 0)
> +#define MTK_DP_TRANS_P0_3430				0x3430
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK			GENMASK
> (1, 0)
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT		BIT(1)
> +#define MTK_DP_TRANS_P0_34A4				0x34a4
> +#define LANE_NUM_DP_TRANS_P0_MASK				GENMASK
> (3, 2)
> +#define MTK_DP_TRANS_P0_3540				0x3540
> +#define FEC_EN_DP_TRANS_P0_MASK					
> BIT(0)
> +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0				
> BIT(3)
> +#define MTK_DP_TRANS_P0_3580				0x3580
> +#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK		BIT(8)
> +#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK		BIT(9)
> +#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK		BIT(10)
> +#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK		BIT(11)
> +#define MTK_DP_TRANS_P0_35C8				0x35c8
> +#define SW_IRQ_CLR_DP_TRANS_P0_MASK				GENMASK
> (15, 0)
> +#define SW_IRQ_STATUS_DP_TRANS_P0_MASK				
> GENMASK(15, 0)
> +#define MTK_DP_TRANS_P0_35D0				0x35d0
> +#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK			GENMASK
> (15, 0)
> +#define MTK_DP_TRANS_P0_35F0				0x35f0
> +#define DP_TRANS_DUMMY_RW_0					BIT(3)
> +#define DP_TRANS_DUMMY_RW_0_MASK				GENMASK
> (3, 2)
> +
> +/* offset: AUX_OFFSET (0x3600) */
> +#define MTK_DP_AUX_P0_360C			0x360c
> +#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK			GENMASK
> (12, 0)
> +#define AUX_TIMEOUT_THR_AUX_TX_P0_VAL			0x1595
> +#define MTK_DP_AUX_P0_3614			0x3614
> +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK		GENMASK(6, 0)
> +#define AUX_RX_UI_CNT_THR_AUX_FOR_26M			13
> +#define MTK_DP_AUX_P0_3618			0x3618
> +#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK			BIT(9)
> +#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK	GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3620			0x3620
> +#define AUX_RD_MODE_AUX_TX_P0_MASK			BIT(9)
> +#define AUX_RX_FIFO_READ_PULSE_TX_P0			BIT(8)
> +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK		GENMASK(7, 0)
> +#define MTK_DP_AUX_P0_3624			0x3624
> +#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK		GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3628			0x3628
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK			GENMASK
> (9, 0)
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE		BIT(0)
> +#define MTK_DP_AUX_P0_362C			0x362c
> +#define AUX_NO_LENGTH_AUX_TX_P0				BIT(0)
> +#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK		BIT(1)
> +#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK		GENMASK(15, 2)
> +#define MTK_DP_AUX_P0_3630			0x3630
> +#define AUX_TX_REQUEST_READY_AUX_TX_P0			BIT(3)
> +#define MTK_DP_AUX_P0_3634			0x3634
> +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK		GENMASK
> (15, 8)
> +#define AUX_TX_OVER_SAMPLE_RATE_FOR_26M			25
> +#define MTK_DP_AUX_P0_3640			0x3640
> +#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(6)
> +#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(5)
> +#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0		BIT(4)
> +#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0			BIT(3)
> +#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0			BIT(2)
> +#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0			BIT(1)
> +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0			BIT(0)
> +#define DP_AUX_P0_3640_VAL				(AUX_400US_TIME
> OUT_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_DATA_RE
> CV_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_ADDR_RE
> CV_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_CMD_REC
> V_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_MCCS_RE
> CV_COMPLETE_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_EDID_RE
> CV_COMPLETE_IRQ_AUX_TX_P0 | \
> +							 AUX_RX_AUX_REC
> V_COMPLETE_IRQ_AUX_TX_P0)
> +#define MTK_DP_AUX_P0_3644			0x3644
> +#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK		GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3648			0x3648
> +#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK		GENMASK
> (15, 0)
> +#define MTK_DP_AUX_P0_364C			0x364c
> +#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK		GENMASK
> (3, 0)
> +#define MTK_DP_AUX_P0_3650			0x3650
> +#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK			GENMASK
> (15, 12)
> +#define PHY_FIFO_RST_AUX_TX_P0_MASK			BIT(9)
> +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0			BIT(8)
> +#define MTK_DP_AUX_P0_3658			0x3658
> +#define AUX_TX_OV_EN_AUX_TX_P0_MASK			BIT(0)
> +#define MTK_DP_AUX_P0_3690			0x3690
> +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0		BIT(8)
> +#define MTK_DP_AUX_P0_3704			0x3704
> +#define AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK	BIT(1)
> +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0		BIT(2)
> +#define MTK_DP_AUX_P0_3708			0x3708
> +#define MTK_DP_AUX_P0_37C8			0x37c8
> +#define MTK_ATOP_EN_AUX_TX_P0				BIT(0)
> +
> +#endif /*_MTK_DP_REG_H_*/


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

* Re: [PATCH v17 04/10] drm/mediatek: dp: Add multiple bridge types support
  2022-09-01  4:41   ` Bo-Chen Chen
  (?)
@ 2022-09-02  7:22     ` CK Hu
  -1 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  7:22 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	liangxu.xu, dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> The bridge types of eDP and DP are different. We add device data to
> this driver and add bridge_type to the device data to define them.

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 16 ++++++++++++++--
>  1 file changed, 14 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index e2ec9b02b1aa..2696c1ac1a47 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -101,6 +101,7 @@ struct mtk_dp {
>  	struct drm_device *drm_dev;
>  	struct drm_dp_aux aux;
>  
> +	const struct mtk_dp_data *data;
>  	struct mtk_dp_info info;
>  	struct mtk_dp_train_info train_info;
>  
> @@ -109,6 +110,9 @@ struct mtk_dp {
>  	struct regmap *regs;
>  };
>  
> +struct mtk_dp_data {
> +	int bridge_type;
> +};
>  static const struct mtk_dp_efuse_fmt
> mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
>  	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
>  		.idx = 3,
> @@ -1871,6 +1875,7 @@ static int mtk_dp_probe(struct platform_device
> *pdev)
>  		return -ENOMEM;
>  
>  	mtk_dp->dev = dev;
> +	mtk_dp->data = (struct mtk_dp_data
> *)of_device_get_match_data(dev);
>  
>  	irq_num = platform_get_irq(pdev, 0);
>  	if (irq_num < 0)
> @@ -1925,7 +1930,7 @@ static int mtk_dp_probe(struct platform_device
> *pdev)
>  
>  	mtk_dp->bridge.ops =
>  		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
> DRM_BRIDGE_OP_HPD;
> -	mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> +	mtk_dp->bridge.type = mtk_dp->data->bridge_type;
>  
>  	drm_bridge_add(&mtk_dp->bridge);
>  
> @@ -1974,8 +1979,15 @@ static int mtk_dp_resume(struct device *dev)
>  
>  static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend,
> mtk_dp_resume);
>  
> +static const struct mtk_dp_data mt8195_edp_data = {
> +	.bridge_type = DRM_MODE_CONNECTOR_eDP,
> +};
> +
>  static const struct of_device_id mtk_dp_of_match[] = {
> -	{ .compatible = "mediatek,mt8195-edp-tx" },
> +	{
> +		.compatible = "mediatek,mt8195-edp-tx",
> +		.data = &mt8195_edp_data,
> +	},
>  	{},
>  };
>  MODULE_DEVICE_TABLE(of, mtk_dp_of_match);


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

* Re: [PATCH v17 04/10] drm/mediatek: dp: Add multiple bridge types support
@ 2022-09-02  7:22     ` CK Hu
  0 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  7:22 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, linux-mediatek, wenst,
	linux-arm-kernel, angelogioacchino.delregno

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> The bridge types of eDP and DP are different. We add device data to
> this driver and add bridge_type to the device data to define them.

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 16 ++++++++++++++--
>  1 file changed, 14 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index e2ec9b02b1aa..2696c1ac1a47 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -101,6 +101,7 @@ struct mtk_dp {
>  	struct drm_device *drm_dev;
>  	struct drm_dp_aux aux;
>  
> +	const struct mtk_dp_data *data;
>  	struct mtk_dp_info info;
>  	struct mtk_dp_train_info train_info;
>  
> @@ -109,6 +110,9 @@ struct mtk_dp {
>  	struct regmap *regs;
>  };
>  
> +struct mtk_dp_data {
> +	int bridge_type;
> +};
>  static const struct mtk_dp_efuse_fmt
> mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
>  	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
>  		.idx = 3,
> @@ -1871,6 +1875,7 @@ static int mtk_dp_probe(struct platform_device
> *pdev)
>  		return -ENOMEM;
>  
>  	mtk_dp->dev = dev;
> +	mtk_dp->data = (struct mtk_dp_data
> *)of_device_get_match_data(dev);
>  
>  	irq_num = platform_get_irq(pdev, 0);
>  	if (irq_num < 0)
> @@ -1925,7 +1930,7 @@ static int mtk_dp_probe(struct platform_device
> *pdev)
>  
>  	mtk_dp->bridge.ops =
>  		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
> DRM_BRIDGE_OP_HPD;
> -	mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> +	mtk_dp->bridge.type = mtk_dp->data->bridge_type;
>  
>  	drm_bridge_add(&mtk_dp->bridge);
>  
> @@ -1974,8 +1979,15 @@ static int mtk_dp_resume(struct device *dev)
>  
>  static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend,
> mtk_dp_resume);
>  
> +static const struct mtk_dp_data mt8195_edp_data = {
> +	.bridge_type = DRM_MODE_CONNECTOR_eDP,
> +};
> +
>  static const struct of_device_id mtk_dp_of_match[] = {
> -	{ .compatible = "mediatek,mt8195-edp-tx" },
> +	{
> +		.compatible = "mediatek,mt8195-edp-tx",
> +		.data = &mt8195_edp_data,
> +	},
>  	{},
>  };
>  MODULE_DEVICE_TABLE(of, mtk_dp_of_match);


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

* Re: [PATCH v17 04/10] drm/mediatek: dp: Add multiple bridge types support
@ 2022-09-02  7:22     ` CK Hu
  0 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  7:22 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	liangxu.xu, dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> The bridge types of eDP and DP are different. We add device data to
> this driver and add bridge_type to the device data to define them.

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 16 ++++++++++++++--
>  1 file changed, 14 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index e2ec9b02b1aa..2696c1ac1a47 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -101,6 +101,7 @@ struct mtk_dp {
>  	struct drm_device *drm_dev;
>  	struct drm_dp_aux aux;
>  
> +	const struct mtk_dp_data *data;
>  	struct mtk_dp_info info;
>  	struct mtk_dp_train_info train_info;
>  
> @@ -109,6 +110,9 @@ struct mtk_dp {
>  	struct regmap *regs;
>  };
>  
> +struct mtk_dp_data {
> +	int bridge_type;
> +};
>  static const struct mtk_dp_efuse_fmt
> mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
>  	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
>  		.idx = 3,
> @@ -1871,6 +1875,7 @@ static int mtk_dp_probe(struct platform_device
> *pdev)
>  		return -ENOMEM;
>  
>  	mtk_dp->dev = dev;
> +	mtk_dp->data = (struct mtk_dp_data
> *)of_device_get_match_data(dev);
>  
>  	irq_num = platform_get_irq(pdev, 0);
>  	if (irq_num < 0)
> @@ -1925,7 +1930,7 @@ static int mtk_dp_probe(struct platform_device
> *pdev)
>  
>  	mtk_dp->bridge.ops =
>  		DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
> DRM_BRIDGE_OP_HPD;
> -	mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> +	mtk_dp->bridge.type = mtk_dp->data->bridge_type;
>  
>  	drm_bridge_add(&mtk_dp->bridge);
>  
> @@ -1974,8 +1979,15 @@ static int mtk_dp_resume(struct device *dev)
>  
>  static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend,
> mtk_dp_resume);
>  
> +static const struct mtk_dp_data mt8195_edp_data = {
> +	.bridge_type = DRM_MODE_CONNECTOR_eDP,
> +};
> +
>  static const struct of_device_id mtk_dp_of_match[] = {
> -	{ .compatible = "mediatek,mt8195-edp-tx" },
> +	{
> +		.compatible = "mediatek,mt8195-edp-tx",
> +		.data = &mt8195_edp_data,
> +	},
>  	{},
>  };
>  MODULE_DEVICE_TABLE(of, mtk_dp_of_match);


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

* Re: [PATCH v17 05/10] drm/mediatek: dp: Add multiple smc commands support
  2022-09-01  4:41   ` Bo-Chen Chen
  (?)
@ 2022-09-02  7:32     ` CK Hu
  -1 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  7:32 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	liangxu.xu, dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> The smc commands of eDP and DP are different. We add smc_cmd to the
> device data to define them.

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index 2696c1ac1a47..971bd744cdb2 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -112,6 +112,7 @@ struct mtk_dp {
>  
>  struct mtk_dp_data {
>  	int bridge_type;
> +	unsigned int smc_cmd;
>  };
>  static const struct mtk_dp_efuse_fmt
> mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
>  	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
> @@ -945,11 +946,11 @@ static void mtk_dp_video_mute(struct mtk_dp
> *mtk_dp, bool enable)
>  			   VIDEO_MUTE_SW_DP_ENC0_P0);
>  
>  	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32,
> -		      MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable,
> +		      mtk_dp->data->smc_cmd, enable,
>  		      0, 0, 0, 0, 0, &res);
>  
>  	dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-
> 0x%lx\n",
> -		MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable, res.a0,
> res.a1);
> +		mtk_dp->data->smc_cmd, enable, res.a0, res.a1);
>  }
>  
>  static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
> @@ -1981,6 +1982,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops,
> mtk_dp_suspend, mtk_dp_resume);
>  
>  static const struct mtk_dp_data mt8195_edp_data = {
>  	.bridge_type = DRM_MODE_CONNECTOR_eDP,
> +	.smc_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
>  };
>  
>  static const struct of_device_id mtk_dp_of_match[] = {


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

* Re: [PATCH v17 05/10] drm/mediatek: dp: Add multiple smc commands support
@ 2022-09-02  7:32     ` CK Hu
  0 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  7:32 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, linux-mediatek, wenst,
	linux-arm-kernel, angelogioacchino.delregno

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> The smc commands of eDP and DP are different. We add smc_cmd to the
> device data to define them.

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index 2696c1ac1a47..971bd744cdb2 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -112,6 +112,7 @@ struct mtk_dp {
>  
>  struct mtk_dp_data {
>  	int bridge_type;
> +	unsigned int smc_cmd;
>  };
>  static const struct mtk_dp_efuse_fmt
> mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
>  	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
> @@ -945,11 +946,11 @@ static void mtk_dp_video_mute(struct mtk_dp
> *mtk_dp, bool enable)
>  			   VIDEO_MUTE_SW_DP_ENC0_P0);
>  
>  	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32,
> -		      MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable,
> +		      mtk_dp->data->smc_cmd, enable,
>  		      0, 0, 0, 0, 0, &res);
>  
>  	dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-
> 0x%lx\n",
> -		MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable, res.a0,
> res.a1);
> +		mtk_dp->data->smc_cmd, enable, res.a0, res.a1);
>  }
>  
>  static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
> @@ -1981,6 +1982,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops,
> mtk_dp_suspend, mtk_dp_resume);
>  
>  static const struct mtk_dp_data mt8195_edp_data = {
>  	.bridge_type = DRM_MODE_CONNECTOR_eDP,
> +	.smc_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
>  };
>  
>  static const struct of_device_id mtk_dp_of_match[] = {


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

* Re: [PATCH v17 05/10] drm/mediatek: dp: Add multiple smc commands support
@ 2022-09-02  7:32     ` CK Hu
  0 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  7:32 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	liangxu.xu, dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> The smc commands of eDP and DP are different. We add smc_cmd to the
> device data to define them.

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index 2696c1ac1a47..971bd744cdb2 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -112,6 +112,7 @@ struct mtk_dp {
>  
>  struct mtk_dp_data {
>  	int bridge_type;
> +	unsigned int smc_cmd;
>  };
>  static const struct mtk_dp_efuse_fmt
> mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
>  	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
> @@ -945,11 +946,11 @@ static void mtk_dp_video_mute(struct mtk_dp
> *mtk_dp, bool enable)
>  			   VIDEO_MUTE_SW_DP_ENC0_P0);
>  
>  	arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32,
> -		      MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable,
> +		      mtk_dp->data->smc_cmd, enable,
>  		      0, 0, 0, 0, 0, &res);
>  
>  	dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-
> 0x%lx\n",
> -		MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable, res.a0,
> res.a1);
> +		mtk_dp->data->smc_cmd, enable, res.a0, res.a1);
>  }
>  
>  static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
> @@ -1981,6 +1982,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops,
> mtk_dp_suspend, mtk_dp_resume);
>  
>  static const struct mtk_dp_data mt8195_edp_data = {
>  	.bridge_type = DRM_MODE_CONNECTOR_eDP,
> +	.smc_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
>  };
>  
>  static const struct of_device_id mtk_dp_of_match[] = {


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

* Re: [PATCH v17 06/10] drm/mediatek: dp: Add multiple calibration data formats support
  2022-09-01  4:41   ` Bo-Chen Chen
  (?)
@ 2022-09-02  7:38     ` CK Hu
  -1 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  7:38 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	liangxu.xu, dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> The calibration data formats of eDP and DP are different. We add
> "const struct mtk_dp_efuse_fmt *efuse_fmt" to the device data to
> define them.

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 9 ++++++---
>  1 file changed, 6 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index 971bd744cdb2..136e13150281 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -113,8 +113,10 @@ struct mtk_dp {
>  struct mtk_dp_data {
>  	int bridge_type;
>  	unsigned int smc_cmd;
> +	const struct mtk_dp_efuse_fmt *efuse_fmt;
>  };
> -static const struct mtk_dp_efuse_fmt
> mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
> +
> +static const struct mtk_dp_efuse_fmt
> mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = {
>  	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
>  		.idx = 3,
>  		.shift = 27,
> @@ -811,7 +813,7 @@ static void mtk_dp_get_calibration_data(struct
> mtk_dp *mtk_dp)
>  	}
>  
>  	for (i = 0; i < MTK_DP_CAL_MAX; i++) {
> -		fmt = &mtk_dp_efuse_data[i];
> +		fmt = &mtk_dp->data->efuse_fmt[i];
>  		cal_data[i] = (buf[fmt->idx] >> fmt->shift) & fmt-
> >mask;
>  
>  		if (cal_data[i] < fmt->min_val || cal_data[i] > fmt-
> >max_val) {
> @@ -827,7 +829,7 @@ static void mtk_dp_get_calibration_data(struct
> mtk_dp *mtk_dp)
>  use_default_val:
>  	dev_warn(mtk_dp->dev, "Use default calibration data\n");
>  	for (i = 0; i < MTK_DP_CAL_MAX; i++)
> -		cal_data[i] = mtk_dp_efuse_data[i].default_val;
> +		cal_data[i] = mtk_dp->data->efuse_fmt[i].default_val;
>  }
>  
>  static void mtk_dp_set_calibration_data(struct mtk_dp *mtk_dp)
> @@ -1983,6 +1985,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops,
> mtk_dp_suspend, mtk_dp_resume);
>  static const struct mtk_dp_data mt8195_edp_data = {
>  	.bridge_type = DRM_MODE_CONNECTOR_eDP,
>  	.smc_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
> +	.efuse_fmt = mt8195_edp_efuse_fmt,
>  };
>  
>  static const struct of_device_id mtk_dp_of_match[] = {


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

* Re: [PATCH v17 06/10] drm/mediatek: dp: Add multiple calibration data formats support
@ 2022-09-02  7:38     ` CK Hu
  0 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  7:38 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, linux-mediatek, wenst,
	linux-arm-kernel, angelogioacchino.delregno

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> The calibration data formats of eDP and DP are different. We add
> "const struct mtk_dp_efuse_fmt *efuse_fmt" to the device data to
> define them.

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 9 ++++++---
>  1 file changed, 6 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index 971bd744cdb2..136e13150281 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -113,8 +113,10 @@ struct mtk_dp {
>  struct mtk_dp_data {
>  	int bridge_type;
>  	unsigned int smc_cmd;
> +	const struct mtk_dp_efuse_fmt *efuse_fmt;
>  };
> -static const struct mtk_dp_efuse_fmt
> mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
> +
> +static const struct mtk_dp_efuse_fmt
> mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = {
>  	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
>  		.idx = 3,
>  		.shift = 27,
> @@ -811,7 +813,7 @@ static void mtk_dp_get_calibration_data(struct
> mtk_dp *mtk_dp)
>  	}
>  
>  	for (i = 0; i < MTK_DP_CAL_MAX; i++) {
> -		fmt = &mtk_dp_efuse_data[i];
> +		fmt = &mtk_dp->data->efuse_fmt[i];
>  		cal_data[i] = (buf[fmt->idx] >> fmt->shift) & fmt-
> >mask;
>  
>  		if (cal_data[i] < fmt->min_val || cal_data[i] > fmt-
> >max_val) {
> @@ -827,7 +829,7 @@ static void mtk_dp_get_calibration_data(struct
> mtk_dp *mtk_dp)
>  use_default_val:
>  	dev_warn(mtk_dp->dev, "Use default calibration data\n");
>  	for (i = 0; i < MTK_DP_CAL_MAX; i++)
> -		cal_data[i] = mtk_dp_efuse_data[i].default_val;
> +		cal_data[i] = mtk_dp->data->efuse_fmt[i].default_val;
>  }
>  
>  static void mtk_dp_set_calibration_data(struct mtk_dp *mtk_dp)
> @@ -1983,6 +1985,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops,
> mtk_dp_suspend, mtk_dp_resume);
>  static const struct mtk_dp_data mt8195_edp_data = {
>  	.bridge_type = DRM_MODE_CONNECTOR_eDP,
>  	.smc_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
> +	.efuse_fmt = mt8195_edp_efuse_fmt,
>  };
>  
>  static const struct of_device_id mtk_dp_of_match[] = {


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

* Re: [PATCH v17 06/10] drm/mediatek: dp: Add multiple calibration data formats support
@ 2022-09-02  7:38     ` CK Hu
  0 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  7:38 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	liangxu.xu, dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> The calibration data formats of eDP and DP are different. We add
> "const struct mtk_dp_efuse_fmt *efuse_fmt" to the device data to
> define them.

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 9 ++++++---
>  1 file changed, 6 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index 971bd744cdb2..136e13150281 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -113,8 +113,10 @@ struct mtk_dp {
>  struct mtk_dp_data {
>  	int bridge_type;
>  	unsigned int smc_cmd;
> +	const struct mtk_dp_efuse_fmt *efuse_fmt;
>  };
> -static const struct mtk_dp_efuse_fmt
> mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
> +
> +static const struct mtk_dp_efuse_fmt
> mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = {
>  	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
>  		.idx = 3,
>  		.shift = 27,
> @@ -811,7 +813,7 @@ static void mtk_dp_get_calibration_data(struct
> mtk_dp *mtk_dp)
>  	}
>  
>  	for (i = 0; i < MTK_DP_CAL_MAX; i++) {
> -		fmt = &mtk_dp_efuse_data[i];
> +		fmt = &mtk_dp->data->efuse_fmt[i];
>  		cal_data[i] = (buf[fmt->idx] >> fmt->shift) & fmt-
> >mask;
>  
>  		if (cal_data[i] < fmt->min_val || cal_data[i] > fmt-
> >max_val) {
> @@ -827,7 +829,7 @@ static void mtk_dp_get_calibration_data(struct
> mtk_dp *mtk_dp)
>  use_default_val:
>  	dev_warn(mtk_dp->dev, "Use default calibration data\n");
>  	for (i = 0; i < MTK_DP_CAL_MAX; i++)
> -		cal_data[i] = mtk_dp_efuse_data[i].default_val;
> +		cal_data[i] = mtk_dp->data->efuse_fmt[i].default_val;
>  }
>  
>  static void mtk_dp_set_calibration_data(struct mtk_dp *mtk_dp)
> @@ -1983,6 +1985,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops,
> mtk_dp_suspend, mtk_dp_resume);
>  static const struct mtk_dp_data mt8195_edp_data = {
>  	.bridge_type = DRM_MODE_CONNECTOR_eDP,
>  	.smc_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
> +	.efuse_fmt = mt8195_edp_efuse_fmt,
>  };
>  
>  static const struct of_device_id mtk_dp_of_match[] = {


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

* Re: [PATCH v17 07/10] drm/mediatek: dp: Determine device of next_bridge
  2022-09-01  4:41   ` Bo-Chen Chen
  (?)
@ 2022-09-02  7:44     ` CK Hu
  -1 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  7:44 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	liangxu.xu, dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> It's not necessary to have a next_bridge for DP device, so we add
> this
> patch to judge this.

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index 136e13150281..e37c9185e4ec 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -1886,7 +1886,10 @@ static int mtk_dp_probe(struct platform_device
> *pdev)
>  				     "failed to request dp irq
> resource\n");
>  
>  	mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 
> 1, 0);
> -	if (IS_ERR(mtk_dp->next_bridge))
> +	if (IS_ERR(mtk_dp->next_bridge) &&
> +	    PTR_ERR(mtk_dp->next_bridge) == -ENODEV)
> +		mtk_dp->next_bridge = NULL;
> +	else if (IS_ERR(mtk_dp->next_bridge))
>  		return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge),
>  				     "Failed to get bridge\n");
>  


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

* Re: [PATCH v17 07/10] drm/mediatek: dp: Determine device of next_bridge
@ 2022-09-02  7:44     ` CK Hu
  0 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  7:44 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, linux-mediatek, wenst,
	linux-arm-kernel, angelogioacchino.delregno

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> It's not necessary to have a next_bridge for DP device, so we add
> this
> patch to judge this.

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index 136e13150281..e37c9185e4ec 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -1886,7 +1886,10 @@ static int mtk_dp_probe(struct platform_device
> *pdev)
>  				     "failed to request dp irq
> resource\n");
>  
>  	mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 
> 1, 0);
> -	if (IS_ERR(mtk_dp->next_bridge))
> +	if (IS_ERR(mtk_dp->next_bridge) &&
> +	    PTR_ERR(mtk_dp->next_bridge) == -ENODEV)
> +		mtk_dp->next_bridge = NULL;
> +	else if (IS_ERR(mtk_dp->next_bridge))
>  		return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge),
>  				     "Failed to get bridge\n");
>  


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

* Re: [PATCH v17 07/10] drm/mediatek: dp: Determine device of next_bridge
@ 2022-09-02  7:44     ` CK Hu
  0 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  7:44 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	liangxu.xu, dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> It's not necessary to have a next_bridge for DP device, so we add
> this
> patch to judge this.

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index 136e13150281..e37c9185e4ec 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -1886,7 +1886,10 @@ static int mtk_dp_probe(struct platform_device
> *pdev)
>  				     "failed to request dp irq
> resource\n");
>  
>  	mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 
> 1, 0);
> -	if (IS_ERR(mtk_dp->next_bridge))
> +	if (IS_ERR(mtk_dp->next_bridge) &&
> +	    PTR_ERR(mtk_dp->next_bridge) == -ENODEV)
> +		mtk_dp->next_bridge = NULL;
> +	else if (IS_ERR(mtk_dp->next_bridge))
>  		return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge),
>  				     "Failed to get bridge\n");
>  


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

* Re: [PATCH v17 08/10] drm/mediatek: dp: Add MT8195 External DisplayPort support
  2022-09-01  4:41   ` Bo-Chen Chen
  (?)
@ 2022-09-02  8:07     ` CK Hu
  -1 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  8:07 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	liangxu.xu, dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> From: Guillaume Ranquet <granquet@baylibre.com>
> 
> Add External DisplayPort support to the MT8195 eDP driver.
> 
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 139
> ++++++++++++++++++++++++++++++
>  1 file changed, 139 insertions(+)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index e37c9185e4ec..11a94927c0d0 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -35,6 +35,7 @@
>  
>  #define MTK_DP_SIP_CONTROL_AARCH32	MTK_SIP_SMC_CMD(0x523)
>  #define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE	(BIT(0) | BIT(5))
> +#define MTK_DP_SIP_ATF_VIDEO_UNMUTE	BIT(5)
>  
>  #define MTK_DP_THREAD_CABLE_STATE_CHG	BIT(0)
>  #define MTK_DP_THREAD_HPD_EVENT		BIT(1)
> @@ -199,6 +200,89 @@ static const struct mtk_dp_efuse_fmt
> mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = {
>  	},
>  };
>  
> +static const struct mtk_dp_efuse_fmt
> mt8195_dp_efuse_fmt[MTK_DP_CAL_MAX] = {
> +	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
> +		.idx = 0,
> +		.shift = 27,
> +		.mask = 0x1f,
> +		.min_val = 1,
> +		.max_val = 0x1e,
> +		.default_val = 0xf,
> +	},
> +	[MTK_DP_CAL_CLKTX_IMPSE] = {
> +		.idx = 0,
> +		.shift = 13,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = {
> +		.idx = 1,
> +		.shift = 28,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = {
> +		.idx = 1,
> +		.shift = 20,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = {
> +		.idx = 1,
> +		.shift = 12,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = {
> +		.idx = 1,
> +		.shift = 4,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = {
> +		.idx = 1,
> +		.shift = 24,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = {
> +		.idx = 1,
> +		.shift = 16,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = {
> +		.idx = 1,
> +		.shift = 8,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = {
> +		.idx = 1,
> +		.shift = 0,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +};
> +
>  static struct regmap_config mtk_dp_regmap_config = {
>  	.reg_bits = 32,
>  	.val_bits = 32,
> @@ -1479,6 +1563,50 @@ static int mtk_dp_dt_parse(struct mtk_dp
> *mtk_dp,
>  	return 0;
>  }
>  
> +static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge
> *bridge)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	enum drm_connector_status ret = connector_status_disconnected;
> +	bool enabled = mtk_dp->enabled;
> +	u8 sink_count = 0;
> +
> +	if (mtk_dp->train_info.cable_plugged_in) {

I would modify this as below when I apply this patch into my tree:

if (!mtk_dp->train_info.cable_plugged_in)
	return ret;

Reviewed-by: CK Hu <ck.hu@mediatek.com>


> +		if (!enabled) {
> +			/* power on aux */
> +			mtk_dp_update_bits(mtk_dp,
> MTK_DP_TOP_PWR_STATE,
> +					   DP_PWR_STATE_BANDGAP_TPLL_LA
> NE,
> +					   DP_PWR_STATE_MASK);
> +
> +			/* power on panel */
> +			drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D0);
> +			usleep_range(2000, 5000);
> +		}
> +		/*
> +		 * Some dongles still source HPD when they do not
> connect to any
> +		 * sink device. To avoid this, we need to read the sink
> count
> +		 * to make sure we do connect to sink devices. After
> this detect
> +		 * function, we just need to check the HPD connection
> to check
> +		 * whether we connect to a sink device.
> +		 */
> +		drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT,
> &sink_count);
> +		if (DP_GET_SINK_COUNT(sink_count))
> +			ret = connector_status_connected;
> +
> +		if (!enabled) {
> +			/* power off panel */
> +			drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D3);
> +			usleep_range(2000, 3000);
> +
> +			/* power off aux */
> +			mtk_dp_update_bits(mtk_dp,
> MTK_DP_TOP_PWR_STATE,
> +					   DP_PWR_STATE_BANDGAP_TPLL,
> +					   DP_PWR_STATE_MASK);
> +		}
> +	}
> +
> +	return ret;
> +}
> +
>  static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
>  				    struct drm_connector *connector)
>  {
> @@ -1865,6 +1993,7 @@ static const struct drm_bridge_funcs
> mtk_dp_bridge_funcs = {
>  	.atomic_disable = mtk_dp_bridge_atomic_disable,
>  	.mode_valid = mtk_dp_bridge_mode_valid,
>  	.get_edid = mtk_dp_get_edid,
> +	.detect = mtk_dp_bdg_detect,
>  };
>  
>  static int mtk_dp_probe(struct platform_device *pdev)
> @@ -1991,11 +2120,21 @@ static const struct mtk_dp_data
> mt8195_edp_data = {
>  	.efuse_fmt = mt8195_edp_efuse_fmt,
>  };
>  
> +static const struct mtk_dp_data mt8195_dp_data = {
> +	.bridge_type = DRM_MODE_CONNECTOR_DisplayPort,
> +	.smc_cmd = MTK_DP_SIP_ATF_VIDEO_UNMUTE,
> +	.efuse_fmt = mt8195_dp_efuse_fmt,
> +};
> +
>  static const struct of_device_id mtk_dp_of_match[] = {
>  	{
>  		.compatible = "mediatek,mt8195-edp-tx",
>  		.data = &mt8195_edp_data,
>  	},
> +	{
> +		.compatible = "mediatek,mt8195-dp-tx",
> +		.data = &mt8195_dp_data,
> +	},
>  	{},
>  };
>  MODULE_DEVICE_TABLE(of, mtk_dp_of_match);


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

* Re: [PATCH v17 08/10] drm/mediatek: dp: Add MT8195 External DisplayPort support
@ 2022-09-02  8:07     ` CK Hu
  0 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  8:07 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, linux-mediatek, wenst,
	linux-arm-kernel, angelogioacchino.delregno

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> From: Guillaume Ranquet <granquet@baylibre.com>
> 
> Add External DisplayPort support to the MT8195 eDP driver.
> 
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 139
> ++++++++++++++++++++++++++++++
>  1 file changed, 139 insertions(+)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index e37c9185e4ec..11a94927c0d0 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -35,6 +35,7 @@
>  
>  #define MTK_DP_SIP_CONTROL_AARCH32	MTK_SIP_SMC_CMD(0x523)
>  #define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE	(BIT(0) | BIT(5))
> +#define MTK_DP_SIP_ATF_VIDEO_UNMUTE	BIT(5)
>  
>  #define MTK_DP_THREAD_CABLE_STATE_CHG	BIT(0)
>  #define MTK_DP_THREAD_HPD_EVENT		BIT(1)
> @@ -199,6 +200,89 @@ static const struct mtk_dp_efuse_fmt
> mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = {
>  	},
>  };
>  
> +static const struct mtk_dp_efuse_fmt
> mt8195_dp_efuse_fmt[MTK_DP_CAL_MAX] = {
> +	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
> +		.idx = 0,
> +		.shift = 27,
> +		.mask = 0x1f,
> +		.min_val = 1,
> +		.max_val = 0x1e,
> +		.default_val = 0xf,
> +	},
> +	[MTK_DP_CAL_CLKTX_IMPSE] = {
> +		.idx = 0,
> +		.shift = 13,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = {
> +		.idx = 1,
> +		.shift = 28,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = {
> +		.idx = 1,
> +		.shift = 20,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = {
> +		.idx = 1,
> +		.shift = 12,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = {
> +		.idx = 1,
> +		.shift = 4,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = {
> +		.idx = 1,
> +		.shift = 24,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = {
> +		.idx = 1,
> +		.shift = 16,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = {
> +		.idx = 1,
> +		.shift = 8,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = {
> +		.idx = 1,
> +		.shift = 0,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +};
> +
>  static struct regmap_config mtk_dp_regmap_config = {
>  	.reg_bits = 32,
>  	.val_bits = 32,
> @@ -1479,6 +1563,50 @@ static int mtk_dp_dt_parse(struct mtk_dp
> *mtk_dp,
>  	return 0;
>  }
>  
> +static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge
> *bridge)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	enum drm_connector_status ret = connector_status_disconnected;
> +	bool enabled = mtk_dp->enabled;
> +	u8 sink_count = 0;
> +
> +	if (mtk_dp->train_info.cable_plugged_in) {

I would modify this as below when I apply this patch into my tree:

if (!mtk_dp->train_info.cable_plugged_in)
	return ret;

Reviewed-by: CK Hu <ck.hu@mediatek.com>


> +		if (!enabled) {
> +			/* power on aux */
> +			mtk_dp_update_bits(mtk_dp,
> MTK_DP_TOP_PWR_STATE,
> +					   DP_PWR_STATE_BANDGAP_TPLL_LA
> NE,
> +					   DP_PWR_STATE_MASK);
> +
> +			/* power on panel */
> +			drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D0);
> +			usleep_range(2000, 5000);
> +		}
> +		/*
> +		 * Some dongles still source HPD when they do not
> connect to any
> +		 * sink device. To avoid this, we need to read the sink
> count
> +		 * to make sure we do connect to sink devices. After
> this detect
> +		 * function, we just need to check the HPD connection
> to check
> +		 * whether we connect to a sink device.
> +		 */
> +		drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT,
> &sink_count);
> +		if (DP_GET_SINK_COUNT(sink_count))
> +			ret = connector_status_connected;
> +
> +		if (!enabled) {
> +			/* power off panel */
> +			drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D3);
> +			usleep_range(2000, 3000);
> +
> +			/* power off aux */
> +			mtk_dp_update_bits(mtk_dp,
> MTK_DP_TOP_PWR_STATE,
> +					   DP_PWR_STATE_BANDGAP_TPLL,
> +					   DP_PWR_STATE_MASK);
> +		}
> +	}
> +
> +	return ret;
> +}
> +
>  static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
>  				    struct drm_connector *connector)
>  {
> @@ -1865,6 +1993,7 @@ static const struct drm_bridge_funcs
> mtk_dp_bridge_funcs = {
>  	.atomic_disable = mtk_dp_bridge_atomic_disable,
>  	.mode_valid = mtk_dp_bridge_mode_valid,
>  	.get_edid = mtk_dp_get_edid,
> +	.detect = mtk_dp_bdg_detect,
>  };
>  
>  static int mtk_dp_probe(struct platform_device *pdev)
> @@ -1991,11 +2120,21 @@ static const struct mtk_dp_data
> mt8195_edp_data = {
>  	.efuse_fmt = mt8195_edp_efuse_fmt,
>  };
>  
> +static const struct mtk_dp_data mt8195_dp_data = {
> +	.bridge_type = DRM_MODE_CONNECTOR_DisplayPort,
> +	.smc_cmd = MTK_DP_SIP_ATF_VIDEO_UNMUTE,
> +	.efuse_fmt = mt8195_dp_efuse_fmt,
> +};
> +
>  static const struct of_device_id mtk_dp_of_match[] = {
>  	{
>  		.compatible = "mediatek,mt8195-edp-tx",
>  		.data = &mt8195_edp_data,
>  	},
> +	{
> +		.compatible = "mediatek,mt8195-dp-tx",
> +		.data = &mt8195_dp_data,
> +	},
>  	{},
>  };
>  MODULE_DEVICE_TABLE(of, mtk_dp_of_match);


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

* Re: [PATCH v17 08/10] drm/mediatek: dp: Add MT8195 External DisplayPort support
@ 2022-09-02  8:07     ` CK Hu
  0 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  8:07 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	liangxu.xu, dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> From: Guillaume Ranquet <granquet@baylibre.com>
> 
> Add External DisplayPort support to the MT8195 eDP driver.
> 
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 139
> ++++++++++++++++++++++++++++++
>  1 file changed, 139 insertions(+)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index e37c9185e4ec..11a94927c0d0 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -35,6 +35,7 @@
>  
>  #define MTK_DP_SIP_CONTROL_AARCH32	MTK_SIP_SMC_CMD(0x523)
>  #define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE	(BIT(0) | BIT(5))
> +#define MTK_DP_SIP_ATF_VIDEO_UNMUTE	BIT(5)
>  
>  #define MTK_DP_THREAD_CABLE_STATE_CHG	BIT(0)
>  #define MTK_DP_THREAD_HPD_EVENT		BIT(1)
> @@ -199,6 +200,89 @@ static const struct mtk_dp_efuse_fmt
> mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = {
>  	},
>  };
>  
> +static const struct mtk_dp_efuse_fmt
> mt8195_dp_efuse_fmt[MTK_DP_CAL_MAX] = {
> +	[MTK_DP_CAL_GLB_BIAS_TRIM] = {
> +		.idx = 0,
> +		.shift = 27,
> +		.mask = 0x1f,
> +		.min_val = 1,
> +		.max_val = 0x1e,
> +		.default_val = 0xf,
> +	},
> +	[MTK_DP_CAL_CLKTX_IMPSE] = {
> +		.idx = 0,
> +		.shift = 13,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = {
> +		.idx = 1,
> +		.shift = 28,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = {
> +		.idx = 1,
> +		.shift = 20,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = {
> +		.idx = 1,
> +		.shift = 12,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = {
> +		.idx = 1,
> +		.shift = 4,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = {
> +		.idx = 1,
> +		.shift = 24,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = {
> +		.idx = 1,
> +		.shift = 16,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = {
> +		.idx = 1,
> +		.shift = 8,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +	[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = {
> +		.idx = 1,
> +		.shift = 0,
> +		.mask = 0xf,
> +		.min_val = 1,
> +		.max_val = 0xe,
> +		.default_val = 0x8,
> +	},
> +};
> +
>  static struct regmap_config mtk_dp_regmap_config = {
>  	.reg_bits = 32,
>  	.val_bits = 32,
> @@ -1479,6 +1563,50 @@ static int mtk_dp_dt_parse(struct mtk_dp
> *mtk_dp,
>  	return 0;
>  }
>  
> +static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge
> *bridge)
> +{
> +	struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +	enum drm_connector_status ret = connector_status_disconnected;
> +	bool enabled = mtk_dp->enabled;
> +	u8 sink_count = 0;
> +
> +	if (mtk_dp->train_info.cable_plugged_in) {

I would modify this as below when I apply this patch into my tree:

if (!mtk_dp->train_info.cable_plugged_in)
	return ret;

Reviewed-by: CK Hu <ck.hu@mediatek.com>


> +		if (!enabled) {
> +			/* power on aux */
> +			mtk_dp_update_bits(mtk_dp,
> MTK_DP_TOP_PWR_STATE,
> +					   DP_PWR_STATE_BANDGAP_TPLL_LA
> NE,
> +					   DP_PWR_STATE_MASK);
> +
> +			/* power on panel */
> +			drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D0);
> +			usleep_range(2000, 5000);
> +		}
> +		/*
> +		 * Some dongles still source HPD when they do not
> connect to any
> +		 * sink device. To avoid this, we need to read the sink
> count
> +		 * to make sure we do connect to sink devices. After
> this detect
> +		 * function, we just need to check the HPD connection
> to check
> +		 * whether we connect to a sink device.
> +		 */
> +		drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT,
> &sink_count);
> +		if (DP_GET_SINK_COUNT(sink_count))
> +			ret = connector_status_connected;
> +
> +		if (!enabled) {
> +			/* power off panel */
> +			drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D3);
> +			usleep_range(2000, 3000);
> +
> +			/* power off aux */
> +			mtk_dp_update_bits(mtk_dp,
> MTK_DP_TOP_PWR_STATE,
> +					   DP_PWR_STATE_BANDGAP_TPLL,
> +					   DP_PWR_STATE_MASK);
> +		}
> +	}
> +
> +	return ret;
> +}
> +
>  static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
>  				    struct drm_connector *connector)
>  {
> @@ -1865,6 +1993,7 @@ static const struct drm_bridge_funcs
> mtk_dp_bridge_funcs = {
>  	.atomic_disable = mtk_dp_bridge_atomic_disable,
>  	.mode_valid = mtk_dp_bridge_mode_valid,
>  	.get_edid = mtk_dp_get_edid,
> +	.detect = mtk_dp_bdg_detect,
>  };
>  
>  static int mtk_dp_probe(struct platform_device *pdev)
> @@ -1991,11 +2120,21 @@ static const struct mtk_dp_data
> mt8195_edp_data = {
>  	.efuse_fmt = mt8195_edp_efuse_fmt,
>  };
>  
> +static const struct mtk_dp_data mt8195_dp_data = {
> +	.bridge_type = DRM_MODE_CONNECTOR_DisplayPort,
> +	.smc_cmd = MTK_DP_SIP_ATF_VIDEO_UNMUTE,
> +	.efuse_fmt = mt8195_dp_efuse_fmt,
> +};
> +
>  static const struct of_device_id mtk_dp_of_match[] = {
>  	{
>  		.compatible = "mediatek,mt8195-edp-tx",
>  		.data = &mt8195_edp_data,
>  	},
> +	{
> +		.compatible = "mediatek,mt8195-dp-tx",
> +		.data = &mt8195_dp_data,
> +	},
>  	{},
>  };
>  MODULE_DEVICE_TABLE(of, mtk_dp_of_match);


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

* Re: [PATCH v17 09/10] drm/mediatek: dp: Add hpd debounce
  2022-09-01  4:41   ` Bo-Chen Chen
  (?)
@ 2022-09-02  8:12     ` CK Hu
  -1 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  8:12 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, linux-mediatek, wenst,
	linux-arm-kernel, angelogioacchino.delregno

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> From: Jitao Shi <jitao.shi@mediatek.com>
> 
> From the DP spec 1.4a chapter 3.3, upstream devices should implement
> HPD signal de-bouncing on an external connection.
> A period of 100ms should be used to detect an HPD connect event.
> To cover these cases, HPD de-bounce should be implemented only after
> HPD low has been detected for at least 100ms.
> 
> Therefore,
> 1. If HPD is low (which means plugging out) for longer than 100ms:
>    we need to do de-bouncing (which means we need to wait for 100ms).
> 2. If HPD low is for less than 100ms:
>    we don't need to care about the de-bouncing.
> 
> In this patch, we start a 100ms timer and use a need_debounce boolean
> to implement the feature.
> 
> Two cases when HPD is high:
> 1. If the timer is expired (>100ms):
>    - need_debounce is true.
>    - When HPD high (plugging event comes), need_debounce will be true
>      and then we need to do de-bouncing (wait for 100ms).
> 2. If the timer is not expired (<100ms):
>    - need_debounce is false.
>    - When HPD high (plugging event comes), need_debounce will be
> false
>      and no need to do de-bouncing.
> 
> HPD_______             __________________
>           |            |<-  100ms   ->
>           |____________|
>           <-  100ms   ->
> 
> Without HPD de-bouncing, USB-C to HDMI Adapaters will not be
> detected.
> 
> The change has been successfully tested with the following devices:
> - Dell Adapter - USB-C to HDMI
> - Acer 1in1 HDMI dongle
> - Ugreen 1in1 HDMI dongle
> - innowatt HDMI + USB3 hub
> - Acer 2in1 HDMI dongle
> - Apple 3in1 HDMI dongle (A2119)
> - J5Create 3in1 HDMI dongle (JAC379)

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> Tested-by: Rex-BC Chen <rex-bc.chen@mediatek.com>
> Reviewed-by: Rex-BC Chen <rex-bc.chen@mediatek.com>
> Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> Reviewed-by: AngeloGioacchino Del Regno <
> angelogioacchino.delregno@collabora.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 25 ++++++++++++++++++++++++-
>  1 file changed, 24 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index 11a94927c0d0..dd34dae417e5 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -87,6 +87,7 @@ struct mtk_dp_efuse_fmt {
>  
>  struct mtk_dp {
>  	bool enabled;
> +	bool need_debounce;
>  	u8 max_lanes;
>  	u8 max_linkrate;
>  	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> @@ -109,6 +110,7 @@ struct mtk_dp {
>  	struct platform_device *phy_dev;
>  	struct phy *phy;
>  	struct regmap *regs;
> +	struct timer_list debounce_timer;
>  };
>  
>  struct mtk_dp_data {
> @@ -1475,14 +1477,24 @@ static irqreturn_t
> mtk_dp_hpd_event_thread(int hpd, void *dev)
>  	unsigned long flags;
>  	u32 status;
>  
> +	if (mtk_dp->need_debounce && mtk_dp-
> >train_info.cable_plugged_in)
> +		msleep(100);
> +
>  	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
>  	status = mtk_dp->irq_thread_handle;
>  	mtk_dp->irq_thread_handle = 0;
>  	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
>  
> -	if (status & MTK_DP_THREAD_CABLE_STATE_CHG)
> +	if (status & MTK_DP_THREAD_CABLE_STATE_CHG) {
>  		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
>  
> +		if (!mtk_dp->train_info.cable_plugged_in) {
> +			mtk_dp->need_debounce = false;
> +			mod_timer(&mtk_dp->debounce_timer,
> +				  jiffies + msecs_to_jiffies(100) - 1);
> +		}
> +	}
> +
>  	if (status & MTK_DP_THREAD_HPD_EVENT)
>  		dev_dbg(mtk_dp->dev, "Receive IRQ from sink
> devices\n");
>  
> @@ -1996,6 +2008,13 @@ static const struct drm_bridge_funcs
> mtk_dp_bridge_funcs = {
>  	.detect = mtk_dp_bdg_detect,
>  };
>  
> +static void mtk_dp_debounce_timer(struct timer_list *t)
> +{
> +	struct mtk_dp *mtk_dp = from_timer(mtk_dp, t, debounce_timer);
> +
> +	mtk_dp->need_debounce = true;
> +}
> +
>  static int mtk_dp_probe(struct platform_device *pdev)
>  {
>  	struct mtk_dp *mtk_dp;
> @@ -2069,6 +2088,9 @@ static int mtk_dp_probe(struct platform_device
> *pdev)
>  
>  	drm_bridge_add(&mtk_dp->bridge);
>  
> +	mtk_dp->need_debounce = true;
> +	timer_setup(&mtk_dp->debounce_timer, mtk_dp_debounce_timer, 0);
> +
>  	pm_runtime_enable(dev);
>  	pm_runtime_get_sync(dev);
>  
> @@ -2081,6 +2103,7 @@ static int mtk_dp_remove(struct platform_device
> *pdev)
>  
>  	pm_runtime_put(&pdev->dev);
>  	pm_runtime_disable(&pdev->dev);
> +	del_timer_sync(&mtk_dp->debounce_timer);
>  	drm_bridge_remove(&mtk_dp->bridge);
>  	platform_device_unregister(mtk_dp->phy_dev);
>  


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

* Re: [PATCH v17 09/10] drm/mediatek: dp: Add hpd debounce
@ 2022-09-02  8:12     ` CK Hu
  0 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  8:12 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	liangxu.xu, dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> From: Jitao Shi <jitao.shi@mediatek.com>
> 
> From the DP spec 1.4a chapter 3.3, upstream devices should implement
> HPD signal de-bouncing on an external connection.
> A period of 100ms should be used to detect an HPD connect event.
> To cover these cases, HPD de-bounce should be implemented only after
> HPD low has been detected for at least 100ms.
> 
> Therefore,
> 1. If HPD is low (which means plugging out) for longer than 100ms:
>    we need to do de-bouncing (which means we need to wait for 100ms).
> 2. If HPD low is for less than 100ms:
>    we don't need to care about the de-bouncing.
> 
> In this patch, we start a 100ms timer and use a need_debounce boolean
> to implement the feature.
> 
> Two cases when HPD is high:
> 1. If the timer is expired (>100ms):
>    - need_debounce is true.
>    - When HPD high (plugging event comes), need_debounce will be true
>      and then we need to do de-bouncing (wait for 100ms).
> 2. If the timer is not expired (<100ms):
>    - need_debounce is false.
>    - When HPD high (plugging event comes), need_debounce will be
> false
>      and no need to do de-bouncing.
> 
> HPD_______             __________________
>           |            |<-  100ms   ->
>           |____________|
>           <-  100ms   ->
> 
> Without HPD de-bouncing, USB-C to HDMI Adapaters will not be
> detected.
> 
> The change has been successfully tested with the following devices:
> - Dell Adapter - USB-C to HDMI
> - Acer 1in1 HDMI dongle
> - Ugreen 1in1 HDMI dongle
> - innowatt HDMI + USB3 hub
> - Acer 2in1 HDMI dongle
> - Apple 3in1 HDMI dongle (A2119)
> - J5Create 3in1 HDMI dongle (JAC379)

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> Tested-by: Rex-BC Chen <rex-bc.chen@mediatek.com>
> Reviewed-by: Rex-BC Chen <rex-bc.chen@mediatek.com>
> Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> Reviewed-by: AngeloGioacchino Del Regno <
> angelogioacchino.delregno@collabora.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 25 ++++++++++++++++++++++++-
>  1 file changed, 24 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index 11a94927c0d0..dd34dae417e5 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -87,6 +87,7 @@ struct mtk_dp_efuse_fmt {
>  
>  struct mtk_dp {
>  	bool enabled;
> +	bool need_debounce;
>  	u8 max_lanes;
>  	u8 max_linkrate;
>  	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> @@ -109,6 +110,7 @@ struct mtk_dp {
>  	struct platform_device *phy_dev;
>  	struct phy *phy;
>  	struct regmap *regs;
> +	struct timer_list debounce_timer;
>  };
>  
>  struct mtk_dp_data {
> @@ -1475,14 +1477,24 @@ static irqreturn_t
> mtk_dp_hpd_event_thread(int hpd, void *dev)
>  	unsigned long flags;
>  	u32 status;
>  
> +	if (mtk_dp->need_debounce && mtk_dp-
> >train_info.cable_plugged_in)
> +		msleep(100);
> +
>  	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
>  	status = mtk_dp->irq_thread_handle;
>  	mtk_dp->irq_thread_handle = 0;
>  	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
>  
> -	if (status & MTK_DP_THREAD_CABLE_STATE_CHG)
> +	if (status & MTK_DP_THREAD_CABLE_STATE_CHG) {
>  		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
>  
> +		if (!mtk_dp->train_info.cable_plugged_in) {
> +			mtk_dp->need_debounce = false;
> +			mod_timer(&mtk_dp->debounce_timer,
> +				  jiffies + msecs_to_jiffies(100) - 1);
> +		}
> +	}
> +
>  	if (status & MTK_DP_THREAD_HPD_EVENT)
>  		dev_dbg(mtk_dp->dev, "Receive IRQ from sink
> devices\n");
>  
> @@ -1996,6 +2008,13 @@ static const struct drm_bridge_funcs
> mtk_dp_bridge_funcs = {
>  	.detect = mtk_dp_bdg_detect,
>  };
>  
> +static void mtk_dp_debounce_timer(struct timer_list *t)
> +{
> +	struct mtk_dp *mtk_dp = from_timer(mtk_dp, t, debounce_timer);
> +
> +	mtk_dp->need_debounce = true;
> +}
> +
>  static int mtk_dp_probe(struct platform_device *pdev)
>  {
>  	struct mtk_dp *mtk_dp;
> @@ -2069,6 +2088,9 @@ static int mtk_dp_probe(struct platform_device
> *pdev)
>  
>  	drm_bridge_add(&mtk_dp->bridge);
>  
> +	mtk_dp->need_debounce = true;
> +	timer_setup(&mtk_dp->debounce_timer, mtk_dp_debounce_timer, 0);
> +
>  	pm_runtime_enable(dev);
>  	pm_runtime_get_sync(dev);
>  
> @@ -2081,6 +2103,7 @@ static int mtk_dp_remove(struct platform_device
> *pdev)
>  
>  	pm_runtime_put(&pdev->dev);
>  	pm_runtime_disable(&pdev->dev);
> +	del_timer_sync(&mtk_dp->debounce_timer);
>  	drm_bridge_remove(&mtk_dp->bridge);
>  	platform_device_unregister(mtk_dp->phy_dev);
>  


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

* Re: [PATCH v17 09/10] drm/mediatek: dp: Add hpd debounce
@ 2022-09-02  8:12     ` CK Hu
  0 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  8:12 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	liangxu.xu, dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> From: Jitao Shi <jitao.shi@mediatek.com>
> 
> From the DP spec 1.4a chapter 3.3, upstream devices should implement
> HPD signal de-bouncing on an external connection.
> A period of 100ms should be used to detect an HPD connect event.
> To cover these cases, HPD de-bounce should be implemented only after
> HPD low has been detected for at least 100ms.
> 
> Therefore,
> 1. If HPD is low (which means plugging out) for longer than 100ms:
>    we need to do de-bouncing (which means we need to wait for 100ms).
> 2. If HPD low is for less than 100ms:
>    we don't need to care about the de-bouncing.
> 
> In this patch, we start a 100ms timer and use a need_debounce boolean
> to implement the feature.
> 
> Two cases when HPD is high:
> 1. If the timer is expired (>100ms):
>    - need_debounce is true.
>    - When HPD high (plugging event comes), need_debounce will be true
>      and then we need to do de-bouncing (wait for 100ms).
> 2. If the timer is not expired (<100ms):
>    - need_debounce is false.
>    - When HPD high (plugging event comes), need_debounce will be
> false
>      and no need to do de-bouncing.
> 
> HPD_______             __________________
>           |            |<-  100ms   ->
>           |____________|
>           <-  100ms   ->
> 
> Without HPD de-bouncing, USB-C to HDMI Adapaters will not be
> detected.
> 
> The change has been successfully tested with the following devices:
> - Dell Adapter - USB-C to HDMI
> - Acer 1in1 HDMI dongle
> - Ugreen 1in1 HDMI dongle
> - innowatt HDMI + USB3 hub
> - Acer 2in1 HDMI dongle
> - Apple 3in1 HDMI dongle (A2119)
> - J5Create 3in1 HDMI dongle (JAC379)

Reviewed-by: CK Hu <ck.hu@mediatek.com>

> 
> Tested-by: Rex-BC Chen <rex-bc.chen@mediatek.com>
> Reviewed-by: Rex-BC Chen <rex-bc.chen@mediatek.com>
> Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> Reviewed-by: AngeloGioacchino Del Regno <
> angelogioacchino.delregno@collabora.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c | 25 ++++++++++++++++++++++++-
>  1 file changed, 24 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index 11a94927c0d0..dd34dae417e5 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -87,6 +87,7 @@ struct mtk_dp_efuse_fmt {
>  
>  struct mtk_dp {
>  	bool enabled;
> +	bool need_debounce;
>  	u8 max_lanes;
>  	u8 max_linkrate;
>  	u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> @@ -109,6 +110,7 @@ struct mtk_dp {
>  	struct platform_device *phy_dev;
>  	struct phy *phy;
>  	struct regmap *regs;
> +	struct timer_list debounce_timer;
>  };
>  
>  struct mtk_dp_data {
> @@ -1475,14 +1477,24 @@ static irqreturn_t
> mtk_dp_hpd_event_thread(int hpd, void *dev)
>  	unsigned long flags;
>  	u32 status;
>  
> +	if (mtk_dp->need_debounce && mtk_dp-
> >train_info.cable_plugged_in)
> +		msleep(100);
> +
>  	spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
>  	status = mtk_dp->irq_thread_handle;
>  	mtk_dp->irq_thread_handle = 0;
>  	spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
>  
> -	if (status & MTK_DP_THREAD_CABLE_STATE_CHG)
> +	if (status & MTK_DP_THREAD_CABLE_STATE_CHG) {
>  		drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
>  
> +		if (!mtk_dp->train_info.cable_plugged_in) {
> +			mtk_dp->need_debounce = false;
> +			mod_timer(&mtk_dp->debounce_timer,
> +				  jiffies + msecs_to_jiffies(100) - 1);
> +		}
> +	}
> +
>  	if (status & MTK_DP_THREAD_HPD_EVENT)
>  		dev_dbg(mtk_dp->dev, "Receive IRQ from sink
> devices\n");
>  
> @@ -1996,6 +2008,13 @@ static const struct drm_bridge_funcs
> mtk_dp_bridge_funcs = {
>  	.detect = mtk_dp_bdg_detect,
>  };
>  
> +static void mtk_dp_debounce_timer(struct timer_list *t)
> +{
> +	struct mtk_dp *mtk_dp = from_timer(mtk_dp, t, debounce_timer);
> +
> +	mtk_dp->need_debounce = true;
> +}
> +
>  static int mtk_dp_probe(struct platform_device *pdev)
>  {
>  	struct mtk_dp *mtk_dp;
> @@ -2069,6 +2088,9 @@ static int mtk_dp_probe(struct platform_device
> *pdev)
>  
>  	drm_bridge_add(&mtk_dp->bridge);
>  
> +	mtk_dp->need_debounce = true;
> +	timer_setup(&mtk_dp->debounce_timer, mtk_dp_debounce_timer, 0);
> +
>  	pm_runtime_enable(dev);
>  	pm_runtime_get_sync(dev);
>  
> @@ -2081,6 +2103,7 @@ static int mtk_dp_remove(struct platform_device
> *pdev)
>  
>  	pm_runtime_put(&pdev->dev);
>  	pm_runtime_disable(&pdev->dev);
> +	del_timer_sync(&mtk_dp->debounce_timer);
>  	drm_bridge_remove(&mtk_dp->bridge);
>  	platform_device_unregister(mtk_dp->phy_dev);
>  


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

* Re: [PATCH v17 10/10] drm/mediatek: dp: Audio support for MT8195
  2022-09-01  4:41   ` Bo-Chen Chen
  (?)
@ 2022-09-02  8:57     ` CK Hu
  -1 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  8:57 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	liangxu.xu, dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> From: Guillaume Ranquet <granquet@baylibre.com>
> 
> This patch adds audio support to the DP driver for MT8195 with up to
> 8
> channels.
> 
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c     | 482
> +++++++++++++++++++++++++-
>  drivers/gpu/drm/mediatek/mtk_dp_reg.h |  51 +++
>  2 files changed, 532 insertions(+), 1 deletion(-)
> 
> 

[snip]

> +#define MTK_DP_ENC0_P0_308C			0x308c
> +#define CH_STATUS_0_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3090			0x3090
> +#define CH_STATUS_1_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3094			0x3094
> +#define CH_STATUS_2_DP_ENC0_P0_MASK			GENMASK(7, 0)
> +#define MTK_DP_ENC0_P0_30A0			0x30a0

Useless, so drop it.

Regards,
CK

> +#define DP_ENC0_30A0_MASK				(BIT(7) |
> BIT(8) | BIT(12))
> +#define MTK_DP_ENC0_P0_30A4			0x30a4
> +#define AU_TS_CFG_DP_ENC0_P0_MASK			GENMASK(7, 0)
> +#define MTK_DP_ENC0_P0_30A8			0x30a8
> 


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

* Re: [PATCH v17 10/10] drm/mediatek: dp: Audio support for MT8195
@ 2022-09-02  8:57     ` CK Hu
  0 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  8:57 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, linux-mediatek, wenst,
	linux-arm-kernel, angelogioacchino.delregno

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> From: Guillaume Ranquet <granquet@baylibre.com>
> 
> This patch adds audio support to the DP driver for MT8195 with up to
> 8
> channels.
> 
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c     | 482
> +++++++++++++++++++++++++-
>  drivers/gpu/drm/mediatek/mtk_dp_reg.h |  51 +++
>  2 files changed, 532 insertions(+), 1 deletion(-)
> 
> 

[snip]

> +#define MTK_DP_ENC0_P0_308C			0x308c
> +#define CH_STATUS_0_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3090			0x3090
> +#define CH_STATUS_1_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3094			0x3094
> +#define CH_STATUS_2_DP_ENC0_P0_MASK			GENMASK(7, 0)
> +#define MTK_DP_ENC0_P0_30A0			0x30a0

Useless, so drop it.

Regards,
CK

> +#define DP_ENC0_30A0_MASK				(BIT(7) |
> BIT(8) | BIT(12))
> +#define MTK_DP_ENC0_P0_30A4			0x30a4
> +#define AU_TS_CFG_DP_ENC0_P0_MASK			GENMASK(7, 0)
> +#define MTK_DP_ENC0_P0_30A8			0x30a8
> 


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

* Re: [PATCH v17 10/10] drm/mediatek: dp: Audio support for MT8195
@ 2022-09-02  8:57     ` CK Hu
  0 siblings, 0 replies; 73+ messages in thread
From: CK Hu @ 2022-09-02  8:57 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: msp, granquet, jitao.shi, wenst, angelogioacchino.delregno,
	liangxu.xu, dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

On Thu, 2022-09-01 at 12:41 +0800, Bo-Chen Chen wrote:
> From: Guillaume Ranquet <granquet@baylibre.com>
> 
> This patch adds audio support to the DP driver for MT8195 with up to
> 8
> channels.
> 
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_dp.c     | 482
> +++++++++++++++++++++++++-
>  drivers/gpu/drm/mediatek/mtk_dp_reg.h |  51 +++
>  2 files changed, 532 insertions(+), 1 deletion(-)
> 
> 

[snip]

> +#define MTK_DP_ENC0_P0_308C			0x308c
> +#define CH_STATUS_0_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3090			0x3090
> +#define CH_STATUS_1_DP_ENC0_P0_MASK			GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3094			0x3094
> +#define CH_STATUS_2_DP_ENC0_P0_MASK			GENMASK(7, 0)
> +#define MTK_DP_ENC0_P0_30A0			0x30a0

Useless, so drop it.

Regards,
CK

> +#define DP_ENC0_30A0_MASK				(BIT(7) |
> BIT(8) | BIT(12))
> +#define MTK_DP_ENC0_P0_30A4			0x30a4
> +#define AU_TS_CFG_DP_ENC0_P0_MASK			GENMASK(7, 0)
> +#define MTK_DP_ENC0_P0_30A8			0x30a8
> 


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

* Re: [PATCH v17 00/10] Add MT8195 DisplayPort driver
  2022-09-01  4:41 ` Bo-Chen Chen
@ 2022-09-04 12:59   ` Dmitry Osipenko
  -1 siblings, 0 replies; 73+ messages in thread
From: Dmitry Osipenko @ 2022-09-04 12:59 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, linux-mediatek, wenst,
	linux-arm-kernel, angelogioacchino.delregno

01.09.2022 07:41, Bo-Chen Chen пишет:
> This patch is separated from v10 which is including dp driver, phy driver
> and dpintf driver. This series is only contained the DisplayPort driver.
> 
> This series can be tested using 5.19-rc2 kernel and I test it in MT8195
> Tomato Chromebook. Modetest these modes:

Applied to drm-misc-next, thanks!

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

* Re: [PATCH v17 00/10] Add MT8195 DisplayPort driver
@ 2022-09-04 12:59   ` Dmitry Osipenko
  0 siblings, 0 replies; 73+ messages in thread
From: Dmitry Osipenko @ 2022-09-04 12:59 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, linux-mediatek, wenst,
	linux-arm-kernel, angelogioacchino.delregno

01.09.2022 07:41, Bo-Chen Chen пишет:
> This patch is separated from v10 which is including dp driver, phy driver
> and dpintf driver. This series is only contained the DisplayPort driver.
> 
> This series can be tested using 5.19-rc2 kernel and I test it in MT8195
> Tomato Chromebook. Modetest these modes:

Applied to drm-misc-next, thanks!

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

* Re: [PATCH v17 02/10] video/hdmi: Add audio_infoframe packing for DP
  2022-09-01  4:41   ` Bo-Chen Chen
@ 2022-09-04 14:56     ` Dmitry Osipenko
  -1 siblings, 0 replies; 73+ messages in thread
From: Dmitry Osipenko @ 2022-09-04 14:56 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, linux-mediatek, wenst,
	linux-arm-kernel, angelogioacchino.delregno

01.09.2022 07:41, Bo-Chen Chen пишет:
> diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h
> index 9e3aff7e68bb..6c0871164771 100644
> --- a/include/drm/display/drm_dp.h
> +++ b/include/drm/display/drm_dp.h
> @@ -1536,6 +1536,8 @@ enum drm_dp_phy {
>  #define DP_SDP_VSC_EXT_CEA		0x21 /* DP 1.4 */
>  /* 0x80+ CEA-861 infoframe types */
>  
> +#define DP_SDP_AUDIO_INFOFRAME_HB2	0x1b

Is there any good reason why this is not grouped with the rest of the
DP_SDP_* defines above?

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

* Re: [PATCH v17 02/10] video/hdmi: Add audio_infoframe packing for DP
@ 2022-09-04 14:56     ` Dmitry Osipenko
  0 siblings, 0 replies; 73+ messages in thread
From: Dmitry Osipenko @ 2022-09-04 14:56 UTC (permalink / raw)
  To: Bo-Chen Chen, chunkuang.hu, p.zabel, daniel, robh+dt,
	krzysztof.kozlowski+dt, mripard, tzimmermann, matthias.bgg,
	deller, airlied
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, linux-mediatek, wenst,
	linux-arm-kernel, angelogioacchino.delregno

01.09.2022 07:41, Bo-Chen Chen пишет:
> diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h
> index 9e3aff7e68bb..6c0871164771 100644
> --- a/include/drm/display/drm_dp.h
> +++ b/include/drm/display/drm_dp.h
> @@ -1536,6 +1536,8 @@ enum drm_dp_phy {
>  #define DP_SDP_VSC_EXT_CEA		0x21 /* DP 1.4 */
>  /* 0x80+ CEA-861 infoframe types */
>  
> +#define DP_SDP_AUDIO_INFOFRAME_HB2	0x1b

Is there any good reason why this is not grouped with the rest of the
DP_SDP_* defines above?

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

* Re: [PATCH v17 00/10] Add MT8195 DisplayPort driver
  2022-09-04 12:59   ` Dmitry Osipenko
  (?)
@ 2022-09-05 10:53     ` Dmitry Osipenko
  -1 siblings, 0 replies; 73+ messages in thread
From: Dmitry Osipenko @ 2022-09-05 10:53 UTC (permalink / raw)
  To: chunkuang.hu, angelogioacchino.delregno
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, linux-mediatek, wenst,
	linux-arm-kernel, Dmitry Osipenko, Bo-Chen Chen, p.zabel, daniel,
	robh+dt, krzysztof.kozlowski+dt, mripard, tzimmermann,
	matthias.bgg, deller, airlied

On 9/4/22 15:59, Dmitry Osipenko wrote:
> 01.09.2022 07:41, Bo-Chen Chen пишет:
>> This patch is separated from v10 which is including dp driver, phy driver
>> and dpintf driver. This series is only contained the DisplayPort driver.
>>
>> This series can be tested using 5.19-rc2 kernel and I test it in MT8195
>> Tomato Chromebook. Modetest these modes:
> 
> Applied to drm-misc-next, thanks!

Hello Chun-Kuang Hu,

Angelo told me today that you wanted to pick up the MTK driver patches
and I applied them all to the drm-misc instead just of the "video/hdmi"
patch. The series was fully reviewed and tested, so I had no doubts when
applied all the patches.

The applied patches can't be reverted, so if you have more changes
prepared for the MTK driver, then please rebase them on top of the
latest drm-misc-next.

Apologizes for this confusion. Please let us know if we can help you.

-- 
Best regards,
Dmitry

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

* Re: [PATCH v17 00/10] Add MT8195 DisplayPort driver
@ 2022-09-05 10:53     ` Dmitry Osipenko
  0 siblings, 0 replies; 73+ messages in thread
From: Dmitry Osipenko @ 2022-09-05 10:53 UTC (permalink / raw)
  To: chunkuang.hu, angelogioacchino.delregno
  Cc: linux-fbdev, airlied, dri-devel, krzysztof.kozlowski+dt,
	Dmitry Osipenko, deller, Project_Global_Chrome_Upstream_Group,
	wenst, Bo-Chen Chen, devicetree, jitao.shi, tzimmermann,
	liangxu.xu, msp, robh+dt, linux-mediatek, matthias.bgg,
	linux-arm-kernel, granquet, linux-kernel

On 9/4/22 15:59, Dmitry Osipenko wrote:
> 01.09.2022 07:41, Bo-Chen Chen пишет:
>> This patch is separated from v10 which is including dp driver, phy driver
>> and dpintf driver. This series is only contained the DisplayPort driver.
>>
>> This series can be tested using 5.19-rc2 kernel and I test it in MT8195
>> Tomato Chromebook. Modetest these modes:
> 
> Applied to drm-misc-next, thanks!

Hello Chun-Kuang Hu,

Angelo told me today that you wanted to pick up the MTK driver patches
and I applied them all to the drm-misc instead just of the "video/hdmi"
patch. The series was fully reviewed and tested, so I had no doubts when
applied all the patches.

The applied patches can't be reverted, so if you have more changes
prepared for the MTK driver, then please rebase them on top of the
latest drm-misc-next.

Apologizes for this confusion. Please let us know if we can help you.

-- 
Best regards,
Dmitry

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

* Re: [PATCH v17 00/10] Add MT8195 DisplayPort driver
@ 2022-09-05 10:53     ` Dmitry Osipenko
  0 siblings, 0 replies; 73+ messages in thread
From: Dmitry Osipenko @ 2022-09-05 10:53 UTC (permalink / raw)
  To: chunkuang.hu, angelogioacchino.delregno
  Cc: devicetree, linux-fbdev, granquet, jitao.shi, liangxu.xu,
	linux-kernel, dri-devel, msp,
	Project_Global_Chrome_Upstream_Group, linux-mediatek, wenst,
	linux-arm-kernel, Dmitry Osipenko, Bo-Chen Chen, p.zabel, daniel,
	robh+dt, krzysztof.kozlowski+dt, mripard, tzimmermann,
	matthias.bgg, deller, airlied

On 9/4/22 15:59, Dmitry Osipenko wrote:
> 01.09.2022 07:41, Bo-Chen Chen пишет:
>> This patch is separated from v10 which is including dp driver, phy driver
>> and dpintf driver. This series is only contained the DisplayPort driver.
>>
>> This series can be tested using 5.19-rc2 kernel and I test it in MT8195
>> Tomato Chromebook. Modetest these modes:
> 
> Applied to drm-misc-next, thanks!

Hello Chun-Kuang Hu,

Angelo told me today that you wanted to pick up the MTK driver patches
and I applied them all to the drm-misc instead just of the "video/hdmi"
patch. The series was fully reviewed and tested, so I had no doubts when
applied all the patches.

The applied patches can't be reverted, so if you have more changes
prepared for the MTK driver, then please rebase them on top of the
latest drm-misc-next.

Apologizes for this confusion. Please let us know if we can help you.

-- 
Best regards,
Dmitry

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

* Re: [PATCH v17 03/10] drm/mediatek: Add MT8195 Embedded DisplayPort driver
  2022-09-01  4:41   ` Bo-Chen Chen
  (?)
@ 2022-09-06  0:07     ` Chun-Kuang Hu
  -1 siblings, 0 replies; 73+ messages in thread
From: Chun-Kuang Hu @ 2022-09-06  0:07 UTC (permalink / raw)
  To: Bo-Chen Chen
  Cc: Chun-Kuang Hu, Philipp Zabel, Daniel Vetter, Rob Herring,
	krzysztof.kozlowski+dt, Maxime Ripard, Thomas Zimmermann,
	Matthias Brugger, deller, David Airlie,
	Markus Schneider-Pargmann, Guillaume Ranquet, Jitao Shi,
	Chen-Yu Tsai, AngeloGioacchino Del Regno, CK Hu, liangxu.xu,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

Please help to fix the compile warning:

../drivers/gpu/drm/mediatek/mtk_dp.c: In function ‘mtk_dp_video_mute’:
../drivers/gpu/drm/mediatek/mtk_dp.c:947:23: warning: format ‘%x’
expects argument of type ‘unsigned int’, but argument 4 has type ‘long
unsigned int’ [-Wformat=]
  947 |  dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
      |                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../include/linux/dev_printk.h:129:27: note: in definition of macro ‘dev_printk’
  129 |   _dev_printk(level, dev, fmt, ##__VA_ARGS__);  \
      |                           ^~~
../include/linux/dev_printk.h:163:31: note: in expansion of macro ‘dev_fmt’
  163 |   dev_printk(KERN_DEBUG, dev, dev_fmt(fmt), ##__VA_ARGS__); \
      |                               ^~~~~~~
../drivers/gpu/drm/mediatek/mtk_dp.c:947:2: note: in expansion of
macro ‘dev_dbg’
  947 |  dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
      |  ^~~~~~~
../drivers/gpu/drm/mediatek/mtk_dp.c:947:36: note: format string is defined here
  947 |  dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
      |                                   ~^
      |                                    |
      |                                    unsigned int
      |                                   %lx

Regards,
Chun-Kuang.

Bo-Chen Chen <rex-bc.chen@mediatek.com> 於 2022年9月1日 週四 中午12:42寫道:
>
> From: Markus Schneider-Pargmann <msp@baylibre.com>
>
> This patch adds a embedded displayport driver for the MediaTek mt8195 SoC.
>
> It supports the MT8195, the embedded DisplayPort units. It offers
> DisplayPort 1.4 with up to 4 lanes.
>
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.
>
> This driver is based on an initial version by
> Jitao shi <jitao.shi@mediatek.com>
>
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/Kconfig      |    9 +
>  drivers/gpu/drm/mediatek/Makefile     |    2 +
>  drivers/gpu/drm/mediatek/mtk_dp.c     | 1999 +++++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_dp_reg.h |  305 ++++
>  4 files changed, 2315 insertions(+)
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
>
> diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> index 2976d21e9a34..e66f4a3b6be0 100644
> --- a/drivers/gpu/drm/mediatek/Kconfig
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -21,6 +21,15 @@ config DRM_MEDIATEK
>           This driver provides kernel mode setting and
>           buffer management to userspace.
>
> +config DRM_MEDIATEK_DP
> +       tristate "DRM DPTX Support for MediaTek SoCs"
> +       depends on DRM_MEDIATEK
> +       select PHY_MTK_DP
> +       select DRM_DISPLAY_HELPER
> +       select DRM_DISPLAY_DP_HELPER
> +       help
> +         DRM/KMS Display Port driver for MediaTek SoCs.
> +
>  config DRM_MEDIATEK_HDMI
>         tristate "DRM HDMI Support for Mediatek SoCs"
>         depends on DRM_MEDIATEK
> diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> index 6e604a933ed0..3517d1c65cd7 100644
> --- a/drivers/gpu/drm/mediatek/Makefile
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -23,3 +23,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
>                           mtk_hdmi_ddc.o
>
>  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> +
> +obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> new file mode 100644
> index 000000000000..e2ec9b02b1aa
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -0,0 +1,1999 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019-2022 MediaTek Inc.
> + * Copyright (c) 2022 BayLibre
> + */
> +
> +#include <drm/display/drm_dp.h>
> +#include <drm/display/drm_dp_helper.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <linux/arm-smccc.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/media-bus-format.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/soc/mediatek/mtk_sip_svc.h>
> +#include <video/videomode.h>
> +
> +#include "mtk_dp_reg.h"
> +
> +#define MTK_DP_SIP_CONTROL_AARCH32     MTK_SIP_SMC_CMD(0x523)
> +#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE        (BIT(0) | BIT(5))
> +
> +#define MTK_DP_THREAD_CABLE_STATE_CHG  BIT(0)
> +#define MTK_DP_THREAD_HPD_EVENT                BIT(1)
> +
> +#define MTK_DP_4P1T 4
> +#define MTK_DP_HDE 2
> +#define MTK_DP_PIX_PER_ADDR 2
> +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x8
> +#define MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY 5
> +#define MTK_DP_TRAIN_DOWNSCALE_RETRY 10
> +
> +enum {
> +       MTK_DP_CAL_GLB_BIAS_TRIM = 0,
> +       MTK_DP_CAL_CLKTX_IMPSE,
> +       MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0,
> +       MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1,
> +       MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2,
> +       MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3,
> +       MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0,
> +       MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1,
> +       MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2,
> +       MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3,
> +       MTK_DP_CAL_MAX,
> +};
> +
> +struct mtk_dp_train_info {
> +       bool sink_ssc;
> +       bool cable_plugged_in;
> +       /* link_rate is in multiple of 0.27Gbps */
> +       int link_rate;
> +       int lane_count;
> +       unsigned int channel_eq_pattern;
> +};
> +
> +struct mtk_dp_info {
> +       enum dp_pixelformat format;
> +       struct videomode vm;
> +};
> +
> +struct mtk_dp_efuse_fmt {
> +       unsigned short idx;
> +       unsigned short shift;
> +       unsigned short mask;
> +       unsigned short min_val;
> +       unsigned short max_val;
> +       unsigned short default_val;
> +};
> +
> +struct mtk_dp {
> +       bool enabled;
> +       u8 max_lanes;
> +       u8 max_linkrate;
> +       u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> +       u32 cal_data[MTK_DP_CAL_MAX];
> +       u32 irq_thread_handle;
> +       /* irq_thread_lock is used to protect irq_thread_handle */
> +       spinlock_t irq_thread_lock;
> +
> +       struct device *dev;
> +       struct drm_bridge bridge;
> +       struct drm_bridge *next_bridge;
> +       struct drm_connector *conn;
> +       struct drm_device *drm_dev;
> +       struct drm_dp_aux aux;
> +
> +       struct mtk_dp_info info;
> +       struct mtk_dp_train_info train_info;
> +
> +       struct platform_device *phy_dev;
> +       struct phy *phy;
> +       struct regmap *regs;
> +};
> +
> +static const struct mtk_dp_efuse_fmt mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
> +       [MTK_DP_CAL_GLB_BIAS_TRIM] = {
> +               .idx = 3,
> +               .shift = 27,
> +               .mask = 0x1f,
> +               .min_val = 1,
> +               .max_val = 0x1e,
> +               .default_val = 0xf,
> +       },
> +       [MTK_DP_CAL_CLKTX_IMPSE] = {
> +               .idx = 0,
> +               .shift = 9,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = {
> +               .idx = 2,
> +               .shift = 28,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = {
> +               .idx = 2,
> +               .shift = 20,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = {
> +               .idx = 2,
> +               .shift = 12,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = {
> +               .idx = 2,
> +               .shift = 4,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = {
> +               .idx = 2,
> +               .shift = 24,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = {
> +               .idx = 2,
> +               .shift = 16,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = {
> +               .idx = 2,
> +               .shift = 8,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = {
> +               .idx = 2,
> +               .shift = 0,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +};
> +
> +static struct regmap_config mtk_dp_regmap_config = {
> +       .reg_bits = 32,
> +       .val_bits = 32,
> +       .reg_stride = 4,
> +       .max_register = SEC_OFFSET + 0x90,
> +       .name = "mtk-dp-registers",
> +};
> +
> +static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
> +{
> +       return container_of(b, struct mtk_dp, bridge);
> +}
> +
> +static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
> +{
> +       u32 read_val;
> +       int ret;
> +
> +       ret = regmap_read(mtk_dp->regs, offset, &read_val);
> +       if (ret) {
> +               dev_err(mtk_dp->dev, "Failed to read register 0x%x: %d\n",
> +                       offset, ret);
> +               return 0;
> +       }
> +
> +       return read_val;
> +}
> +
> +static int mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
> +{
> +       int ret = regmap_write(mtk_dp->regs, offset, val);
> +
> +       if (ret)
> +               dev_err(mtk_dp->dev,
> +                       "Failed to write register 0x%x with value 0x%x\n",
> +                       offset, val);
> +       return ret;
> +}
> +
> +static int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset,
> +                             u32 val, u32 mask)
> +{
> +       int ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
> +
> +       if (ret)
> +               dev_err(mtk_dp->dev,
> +                       "Failed to update register 0x%x with value 0x%x, mask 0x%x\n",
> +                       offset, val, mask);
> +       return ret;
> +}
> +
> +static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32 offset, u8 *buf,
> +                                   size_t length)
> +{
> +       int i;
> +
> +       /* 2 bytes per register */
> +       for (i = 0; i < length; i += 2) {
> +               u32 val = buf[i] | (i + 1 < length ? buf[i + 1] << 8 : 0);
> +
> +               if (mtk_dp_write(mtk_dp, offset + i * 2, val))
> +                       return;
> +       }
> +}
> +
> +static void mtk_dp_msa_bypass_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       u32 mask = HTOTAL_SEL_DP_ENC0_P0 | VTOTAL_SEL_DP_ENC0_P0 |
> +                  HSTART_SEL_DP_ENC0_P0 | VSTART_SEL_DP_ENC0_P0 |
> +                  HWIDTH_SEL_DP_ENC0_P0 | VHEIGHT_SEL_DP_ENC0_P0 |
> +                  HSP_SEL_DP_ENC0_P0 | HSW_SEL_DP_ENC0_P0 |
> +                  VSP_SEL_DP_ENC0_P0 | VSW_SEL_DP_ENC0_P0;
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, enable ? 0 : mask, mask);
> +}
> +
> +static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
> +{
> +       struct drm_display_mode mode;
> +       struct videomode *vm = &mtk_dp->info.vm;
> +
> +       drm_display_mode_from_videomode(vm, &mode);
> +
> +       /* horizontal */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010,
> +                          mode.htotal, HTOTAL_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
> +                          vm->hsync_len + vm->hback_porch,
> +                          HSTART_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> +                          vm->hsync_len, HSW_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> +                          0, HSP_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020,
> +                          vm->hactive, HWIDTH_SW_DP_ENC0_P0_MASK);
> +
> +       /* vertical */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014,
> +                          mode.vtotal, VTOTAL_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
> +                          vm->vsync_len + vm->vback_porch,
> +                          VSTART_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> +                          vm->vsync_len, VSW_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> +                          0, VSP_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024,
> +                          vm->vactive, VHEIGHT_SW_DP_ENC0_P0_MASK);
> +
> +       /* horizontal */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064,
> +                          vm->hactive, HDE_NUM_LAST_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154,
> +                          mode.htotal, PGEN_HTOTAL_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
> +                          vm->hfront_porch,
> +                          PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C,
> +                          vm->hsync_len,
> +                          PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
> +                          vm->hback_porch + vm->hsync_len,
> +                          PGEN_HFDE_START_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164,
> +                          vm->hactive,
> +                          PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +
> +       /* vertical */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168,
> +                          mode.vtotal,
> +                          PGEN_VTOTAL_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
> +                          vm->vfront_porch,
> +                          PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170,
> +                          vm->vsync_len,
> +                          PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
> +                          vm->vback_porch + vm->vsync_len,
> +                          PGEN_VFDE_START_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178,
> +                          vm->vactive,
> +                          PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +}
> +
> +static int mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
> +                                  enum dp_pixelformat color_format)
> +{
> +       u32 val;
> +
> +       /* update MISC0 */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +                          color_format << DP_TEST_COLOR_FORMAT_SHIFT,
> +                          DP_TEST_COLOR_FORMAT_MASK);
> +
> +       switch (color_format) {
> +       case DP_PIXELFORMAT_YUV422:
> +               val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
> +               break;
> +       case DP_PIXELFORMAT_RGB:
> +               val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
> +               break;
> +       default:
> +               drm_warn(mtk_dp->drm_dev, "Unsupported color format: %d\n",
> +                        color_format);
> +               return -EINVAL;
> +       }
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +                          val, PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
> +       return 0;
> +}
> +
> +static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp)
> +{
> +       /* Only support 8 bits currently */
> +       /* Update MISC0 */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +                          DP_MSA_MISC_8_BPC, DP_TEST_BIT_DEPTH_MASK);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +                          VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT,
> +                          VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_config_mn_mode(struct mtk_dp *mtk_dp)
> +{
> +       /* 0: hw mode, 1: sw mode */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +                          0, VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32 val)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +                          val, SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +                          VIDEO_MN_GEN_EN_DP_ENC0_P0,
> +                          VIDEO_MN_GEN_EN_DP_ENC0_P0);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> +                          SDP_DOWN_CNT_DP_ENC0_P0_VAL,
> +                          SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +                          SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL,
> +                          SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
> +                          VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL << 8,
> +                          VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +                          FIFO_READ_START_POINT_DP_ENC1_P0_VAL << 12,
> +                          FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
> +       mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368, DP_ENC1_P0_3368_VAL);
> +}
> +
> +static void mtk_dp_pg_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038,
> +                          enable ? VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK : 0,
> +                          VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
> +                          PGEN_PATTERN_SEL_VAL << 4, PGEN_PATTERN_SEL_MASK);
> +}
> +
> +static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640, DP_AUX_P0_3640_VAL);
> +}
> +
> +static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32 addr)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644,
> +                          cmd, MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648,
> +                          addr, MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C,
> +                          addr >> 16, MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_clear_fifo(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +                          MCU_ACK_TRAN_COMPLETE_AUX_TX_P0,
> +                          MCU_ACK_TRAN_COMPLETE_AUX_TX_P0 |
> +                          PHY_FIFO_RST_AUX_TX_P0_MASK |
> +                          MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
> +                          AUX_TX_REQUEST_READY_AUX_TX_P0,
> +                          AUX_TX_REQUEST_READY_AUX_TX_P0);
> +}
> +
> +static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> +                                      size_t length)
> +{
> +       mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf, length);
> +}
> +
> +static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> +                                   size_t length, int read_delay)
> +{
> +       int read_pos;
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> +                          0, AUX_RD_MODE_AUX_TX_P0_MASK);
> +
> +       for (read_pos = 0; read_pos < length; read_pos++) {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> +                                  AUX_RX_FIFO_READ_PULSE_TX_P0,
> +                                  AUX_RX_FIFO_READ_PULSE_TX_P0);
> +
> +               /* Hardware needs time to update the data */
> +               usleep_range(read_delay, read_delay * 2);
> +               buf[read_pos] = (u8)(mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3620) &
> +                                    AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK);
> +       }
> +}
> +
> +static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t length)
> +{
> +       if (length > 0) {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +                                  (length - 1) << 12,
> +                                  MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +                                  0,
> +                                  AUX_NO_LENGTH_AUX_TX_P0 |
> +                                  AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +                                  AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +       } else {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +                                  AUX_NO_LENGTH_AUX_TX_P0,
> +                                  AUX_NO_LENGTH_AUX_TX_P0 |
> +                                  AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +                                  AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +       }
> +}
> +
> +static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp, bool is_read)
> +{
> +       int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
> +
> +       while (--wait_reply) {
> +               u32 aux_irq_status;
> +
> +               if (is_read) {
> +                       u32 fifo_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3618);
> +
> +                       if (fifo_status &
> +                           (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
> +                            AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
> +                               return 0;
> +                       }
> +               }
> +
> +               aux_irq_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3640);
> +               if (aux_irq_status & AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
> +                       return 0;
> +
> +               if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0)
> +                       return -ETIMEDOUT;
> +
> +               /* Give the hardware a chance to reach completion before retrying */
> +               usleep_range(100, 500);
> +       }
> +
> +       return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool is_read, u8 cmd,
> +                                 u32 addr, u8 *buf, size_t length)
> +{
> +       int ret;
> +       u32 reply_cmd;
> +
> +       if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
> +                       (cmd == DP_AUX_NATIVE_READ && !length)))
> +               return -EINVAL;
> +
> +       if (!is_read)
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> +                                  AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0,
> +                                  AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0);
> +
> +       /* We need to clear fifo and irq before sending commands to the sink device. */
> +       mtk_dp_aux_clear_fifo(mtk_dp);
> +       mtk_dp_aux_irq_clear(mtk_dp);
> +
> +       mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
> +       mtk_dp_aux_set_length(mtk_dp, length);
> +
> +       if (!is_read) {
> +               if (length)
> +                       mtk_dp_aux_fill_write_fifo(mtk_dp, buf, length);
> +
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> +                                  AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK,
> +                                  AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK);
> +       }
> +
> +       mtk_dp_aux_request_ready(mtk_dp);
> +
> +       /* Wait for feedback from sink device. */
> +       ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
> +
> +       reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
> +                   AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
> +
> +       if (ret || reply_cmd) {
> +               u32 phy_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3628) &
> +                                AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
> +               if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
> +                       drm_err(mtk_dp->drm_dev,
> +                               "AUX Rx Aux hang, need SW reset\n");
> +                       return -EIO;
> +               }
> +
> +               return -ETIMEDOUT;
> +       }
> +
> +       if (!length) {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +                                  0,
> +                                  AUX_NO_LENGTH_AUX_TX_P0 |
> +                                  AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +                                  AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +       } else if (is_read) {
> +               int read_delay;
> +
> +               if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
> +                   cmd == DP_AUX_I2C_READ)
> +                       read_delay = 500;
> +               else
> +                       read_delay = 100;
> +
> +               mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length, read_delay);
> +       }
> +
> +       return 0;
> +}
> +
> +static void mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int lane_num,
> +                                         int swing_val, int preemphasis)
> +{
> +       u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
> +
> +       dev_dbg(mtk_dp->dev,
> +               "link training: swing_val = 0x%x, pre-emphasis = 0x%x\n",
> +               swing_val, preemphasis);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +                          swing_val << (DP_TX0_VOLT_SWING_SHIFT + lane_shift),
> +                          DP_TX0_VOLT_SWING_MASK << lane_shift);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +                          preemphasis << (DP_TX0_PRE_EMPH_SHIFT + lane_shift),
> +                          DP_TX0_PRE_EMPH_MASK << lane_shift);
> +}
> +
> +static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +                          0,
> +                          DP_TX0_VOLT_SWING_MASK |
> +                          DP_TX1_VOLT_SWING_MASK |
> +                          DP_TX2_VOLT_SWING_MASK |
> +                          DP_TX3_VOLT_SWING_MASK |
> +                          DP_TX0_PRE_EMPH_MASK |
> +                          DP_TX1_PRE_EMPH_MASK |
> +                          DP_TX2_PRE_EMPH_MASK |
> +                          DP_TX3_PRE_EMPH_MASK);
> +}
> +
> +static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +       u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
> +                        SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
> +
> +       if (irq_status) {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
> +                                  irq_status, SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
> +                                  0, SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +       }
> +
> +       return irq_status;
> +}
> +
> +static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +       u32 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
> +                         IRQ_STATUS_DP_TRANS_P0_MASK) >> 12;
> +
> +       if (irq_status) {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +                                  irq_status, IRQ_CLR_DP_TRANS_P0_MASK);
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +                                  0, IRQ_CLR_DP_TRANS_P0_MASK);
> +       }
> +
> +       return irq_status;
> +}
> +
> +static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +                          enable ? 0 :
> +                          IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
> +                          IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
> +                          IRQ_MASK_DP_TRANS_P0_INT_IRQ,
> +                          IRQ_MASK_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
> +                          XTAL_FREQ_DP_TRANS_P0_DEFAULT,
> +                          XTAL_FREQ_DP_TRANS_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> +                          FEC_CLOCK_EN_MODE_DP_TRANS_P0,
> +                          FEC_CLOCK_EN_MODE_DP_TRANS_P0);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> +                          AUDIO_CH_SRC_SEL_DP_ENC0_P0,
> +                          AUDIO_CH_SRC_SEL_DP_ENC0_P0);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
> +                          0, SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK,
> +                          IRQ_MASK_AUX_TOP_IRQ, IRQ_MASK_AUX_TOP_IRQ);
> +}
> +
> +static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp *mtk_dp)
> +{
> +       u32 val;
> +       /* Debounce threshold */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +                          8, HPD_DEB_THD_DP_TRANS_P0_MASK);
> +
> +       val = (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
> +              HPD_INT_THD_DP_TRANS_P0_UPPER_1100US) << 4;
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +                          val, HPD_INT_THD_DP_TRANS_P0_MASK);
> +
> +       /*
> +        * Connect threshold 1.5ms + 5 x 0.1ms = 2ms
> +        * Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
> +        */
> +       val = (5 << 8) | (5 << 12);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +                          val,
> +                          HPD_DISC_THD_DP_TRANS_P0_MASK |
> +                          HPD_CONN_THD_DP_TRANS_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
> +                          HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
> +                          HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
> +{
> +       /* modify timeout threshold = 0x1595 */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C,
> +                          AUX_TIMEOUT_THR_AUX_TX_P0_VAL,
> +                          AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658,
> +                          0, AUX_TX_OV_EN_AUX_TX_P0_MASK);
> +       /* 25 for 26M */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
> +                          AUX_TX_OVER_SAMPLE_RATE_FOR_26M << 8,
> +                          AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
> +       /* 13 for 26M */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
> +                          AUX_RX_UI_CNT_THR_AUX_FOR_26M,
> +                          AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
> +                          MTK_ATOP_EN_AUX_TX_P0,
> +                          MTK_ATOP_EN_AUX_TX_P0);
> +}
> +
> +static void mtk_dp_initialize_digital_settings(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
> +                          0, VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
> +                          BS2BS_MODE_DP_ENC1_P0_VAL << 12,
> +                          BS2BS_MODE_DP_ENC1_P0_MASK);
> +
> +       /* dp tx encoder reset all sw */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +                          DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0,
> +                          DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
> +
> +       /* Wait for sw reset to complete */
> +       usleep_range(1000, 5000);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +                          0, DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
> +}
> +
> +static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> +                          DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0,
> +                          DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
> +
> +       /* Wait for sw reset to complete */
> +       usleep_range(1000, 5000);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> +                          0, DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
> +}
> +
> +static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
> +                          lanes == 0 ? 0 : DP_TRANS_DUMMY_RW_0,
> +                          DP_TRANS_DUMMY_RW_0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +                          lanes, LANE_NUM_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
> +                          lanes << 2, LANE_NUM_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
> +{
> +       const struct mtk_dp_efuse_fmt *fmt;
> +       struct device *dev = mtk_dp->dev;
> +       struct nvmem_cell *cell;
> +       u32 *cal_data = mtk_dp->cal_data;
> +       u32 *buf;
> +       int i;
> +       size_t len;
> +
> +       cell = nvmem_cell_get(dev, "dp_calibration_data");
> +       if (IS_ERR(cell)) {
> +               dev_warn(dev, "Failed to get nvmem cell dp_calibration_data\n");
> +               goto use_default_val;
> +       }
> +
> +       buf = (u32 *)nvmem_cell_read(cell, &len);
> +       nvmem_cell_put(cell);
> +
> +       if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
> +               dev_warn(dev, "Failed to read nvmem_cell_read\n");
> +
> +               if (!IS_ERR(buf))
> +                       kfree(buf);
> +
> +               goto use_default_val;
> +       }
> +
> +       for (i = 0; i < MTK_DP_CAL_MAX; i++) {
> +               fmt = &mtk_dp_efuse_data[i];
> +               cal_data[i] = (buf[fmt->idx] >> fmt->shift) & fmt->mask;
> +
> +               if (cal_data[i] < fmt->min_val || cal_data[i] > fmt->max_val) {
> +                       dev_warn(mtk_dp->dev, "Invalid efuse data, idx = %d\n", i);
> +                       kfree(buf);
> +                       goto use_default_val;
> +               }
> +       }
> +       kfree(buf);
> +
> +       return;
> +
> +use_default_val:
> +       dev_warn(mtk_dp->dev, "Use default calibration data\n");
> +       for (i = 0; i < MTK_DP_CAL_MAX; i++)
> +               cal_data[i] = mtk_dp_efuse_data[i].default_val;
> +}
> +
> +static void mtk_dp_set_calibration_data(struct mtk_dp *mtk_dp)
> +{
> +       u32 *cal_data = mtk_dp->cal_data;
> +
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
> +                          cal_data[MTK_DP_CAL_CLKTX_IMPSE] << 20,
> +                          RG_CKM_PT0_CKTX_IMPSEL);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
> +                          cal_data[MTK_DP_CAL_GLB_BIAS_TRIM] << 16,
> +                          RG_XTP_GLB_BIAS_INTR_CTRL);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] << 12,
> +                          RG_XTP_LN0_TX_IMPSEL_PMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] << 16,
> +                          RG_XTP_LN0_TX_IMPSEL_NMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] << 12,
> +                          RG_XTP_LN1_TX_IMPSEL_PMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] << 16,
> +                          RG_XTP_LN1_TX_IMPSEL_NMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] << 12,
> +                          RG_XTP_LN2_TX_IMPSEL_PMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] << 16,
> +                          RG_XTP_LN2_TX_IMPSEL_NMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] << 12,
> +                          RG_XTP_LN3_TX_IMPSEL_PMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] << 16,
> +                          RG_XTP_LN3_TX_IMPSEL_NMOS);
> +}
> +
> +static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
> +                               u32 link_rate, int lane_count)
> +{
> +       int ret;
> +       union phy_configure_opts phy_opts = {
> +               .dp = {
> +                       .link_rate = drm_dp_bw_code_to_link_rate(link_rate) / 100,
> +                       .set_rate = 1,
> +                       .lanes = lane_count,
> +                       .set_lanes = 1,
> +                       .ssc = mtk_dp->train_info.sink_ssc,
> +               }
> +       };
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP,
> +                          DP_PWR_STATE_MASK);
> +
> +       ret = phy_configure(mtk_dp->phy, &phy_opts);
> +       if (ret)
> +               return ret;
> +
> +       mtk_dp_set_calibration_data(mtk_dp);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                          DP_PWR_STATE_BANDGAP_TPLL_LANE, DP_PWR_STATE_MASK);
> +
> +       return 0;
> +}
> +
> +static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
> +                 POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
> +                 POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
> +                 POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580,
> +                          enable ? val : 0, val);
> +}
> +
> +static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int pattern)
> +{
> +       /* TPS1 */
> +       if (pattern == 1)
> +               mtk_dp_set_idle_pattern(mtk_dp, false);
> +
> +       mtk_dp_update_bits(mtk_dp,
> +                          MTK_DP_TRANS_P0_3400,
> +                          pattern ? BIT(pattern - 1) << 12 : 0,
> +                          PATTERN1_EN_DP_TRANS_P0_MASK |
> +                          PATTERN2_EN_DP_TRANS_P0_MASK |
> +                          PATTERN3_EN_DP_TRANS_P0_MASK |
> +                          PATTERN4_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +                          ENHANCED_FRAME_EN_DP_ENC0_P0,
> +                          ENHANCED_FRAME_EN_DP_ENC0_P0);
> +}
> +
> +static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
> +                          enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
> +                          DP_SCR_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       struct arm_smccc_res res;
> +       u32 val = VIDEO_MUTE_SEL_DP_ENC0_P0 |
> +                 (enable ? VIDEO_MUTE_SW_DP_ENC0_P0 : 0);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +                          val,
> +                          VIDEO_MUTE_SEL_DP_ENC0_P0 |
> +                          VIDEO_MUTE_SW_DP_ENC0_P0);
> +
> +       arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32,
> +                     MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable,
> +                     0, 0, 0, 0, 0, &res);
> +
> +       dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
> +               MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable, res.a0, res.a1);
> +}
> +
> +static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
> +                          0, SW_RST_B_PHYD);
> +
> +       /* Wait for power enable */
> +       usleep_range(10, 200);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
> +                          SW_RST_B_PHYD, SW_RST_B_PHYD);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                          DP_PWR_STATE_BANDGAP_TPLL, DP_PWR_STATE_MASK);
> +       mtk_dp_write(mtk_dp, MTK_DP_1040,
> +                    RG_DPAUX_RX_VALID_DEGLITCH_EN | RG_XTP_GLB_CKDET_EN |
> +                    RG_DPAUX_RX_EN);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_0034, 0, DA_CKM_CKTX0_EN_FORCE_EN);
> +}
> +
> +static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_0034,
> +                          DA_CKM_CKTX0_EN_FORCE_EN, DA_CKM_CKTX0_EN_FORCE_EN);
> +
> +       /* Disable RX */
> +       mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
> +       mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
> +                    0x550 | FUSE_SEL | MEM_ISO_EN);
> +}
> +
> +static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp->train_info.link_rate = DP_LINK_BW_5_4;
> +       mtk_dp->train_info.lane_count = mtk_dp->max_lanes;
> +       mtk_dp->train_info.cable_plugged_in = false;
> +
> +       mtk_dp->info.format = DP_PIXELFORMAT_RGB;
> +       memset(&mtk_dp->info.vm, 0, sizeof(struct videomode));
> +}
> +
> +static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
> +{
> +       u32 sram_read_start = min_t(u32, MTK_DP_TBC_BUF_READ_START_ADDR,
> +                                   mtk_dp->info.vm.hactive /
> +                                   mtk_dp->train_info.lane_count /
> +                                   MTK_DP_4P1T / MTK_DP_HDE /
> +                                   MTK_DP_PIX_PER_ADDR);
> +       mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
> +       mtk_dp_setup_encoder(mtk_dp);
> +}
> +
> +static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_setup_tu(mtk_dp);
> +}
> +
> +static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes,
> +                                         u8 dpcd_adjust_req[2])
> +{
> +       int lane;
> +
> +       for (lane = 0; lane < lanes; ++lane) {
> +               u8 val;
> +               u8 swing;
> +               u8 preemphasis;
> +               int index = lane / 2;
> +               int shift = lane % 2 ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
> +
> +               swing = (dpcd_adjust_req[index] >> shift) &
> +                       DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
> +               preemphasis = ((dpcd_adjust_req[index] >> shift) &
> +                              DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
> +                             DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
> +               val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
> +                     preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +
> +               if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
> +                       val |= DP_TRAIN_MAX_SWING_REACHED;
> +               if (preemphasis == 3)
> +                       val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> +
> +               mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing, preemphasis);
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET + lane,
> +                                  val);
> +       }
> +}
> +
> +static void mtk_dp_pattern(struct mtk_dp *mtk_dp, bool is_tps1)
> +{
> +       int pattern;
> +       unsigned int aux_offset;
> +
> +       if (is_tps1) {
> +               pattern = 1;
> +               aux_offset = DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1;
> +       } else {
> +               aux_offset = mtk_dp->train_info.channel_eq_pattern;
> +
> +               switch (mtk_dp->train_info.channel_eq_pattern) {
> +               case DP_TRAINING_PATTERN_4:
> +                       pattern = 4;
> +                       break;
> +               case DP_TRAINING_PATTERN_3:
> +                       pattern = 3;
> +                       aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
> +                       break;
> +               case DP_TRAINING_PATTERN_2:
> +               default:
> +                       pattern = 2;
> +                       aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
> +                       break;
> +               }
> +       }
> +
> +       mtk_dp_train_set_pattern(mtk_dp, pattern);
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET, aux_offset);
> +}
> +
> +static int mtk_dp_train_setting(struct mtk_dp *mtk_dp, u8 target_link_rate,
> +                               u8 target_lane_count)
> +{
> +       int ret;
> +
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET, target_link_rate);
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> +                          target_lane_count | DP_LANE_COUNT_ENHANCED_FRAME_EN);
> +
> +       if (mtk_dp->train_info.sink_ssc)
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
> +                                  DP_SPREAD_AMP_0_5);
> +
> +       mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
> +       ret = mtk_dp_phy_configure(mtk_dp, target_link_rate, target_lane_count);
> +       if (ret)
> +               return ret;
> +
> +       dev_dbg(mtk_dp->dev,
> +               "Link train target_link_rate = 0x%x, target_lane_count = 0x%x\n",
> +               target_link_rate, target_lane_count);
> +
> +       return 0;
> +}
> +
> +static int mtk_dp_train_cr(struct mtk_dp *mtk_dp, u8 target_lane_count)
> +{
> +       u8 lane_adjust[2] = {};
> +       u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +       u8 prev_lane_adjust = 0xff;
> +       int train_retries = 0;
> +       int voltage_retries = 0;
> +
> +       mtk_dp_pattern(mtk_dp, true);
> +
> +       /* In DP spec 1.4, the retry count of CR is defined as 10. */
> +       do {
> +               train_retries++;
> +               if (!mtk_dp->train_info.cable_plugged_in) {
> +                       mtk_dp_train_set_pattern(mtk_dp, 0);
> +                       return -ENODEV;
> +               }
> +
> +               drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
> +                                lane_adjust, sizeof(lane_adjust));
> +               mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
> +                                             lane_adjust);
> +
> +               drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
> +                                                      mtk_dp->rx_cap);
> +
> +               /* check link status from sink device */
> +               drm_dp_dpcd_read_link_status(&mtk_dp->aux, link_status);
> +               if (drm_dp_clock_recovery_ok(link_status,
> +                                            target_lane_count)) {
> +                       dev_dbg(mtk_dp->dev, "Link train CR pass\n");
> +                       return 0;
> +               }
> +
> +               /*
> +                * In DP spec 1.4, if current voltage level is the same
> +                * with previous voltage level, we need to retry 5 times.
> +                */
> +               if (prev_lane_adjust == link_status[4]) {
> +                       voltage_retries++;
> +                       /*
> +                        * Condition of CR fail:
> +                        * 1. Failed to pass CR using the same voltage
> +                        *    level over five times.
> +                        * 2. Failed to pass CR when the current voltage
> +                        *    level is the same with previous voltage
> +                        *    level and reach max voltage level (3).
> +                        */
> +                       if (voltage_retries > MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY ||
> +                           (prev_lane_adjust & DP_ADJUST_VOLTAGE_SWING_LANE0_MASK) == 3) {
> +                               dev_dbg(mtk_dp->dev, "Link train CR fail\n");
> +                               break;
> +                       }
> +               } else {
> +                       /*
> +                        * If the voltage level is changed, we need to
> +                        * re-calculate this retry count.
> +                        */
> +                       voltage_retries = 0;
> +               }
> +               prev_lane_adjust = link_status[4];
> +       } while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
> +
> +       /* Failed to train CR, and disable pattern. */
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +                          DP_TRAINING_PATTERN_DISABLE);
> +       mtk_dp_train_set_pattern(mtk_dp, 0);
> +
> +       return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_train_eq(struct mtk_dp *mtk_dp, u8 target_lane_count)
> +{
> +       u8 lane_adjust[2] = {};
> +       u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +       int train_retries = 0;
> +
> +       mtk_dp_pattern(mtk_dp, false);
> +
> +       do {
> +               train_retries++;
> +               if (!mtk_dp->train_info.cable_plugged_in) {
> +                       mtk_dp_train_set_pattern(mtk_dp, 0);
> +                       return -ENODEV;
> +               }
> +
> +               drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
> +                                lane_adjust, sizeof(lane_adjust));
> +               mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
> +                                             lane_adjust);
> +
> +               drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
> +                                                  mtk_dp->rx_cap);
> +
> +               /* check link status from sink device */
> +               drm_dp_dpcd_read_link_status(&mtk_dp->aux, link_status);
> +               if (drm_dp_channel_eq_ok(link_status, target_lane_count)) {
> +                       dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
> +
> +                       /* Training done, and disable pattern. */
> +                       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +                                          DP_TRAINING_PATTERN_DISABLE);
> +                       mtk_dp_train_set_pattern(mtk_dp, 0);
> +                       return 0;
> +               }
> +               dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
> +       } while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
> +
> +       /* Failed to train EQ, and disable pattern. */
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +                          DP_TRAINING_PATTERN_DISABLE);
> +       mtk_dp_train_set_pattern(mtk_dp, 0);
> +
> +       return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
> +{
> +       u8 val;
> +       ssize_t ret;
> +
> +       drm_dp_read_dpcd_caps(&mtk_dp->aux, mtk_dp->rx_cap);
> +
> +       if (drm_dp_tps4_supported(mtk_dp->rx_cap))
> +               mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_4;
> +       else if (drm_dp_tps3_supported(mtk_dp->rx_cap))
> +               mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_3;
> +       else
> +               mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_2;
> +
> +       mtk_dp->train_info.sink_ssc = drm_dp_max_downspread(mtk_dp->rx_cap);
> +
> +       ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
> +       if (ret < 1) {
> +               drm_err(mtk_dp->drm_dev, "Read mstm cap failed\n");
> +               return ret == 0 ? -EIO : ret;
> +       }
> +
> +       if (val & DP_MST_CAP) {
> +               /* Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 */
> +               ret = drm_dp_dpcd_readb(&mtk_dp->aux,
> +                                       DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
> +                                       &val);
> +               if (ret < 1) {
> +                       drm_err(mtk_dp->drm_dev, "Read irq vector failed\n");
> +                       return ret == 0 ? -EIO : ret;
> +               }
> +
> +               if (val)
> +                       drm_dp_dpcd_writeb(&mtk_dp->aux,
> +                                          DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
> +                                          val);
> +       }
> +
> +       return 0;
> +}
> +
> +static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
> +{
> +       phy_reset(mtk_dp->phy);
> +       mtk_dp_reset_swing_pre_emphasis(mtk_dp);
> +}
> +
> +static int mtk_dp_training(struct mtk_dp *mtk_dp)
> +{
> +       int ret;
> +       u8 lane_count, link_rate, train_limit, max_link_rate;
> +
> +       link_rate = min_t(u8, mtk_dp->max_linkrate,
> +                         mtk_dp->rx_cap[DP_MAX_LINK_RATE]);
> +       max_link_rate = link_rate;
> +       lane_count = min_t(u8, mtk_dp->max_lanes,
> +                          drm_dp_max_lane_count(mtk_dp->rx_cap));
> +
> +       /*
> +        * TPS are generated by the hardware pattern generator. From the
> +        * hardware setting we need to disable this scramble setting before
> +        * use the TPS pattern generator.
> +        */
> +       mtk_dp_training_set_scramble(mtk_dp, false);
> +
> +       for (train_limit = 6; train_limit > 0; train_limit--) {
> +               mtk_dp_train_change_mode(mtk_dp);
> +
> +               ret = mtk_dp_train_setting(mtk_dp, link_rate, lane_count);
> +               if (ret)
> +                       return ret;
> +
> +               ret = mtk_dp_train_cr(mtk_dp, lane_count);
> +               if (ret == -ENODEV) {
> +                       return ret;
> +               } else if (ret) {
> +                       /* reduce link rate */
> +                       switch (link_rate) {
> +                       case DP_LINK_BW_1_62:
> +                               lane_count = lane_count / 2;
> +                               link_rate = max_link_rate;
> +                               if (lane_count == 0)
> +                                       return -EIO;
> +                               break;
> +                       case DP_LINK_BW_2_7:
> +                               link_rate = DP_LINK_BW_1_62;
> +                               break;
> +                       case DP_LINK_BW_5_4:
> +                               link_rate = DP_LINK_BW_2_7;
> +                               break;
> +                       case DP_LINK_BW_8_1:
> +                               link_rate = DP_LINK_BW_5_4;
> +                               break;
> +                       default:
> +                               return -EINVAL;
> +                       };
> +                       continue;
> +               }
> +
> +               ret = mtk_dp_train_eq(mtk_dp, lane_count);
> +               if (ret == -ENODEV) {
> +                       return ret;
> +               } else if (ret) {
> +                       /* reduce lane count */
> +                       if (lane_count == 0)
> +                               return -EIO;
> +                       lane_count /= 2;
> +                       continue;
> +               }
> +
> +               /* if we can run to this, training is done. */
> +               break;
> +       }
> +
> +       if (train_limit == 0)
> +               return -ETIMEDOUT;
> +
> +       mtk_dp->train_info.link_rate = link_rate;
> +       mtk_dp->train_info.lane_count = lane_count;
> +
> +       /*
> +        * After training done, we need to output normal stream instead of TPS,
> +        * so we need to enable scramble.
> +        */
> +       mtk_dp_training_set_scramble(mtk_dp, true);
> +       mtk_dp_set_enhanced_frame_mode(mtk_dp);
> +
> +       return 0;
> +}
> +
> +static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       /* the mute sequence is different between enable and disable */
> +       if (enable) {
> +               mtk_dp_msa_bypass_enable(mtk_dp, false);
> +               mtk_dp_pg_enable(mtk_dp, false);
> +               mtk_dp_set_tx_out(mtk_dp);
> +               mtk_dp_video_mute(mtk_dp, false);
> +       } else {
> +               mtk_dp_video_mute(mtk_dp, true);
> +               mtk_dp_pg_enable(mtk_dp, true);
> +               mtk_dp_msa_bypass_enable(mtk_dp, true);
> +       }
> +}
> +
> +static int mtk_dp_video_config(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_config_mn_mode(mtk_dp);
> +       mtk_dp_set_msa(mtk_dp);
> +       mtk_dp_set_color_depth(mtk_dp);
> +       return mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
> +}
> +
> +static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_set_idle_pattern(mtk_dp, true);
> +       mtk_dp_initialize_priv_data(mtk_dp);
> +
> +       mtk_dp_initialize_settings(mtk_dp);
> +       mtk_dp_initialize_aux_settings(mtk_dp);
> +       mtk_dp_initialize_digital_settings(mtk_dp);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
> +                          RX_REPLY_COMPLETE_MODE_AUX_TX_P0,
> +                          RX_REPLY_COMPLETE_MODE_AUX_TX_P0);
> +       mtk_dp_initialize_hpd_detect_settings(mtk_dp);
> +
> +       mtk_dp_digital_sw_reset(mtk_dp);
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
> +{
> +       struct mtk_dp *mtk_dp = dev;
> +       unsigned long flags;
> +       u32 status;
> +
> +       spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
> +       status = mtk_dp->irq_thread_handle;
> +       mtk_dp->irq_thread_handle = 0;
> +       spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
> +
> +       if (status & MTK_DP_THREAD_CABLE_STATE_CHG)
> +               drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
> +
> +       if (status & MTK_DP_THREAD_HPD_EVENT)
> +               dev_dbg(mtk_dp->dev, "Receive IRQ from sink devices\n");
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
> +{
> +       struct mtk_dp *mtk_dp = dev;
> +       bool cable_sta_chg = false;
> +       unsigned long flags;
> +       u32 irq_status = mtk_dp_swirq_get_clear(mtk_dp) |
> +                        mtk_dp_hwirq_get_clear(mtk_dp);
> +
> +       if (!irq_status)
> +               return IRQ_HANDLED;
> +
> +       spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
> +
> +       if (irq_status & MTK_DP_HPD_INTERRUPT)
> +               mtk_dp->irq_thread_handle |= MTK_DP_THREAD_HPD_EVENT;
> +
> +       /* Cable state is changed. */
> +       if (irq_status != MTK_DP_HPD_INTERRUPT) {
> +               mtk_dp->irq_thread_handle |= MTK_DP_THREAD_CABLE_STATE_CHG;
> +               cable_sta_chg = true;
> +       }
> +
> +       spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
> +
> +       if (cable_sta_chg) {
> +               if (!!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
> +                      HPD_DB_DP_TRANS_P0_MASK))
> +                       mtk_dp->train_info.cable_plugged_in = true;
> +               else
> +                       mtk_dp->train_info.cable_plugged_in = false;
> +       }
> +
> +       return IRQ_WAKE_THREAD;
> +}
> +
> +static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
> +                          struct platform_device *pdev)
> +{
> +       struct device_node *endpoint;
> +       struct device *dev = &pdev->dev;
> +       int ret;
> +       void __iomem *base;
> +       u32 linkrate;
> +       int len;
> +
> +       base = devm_platform_ioremap_resource(pdev, 0);
> +       if (IS_ERR(base))
> +               return PTR_ERR(base);
> +
> +       mtk_dp->regs = devm_regmap_init_mmio(dev, base, &mtk_dp_regmap_config);
> +       if (IS_ERR(mtk_dp->regs))
> +               return PTR_ERR(mtk_dp->regs);
> +
> +       endpoint = of_graph_get_endpoint_by_regs(pdev->dev.of_node, 1, -1);
> +       len = of_property_count_elems_of_size(endpoint,
> +                                             "data-lanes", sizeof(u32));
> +       if (len < 0 || len > 4 || len == 3) {
> +               dev_err(dev, "invalid data lane size: %d\n", len);
> +               return -EINVAL;
> +       }
> +
> +       mtk_dp->max_lanes = len;
> +
> +       ret = device_property_read_u32(dev, "max-linkrate-mhz", &linkrate);
> +       if (ret) {
> +               dev_err(dev, "failed to read max linkrate: %d\n", ret);
> +               return ret;
> +       }
> +
> +       mtk_dp->max_linkrate = drm_dp_link_rate_to_bw_code(linkrate * 100);
> +
> +       return 0;
> +}
> +
> +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> +                                   struct drm_connector *connector)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       bool enabled = mtk_dp->enabled;
> +       struct edid *new_edid = NULL;
> +
> +       if (!enabled) {
> +               drm_bridge_chain_pre_enable(bridge);
> +
> +               /* power on aux */
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                                  DP_PWR_STATE_BANDGAP_TPLL_LANE,
> +                                  DP_PWR_STATE_MASK);
> +
> +               /* power on panel */
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> +               usleep_range(2000, 5000);
> +       }
> +
> +       new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> +
> +       /*
> +        * Parse capability here to let atomic_get_input_bus_fmts and
> +        * mode_valid use the capability to calculate sink bitrates.
> +        */
> +       if (mtk_dp_parse_capabilities(mtk_dp)) {
> +               drm_err(mtk_dp->drm_dev, "Can't parse capabilities\n");
> +               new_edid = NULL;
> +       }
> +
> +       if (!enabled) {
> +               /* power off panel */
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
> +               usleep_range(2000, 3000);
> +
> +               /* power off aux */
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                                  DP_PWR_STATE_BANDGAP_TPLL,
> +                                  DP_PWR_STATE_MASK);
> +
> +               drm_bridge_chain_post_disable(bridge);
> +       }
> +
> +       return new_edid;
> +}
> +
> +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> +                                  struct drm_dp_aux_msg *msg)
> +{
> +       struct mtk_dp *mtk_dp;
> +       bool is_read;
> +       u8 request;
> +       size_t accessed_bytes = 0;
> +       int ret;
> +
> +       mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> +
> +       if (!mtk_dp->train_info.cable_plugged_in) {
> +               ret = -EAGAIN;
> +               goto err;
> +       }
> +
> +       switch (msg->request) {
> +       case DP_AUX_I2C_MOT:
> +       case DP_AUX_I2C_WRITE:
> +       case DP_AUX_NATIVE_WRITE:
> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> +               request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> +               is_read = false;
> +               break;
> +       case DP_AUX_I2C_READ:
> +       case DP_AUX_NATIVE_READ:
> +       case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> +               request = msg->request;
> +               is_read = true;
> +               break;
> +       default:
> +               drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> +                       msg->request);
> +               ret = -EINVAL;
> +               goto err;
> +       }
> +
> +       do {
> +               size_t to_access = min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> +                                        msg->size - accessed_bytes);
> +
> +               ret = mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> +                                            msg->address + accessed_bytes,
> +                                            msg->buffer + accessed_bytes,
> +                                            to_access);
> +
> +               if (ret) {
> +                       drm_info(mtk_dp->drm_dev,
> +                                "Failed to do AUX transfer: %d\n", ret);
> +                       goto err;
> +               }
> +               accessed_bytes += to_access;
> +       } while (accessed_bytes < msg->size);
> +
> +       msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +       return msg->size;
> +err:
> +       msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> +       return ret;
> +}
> +
> +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> +{
> +       int ret;
> +
> +       ret = phy_init(mtk_dp->phy);
> +       if (ret) {
> +               dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> +               return ret;
> +       }
> +
> +       mtk_dp_init_port(mtk_dp);
> +       mtk_dp_power_enable(mtk_dp);
> +
> +       return 0;
> +}
> +
> +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_power_disable(mtk_dp);
> +       phy_exit(mtk_dp->phy);
> +}
> +
> +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> +                               enum drm_bridge_attach_flags flags)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       int ret;
> +
> +       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> +               dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> +               return -EINVAL;
> +       }
> +
> +       mtk_dp->aux.drm_dev = bridge->dev;
> +       ret = drm_dp_aux_register(&mtk_dp->aux);
> +       if (ret) {
> +               dev_err(mtk_dp->dev,
> +                       "failed to register DP AUX channel: %d\n", ret);
> +               return ret;
> +       }
> +
> +       ret = mtk_dp_poweron(mtk_dp);
> +       if (ret)
> +               goto err_aux_register;
> +
> +       if (mtk_dp->next_bridge) {
> +               ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> +                                       &mtk_dp->bridge, flags);
> +               if (ret) {
> +                       drm_warn(mtk_dp->drm_dev,
> +                                "Failed to attach external bridge: %d\n", ret);
> +                       goto err_bridge_attach;
> +               }
> +       }
> +
> +       mtk_dp->drm_dev = bridge->dev;
> +
> +       mtk_dp_hwirq_enable(mtk_dp, true);
> +
> +       return 0;
> +
> +err_bridge_attach:
> +       mtk_dp_poweroff(mtk_dp);
> +err_aux_register:
> +       drm_dp_aux_unregister(&mtk_dp->aux);
> +       return ret;
> +}
> +
> +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +       mtk_dp_hwirq_enable(mtk_dp, false);
> +       mtk_dp->drm_dev = NULL;
> +       mtk_dp_poweroff(mtk_dp);
> +       drm_dp_aux_unregister(&mtk_dp->aux);
> +}
> +
> +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> +                                       struct drm_bridge_state *old_state)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       int ret;
> +
> +       mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> +                                                               bridge->encoder);
> +       if (!mtk_dp->conn) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector is missing\n");
> +               return;
> +       }
> +
> +       /* power on aux */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                          DP_PWR_STATE_BANDGAP_TPLL_LANE,
> +                          DP_PWR_STATE_MASK);
> +
> +       if (mtk_dp->train_info.cable_plugged_in) {
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> +               usleep_range(2000, 5000);
> +       }
> +
> +       /* Training */
> +       ret = mtk_dp_training(mtk_dp);
> +       if (ret) {
> +               drm_err(mtk_dp->drm_dev, "Training failed, %d\n", ret);
> +               goto power_off_aux;
> +       }
> +
> +       ret = mtk_dp_video_config(mtk_dp);
> +       if (ret)
> +               goto power_off_aux;
> +
> +       mtk_dp_video_enable(mtk_dp, true);
> +
> +       mtk_dp->enabled = true;
> +
> +       return;
> +power_off_aux:
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                          DP_PWR_STATE_BANDGAP_TPLL,
> +                          DP_PWR_STATE_MASK);
> +}
> +
> +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> +                                        struct drm_bridge_state *old_state)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +       mtk_dp->enabled = false;
> +       mtk_dp_video_enable(mtk_dp, false);
> +
> +       if (mtk_dp->train_info.cable_plugged_in) {
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
> +               usleep_range(2000, 3000);
> +       }
> +
> +       /* power off aux */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                          DP_PWR_STATE_BANDGAP_TPLL,
> +                          DP_PWR_STATE_MASK);
> +
> +       /* Ensure the sink is muted */
> +       msleep(20);
> +}
> +
> +static enum drm_mode_status
> +mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
> +                        const struct drm_display_info *info,
> +                        const struct drm_display_mode *mode)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       u32 bpp = info->color_formats & DRM_COLOR_FORMAT_YCBCR422 ? 16 : 24;
> +       u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
> +                             drm_dp_max_lane_count(mtk_dp->rx_cap),
> +                        drm_dp_bw_code_to_link_rate(mtk_dp->max_linkrate) *
> +                        mtk_dp->max_lanes);
> +
> +       if (rate < mode->clock * bpp / 8)
> +               return MODE_CLOCK_HIGH;
> +
> +       return MODE_OK;
> +}
> +
> +static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
> +                                                    struct drm_bridge_state *bridge_state,
> +                                                    struct drm_crtc_state *crtc_state,
> +                                                    struct drm_connector_state *conn_state,
> +                                                    unsigned int *num_output_fmts)
> +{
> +       u32 *output_fmts;
> +
> +       *num_output_fmts = 0;
> +       output_fmts = kmalloc(sizeof(*output_fmts), GFP_KERNEL);
> +       if (!output_fmts)
> +               return NULL;
> +       *num_output_fmts = 1;
> +       output_fmts[0] = MEDIA_BUS_FMT_FIXED;
> +       return output_fmts;
> +}
> +
> +static const u32 mt8195_input_fmts[] = {
> +       MEDIA_BUS_FMT_RGB888_1X24,
> +       MEDIA_BUS_FMT_YUV8_1X24,
> +       MEDIA_BUS_FMT_YUYV8_1X16,
> +};
> +
> +static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> +                                                   struct drm_bridge_state *bridge_state,
> +                                                   struct drm_crtc_state *crtc_state,
> +                                                   struct drm_connector_state *conn_state,
> +                                                   u32 output_fmt,
> +                                                   unsigned int *num_input_fmts)
> +{
> +       u32 *input_fmts;
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> +       struct drm_display_info *display_info =
> +               &conn_state->connector->display_info;
> +       u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
> +                             drm_dp_max_lane_count(mtk_dp->rx_cap),
> +                        drm_dp_bw_code_to_link_rate(mtk_dp->max_linkrate) *
> +                        mtk_dp->max_lanes);
> +
> +       *num_input_fmts = 0;
> +
> +       /*
> +        * If the linkrate is smaller than datarate of RGB888, larger than
> +        * datarate of YUV422 and sink device supports YUV422, we output YUV422
> +        * format. Use this condition, we can support more resolution.
> +        */
> +       if ((rate < (mode->clock * 24 / 8)) &&
> +           (rate > (mode->clock * 16 / 8)) &&
> +           (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) {
> +               input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL);
> +               if (!input_fmts)
> +                       return NULL;
> +               *num_input_fmts = 1;
> +               input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
> +       } else {
> +               input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts),
> +                                    sizeof(*input_fmts),
> +                                    GFP_KERNEL);
> +               if (!input_fmts)
> +                       return NULL;
> +
> +               *num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
> +               memcpy(input_fmts, mt8195_input_fmts, sizeof(mt8195_input_fmts));
> +       }
> +
> +       return input_fmts;
> +}
> +
> +static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
> +                                     struct drm_bridge_state *bridge_state,
> +                                     struct drm_crtc_state *crtc_state,
> +                                     struct drm_connector_state *conn_state)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       struct drm_crtc *crtc = conn_state->crtc;
> +       unsigned int input_bus_format;
> +
> +       input_bus_format = bridge_state->input_bus_cfg.format;
> +
> +       dev_dbg(mtk_dp->dev, "input format 0x%04x, output format 0x%04x\n",
> +               bridge_state->input_bus_cfg.format,
> +                bridge_state->output_bus_cfg.format);
> +
> +       if (input_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> +               mtk_dp->info.format = DP_PIXELFORMAT_YUV422;
> +       else
> +               mtk_dp->info.format = DP_PIXELFORMAT_RGB;
> +
> +       if (!crtc) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector state doesn't have a crtc\n");
> +               return -EINVAL;
> +       }
> +
> +       drm_display_mode_to_videomode(&crtc_state->adjusted_mode, &mtk_dp->info.vm);
> +
> +       return 0;
> +}
> +
> +static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
> +       .atomic_check = mtk_dp_bridge_atomic_check,
> +       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> +       .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> +       .atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts,
> +       .atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts,
> +       .atomic_reset = drm_atomic_helper_bridge_reset,
> +       .attach = mtk_dp_bridge_attach,
> +       .detach = mtk_dp_bridge_detach,
> +       .atomic_enable = mtk_dp_bridge_atomic_enable,
> +       .atomic_disable = mtk_dp_bridge_atomic_disable,
> +       .mode_valid = mtk_dp_bridge_mode_valid,
> +       .get_edid = mtk_dp_get_edid,
> +};
> +
> +static int mtk_dp_probe(struct platform_device *pdev)
> +{
> +       struct mtk_dp *mtk_dp;
> +       struct device *dev = &pdev->dev;
> +       int ret, irq_num;
> +
> +       mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> +       if (!mtk_dp)
> +               return -ENOMEM;
> +
> +       mtk_dp->dev = dev;
> +
> +       irq_num = platform_get_irq(pdev, 0);
> +       if (irq_num < 0)
> +               return dev_err_probe(dev, irq_num,
> +                                    "failed to request dp irq resource\n");
> +
> +       mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
> +       if (IS_ERR(mtk_dp->next_bridge))
> +               return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge),
> +                                    "Failed to get bridge\n");
> +
> +       ret = mtk_dp_dt_parse(mtk_dp, pdev);
> +       if (ret)
> +               return dev_err_probe(dev, ret, "Failed to parse dt\n");
> +
> +       drm_dp_aux_init(&mtk_dp->aux);
> +       mtk_dp->aux.name = "aux_mtk_dp";
> +       mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> +
> +       spin_lock_init(&mtk_dp->irq_thread_lock);
> +
> +       ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
> +                                       mtk_dp_hpd_event_thread,
> +                                       IRQ_TYPE_LEVEL_HIGH, dev_name(dev),
> +                                       mtk_dp);
> +       if (ret)
> +               return dev_err_probe(dev, ret,
> +                                    "failed to request mediatek dptx irq\n");
> +
> +       platform_set_drvdata(pdev, mtk_dp);
> +
> +       mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
> +                                                       PLATFORM_DEVID_AUTO,
> +                                                       &mtk_dp->regs,
> +                                                       sizeof(struct regmap *));
> +       if (IS_ERR(mtk_dp->phy_dev))
> +               return dev_err_probe(dev, PTR_ERR(mtk_dp->phy_dev),
> +                                    "Failed to create device mediatek-dp-phy\n");
> +
> +       mtk_dp_get_calibration_data(mtk_dp);
> +
> +       mtk_dp->phy = devm_phy_get(&mtk_dp->phy_dev->dev, "dp");
> +
> +       if (IS_ERR(mtk_dp->phy)) {
> +               platform_device_unregister(mtk_dp->phy_dev);
> +               return dev_err_probe(dev, PTR_ERR(mtk_dp->phy),
> +                                    "Failed to get phy\n");
> +       }
> +
> +       mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
> +       mtk_dp->bridge.of_node = dev->of_node;
> +
> +       mtk_dp->bridge.ops =
> +               DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
> +       mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> +
> +       drm_bridge_add(&mtk_dp->bridge);
> +
> +       pm_runtime_enable(dev);
> +       pm_runtime_get_sync(dev);
> +
> +       return 0;
> +}
> +
> +static int mtk_dp_remove(struct platform_device *pdev)
> +{
> +       struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
> +
> +       pm_runtime_put(&pdev->dev);
> +       pm_runtime_disable(&pdev->dev);
> +       drm_bridge_remove(&mtk_dp->bridge);
> +       platform_device_unregister(mtk_dp->phy_dev);
> +
> +       return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int mtk_dp_suspend(struct device *dev)
> +{
> +       struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +       mtk_dp_power_disable(mtk_dp);
> +       mtk_dp_hwirq_enable(mtk_dp, false);
> +       pm_runtime_put_sync(dev);
> +
> +       return 0;
> +}
> +
> +static int mtk_dp_resume(struct device *dev)
> +{
> +       struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +       pm_runtime_get_sync(dev);
> +       mtk_dp_init_port(mtk_dp);
> +       mtk_dp_hwirq_enable(mtk_dp, true);
> +       mtk_dp_power_enable(mtk_dp);
> +
> +       return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
> +
> +static const struct of_device_id mtk_dp_of_match[] = {
> +       { .compatible = "mediatek,mt8195-edp-tx" },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
> +
> +struct platform_driver mtk_dp_driver = {
> +       .probe = mtk_dp_probe,
> +       .remove = mtk_dp_remove,
> +       .driver = {
> +               .name = "mediatek-drm-dp",
> +               .of_match_table = mtk_dp_of_match,
> +               .pm = &mtk_dp_pm_ops,
> +       },
> +};
> +
> +module_platform_driver(mtk_dp_driver);
> +
> +MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
> +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> +MODULE_AUTHOR("Bo-Chen Chen <rex-bc.chen@mediatek.com>");
> +MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> new file mode 100644
> index 000000000000..3f01ba44871f
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> @@ -0,0 +1,305 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019-2022 MediaTek Inc.
> + * Copyright (c) 2022 BayLibre
> + */
> +#ifndef _MTK_DP_REG_H_
> +#define _MTK_DP_REG_H_
> +
> +#define SEC_OFFSET     0x4000
> +
> +#define MTK_DP_HPD_DISCONNECT          BIT(1)
> +#define MTK_DP_HPD_CONNECT             BIT(2)
> +#define MTK_DP_HPD_INTERRUPT           BIT(3)
> +
> +/* offset: 0x0 */
> +#define DP_PHY_GLB_BIAS_GEN_00         0x0
> +#define RG_XTP_GLB_BIAS_INTR_CTRL              GENMASK(20, 16)
> +#define DP_PHY_GLB_DPAUX_TX            0x8
> +#define RG_CKM_PT0_CKTX_IMPSEL                 GENMASK(23, 20)
> +#define MTK_DP_0034                    0x34
> +#define DA_XTP_GLB_CKDET_EN_FORCE_VAL          BIT(15)
> +#define DA_XTP_GLB_CKDET_EN_FORCE_EN           BIT(14)
> +#define DA_CKM_INTCKTX_EN_FORCE_VAL            BIT(13)
> +#define DA_CKM_INTCKTX_EN_FORCE_EN             BIT(12)
> +#define DA_CKM_CKTX0_EN_FORCE_VAL              BIT(11)
> +#define DA_CKM_CKTX0_EN_FORCE_EN               BIT(10)
> +#define DA_CKM_XTAL_CK_FORCE_VAL               BIT(9)
> +#define DA_CKM_XTAL_CK_FORCE_EN                        BIT(8)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_VAL           BIT(7)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_EN            BIT(6)
> +#define DA_CKM_BIAS_EN_FORCE_VAL               BIT(5)
> +#define DA_CKM_BIAS_EN_FORCE_EN                        BIT(4)
> +#define DA_XTP_GLB_AVD10_ON_FORCE_VAL          BIT(3)
> +#define DA_XTP_GLB_AVD10_ON_FORCE              BIT(2)
> +#define DA_XTP_GLB_LDO_EN_FORCE_VAL            BIT(1)
> +#define DA_XTP_GLB_LDO_EN_FORCE_EN             BIT(0)
> +#define DP_PHY_LANE_TX_0               0x104
> +#define RG_XTP_LN0_TX_IMPSEL_PMOS              GENMASK(15, 12)
> +#define RG_XTP_LN0_TX_IMPSEL_NMOS              GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_1               0x204
> +#define RG_XTP_LN1_TX_IMPSEL_PMOS              GENMASK(15, 12)
> +#define RG_XTP_LN1_TX_IMPSEL_NMOS              GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_2               0x304
> +#define RG_XTP_LN2_TX_IMPSEL_PMOS              GENMASK(15, 12)
> +#define RG_XTP_LN2_TX_IMPSEL_NMOS              GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_3               0x404
> +#define RG_XTP_LN3_TX_IMPSEL_PMOS              GENMASK(15, 12)
> +#define RG_XTP_LN3_TX_IMPSEL_NMOS              GENMASK(19, 16)
> +#define MTK_DP_1040                    0x1040
> +#define RG_DPAUX_RX_VALID_DEGLITCH_EN          BIT(2)
> +#define RG_XTP_GLB_CKDET_EN                    BIT(1)
> +#define RG_DPAUX_RX_EN                         BIT(0)
> +
> +/* offset: TOP_OFFSET (0x2000) */
> +#define MTK_DP_TOP_PWR_STATE           0x2000
> +#define DP_PWR_STATE_MASK                      GENMASK(1, 0)
> +#define DP_PWR_STATE_BANDGAP                   BIT(0)
> +#define DP_PWR_STATE_BANDGAP_TPLL              BIT(1)
> +#define DP_PWR_STATE_BANDGAP_TPLL_LANE         GENMASK(1, 0)
> +#define MTK_DP_TOP_SWING_EMP           0x2004
> +#define DP_TX0_VOLT_SWING_MASK                 GENMASK(1, 0)
> +#define DP_TX0_VOLT_SWING_SHIFT                        0
> +#define DP_TX0_PRE_EMPH_MASK                   GENMASK(3, 2)
> +#define DP_TX0_PRE_EMPH_SHIFT                  2
> +#define DP_TX1_VOLT_SWING_MASK                 GENMASK(9, 8)
> +#define DP_TX1_VOLT_SWING_SHIFT                        8
> +#define DP_TX1_PRE_EMPH_MASK                   GENMASK(11, 10)
> +#define DP_TX2_VOLT_SWING_MASK                 GENMASK(17, 16)
> +#define DP_TX2_PRE_EMPH_MASK                   GENMASK(19, 18)
> +#define DP_TX3_VOLT_SWING_MASK                 GENMASK(25, 24)
> +#define DP_TX3_PRE_EMPH_MASK                   GENMASK(27, 26)
> +#define MTK_DP_TOP_RESET_AND_PROBE     0x2020
> +#define SW_RST_B_PHYD                          BIT(4)
> +#define MTK_DP_TOP_IRQ_MASK            0x202c
> +#define IRQ_MASK_AUX_TOP_IRQ                   BIT(2)
> +#define MTK_DP_TOP_MEM_PD              0x2038
> +#define MEM_ISO_EN                             BIT(0)
> +#define FUSE_SEL                               BIT(2)
> +
> +/* offset: ENC0_OFFSET (0x3000) */
> +#define MTK_DP_ENC0_P0_3000                    0x3000
> +#define LANE_NUM_DP_ENC0_P0_MASK                       GENMASK(1, 0)
> +#define VIDEO_MUTE_SW_DP_ENC0_P0                       BIT(2)
> +#define VIDEO_MUTE_SEL_DP_ENC0_P0                      BIT(3)
> +#define ENHANCED_FRAME_EN_DP_ENC0_P0                   BIT(4)
> +#define MTK_DP_ENC0_P0_3004                    0x3004
> +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK               BIT(8)
> +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0           BIT(9)
> +#define MTK_DP_ENC0_P0_3010                    0x3010
> +#define HTOTAL_SW_DP_ENC0_P0_MASK                      GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3014                    0x3014
> +#define VTOTAL_SW_DP_ENC0_P0_MASK                      GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3018                    0x3018
> +#define HSTART_SW_DP_ENC0_P0_MASK                      GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_301C                    0x301c
> +#define VSTART_SW_DP_ENC0_P0_MASK                      GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3020                    0x3020
> +#define HWIDTH_SW_DP_ENC0_P0_MASK                      GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3024                    0x3024
> +#define VHEIGHT_SW_DP_ENC0_P0_MASK                     GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3028                    0x3028
> +#define HSW_SW_DP_ENC0_P0_MASK                         GENMASK(14, 0)
> +#define HSP_SW_DP_ENC0_P0_MASK                         BIT(15)
> +#define MTK_DP_ENC0_P0_302C                    0x302c
> +#define VSW_SW_DP_ENC0_P0_MASK                         GENMASK(14, 0)
> +#define VSP_SW_DP_ENC0_P0_MASK                         BIT(15)
> +#define MTK_DP_ENC0_P0_3030                    0x3030
> +#define HTOTAL_SEL_DP_ENC0_P0                          BIT(0)
> +#define VTOTAL_SEL_DP_ENC0_P0                          BIT(1)
> +#define HSTART_SEL_DP_ENC0_P0                          BIT(2)
> +#define VSTART_SEL_DP_ENC0_P0                          BIT(3)
> +#define HWIDTH_SEL_DP_ENC0_P0                          BIT(4)
> +#define VHEIGHT_SEL_DP_ENC0_P0                         BIT(5)
> +#define HSP_SEL_DP_ENC0_P0                             BIT(6)
> +#define HSW_SEL_DP_ENC0_P0                             BIT(7)
> +#define VSP_SEL_DP_ENC0_P0                             BIT(8)
> +#define VSW_SEL_DP_ENC0_P0                             BIT(9)
> +#define MTK_DP_ENC0_P0_3034                    0x3034
> +#define MTK_DP_ENC0_P0_3038                    0x3038
> +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK               BIT(11)
> +#define MTK_DP_ENC0_P0_303C                    0x303c
> +#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK           GENMASK(5, 0)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK              GENMASK(10, 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT             (0 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT             (1 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT             (2 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT              (3 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT              (4 << 8)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK            GENMASK(14, 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB             (0 << 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422                (1 << 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420                (2 << 12)
> +#define VIDEO_MN_GEN_EN_DP_ENC0_P0                     BIT(15)
> +#define MTK_DP_ENC0_P0_3040                    0x3040
> +#define SDP_DOWN_CNT_DP_ENC0_P0_VAL                    0x20
> +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK              GENMASK(11, 0)
> +#define MTK_DP_ENC0_P0_304C                    0x304c
> +#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK                        BIT(2)
> +#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK          BIT(8)
> +#define MTK_DP_ENC0_P0_3064                    0x3064
> +#define HDE_NUM_LAST_DP_ENC0_P0_MASK                   GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3154                    0x3154
> +#define PGEN_HTOTAL_DP_ENC0_P0_MASK                    GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_3158                    0x3158
> +#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK              GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_315C                    0x315c
> +#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK         GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_3160                    0x3160
> +#define PGEN_HFDE_START_DP_ENC0_P0_MASK                        GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_3164                    0x3164
> +#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK         GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_3168                    0x3168
> +#define PGEN_VTOTAL_DP_ENC0_P0_MASK                    GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_316C                    0x316c
> +#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK              GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_3170                    0x3170
> +#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK         GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_3174                    0x3174
> +#define PGEN_VFDE_START_DP_ENC0_P0_MASK                        GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_3178                    0x3178
> +#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK         GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_31B0                    0x31b0
> +#define PGEN_PATTERN_SEL_VAL                           4
> +#define PGEN_PATTERN_SEL_MASK                          GENMASK(6, 4)
> +#define MTK_DP_ENC0_P0_31EC                    0x31ec
> +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0                    BIT(4)
> +#define ISRC1_HB3_DP_ENC0_P0_MASK                      GENMASK(15, 8)
> +
> +/* offset: ENC1_OFFSET (0x3200) */
> +#define MTK_DP_ENC1_P0_3300                    0x3300
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL             2
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK            GENMASK(9, 8)
> +#define MTK_DP_ENC1_P0_3364                    0x3364
> +#define SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL          0x20
> +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK    GENMASK(11, 0)
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_VAL           4
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK          GENMASK(15, 12)
> +#define MTK_DP_ENC1_P0_3368                    0x3368
> +#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0       BIT(0)
> +#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0               BIT(4)
> +#define SDP_DP13_EN_DP_ENC1_P0                         BIT(8)
> +#define BS2BS_MODE_DP_ENC1_P0                          BIT(12)
> +#define BS2BS_MODE_DP_ENC1_P0_MASK                     GENMASK(13, 12)
> +#define BS2BS_MODE_DP_ENC1_P0_VAL                      1
> +#define DP_ENC1_P0_3368_VAL                            (VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0 | \
> +                                                        VIDEO_STABLE_CNT_THRD_DP_ENC1_P0 | \
> +                                                        SDP_DP13_EN_DP_ENC1_P0 | \
> +                                                        BS2BS_MODE_DP_ENC1_P0)
> +
> +/* offset: TRANS_OFFSET (0x3400) */
> +#define MTK_DP_TRANS_P0_3400                           0x3400
> +#define PATTERN1_EN_DP_TRANS_P0_MASK                           BIT(12)
> +#define PATTERN2_EN_DP_TRANS_P0_MASK                           BIT(13)
> +#define PATTERN3_EN_DP_TRANS_P0_MASK                           BIT(14)
> +#define PATTERN4_EN_DP_TRANS_P0_MASK                           BIT(15)
> +#define MTK_DP_TRANS_P0_3404                           0x3404
> +#define DP_SCR_EN_DP_TRANS_P0_MASK                             BIT(0)
> +#define MTK_DP_TRANS_P0_340C                           0x340c
> +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0              BIT(13)
> +#define MTK_DP_TRANS_P0_3410                           0x3410
> +#define HPD_DEB_THD_DP_TRANS_P0_MASK                           GENMASK(3, 0)
> +#define HPD_INT_THD_DP_TRANS_P0_MASK                           GENMASK(7, 4)
> +#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US                    (2 << 4)
> +#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US                   (2 << 6)
> +#define HPD_DISC_THD_DP_TRANS_P0_MASK                          GENMASK(11, 8)
> +#define HPD_CONN_THD_DP_TRANS_P0_MASK                          GENMASK(15, 12)
> +#define MTK_DP_TRANS_P0_3414                           0x3414
> +#define HPD_DB_DP_TRANS_P0_MASK                                        BIT(2)
> +#define MTK_DP_TRANS_P0_3418                           0x3418
> +#define IRQ_CLR_DP_TRANS_P0_MASK                               GENMASK(3, 0)
> +#define IRQ_MASK_DP_TRANS_P0_MASK                              GENMASK(7, 4)
> +#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ                          (BIT(1) << 4)
> +#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ                          (BIT(2) << 4)
> +#define IRQ_MASK_DP_TRANS_P0_INT_IRQ                           (BIT(3) << 4)
> +#define IRQ_STATUS_DP_TRANS_P0_MASK                            GENMASK(15, 12)
> +#define MTK_DP_TRANS_P0_342C                           0x342c
> +#define XTAL_FREQ_DP_TRANS_P0_DEFAULT                          (BIT(0) | BIT(3) | BIT(5) | BIT(6))
> +#define XTAL_FREQ_DP_TRANS_P0_MASK                             GENMASK(7, 0)
> +#define MTK_DP_TRANS_P0_3430                           0x3430
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK                       GENMASK(1, 0)
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT             BIT(1)
> +#define MTK_DP_TRANS_P0_34A4                           0x34a4
> +#define LANE_NUM_DP_TRANS_P0_MASK                              GENMASK(3, 2)
> +#define MTK_DP_TRANS_P0_3540                           0x3540
> +#define FEC_EN_DP_TRANS_P0_MASK                                        BIT(0)
> +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0                          BIT(3)
> +#define MTK_DP_TRANS_P0_3580                           0x3580
> +#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK               BIT(8)
> +#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK               BIT(9)
> +#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK               BIT(10)
> +#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK               BIT(11)
> +#define MTK_DP_TRANS_P0_35C8                           0x35c8
> +#define SW_IRQ_CLR_DP_TRANS_P0_MASK                            GENMASK(15, 0)
> +#define SW_IRQ_STATUS_DP_TRANS_P0_MASK                         GENMASK(15, 0)
> +#define MTK_DP_TRANS_P0_35D0                           0x35d0
> +#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK                   GENMASK(15, 0)
> +#define MTK_DP_TRANS_P0_35F0                           0x35f0
> +#define DP_TRANS_DUMMY_RW_0                                    BIT(3)
> +#define DP_TRANS_DUMMY_RW_0_MASK                               GENMASK(3, 2)
> +
> +/* offset: AUX_OFFSET (0x3600) */
> +#define MTK_DP_AUX_P0_360C                     0x360c
> +#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK                 GENMASK(12, 0)
> +#define AUX_TIMEOUT_THR_AUX_TX_P0_VAL                  0x1595
> +#define MTK_DP_AUX_P0_3614                     0x3614
> +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK               GENMASK(6, 0)
> +#define AUX_RX_UI_CNT_THR_AUX_FOR_26M                  13
> +#define MTK_DP_AUX_P0_3618                     0x3618
> +#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK                        BIT(9)
> +#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK       GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3620                     0x3620
> +#define AUX_RD_MODE_AUX_TX_P0_MASK                     BIT(9)
> +#define AUX_RX_FIFO_READ_PULSE_TX_P0                   BIT(8)
> +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK           GENMASK(7, 0)
> +#define MTK_DP_AUX_P0_3624                     0x3624
> +#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK            GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3628                     0x3628
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK                        GENMASK(9, 0)
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE             BIT(0)
> +#define MTK_DP_AUX_P0_362C                     0x362c
> +#define AUX_NO_LENGTH_AUX_TX_P0                                BIT(0)
> +#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK              BIT(1)
> +#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK               GENMASK(15, 2)
> +#define MTK_DP_AUX_P0_3630                     0x3630
> +#define AUX_TX_REQUEST_READY_AUX_TX_P0                 BIT(3)
> +#define MTK_DP_AUX_P0_3634                     0x3634
> +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK         GENMASK(15, 8)
> +#define AUX_TX_OVER_SAMPLE_RATE_FOR_26M                        25
> +#define MTK_DP_AUX_P0_3640                     0x3640
> +#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0         BIT(6)
> +#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0                BIT(5)
> +#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0                BIT(4)
> +#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0                  BIT(3)
> +#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0                 BIT(2)
> +#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0                 BIT(1)
> +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0                        BIT(0)
> +#define DP_AUX_P0_3640_VAL                             (AUX_400US_TIMEOUT_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_DATA_RECV_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_CMD_RECV_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
> +#define MTK_DP_AUX_P0_3644                     0x3644
> +#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK             GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3648                     0x3648
> +#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK         GENMASK(15, 0)
> +#define MTK_DP_AUX_P0_364C                     0x364c
> +#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK         GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3650                     0x3650
> +#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK                        GENMASK(15, 12)
> +#define PHY_FIFO_RST_AUX_TX_P0_MASK                    BIT(9)
> +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0                        BIT(8)
> +#define MTK_DP_AUX_P0_3658                     0x3658
> +#define AUX_TX_OV_EN_AUX_TX_P0_MASK                    BIT(0)
> +#define MTK_DP_AUX_P0_3690                     0x3690
> +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0               BIT(8)
> +#define MTK_DP_AUX_P0_3704                     0x3704
> +#define AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK    BIT(1)
> +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0              BIT(2)
> +#define MTK_DP_AUX_P0_3708                     0x3708
> +#define MTK_DP_AUX_P0_37C8                     0x37c8
> +#define MTK_ATOP_EN_AUX_TX_P0                          BIT(0)
> +
> +#endif /*_MTK_DP_REG_H_*/
> --
> 2.18.0
>

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

* Re: [PATCH v17 03/10] drm/mediatek: Add MT8195 Embedded DisplayPort driver
@ 2022-09-06  0:07     ` Chun-Kuang Hu
  0 siblings, 0 replies; 73+ messages in thread
From: Chun-Kuang Hu @ 2022-09-06  0:07 UTC (permalink / raw)
  To: Bo-Chen Chen
  Cc: linux-fbdev, devicetree, David Airlie, dri-devel,
	krzysztof.kozlowski+dt, deller,
	Project_Global_Chrome_Upstream_Group, Chen-Yu Tsai,
	Chun-Kuang Hu, Jitao Shi, Thomas Zimmermann, liangxu.xu,
	Markus Schneider-Pargmann, Rob Herring, linux-mediatek,
	Matthias Brugger, linux-arm-kernel, AngeloGioacchino Del Regno,
	Guillaume Ranquet, linux-kernel

Hi, Bo-Chen:

Please help to fix the compile warning:

../drivers/gpu/drm/mediatek/mtk_dp.c: In function ‘mtk_dp_video_mute’:
../drivers/gpu/drm/mediatek/mtk_dp.c:947:23: warning: format ‘%x’
expects argument of type ‘unsigned int’, but argument 4 has type ‘long
unsigned int’ [-Wformat=]
  947 |  dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
      |                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../include/linux/dev_printk.h:129:27: note: in definition of macro ‘dev_printk’
  129 |   _dev_printk(level, dev, fmt, ##__VA_ARGS__);  \
      |                           ^~~
../include/linux/dev_printk.h:163:31: note: in expansion of macro ‘dev_fmt’
  163 |   dev_printk(KERN_DEBUG, dev, dev_fmt(fmt), ##__VA_ARGS__); \
      |                               ^~~~~~~
../drivers/gpu/drm/mediatek/mtk_dp.c:947:2: note: in expansion of
macro ‘dev_dbg’
  947 |  dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
      |  ^~~~~~~
../drivers/gpu/drm/mediatek/mtk_dp.c:947:36: note: format string is defined here
  947 |  dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
      |                                   ~^
      |                                    |
      |                                    unsigned int
      |                                   %lx

Regards,
Chun-Kuang.

Bo-Chen Chen <rex-bc.chen@mediatek.com> 於 2022年9月1日 週四 中午12:42寫道:
>
> From: Markus Schneider-Pargmann <msp@baylibre.com>
>
> This patch adds a embedded displayport driver for the MediaTek mt8195 SoC.
>
> It supports the MT8195, the embedded DisplayPort units. It offers
> DisplayPort 1.4 with up to 4 lanes.
>
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.
>
> This driver is based on an initial version by
> Jitao shi <jitao.shi@mediatek.com>
>
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/Kconfig      |    9 +
>  drivers/gpu/drm/mediatek/Makefile     |    2 +
>  drivers/gpu/drm/mediatek/mtk_dp.c     | 1999 +++++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_dp_reg.h |  305 ++++
>  4 files changed, 2315 insertions(+)
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
>
> diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> index 2976d21e9a34..e66f4a3b6be0 100644
> --- a/drivers/gpu/drm/mediatek/Kconfig
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -21,6 +21,15 @@ config DRM_MEDIATEK
>           This driver provides kernel mode setting and
>           buffer management to userspace.
>
> +config DRM_MEDIATEK_DP
> +       tristate "DRM DPTX Support for MediaTek SoCs"
> +       depends on DRM_MEDIATEK
> +       select PHY_MTK_DP
> +       select DRM_DISPLAY_HELPER
> +       select DRM_DISPLAY_DP_HELPER
> +       help
> +         DRM/KMS Display Port driver for MediaTek SoCs.
> +
>  config DRM_MEDIATEK_HDMI
>         tristate "DRM HDMI Support for Mediatek SoCs"
>         depends on DRM_MEDIATEK
> diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> index 6e604a933ed0..3517d1c65cd7 100644
> --- a/drivers/gpu/drm/mediatek/Makefile
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -23,3 +23,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
>                           mtk_hdmi_ddc.o
>
>  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> +
> +obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> new file mode 100644
> index 000000000000..e2ec9b02b1aa
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -0,0 +1,1999 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019-2022 MediaTek Inc.
> + * Copyright (c) 2022 BayLibre
> + */
> +
> +#include <drm/display/drm_dp.h>
> +#include <drm/display/drm_dp_helper.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <linux/arm-smccc.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/media-bus-format.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/soc/mediatek/mtk_sip_svc.h>
> +#include <video/videomode.h>
> +
> +#include "mtk_dp_reg.h"
> +
> +#define MTK_DP_SIP_CONTROL_AARCH32     MTK_SIP_SMC_CMD(0x523)
> +#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE        (BIT(0) | BIT(5))
> +
> +#define MTK_DP_THREAD_CABLE_STATE_CHG  BIT(0)
> +#define MTK_DP_THREAD_HPD_EVENT                BIT(1)
> +
> +#define MTK_DP_4P1T 4
> +#define MTK_DP_HDE 2
> +#define MTK_DP_PIX_PER_ADDR 2
> +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x8
> +#define MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY 5
> +#define MTK_DP_TRAIN_DOWNSCALE_RETRY 10
> +
> +enum {
> +       MTK_DP_CAL_GLB_BIAS_TRIM = 0,
> +       MTK_DP_CAL_CLKTX_IMPSE,
> +       MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0,
> +       MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1,
> +       MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2,
> +       MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3,
> +       MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0,
> +       MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1,
> +       MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2,
> +       MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3,
> +       MTK_DP_CAL_MAX,
> +};
> +
> +struct mtk_dp_train_info {
> +       bool sink_ssc;
> +       bool cable_plugged_in;
> +       /* link_rate is in multiple of 0.27Gbps */
> +       int link_rate;
> +       int lane_count;
> +       unsigned int channel_eq_pattern;
> +};
> +
> +struct mtk_dp_info {
> +       enum dp_pixelformat format;
> +       struct videomode vm;
> +};
> +
> +struct mtk_dp_efuse_fmt {
> +       unsigned short idx;
> +       unsigned short shift;
> +       unsigned short mask;
> +       unsigned short min_val;
> +       unsigned short max_val;
> +       unsigned short default_val;
> +};
> +
> +struct mtk_dp {
> +       bool enabled;
> +       u8 max_lanes;
> +       u8 max_linkrate;
> +       u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> +       u32 cal_data[MTK_DP_CAL_MAX];
> +       u32 irq_thread_handle;
> +       /* irq_thread_lock is used to protect irq_thread_handle */
> +       spinlock_t irq_thread_lock;
> +
> +       struct device *dev;
> +       struct drm_bridge bridge;
> +       struct drm_bridge *next_bridge;
> +       struct drm_connector *conn;
> +       struct drm_device *drm_dev;
> +       struct drm_dp_aux aux;
> +
> +       struct mtk_dp_info info;
> +       struct mtk_dp_train_info train_info;
> +
> +       struct platform_device *phy_dev;
> +       struct phy *phy;
> +       struct regmap *regs;
> +};
> +
> +static const struct mtk_dp_efuse_fmt mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
> +       [MTK_DP_CAL_GLB_BIAS_TRIM] = {
> +               .idx = 3,
> +               .shift = 27,
> +               .mask = 0x1f,
> +               .min_val = 1,
> +               .max_val = 0x1e,
> +               .default_val = 0xf,
> +       },
> +       [MTK_DP_CAL_CLKTX_IMPSE] = {
> +               .idx = 0,
> +               .shift = 9,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = {
> +               .idx = 2,
> +               .shift = 28,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = {
> +               .idx = 2,
> +               .shift = 20,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = {
> +               .idx = 2,
> +               .shift = 12,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = {
> +               .idx = 2,
> +               .shift = 4,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = {
> +               .idx = 2,
> +               .shift = 24,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = {
> +               .idx = 2,
> +               .shift = 16,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = {
> +               .idx = 2,
> +               .shift = 8,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = {
> +               .idx = 2,
> +               .shift = 0,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +};
> +
> +static struct regmap_config mtk_dp_regmap_config = {
> +       .reg_bits = 32,
> +       .val_bits = 32,
> +       .reg_stride = 4,
> +       .max_register = SEC_OFFSET + 0x90,
> +       .name = "mtk-dp-registers",
> +};
> +
> +static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
> +{
> +       return container_of(b, struct mtk_dp, bridge);
> +}
> +
> +static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
> +{
> +       u32 read_val;
> +       int ret;
> +
> +       ret = regmap_read(mtk_dp->regs, offset, &read_val);
> +       if (ret) {
> +               dev_err(mtk_dp->dev, "Failed to read register 0x%x: %d\n",
> +                       offset, ret);
> +               return 0;
> +       }
> +
> +       return read_val;
> +}
> +
> +static int mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
> +{
> +       int ret = regmap_write(mtk_dp->regs, offset, val);
> +
> +       if (ret)
> +               dev_err(mtk_dp->dev,
> +                       "Failed to write register 0x%x with value 0x%x\n",
> +                       offset, val);
> +       return ret;
> +}
> +
> +static int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset,
> +                             u32 val, u32 mask)
> +{
> +       int ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
> +
> +       if (ret)
> +               dev_err(mtk_dp->dev,
> +                       "Failed to update register 0x%x with value 0x%x, mask 0x%x\n",
> +                       offset, val, mask);
> +       return ret;
> +}
> +
> +static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32 offset, u8 *buf,
> +                                   size_t length)
> +{
> +       int i;
> +
> +       /* 2 bytes per register */
> +       for (i = 0; i < length; i += 2) {
> +               u32 val = buf[i] | (i + 1 < length ? buf[i + 1] << 8 : 0);
> +
> +               if (mtk_dp_write(mtk_dp, offset + i * 2, val))
> +                       return;
> +       }
> +}
> +
> +static void mtk_dp_msa_bypass_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       u32 mask = HTOTAL_SEL_DP_ENC0_P0 | VTOTAL_SEL_DP_ENC0_P0 |
> +                  HSTART_SEL_DP_ENC0_P0 | VSTART_SEL_DP_ENC0_P0 |
> +                  HWIDTH_SEL_DP_ENC0_P0 | VHEIGHT_SEL_DP_ENC0_P0 |
> +                  HSP_SEL_DP_ENC0_P0 | HSW_SEL_DP_ENC0_P0 |
> +                  VSP_SEL_DP_ENC0_P0 | VSW_SEL_DP_ENC0_P0;
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, enable ? 0 : mask, mask);
> +}
> +
> +static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
> +{
> +       struct drm_display_mode mode;
> +       struct videomode *vm = &mtk_dp->info.vm;
> +
> +       drm_display_mode_from_videomode(vm, &mode);
> +
> +       /* horizontal */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010,
> +                          mode.htotal, HTOTAL_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
> +                          vm->hsync_len + vm->hback_porch,
> +                          HSTART_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> +                          vm->hsync_len, HSW_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> +                          0, HSP_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020,
> +                          vm->hactive, HWIDTH_SW_DP_ENC0_P0_MASK);
> +
> +       /* vertical */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014,
> +                          mode.vtotal, VTOTAL_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
> +                          vm->vsync_len + vm->vback_porch,
> +                          VSTART_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> +                          vm->vsync_len, VSW_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> +                          0, VSP_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024,
> +                          vm->vactive, VHEIGHT_SW_DP_ENC0_P0_MASK);
> +
> +       /* horizontal */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064,
> +                          vm->hactive, HDE_NUM_LAST_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154,
> +                          mode.htotal, PGEN_HTOTAL_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
> +                          vm->hfront_porch,
> +                          PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C,
> +                          vm->hsync_len,
> +                          PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
> +                          vm->hback_porch + vm->hsync_len,
> +                          PGEN_HFDE_START_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164,
> +                          vm->hactive,
> +                          PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +
> +       /* vertical */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168,
> +                          mode.vtotal,
> +                          PGEN_VTOTAL_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
> +                          vm->vfront_porch,
> +                          PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170,
> +                          vm->vsync_len,
> +                          PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
> +                          vm->vback_porch + vm->vsync_len,
> +                          PGEN_VFDE_START_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178,
> +                          vm->vactive,
> +                          PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +}
> +
> +static int mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
> +                                  enum dp_pixelformat color_format)
> +{
> +       u32 val;
> +
> +       /* update MISC0 */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +                          color_format << DP_TEST_COLOR_FORMAT_SHIFT,
> +                          DP_TEST_COLOR_FORMAT_MASK);
> +
> +       switch (color_format) {
> +       case DP_PIXELFORMAT_YUV422:
> +               val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
> +               break;
> +       case DP_PIXELFORMAT_RGB:
> +               val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
> +               break;
> +       default:
> +               drm_warn(mtk_dp->drm_dev, "Unsupported color format: %d\n",
> +                        color_format);
> +               return -EINVAL;
> +       }
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +                          val, PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
> +       return 0;
> +}
> +
> +static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp)
> +{
> +       /* Only support 8 bits currently */
> +       /* Update MISC0 */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +                          DP_MSA_MISC_8_BPC, DP_TEST_BIT_DEPTH_MASK);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +                          VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT,
> +                          VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_config_mn_mode(struct mtk_dp *mtk_dp)
> +{
> +       /* 0: hw mode, 1: sw mode */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +                          0, VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32 val)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +                          val, SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +                          VIDEO_MN_GEN_EN_DP_ENC0_P0,
> +                          VIDEO_MN_GEN_EN_DP_ENC0_P0);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> +                          SDP_DOWN_CNT_DP_ENC0_P0_VAL,
> +                          SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +                          SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL,
> +                          SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
> +                          VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL << 8,
> +                          VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +                          FIFO_READ_START_POINT_DP_ENC1_P0_VAL << 12,
> +                          FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
> +       mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368, DP_ENC1_P0_3368_VAL);
> +}
> +
> +static void mtk_dp_pg_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038,
> +                          enable ? VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK : 0,
> +                          VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
> +                          PGEN_PATTERN_SEL_VAL << 4, PGEN_PATTERN_SEL_MASK);
> +}
> +
> +static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640, DP_AUX_P0_3640_VAL);
> +}
> +
> +static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32 addr)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644,
> +                          cmd, MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648,
> +                          addr, MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C,
> +                          addr >> 16, MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_clear_fifo(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +                          MCU_ACK_TRAN_COMPLETE_AUX_TX_P0,
> +                          MCU_ACK_TRAN_COMPLETE_AUX_TX_P0 |
> +                          PHY_FIFO_RST_AUX_TX_P0_MASK |
> +                          MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
> +                          AUX_TX_REQUEST_READY_AUX_TX_P0,
> +                          AUX_TX_REQUEST_READY_AUX_TX_P0);
> +}
> +
> +static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> +                                      size_t length)
> +{
> +       mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf, length);
> +}
> +
> +static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> +                                   size_t length, int read_delay)
> +{
> +       int read_pos;
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> +                          0, AUX_RD_MODE_AUX_TX_P0_MASK);
> +
> +       for (read_pos = 0; read_pos < length; read_pos++) {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> +                                  AUX_RX_FIFO_READ_PULSE_TX_P0,
> +                                  AUX_RX_FIFO_READ_PULSE_TX_P0);
> +
> +               /* Hardware needs time to update the data */
> +               usleep_range(read_delay, read_delay * 2);
> +               buf[read_pos] = (u8)(mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3620) &
> +                                    AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK);
> +       }
> +}
> +
> +static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t length)
> +{
> +       if (length > 0) {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +                                  (length - 1) << 12,
> +                                  MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +                                  0,
> +                                  AUX_NO_LENGTH_AUX_TX_P0 |
> +                                  AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +                                  AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +       } else {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +                                  AUX_NO_LENGTH_AUX_TX_P0,
> +                                  AUX_NO_LENGTH_AUX_TX_P0 |
> +                                  AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +                                  AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +       }
> +}
> +
> +static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp, bool is_read)
> +{
> +       int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
> +
> +       while (--wait_reply) {
> +               u32 aux_irq_status;
> +
> +               if (is_read) {
> +                       u32 fifo_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3618);
> +
> +                       if (fifo_status &
> +                           (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
> +                            AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
> +                               return 0;
> +                       }
> +               }
> +
> +               aux_irq_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3640);
> +               if (aux_irq_status & AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
> +                       return 0;
> +
> +               if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0)
> +                       return -ETIMEDOUT;
> +
> +               /* Give the hardware a chance to reach completion before retrying */
> +               usleep_range(100, 500);
> +       }
> +
> +       return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool is_read, u8 cmd,
> +                                 u32 addr, u8 *buf, size_t length)
> +{
> +       int ret;
> +       u32 reply_cmd;
> +
> +       if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
> +                       (cmd == DP_AUX_NATIVE_READ && !length)))
> +               return -EINVAL;
> +
> +       if (!is_read)
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> +                                  AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0,
> +                                  AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0);
> +
> +       /* We need to clear fifo and irq before sending commands to the sink device. */
> +       mtk_dp_aux_clear_fifo(mtk_dp);
> +       mtk_dp_aux_irq_clear(mtk_dp);
> +
> +       mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
> +       mtk_dp_aux_set_length(mtk_dp, length);
> +
> +       if (!is_read) {
> +               if (length)
> +                       mtk_dp_aux_fill_write_fifo(mtk_dp, buf, length);
> +
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> +                                  AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK,
> +                                  AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK);
> +       }
> +
> +       mtk_dp_aux_request_ready(mtk_dp);
> +
> +       /* Wait for feedback from sink device. */
> +       ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
> +
> +       reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
> +                   AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
> +
> +       if (ret || reply_cmd) {
> +               u32 phy_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3628) &
> +                                AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
> +               if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
> +                       drm_err(mtk_dp->drm_dev,
> +                               "AUX Rx Aux hang, need SW reset\n");
> +                       return -EIO;
> +               }
> +
> +               return -ETIMEDOUT;
> +       }
> +
> +       if (!length) {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +                                  0,
> +                                  AUX_NO_LENGTH_AUX_TX_P0 |
> +                                  AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +                                  AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +       } else if (is_read) {
> +               int read_delay;
> +
> +               if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
> +                   cmd == DP_AUX_I2C_READ)
> +                       read_delay = 500;
> +               else
> +                       read_delay = 100;
> +
> +               mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length, read_delay);
> +       }
> +
> +       return 0;
> +}
> +
> +static void mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int lane_num,
> +                                         int swing_val, int preemphasis)
> +{
> +       u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
> +
> +       dev_dbg(mtk_dp->dev,
> +               "link training: swing_val = 0x%x, pre-emphasis = 0x%x\n",
> +               swing_val, preemphasis);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +                          swing_val << (DP_TX0_VOLT_SWING_SHIFT + lane_shift),
> +                          DP_TX0_VOLT_SWING_MASK << lane_shift);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +                          preemphasis << (DP_TX0_PRE_EMPH_SHIFT + lane_shift),
> +                          DP_TX0_PRE_EMPH_MASK << lane_shift);
> +}
> +
> +static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +                          0,
> +                          DP_TX0_VOLT_SWING_MASK |
> +                          DP_TX1_VOLT_SWING_MASK |
> +                          DP_TX2_VOLT_SWING_MASK |
> +                          DP_TX3_VOLT_SWING_MASK |
> +                          DP_TX0_PRE_EMPH_MASK |
> +                          DP_TX1_PRE_EMPH_MASK |
> +                          DP_TX2_PRE_EMPH_MASK |
> +                          DP_TX3_PRE_EMPH_MASK);
> +}
> +
> +static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +       u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
> +                        SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
> +
> +       if (irq_status) {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
> +                                  irq_status, SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
> +                                  0, SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +       }
> +
> +       return irq_status;
> +}
> +
> +static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +       u32 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
> +                         IRQ_STATUS_DP_TRANS_P0_MASK) >> 12;
> +
> +       if (irq_status) {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +                                  irq_status, IRQ_CLR_DP_TRANS_P0_MASK);
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +                                  0, IRQ_CLR_DP_TRANS_P0_MASK);
> +       }
> +
> +       return irq_status;
> +}
> +
> +static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +                          enable ? 0 :
> +                          IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
> +                          IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
> +                          IRQ_MASK_DP_TRANS_P0_INT_IRQ,
> +                          IRQ_MASK_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
> +                          XTAL_FREQ_DP_TRANS_P0_DEFAULT,
> +                          XTAL_FREQ_DP_TRANS_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> +                          FEC_CLOCK_EN_MODE_DP_TRANS_P0,
> +                          FEC_CLOCK_EN_MODE_DP_TRANS_P0);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> +                          AUDIO_CH_SRC_SEL_DP_ENC0_P0,
> +                          AUDIO_CH_SRC_SEL_DP_ENC0_P0);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
> +                          0, SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK,
> +                          IRQ_MASK_AUX_TOP_IRQ, IRQ_MASK_AUX_TOP_IRQ);
> +}
> +
> +static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp *mtk_dp)
> +{
> +       u32 val;
> +       /* Debounce threshold */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +                          8, HPD_DEB_THD_DP_TRANS_P0_MASK);
> +
> +       val = (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
> +              HPD_INT_THD_DP_TRANS_P0_UPPER_1100US) << 4;
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +                          val, HPD_INT_THD_DP_TRANS_P0_MASK);
> +
> +       /*
> +        * Connect threshold 1.5ms + 5 x 0.1ms = 2ms
> +        * Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
> +        */
> +       val = (5 << 8) | (5 << 12);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +                          val,
> +                          HPD_DISC_THD_DP_TRANS_P0_MASK |
> +                          HPD_CONN_THD_DP_TRANS_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
> +                          HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
> +                          HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
> +{
> +       /* modify timeout threshold = 0x1595 */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C,
> +                          AUX_TIMEOUT_THR_AUX_TX_P0_VAL,
> +                          AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658,
> +                          0, AUX_TX_OV_EN_AUX_TX_P0_MASK);
> +       /* 25 for 26M */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
> +                          AUX_TX_OVER_SAMPLE_RATE_FOR_26M << 8,
> +                          AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
> +       /* 13 for 26M */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
> +                          AUX_RX_UI_CNT_THR_AUX_FOR_26M,
> +                          AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
> +                          MTK_ATOP_EN_AUX_TX_P0,
> +                          MTK_ATOP_EN_AUX_TX_P0);
> +}
> +
> +static void mtk_dp_initialize_digital_settings(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
> +                          0, VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
> +                          BS2BS_MODE_DP_ENC1_P0_VAL << 12,
> +                          BS2BS_MODE_DP_ENC1_P0_MASK);
> +
> +       /* dp tx encoder reset all sw */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +                          DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0,
> +                          DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
> +
> +       /* Wait for sw reset to complete */
> +       usleep_range(1000, 5000);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +                          0, DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
> +}
> +
> +static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> +                          DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0,
> +                          DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
> +
> +       /* Wait for sw reset to complete */
> +       usleep_range(1000, 5000);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> +                          0, DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
> +}
> +
> +static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
> +                          lanes == 0 ? 0 : DP_TRANS_DUMMY_RW_0,
> +                          DP_TRANS_DUMMY_RW_0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +                          lanes, LANE_NUM_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
> +                          lanes << 2, LANE_NUM_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
> +{
> +       const struct mtk_dp_efuse_fmt *fmt;
> +       struct device *dev = mtk_dp->dev;
> +       struct nvmem_cell *cell;
> +       u32 *cal_data = mtk_dp->cal_data;
> +       u32 *buf;
> +       int i;
> +       size_t len;
> +
> +       cell = nvmem_cell_get(dev, "dp_calibration_data");
> +       if (IS_ERR(cell)) {
> +               dev_warn(dev, "Failed to get nvmem cell dp_calibration_data\n");
> +               goto use_default_val;
> +       }
> +
> +       buf = (u32 *)nvmem_cell_read(cell, &len);
> +       nvmem_cell_put(cell);
> +
> +       if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
> +               dev_warn(dev, "Failed to read nvmem_cell_read\n");
> +
> +               if (!IS_ERR(buf))
> +                       kfree(buf);
> +
> +               goto use_default_val;
> +       }
> +
> +       for (i = 0; i < MTK_DP_CAL_MAX; i++) {
> +               fmt = &mtk_dp_efuse_data[i];
> +               cal_data[i] = (buf[fmt->idx] >> fmt->shift) & fmt->mask;
> +
> +               if (cal_data[i] < fmt->min_val || cal_data[i] > fmt->max_val) {
> +                       dev_warn(mtk_dp->dev, "Invalid efuse data, idx = %d\n", i);
> +                       kfree(buf);
> +                       goto use_default_val;
> +               }
> +       }
> +       kfree(buf);
> +
> +       return;
> +
> +use_default_val:
> +       dev_warn(mtk_dp->dev, "Use default calibration data\n");
> +       for (i = 0; i < MTK_DP_CAL_MAX; i++)
> +               cal_data[i] = mtk_dp_efuse_data[i].default_val;
> +}
> +
> +static void mtk_dp_set_calibration_data(struct mtk_dp *mtk_dp)
> +{
> +       u32 *cal_data = mtk_dp->cal_data;
> +
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
> +                          cal_data[MTK_DP_CAL_CLKTX_IMPSE] << 20,
> +                          RG_CKM_PT0_CKTX_IMPSEL);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
> +                          cal_data[MTK_DP_CAL_GLB_BIAS_TRIM] << 16,
> +                          RG_XTP_GLB_BIAS_INTR_CTRL);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] << 12,
> +                          RG_XTP_LN0_TX_IMPSEL_PMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] << 16,
> +                          RG_XTP_LN0_TX_IMPSEL_NMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] << 12,
> +                          RG_XTP_LN1_TX_IMPSEL_PMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] << 16,
> +                          RG_XTP_LN1_TX_IMPSEL_NMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] << 12,
> +                          RG_XTP_LN2_TX_IMPSEL_PMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] << 16,
> +                          RG_XTP_LN2_TX_IMPSEL_NMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] << 12,
> +                          RG_XTP_LN3_TX_IMPSEL_PMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] << 16,
> +                          RG_XTP_LN3_TX_IMPSEL_NMOS);
> +}
> +
> +static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
> +                               u32 link_rate, int lane_count)
> +{
> +       int ret;
> +       union phy_configure_opts phy_opts = {
> +               .dp = {
> +                       .link_rate = drm_dp_bw_code_to_link_rate(link_rate) / 100,
> +                       .set_rate = 1,
> +                       .lanes = lane_count,
> +                       .set_lanes = 1,
> +                       .ssc = mtk_dp->train_info.sink_ssc,
> +               }
> +       };
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP,
> +                          DP_PWR_STATE_MASK);
> +
> +       ret = phy_configure(mtk_dp->phy, &phy_opts);
> +       if (ret)
> +               return ret;
> +
> +       mtk_dp_set_calibration_data(mtk_dp);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                          DP_PWR_STATE_BANDGAP_TPLL_LANE, DP_PWR_STATE_MASK);
> +
> +       return 0;
> +}
> +
> +static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
> +                 POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
> +                 POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
> +                 POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580,
> +                          enable ? val : 0, val);
> +}
> +
> +static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int pattern)
> +{
> +       /* TPS1 */
> +       if (pattern == 1)
> +               mtk_dp_set_idle_pattern(mtk_dp, false);
> +
> +       mtk_dp_update_bits(mtk_dp,
> +                          MTK_DP_TRANS_P0_3400,
> +                          pattern ? BIT(pattern - 1) << 12 : 0,
> +                          PATTERN1_EN_DP_TRANS_P0_MASK |
> +                          PATTERN2_EN_DP_TRANS_P0_MASK |
> +                          PATTERN3_EN_DP_TRANS_P0_MASK |
> +                          PATTERN4_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +                          ENHANCED_FRAME_EN_DP_ENC0_P0,
> +                          ENHANCED_FRAME_EN_DP_ENC0_P0);
> +}
> +
> +static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
> +                          enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
> +                          DP_SCR_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       struct arm_smccc_res res;
> +       u32 val = VIDEO_MUTE_SEL_DP_ENC0_P0 |
> +                 (enable ? VIDEO_MUTE_SW_DP_ENC0_P0 : 0);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +                          val,
> +                          VIDEO_MUTE_SEL_DP_ENC0_P0 |
> +                          VIDEO_MUTE_SW_DP_ENC0_P0);
> +
> +       arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32,
> +                     MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable,
> +                     0, 0, 0, 0, 0, &res);
> +
> +       dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
> +               MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable, res.a0, res.a1);
> +}
> +
> +static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
> +                          0, SW_RST_B_PHYD);
> +
> +       /* Wait for power enable */
> +       usleep_range(10, 200);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
> +                          SW_RST_B_PHYD, SW_RST_B_PHYD);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                          DP_PWR_STATE_BANDGAP_TPLL, DP_PWR_STATE_MASK);
> +       mtk_dp_write(mtk_dp, MTK_DP_1040,
> +                    RG_DPAUX_RX_VALID_DEGLITCH_EN | RG_XTP_GLB_CKDET_EN |
> +                    RG_DPAUX_RX_EN);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_0034, 0, DA_CKM_CKTX0_EN_FORCE_EN);
> +}
> +
> +static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_0034,
> +                          DA_CKM_CKTX0_EN_FORCE_EN, DA_CKM_CKTX0_EN_FORCE_EN);
> +
> +       /* Disable RX */
> +       mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
> +       mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
> +                    0x550 | FUSE_SEL | MEM_ISO_EN);
> +}
> +
> +static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp->train_info.link_rate = DP_LINK_BW_5_4;
> +       mtk_dp->train_info.lane_count = mtk_dp->max_lanes;
> +       mtk_dp->train_info.cable_plugged_in = false;
> +
> +       mtk_dp->info.format = DP_PIXELFORMAT_RGB;
> +       memset(&mtk_dp->info.vm, 0, sizeof(struct videomode));
> +}
> +
> +static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
> +{
> +       u32 sram_read_start = min_t(u32, MTK_DP_TBC_BUF_READ_START_ADDR,
> +                                   mtk_dp->info.vm.hactive /
> +                                   mtk_dp->train_info.lane_count /
> +                                   MTK_DP_4P1T / MTK_DP_HDE /
> +                                   MTK_DP_PIX_PER_ADDR);
> +       mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
> +       mtk_dp_setup_encoder(mtk_dp);
> +}
> +
> +static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_setup_tu(mtk_dp);
> +}
> +
> +static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes,
> +                                         u8 dpcd_adjust_req[2])
> +{
> +       int lane;
> +
> +       for (lane = 0; lane < lanes; ++lane) {
> +               u8 val;
> +               u8 swing;
> +               u8 preemphasis;
> +               int index = lane / 2;
> +               int shift = lane % 2 ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
> +
> +               swing = (dpcd_adjust_req[index] >> shift) &
> +                       DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
> +               preemphasis = ((dpcd_adjust_req[index] >> shift) &
> +                              DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
> +                             DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
> +               val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
> +                     preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +
> +               if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
> +                       val |= DP_TRAIN_MAX_SWING_REACHED;
> +               if (preemphasis == 3)
> +                       val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> +
> +               mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing, preemphasis);
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET + lane,
> +                                  val);
> +       }
> +}
> +
> +static void mtk_dp_pattern(struct mtk_dp *mtk_dp, bool is_tps1)
> +{
> +       int pattern;
> +       unsigned int aux_offset;
> +
> +       if (is_tps1) {
> +               pattern = 1;
> +               aux_offset = DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1;
> +       } else {
> +               aux_offset = mtk_dp->train_info.channel_eq_pattern;
> +
> +               switch (mtk_dp->train_info.channel_eq_pattern) {
> +               case DP_TRAINING_PATTERN_4:
> +                       pattern = 4;
> +                       break;
> +               case DP_TRAINING_PATTERN_3:
> +                       pattern = 3;
> +                       aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
> +                       break;
> +               case DP_TRAINING_PATTERN_2:
> +               default:
> +                       pattern = 2;
> +                       aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
> +                       break;
> +               }
> +       }
> +
> +       mtk_dp_train_set_pattern(mtk_dp, pattern);
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET, aux_offset);
> +}
> +
> +static int mtk_dp_train_setting(struct mtk_dp *mtk_dp, u8 target_link_rate,
> +                               u8 target_lane_count)
> +{
> +       int ret;
> +
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET, target_link_rate);
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> +                          target_lane_count | DP_LANE_COUNT_ENHANCED_FRAME_EN);
> +
> +       if (mtk_dp->train_info.sink_ssc)
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
> +                                  DP_SPREAD_AMP_0_5);
> +
> +       mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
> +       ret = mtk_dp_phy_configure(mtk_dp, target_link_rate, target_lane_count);
> +       if (ret)
> +               return ret;
> +
> +       dev_dbg(mtk_dp->dev,
> +               "Link train target_link_rate = 0x%x, target_lane_count = 0x%x\n",
> +               target_link_rate, target_lane_count);
> +
> +       return 0;
> +}
> +
> +static int mtk_dp_train_cr(struct mtk_dp *mtk_dp, u8 target_lane_count)
> +{
> +       u8 lane_adjust[2] = {};
> +       u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +       u8 prev_lane_adjust = 0xff;
> +       int train_retries = 0;
> +       int voltage_retries = 0;
> +
> +       mtk_dp_pattern(mtk_dp, true);
> +
> +       /* In DP spec 1.4, the retry count of CR is defined as 10. */
> +       do {
> +               train_retries++;
> +               if (!mtk_dp->train_info.cable_plugged_in) {
> +                       mtk_dp_train_set_pattern(mtk_dp, 0);
> +                       return -ENODEV;
> +               }
> +
> +               drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
> +                                lane_adjust, sizeof(lane_adjust));
> +               mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
> +                                             lane_adjust);
> +
> +               drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
> +                                                      mtk_dp->rx_cap);
> +
> +               /* check link status from sink device */
> +               drm_dp_dpcd_read_link_status(&mtk_dp->aux, link_status);
> +               if (drm_dp_clock_recovery_ok(link_status,
> +                                            target_lane_count)) {
> +                       dev_dbg(mtk_dp->dev, "Link train CR pass\n");
> +                       return 0;
> +               }
> +
> +               /*
> +                * In DP spec 1.4, if current voltage level is the same
> +                * with previous voltage level, we need to retry 5 times.
> +                */
> +               if (prev_lane_adjust == link_status[4]) {
> +                       voltage_retries++;
> +                       /*
> +                        * Condition of CR fail:
> +                        * 1. Failed to pass CR using the same voltage
> +                        *    level over five times.
> +                        * 2. Failed to pass CR when the current voltage
> +                        *    level is the same with previous voltage
> +                        *    level and reach max voltage level (3).
> +                        */
> +                       if (voltage_retries > MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY ||
> +                           (prev_lane_adjust & DP_ADJUST_VOLTAGE_SWING_LANE0_MASK) == 3) {
> +                               dev_dbg(mtk_dp->dev, "Link train CR fail\n");
> +                               break;
> +                       }
> +               } else {
> +                       /*
> +                        * If the voltage level is changed, we need to
> +                        * re-calculate this retry count.
> +                        */
> +                       voltage_retries = 0;
> +               }
> +               prev_lane_adjust = link_status[4];
> +       } while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
> +
> +       /* Failed to train CR, and disable pattern. */
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +                          DP_TRAINING_PATTERN_DISABLE);
> +       mtk_dp_train_set_pattern(mtk_dp, 0);
> +
> +       return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_train_eq(struct mtk_dp *mtk_dp, u8 target_lane_count)
> +{
> +       u8 lane_adjust[2] = {};
> +       u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +       int train_retries = 0;
> +
> +       mtk_dp_pattern(mtk_dp, false);
> +
> +       do {
> +               train_retries++;
> +               if (!mtk_dp->train_info.cable_plugged_in) {
> +                       mtk_dp_train_set_pattern(mtk_dp, 0);
> +                       return -ENODEV;
> +               }
> +
> +               drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
> +                                lane_adjust, sizeof(lane_adjust));
> +               mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
> +                                             lane_adjust);
> +
> +               drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
> +                                                  mtk_dp->rx_cap);
> +
> +               /* check link status from sink device */
> +               drm_dp_dpcd_read_link_status(&mtk_dp->aux, link_status);
> +               if (drm_dp_channel_eq_ok(link_status, target_lane_count)) {
> +                       dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
> +
> +                       /* Training done, and disable pattern. */
> +                       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +                                          DP_TRAINING_PATTERN_DISABLE);
> +                       mtk_dp_train_set_pattern(mtk_dp, 0);
> +                       return 0;
> +               }
> +               dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
> +       } while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
> +
> +       /* Failed to train EQ, and disable pattern. */
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +                          DP_TRAINING_PATTERN_DISABLE);
> +       mtk_dp_train_set_pattern(mtk_dp, 0);
> +
> +       return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
> +{
> +       u8 val;
> +       ssize_t ret;
> +
> +       drm_dp_read_dpcd_caps(&mtk_dp->aux, mtk_dp->rx_cap);
> +
> +       if (drm_dp_tps4_supported(mtk_dp->rx_cap))
> +               mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_4;
> +       else if (drm_dp_tps3_supported(mtk_dp->rx_cap))
> +               mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_3;
> +       else
> +               mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_2;
> +
> +       mtk_dp->train_info.sink_ssc = drm_dp_max_downspread(mtk_dp->rx_cap);
> +
> +       ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
> +       if (ret < 1) {
> +               drm_err(mtk_dp->drm_dev, "Read mstm cap failed\n");
> +               return ret == 0 ? -EIO : ret;
> +       }
> +
> +       if (val & DP_MST_CAP) {
> +               /* Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 */
> +               ret = drm_dp_dpcd_readb(&mtk_dp->aux,
> +                                       DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
> +                                       &val);
> +               if (ret < 1) {
> +                       drm_err(mtk_dp->drm_dev, "Read irq vector failed\n");
> +                       return ret == 0 ? -EIO : ret;
> +               }
> +
> +               if (val)
> +                       drm_dp_dpcd_writeb(&mtk_dp->aux,
> +                                          DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
> +                                          val);
> +       }
> +
> +       return 0;
> +}
> +
> +static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
> +{
> +       phy_reset(mtk_dp->phy);
> +       mtk_dp_reset_swing_pre_emphasis(mtk_dp);
> +}
> +
> +static int mtk_dp_training(struct mtk_dp *mtk_dp)
> +{
> +       int ret;
> +       u8 lane_count, link_rate, train_limit, max_link_rate;
> +
> +       link_rate = min_t(u8, mtk_dp->max_linkrate,
> +                         mtk_dp->rx_cap[DP_MAX_LINK_RATE]);
> +       max_link_rate = link_rate;
> +       lane_count = min_t(u8, mtk_dp->max_lanes,
> +                          drm_dp_max_lane_count(mtk_dp->rx_cap));
> +
> +       /*
> +        * TPS are generated by the hardware pattern generator. From the
> +        * hardware setting we need to disable this scramble setting before
> +        * use the TPS pattern generator.
> +        */
> +       mtk_dp_training_set_scramble(mtk_dp, false);
> +
> +       for (train_limit = 6; train_limit > 0; train_limit--) {
> +               mtk_dp_train_change_mode(mtk_dp);
> +
> +               ret = mtk_dp_train_setting(mtk_dp, link_rate, lane_count);
> +               if (ret)
> +                       return ret;
> +
> +               ret = mtk_dp_train_cr(mtk_dp, lane_count);
> +               if (ret == -ENODEV) {
> +                       return ret;
> +               } else if (ret) {
> +                       /* reduce link rate */
> +                       switch (link_rate) {
> +                       case DP_LINK_BW_1_62:
> +                               lane_count = lane_count / 2;
> +                               link_rate = max_link_rate;
> +                               if (lane_count == 0)
> +                                       return -EIO;
> +                               break;
> +                       case DP_LINK_BW_2_7:
> +                               link_rate = DP_LINK_BW_1_62;
> +                               break;
> +                       case DP_LINK_BW_5_4:
> +                               link_rate = DP_LINK_BW_2_7;
> +                               break;
> +                       case DP_LINK_BW_8_1:
> +                               link_rate = DP_LINK_BW_5_4;
> +                               break;
> +                       default:
> +                               return -EINVAL;
> +                       };
> +                       continue;
> +               }
> +
> +               ret = mtk_dp_train_eq(mtk_dp, lane_count);
> +               if (ret == -ENODEV) {
> +                       return ret;
> +               } else if (ret) {
> +                       /* reduce lane count */
> +                       if (lane_count == 0)
> +                               return -EIO;
> +                       lane_count /= 2;
> +                       continue;
> +               }
> +
> +               /* if we can run to this, training is done. */
> +               break;
> +       }
> +
> +       if (train_limit == 0)
> +               return -ETIMEDOUT;
> +
> +       mtk_dp->train_info.link_rate = link_rate;
> +       mtk_dp->train_info.lane_count = lane_count;
> +
> +       /*
> +        * After training done, we need to output normal stream instead of TPS,
> +        * so we need to enable scramble.
> +        */
> +       mtk_dp_training_set_scramble(mtk_dp, true);
> +       mtk_dp_set_enhanced_frame_mode(mtk_dp);
> +
> +       return 0;
> +}
> +
> +static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       /* the mute sequence is different between enable and disable */
> +       if (enable) {
> +               mtk_dp_msa_bypass_enable(mtk_dp, false);
> +               mtk_dp_pg_enable(mtk_dp, false);
> +               mtk_dp_set_tx_out(mtk_dp);
> +               mtk_dp_video_mute(mtk_dp, false);
> +       } else {
> +               mtk_dp_video_mute(mtk_dp, true);
> +               mtk_dp_pg_enable(mtk_dp, true);
> +               mtk_dp_msa_bypass_enable(mtk_dp, true);
> +       }
> +}
> +
> +static int mtk_dp_video_config(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_config_mn_mode(mtk_dp);
> +       mtk_dp_set_msa(mtk_dp);
> +       mtk_dp_set_color_depth(mtk_dp);
> +       return mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
> +}
> +
> +static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_set_idle_pattern(mtk_dp, true);
> +       mtk_dp_initialize_priv_data(mtk_dp);
> +
> +       mtk_dp_initialize_settings(mtk_dp);
> +       mtk_dp_initialize_aux_settings(mtk_dp);
> +       mtk_dp_initialize_digital_settings(mtk_dp);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
> +                          RX_REPLY_COMPLETE_MODE_AUX_TX_P0,
> +                          RX_REPLY_COMPLETE_MODE_AUX_TX_P0);
> +       mtk_dp_initialize_hpd_detect_settings(mtk_dp);
> +
> +       mtk_dp_digital_sw_reset(mtk_dp);
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
> +{
> +       struct mtk_dp *mtk_dp = dev;
> +       unsigned long flags;
> +       u32 status;
> +
> +       spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
> +       status = mtk_dp->irq_thread_handle;
> +       mtk_dp->irq_thread_handle = 0;
> +       spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
> +
> +       if (status & MTK_DP_THREAD_CABLE_STATE_CHG)
> +               drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
> +
> +       if (status & MTK_DP_THREAD_HPD_EVENT)
> +               dev_dbg(mtk_dp->dev, "Receive IRQ from sink devices\n");
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
> +{
> +       struct mtk_dp *mtk_dp = dev;
> +       bool cable_sta_chg = false;
> +       unsigned long flags;
> +       u32 irq_status = mtk_dp_swirq_get_clear(mtk_dp) |
> +                        mtk_dp_hwirq_get_clear(mtk_dp);
> +
> +       if (!irq_status)
> +               return IRQ_HANDLED;
> +
> +       spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
> +
> +       if (irq_status & MTK_DP_HPD_INTERRUPT)
> +               mtk_dp->irq_thread_handle |= MTK_DP_THREAD_HPD_EVENT;
> +
> +       /* Cable state is changed. */
> +       if (irq_status != MTK_DP_HPD_INTERRUPT) {
> +               mtk_dp->irq_thread_handle |= MTK_DP_THREAD_CABLE_STATE_CHG;
> +               cable_sta_chg = true;
> +       }
> +
> +       spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
> +
> +       if (cable_sta_chg) {
> +               if (!!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
> +                      HPD_DB_DP_TRANS_P0_MASK))
> +                       mtk_dp->train_info.cable_plugged_in = true;
> +               else
> +                       mtk_dp->train_info.cable_plugged_in = false;
> +       }
> +
> +       return IRQ_WAKE_THREAD;
> +}
> +
> +static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
> +                          struct platform_device *pdev)
> +{
> +       struct device_node *endpoint;
> +       struct device *dev = &pdev->dev;
> +       int ret;
> +       void __iomem *base;
> +       u32 linkrate;
> +       int len;
> +
> +       base = devm_platform_ioremap_resource(pdev, 0);
> +       if (IS_ERR(base))
> +               return PTR_ERR(base);
> +
> +       mtk_dp->regs = devm_regmap_init_mmio(dev, base, &mtk_dp_regmap_config);
> +       if (IS_ERR(mtk_dp->regs))
> +               return PTR_ERR(mtk_dp->regs);
> +
> +       endpoint = of_graph_get_endpoint_by_regs(pdev->dev.of_node, 1, -1);
> +       len = of_property_count_elems_of_size(endpoint,
> +                                             "data-lanes", sizeof(u32));
> +       if (len < 0 || len > 4 || len == 3) {
> +               dev_err(dev, "invalid data lane size: %d\n", len);
> +               return -EINVAL;
> +       }
> +
> +       mtk_dp->max_lanes = len;
> +
> +       ret = device_property_read_u32(dev, "max-linkrate-mhz", &linkrate);
> +       if (ret) {
> +               dev_err(dev, "failed to read max linkrate: %d\n", ret);
> +               return ret;
> +       }
> +
> +       mtk_dp->max_linkrate = drm_dp_link_rate_to_bw_code(linkrate * 100);
> +
> +       return 0;
> +}
> +
> +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> +                                   struct drm_connector *connector)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       bool enabled = mtk_dp->enabled;
> +       struct edid *new_edid = NULL;
> +
> +       if (!enabled) {
> +               drm_bridge_chain_pre_enable(bridge);
> +
> +               /* power on aux */
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                                  DP_PWR_STATE_BANDGAP_TPLL_LANE,
> +                                  DP_PWR_STATE_MASK);
> +
> +               /* power on panel */
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> +               usleep_range(2000, 5000);
> +       }
> +
> +       new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> +
> +       /*
> +        * Parse capability here to let atomic_get_input_bus_fmts and
> +        * mode_valid use the capability to calculate sink bitrates.
> +        */
> +       if (mtk_dp_parse_capabilities(mtk_dp)) {
> +               drm_err(mtk_dp->drm_dev, "Can't parse capabilities\n");
> +               new_edid = NULL;
> +       }
> +
> +       if (!enabled) {
> +               /* power off panel */
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
> +               usleep_range(2000, 3000);
> +
> +               /* power off aux */
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                                  DP_PWR_STATE_BANDGAP_TPLL,
> +                                  DP_PWR_STATE_MASK);
> +
> +               drm_bridge_chain_post_disable(bridge);
> +       }
> +
> +       return new_edid;
> +}
> +
> +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> +                                  struct drm_dp_aux_msg *msg)
> +{
> +       struct mtk_dp *mtk_dp;
> +       bool is_read;
> +       u8 request;
> +       size_t accessed_bytes = 0;
> +       int ret;
> +
> +       mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> +
> +       if (!mtk_dp->train_info.cable_plugged_in) {
> +               ret = -EAGAIN;
> +               goto err;
> +       }
> +
> +       switch (msg->request) {
> +       case DP_AUX_I2C_MOT:
> +       case DP_AUX_I2C_WRITE:
> +       case DP_AUX_NATIVE_WRITE:
> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> +               request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> +               is_read = false;
> +               break;
> +       case DP_AUX_I2C_READ:
> +       case DP_AUX_NATIVE_READ:
> +       case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> +               request = msg->request;
> +               is_read = true;
> +               break;
> +       default:
> +               drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> +                       msg->request);
> +               ret = -EINVAL;
> +               goto err;
> +       }
> +
> +       do {
> +               size_t to_access = min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> +                                        msg->size - accessed_bytes);
> +
> +               ret = mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> +                                            msg->address + accessed_bytes,
> +                                            msg->buffer + accessed_bytes,
> +                                            to_access);
> +
> +               if (ret) {
> +                       drm_info(mtk_dp->drm_dev,
> +                                "Failed to do AUX transfer: %d\n", ret);
> +                       goto err;
> +               }
> +               accessed_bytes += to_access;
> +       } while (accessed_bytes < msg->size);
> +
> +       msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +       return msg->size;
> +err:
> +       msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> +       return ret;
> +}
> +
> +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> +{
> +       int ret;
> +
> +       ret = phy_init(mtk_dp->phy);
> +       if (ret) {
> +               dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> +               return ret;
> +       }
> +
> +       mtk_dp_init_port(mtk_dp);
> +       mtk_dp_power_enable(mtk_dp);
> +
> +       return 0;
> +}
> +
> +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_power_disable(mtk_dp);
> +       phy_exit(mtk_dp->phy);
> +}
> +
> +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> +                               enum drm_bridge_attach_flags flags)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       int ret;
> +
> +       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> +               dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> +               return -EINVAL;
> +       }
> +
> +       mtk_dp->aux.drm_dev = bridge->dev;
> +       ret = drm_dp_aux_register(&mtk_dp->aux);
> +       if (ret) {
> +               dev_err(mtk_dp->dev,
> +                       "failed to register DP AUX channel: %d\n", ret);
> +               return ret;
> +       }
> +
> +       ret = mtk_dp_poweron(mtk_dp);
> +       if (ret)
> +               goto err_aux_register;
> +
> +       if (mtk_dp->next_bridge) {
> +               ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> +                                       &mtk_dp->bridge, flags);
> +               if (ret) {
> +                       drm_warn(mtk_dp->drm_dev,
> +                                "Failed to attach external bridge: %d\n", ret);
> +                       goto err_bridge_attach;
> +               }
> +       }
> +
> +       mtk_dp->drm_dev = bridge->dev;
> +
> +       mtk_dp_hwirq_enable(mtk_dp, true);
> +
> +       return 0;
> +
> +err_bridge_attach:
> +       mtk_dp_poweroff(mtk_dp);
> +err_aux_register:
> +       drm_dp_aux_unregister(&mtk_dp->aux);
> +       return ret;
> +}
> +
> +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +       mtk_dp_hwirq_enable(mtk_dp, false);
> +       mtk_dp->drm_dev = NULL;
> +       mtk_dp_poweroff(mtk_dp);
> +       drm_dp_aux_unregister(&mtk_dp->aux);
> +}
> +
> +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> +                                       struct drm_bridge_state *old_state)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       int ret;
> +
> +       mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> +                                                               bridge->encoder);
> +       if (!mtk_dp->conn) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector is missing\n");
> +               return;
> +       }
> +
> +       /* power on aux */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                          DP_PWR_STATE_BANDGAP_TPLL_LANE,
> +                          DP_PWR_STATE_MASK);
> +
> +       if (mtk_dp->train_info.cable_plugged_in) {
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> +               usleep_range(2000, 5000);
> +       }
> +
> +       /* Training */
> +       ret = mtk_dp_training(mtk_dp);
> +       if (ret) {
> +               drm_err(mtk_dp->drm_dev, "Training failed, %d\n", ret);
> +               goto power_off_aux;
> +       }
> +
> +       ret = mtk_dp_video_config(mtk_dp);
> +       if (ret)
> +               goto power_off_aux;
> +
> +       mtk_dp_video_enable(mtk_dp, true);
> +
> +       mtk_dp->enabled = true;
> +
> +       return;
> +power_off_aux:
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                          DP_PWR_STATE_BANDGAP_TPLL,
> +                          DP_PWR_STATE_MASK);
> +}
> +
> +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> +                                        struct drm_bridge_state *old_state)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +       mtk_dp->enabled = false;
> +       mtk_dp_video_enable(mtk_dp, false);
> +
> +       if (mtk_dp->train_info.cable_plugged_in) {
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
> +               usleep_range(2000, 3000);
> +       }
> +
> +       /* power off aux */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                          DP_PWR_STATE_BANDGAP_TPLL,
> +                          DP_PWR_STATE_MASK);
> +
> +       /* Ensure the sink is muted */
> +       msleep(20);
> +}
> +
> +static enum drm_mode_status
> +mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
> +                        const struct drm_display_info *info,
> +                        const struct drm_display_mode *mode)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       u32 bpp = info->color_formats & DRM_COLOR_FORMAT_YCBCR422 ? 16 : 24;
> +       u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
> +                             drm_dp_max_lane_count(mtk_dp->rx_cap),
> +                        drm_dp_bw_code_to_link_rate(mtk_dp->max_linkrate) *
> +                        mtk_dp->max_lanes);
> +
> +       if (rate < mode->clock * bpp / 8)
> +               return MODE_CLOCK_HIGH;
> +
> +       return MODE_OK;
> +}
> +
> +static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
> +                                                    struct drm_bridge_state *bridge_state,
> +                                                    struct drm_crtc_state *crtc_state,
> +                                                    struct drm_connector_state *conn_state,
> +                                                    unsigned int *num_output_fmts)
> +{
> +       u32 *output_fmts;
> +
> +       *num_output_fmts = 0;
> +       output_fmts = kmalloc(sizeof(*output_fmts), GFP_KERNEL);
> +       if (!output_fmts)
> +               return NULL;
> +       *num_output_fmts = 1;
> +       output_fmts[0] = MEDIA_BUS_FMT_FIXED;
> +       return output_fmts;
> +}
> +
> +static const u32 mt8195_input_fmts[] = {
> +       MEDIA_BUS_FMT_RGB888_1X24,
> +       MEDIA_BUS_FMT_YUV8_1X24,
> +       MEDIA_BUS_FMT_YUYV8_1X16,
> +};
> +
> +static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> +                                                   struct drm_bridge_state *bridge_state,
> +                                                   struct drm_crtc_state *crtc_state,
> +                                                   struct drm_connector_state *conn_state,
> +                                                   u32 output_fmt,
> +                                                   unsigned int *num_input_fmts)
> +{
> +       u32 *input_fmts;
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> +       struct drm_display_info *display_info =
> +               &conn_state->connector->display_info;
> +       u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
> +                             drm_dp_max_lane_count(mtk_dp->rx_cap),
> +                        drm_dp_bw_code_to_link_rate(mtk_dp->max_linkrate) *
> +                        mtk_dp->max_lanes);
> +
> +       *num_input_fmts = 0;
> +
> +       /*
> +        * If the linkrate is smaller than datarate of RGB888, larger than
> +        * datarate of YUV422 and sink device supports YUV422, we output YUV422
> +        * format. Use this condition, we can support more resolution.
> +        */
> +       if ((rate < (mode->clock * 24 / 8)) &&
> +           (rate > (mode->clock * 16 / 8)) &&
> +           (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) {
> +               input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL);
> +               if (!input_fmts)
> +                       return NULL;
> +               *num_input_fmts = 1;
> +               input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
> +       } else {
> +               input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts),
> +                                    sizeof(*input_fmts),
> +                                    GFP_KERNEL);
> +               if (!input_fmts)
> +                       return NULL;
> +
> +               *num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
> +               memcpy(input_fmts, mt8195_input_fmts, sizeof(mt8195_input_fmts));
> +       }
> +
> +       return input_fmts;
> +}
> +
> +static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
> +                                     struct drm_bridge_state *bridge_state,
> +                                     struct drm_crtc_state *crtc_state,
> +                                     struct drm_connector_state *conn_state)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       struct drm_crtc *crtc = conn_state->crtc;
> +       unsigned int input_bus_format;
> +
> +       input_bus_format = bridge_state->input_bus_cfg.format;
> +
> +       dev_dbg(mtk_dp->dev, "input format 0x%04x, output format 0x%04x\n",
> +               bridge_state->input_bus_cfg.format,
> +                bridge_state->output_bus_cfg.format);
> +
> +       if (input_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> +               mtk_dp->info.format = DP_PIXELFORMAT_YUV422;
> +       else
> +               mtk_dp->info.format = DP_PIXELFORMAT_RGB;
> +
> +       if (!crtc) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector state doesn't have a crtc\n");
> +               return -EINVAL;
> +       }
> +
> +       drm_display_mode_to_videomode(&crtc_state->adjusted_mode, &mtk_dp->info.vm);
> +
> +       return 0;
> +}
> +
> +static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
> +       .atomic_check = mtk_dp_bridge_atomic_check,
> +       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> +       .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> +       .atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts,
> +       .atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts,
> +       .atomic_reset = drm_atomic_helper_bridge_reset,
> +       .attach = mtk_dp_bridge_attach,
> +       .detach = mtk_dp_bridge_detach,
> +       .atomic_enable = mtk_dp_bridge_atomic_enable,
> +       .atomic_disable = mtk_dp_bridge_atomic_disable,
> +       .mode_valid = mtk_dp_bridge_mode_valid,
> +       .get_edid = mtk_dp_get_edid,
> +};
> +
> +static int mtk_dp_probe(struct platform_device *pdev)
> +{
> +       struct mtk_dp *mtk_dp;
> +       struct device *dev = &pdev->dev;
> +       int ret, irq_num;
> +
> +       mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> +       if (!mtk_dp)
> +               return -ENOMEM;
> +
> +       mtk_dp->dev = dev;
> +
> +       irq_num = platform_get_irq(pdev, 0);
> +       if (irq_num < 0)
> +               return dev_err_probe(dev, irq_num,
> +                                    "failed to request dp irq resource\n");
> +
> +       mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
> +       if (IS_ERR(mtk_dp->next_bridge))
> +               return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge),
> +                                    "Failed to get bridge\n");
> +
> +       ret = mtk_dp_dt_parse(mtk_dp, pdev);
> +       if (ret)
> +               return dev_err_probe(dev, ret, "Failed to parse dt\n");
> +
> +       drm_dp_aux_init(&mtk_dp->aux);
> +       mtk_dp->aux.name = "aux_mtk_dp";
> +       mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> +
> +       spin_lock_init(&mtk_dp->irq_thread_lock);
> +
> +       ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
> +                                       mtk_dp_hpd_event_thread,
> +                                       IRQ_TYPE_LEVEL_HIGH, dev_name(dev),
> +                                       mtk_dp);
> +       if (ret)
> +               return dev_err_probe(dev, ret,
> +                                    "failed to request mediatek dptx irq\n");
> +
> +       platform_set_drvdata(pdev, mtk_dp);
> +
> +       mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
> +                                                       PLATFORM_DEVID_AUTO,
> +                                                       &mtk_dp->regs,
> +                                                       sizeof(struct regmap *));
> +       if (IS_ERR(mtk_dp->phy_dev))
> +               return dev_err_probe(dev, PTR_ERR(mtk_dp->phy_dev),
> +                                    "Failed to create device mediatek-dp-phy\n");
> +
> +       mtk_dp_get_calibration_data(mtk_dp);
> +
> +       mtk_dp->phy = devm_phy_get(&mtk_dp->phy_dev->dev, "dp");
> +
> +       if (IS_ERR(mtk_dp->phy)) {
> +               platform_device_unregister(mtk_dp->phy_dev);
> +               return dev_err_probe(dev, PTR_ERR(mtk_dp->phy),
> +                                    "Failed to get phy\n");
> +       }
> +
> +       mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
> +       mtk_dp->bridge.of_node = dev->of_node;
> +
> +       mtk_dp->bridge.ops =
> +               DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
> +       mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> +
> +       drm_bridge_add(&mtk_dp->bridge);
> +
> +       pm_runtime_enable(dev);
> +       pm_runtime_get_sync(dev);
> +
> +       return 0;
> +}
> +
> +static int mtk_dp_remove(struct platform_device *pdev)
> +{
> +       struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
> +
> +       pm_runtime_put(&pdev->dev);
> +       pm_runtime_disable(&pdev->dev);
> +       drm_bridge_remove(&mtk_dp->bridge);
> +       platform_device_unregister(mtk_dp->phy_dev);
> +
> +       return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int mtk_dp_suspend(struct device *dev)
> +{
> +       struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +       mtk_dp_power_disable(mtk_dp);
> +       mtk_dp_hwirq_enable(mtk_dp, false);
> +       pm_runtime_put_sync(dev);
> +
> +       return 0;
> +}
> +
> +static int mtk_dp_resume(struct device *dev)
> +{
> +       struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +       pm_runtime_get_sync(dev);
> +       mtk_dp_init_port(mtk_dp);
> +       mtk_dp_hwirq_enable(mtk_dp, true);
> +       mtk_dp_power_enable(mtk_dp);
> +
> +       return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
> +
> +static const struct of_device_id mtk_dp_of_match[] = {
> +       { .compatible = "mediatek,mt8195-edp-tx" },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
> +
> +struct platform_driver mtk_dp_driver = {
> +       .probe = mtk_dp_probe,
> +       .remove = mtk_dp_remove,
> +       .driver = {
> +               .name = "mediatek-drm-dp",
> +               .of_match_table = mtk_dp_of_match,
> +               .pm = &mtk_dp_pm_ops,
> +       },
> +};
> +
> +module_platform_driver(mtk_dp_driver);
> +
> +MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
> +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> +MODULE_AUTHOR("Bo-Chen Chen <rex-bc.chen@mediatek.com>");
> +MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> new file mode 100644
> index 000000000000..3f01ba44871f
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> @@ -0,0 +1,305 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019-2022 MediaTek Inc.
> + * Copyright (c) 2022 BayLibre
> + */
> +#ifndef _MTK_DP_REG_H_
> +#define _MTK_DP_REG_H_
> +
> +#define SEC_OFFSET     0x4000
> +
> +#define MTK_DP_HPD_DISCONNECT          BIT(1)
> +#define MTK_DP_HPD_CONNECT             BIT(2)
> +#define MTK_DP_HPD_INTERRUPT           BIT(3)
> +
> +/* offset: 0x0 */
> +#define DP_PHY_GLB_BIAS_GEN_00         0x0
> +#define RG_XTP_GLB_BIAS_INTR_CTRL              GENMASK(20, 16)
> +#define DP_PHY_GLB_DPAUX_TX            0x8
> +#define RG_CKM_PT0_CKTX_IMPSEL                 GENMASK(23, 20)
> +#define MTK_DP_0034                    0x34
> +#define DA_XTP_GLB_CKDET_EN_FORCE_VAL          BIT(15)
> +#define DA_XTP_GLB_CKDET_EN_FORCE_EN           BIT(14)
> +#define DA_CKM_INTCKTX_EN_FORCE_VAL            BIT(13)
> +#define DA_CKM_INTCKTX_EN_FORCE_EN             BIT(12)
> +#define DA_CKM_CKTX0_EN_FORCE_VAL              BIT(11)
> +#define DA_CKM_CKTX0_EN_FORCE_EN               BIT(10)
> +#define DA_CKM_XTAL_CK_FORCE_VAL               BIT(9)
> +#define DA_CKM_XTAL_CK_FORCE_EN                        BIT(8)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_VAL           BIT(7)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_EN            BIT(6)
> +#define DA_CKM_BIAS_EN_FORCE_VAL               BIT(5)
> +#define DA_CKM_BIAS_EN_FORCE_EN                        BIT(4)
> +#define DA_XTP_GLB_AVD10_ON_FORCE_VAL          BIT(3)
> +#define DA_XTP_GLB_AVD10_ON_FORCE              BIT(2)
> +#define DA_XTP_GLB_LDO_EN_FORCE_VAL            BIT(1)
> +#define DA_XTP_GLB_LDO_EN_FORCE_EN             BIT(0)
> +#define DP_PHY_LANE_TX_0               0x104
> +#define RG_XTP_LN0_TX_IMPSEL_PMOS              GENMASK(15, 12)
> +#define RG_XTP_LN0_TX_IMPSEL_NMOS              GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_1               0x204
> +#define RG_XTP_LN1_TX_IMPSEL_PMOS              GENMASK(15, 12)
> +#define RG_XTP_LN1_TX_IMPSEL_NMOS              GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_2               0x304
> +#define RG_XTP_LN2_TX_IMPSEL_PMOS              GENMASK(15, 12)
> +#define RG_XTP_LN2_TX_IMPSEL_NMOS              GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_3               0x404
> +#define RG_XTP_LN3_TX_IMPSEL_PMOS              GENMASK(15, 12)
> +#define RG_XTP_LN3_TX_IMPSEL_NMOS              GENMASK(19, 16)
> +#define MTK_DP_1040                    0x1040
> +#define RG_DPAUX_RX_VALID_DEGLITCH_EN          BIT(2)
> +#define RG_XTP_GLB_CKDET_EN                    BIT(1)
> +#define RG_DPAUX_RX_EN                         BIT(0)
> +
> +/* offset: TOP_OFFSET (0x2000) */
> +#define MTK_DP_TOP_PWR_STATE           0x2000
> +#define DP_PWR_STATE_MASK                      GENMASK(1, 0)
> +#define DP_PWR_STATE_BANDGAP                   BIT(0)
> +#define DP_PWR_STATE_BANDGAP_TPLL              BIT(1)
> +#define DP_PWR_STATE_BANDGAP_TPLL_LANE         GENMASK(1, 0)
> +#define MTK_DP_TOP_SWING_EMP           0x2004
> +#define DP_TX0_VOLT_SWING_MASK                 GENMASK(1, 0)
> +#define DP_TX0_VOLT_SWING_SHIFT                        0
> +#define DP_TX0_PRE_EMPH_MASK                   GENMASK(3, 2)
> +#define DP_TX0_PRE_EMPH_SHIFT                  2
> +#define DP_TX1_VOLT_SWING_MASK                 GENMASK(9, 8)
> +#define DP_TX1_VOLT_SWING_SHIFT                        8
> +#define DP_TX1_PRE_EMPH_MASK                   GENMASK(11, 10)
> +#define DP_TX2_VOLT_SWING_MASK                 GENMASK(17, 16)
> +#define DP_TX2_PRE_EMPH_MASK                   GENMASK(19, 18)
> +#define DP_TX3_VOLT_SWING_MASK                 GENMASK(25, 24)
> +#define DP_TX3_PRE_EMPH_MASK                   GENMASK(27, 26)
> +#define MTK_DP_TOP_RESET_AND_PROBE     0x2020
> +#define SW_RST_B_PHYD                          BIT(4)
> +#define MTK_DP_TOP_IRQ_MASK            0x202c
> +#define IRQ_MASK_AUX_TOP_IRQ                   BIT(2)
> +#define MTK_DP_TOP_MEM_PD              0x2038
> +#define MEM_ISO_EN                             BIT(0)
> +#define FUSE_SEL                               BIT(2)
> +
> +/* offset: ENC0_OFFSET (0x3000) */
> +#define MTK_DP_ENC0_P0_3000                    0x3000
> +#define LANE_NUM_DP_ENC0_P0_MASK                       GENMASK(1, 0)
> +#define VIDEO_MUTE_SW_DP_ENC0_P0                       BIT(2)
> +#define VIDEO_MUTE_SEL_DP_ENC0_P0                      BIT(3)
> +#define ENHANCED_FRAME_EN_DP_ENC0_P0                   BIT(4)
> +#define MTK_DP_ENC0_P0_3004                    0x3004
> +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK               BIT(8)
> +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0           BIT(9)
> +#define MTK_DP_ENC0_P0_3010                    0x3010
> +#define HTOTAL_SW_DP_ENC0_P0_MASK                      GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3014                    0x3014
> +#define VTOTAL_SW_DP_ENC0_P0_MASK                      GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3018                    0x3018
> +#define HSTART_SW_DP_ENC0_P0_MASK                      GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_301C                    0x301c
> +#define VSTART_SW_DP_ENC0_P0_MASK                      GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3020                    0x3020
> +#define HWIDTH_SW_DP_ENC0_P0_MASK                      GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3024                    0x3024
> +#define VHEIGHT_SW_DP_ENC0_P0_MASK                     GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3028                    0x3028
> +#define HSW_SW_DP_ENC0_P0_MASK                         GENMASK(14, 0)
> +#define HSP_SW_DP_ENC0_P0_MASK                         BIT(15)
> +#define MTK_DP_ENC0_P0_302C                    0x302c
> +#define VSW_SW_DP_ENC0_P0_MASK                         GENMASK(14, 0)
> +#define VSP_SW_DP_ENC0_P0_MASK                         BIT(15)
> +#define MTK_DP_ENC0_P0_3030                    0x3030
> +#define HTOTAL_SEL_DP_ENC0_P0                          BIT(0)
> +#define VTOTAL_SEL_DP_ENC0_P0                          BIT(1)
> +#define HSTART_SEL_DP_ENC0_P0                          BIT(2)
> +#define VSTART_SEL_DP_ENC0_P0                          BIT(3)
> +#define HWIDTH_SEL_DP_ENC0_P0                          BIT(4)
> +#define VHEIGHT_SEL_DP_ENC0_P0                         BIT(5)
> +#define HSP_SEL_DP_ENC0_P0                             BIT(6)
> +#define HSW_SEL_DP_ENC0_P0                             BIT(7)
> +#define VSP_SEL_DP_ENC0_P0                             BIT(8)
> +#define VSW_SEL_DP_ENC0_P0                             BIT(9)
> +#define MTK_DP_ENC0_P0_3034                    0x3034
> +#define MTK_DP_ENC0_P0_3038                    0x3038
> +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK               BIT(11)
> +#define MTK_DP_ENC0_P0_303C                    0x303c
> +#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK           GENMASK(5, 0)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK              GENMASK(10, 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT             (0 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT             (1 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT             (2 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT              (3 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT              (4 << 8)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK            GENMASK(14, 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB             (0 << 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422                (1 << 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420                (2 << 12)
> +#define VIDEO_MN_GEN_EN_DP_ENC0_P0                     BIT(15)
> +#define MTK_DP_ENC0_P0_3040                    0x3040
> +#define SDP_DOWN_CNT_DP_ENC0_P0_VAL                    0x20
> +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK              GENMASK(11, 0)
> +#define MTK_DP_ENC0_P0_304C                    0x304c
> +#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK                        BIT(2)
> +#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK          BIT(8)
> +#define MTK_DP_ENC0_P0_3064                    0x3064
> +#define HDE_NUM_LAST_DP_ENC0_P0_MASK                   GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3154                    0x3154
> +#define PGEN_HTOTAL_DP_ENC0_P0_MASK                    GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_3158                    0x3158
> +#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK              GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_315C                    0x315c
> +#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK         GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_3160                    0x3160
> +#define PGEN_HFDE_START_DP_ENC0_P0_MASK                        GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_3164                    0x3164
> +#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK         GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_3168                    0x3168
> +#define PGEN_VTOTAL_DP_ENC0_P0_MASK                    GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_316C                    0x316c
> +#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK              GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_3170                    0x3170
> +#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK         GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_3174                    0x3174
> +#define PGEN_VFDE_START_DP_ENC0_P0_MASK                        GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_3178                    0x3178
> +#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK         GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_31B0                    0x31b0
> +#define PGEN_PATTERN_SEL_VAL                           4
> +#define PGEN_PATTERN_SEL_MASK                          GENMASK(6, 4)
> +#define MTK_DP_ENC0_P0_31EC                    0x31ec
> +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0                    BIT(4)
> +#define ISRC1_HB3_DP_ENC0_P0_MASK                      GENMASK(15, 8)
> +
> +/* offset: ENC1_OFFSET (0x3200) */
> +#define MTK_DP_ENC1_P0_3300                    0x3300
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL             2
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK            GENMASK(9, 8)
> +#define MTK_DP_ENC1_P0_3364                    0x3364
> +#define SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL          0x20
> +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK    GENMASK(11, 0)
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_VAL           4
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK          GENMASK(15, 12)
> +#define MTK_DP_ENC1_P0_3368                    0x3368
> +#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0       BIT(0)
> +#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0               BIT(4)
> +#define SDP_DP13_EN_DP_ENC1_P0                         BIT(8)
> +#define BS2BS_MODE_DP_ENC1_P0                          BIT(12)
> +#define BS2BS_MODE_DP_ENC1_P0_MASK                     GENMASK(13, 12)
> +#define BS2BS_MODE_DP_ENC1_P0_VAL                      1
> +#define DP_ENC1_P0_3368_VAL                            (VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0 | \
> +                                                        VIDEO_STABLE_CNT_THRD_DP_ENC1_P0 | \
> +                                                        SDP_DP13_EN_DP_ENC1_P0 | \
> +                                                        BS2BS_MODE_DP_ENC1_P0)
> +
> +/* offset: TRANS_OFFSET (0x3400) */
> +#define MTK_DP_TRANS_P0_3400                           0x3400
> +#define PATTERN1_EN_DP_TRANS_P0_MASK                           BIT(12)
> +#define PATTERN2_EN_DP_TRANS_P0_MASK                           BIT(13)
> +#define PATTERN3_EN_DP_TRANS_P0_MASK                           BIT(14)
> +#define PATTERN4_EN_DP_TRANS_P0_MASK                           BIT(15)
> +#define MTK_DP_TRANS_P0_3404                           0x3404
> +#define DP_SCR_EN_DP_TRANS_P0_MASK                             BIT(0)
> +#define MTK_DP_TRANS_P0_340C                           0x340c
> +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0              BIT(13)
> +#define MTK_DP_TRANS_P0_3410                           0x3410
> +#define HPD_DEB_THD_DP_TRANS_P0_MASK                           GENMASK(3, 0)
> +#define HPD_INT_THD_DP_TRANS_P0_MASK                           GENMASK(7, 4)
> +#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US                    (2 << 4)
> +#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US                   (2 << 6)
> +#define HPD_DISC_THD_DP_TRANS_P0_MASK                          GENMASK(11, 8)
> +#define HPD_CONN_THD_DP_TRANS_P0_MASK                          GENMASK(15, 12)
> +#define MTK_DP_TRANS_P0_3414                           0x3414
> +#define HPD_DB_DP_TRANS_P0_MASK                                        BIT(2)
> +#define MTK_DP_TRANS_P0_3418                           0x3418
> +#define IRQ_CLR_DP_TRANS_P0_MASK                               GENMASK(3, 0)
> +#define IRQ_MASK_DP_TRANS_P0_MASK                              GENMASK(7, 4)
> +#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ                          (BIT(1) << 4)
> +#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ                          (BIT(2) << 4)
> +#define IRQ_MASK_DP_TRANS_P0_INT_IRQ                           (BIT(3) << 4)
> +#define IRQ_STATUS_DP_TRANS_P0_MASK                            GENMASK(15, 12)
> +#define MTK_DP_TRANS_P0_342C                           0x342c
> +#define XTAL_FREQ_DP_TRANS_P0_DEFAULT                          (BIT(0) | BIT(3) | BIT(5) | BIT(6))
> +#define XTAL_FREQ_DP_TRANS_P0_MASK                             GENMASK(7, 0)
> +#define MTK_DP_TRANS_P0_3430                           0x3430
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK                       GENMASK(1, 0)
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT             BIT(1)
> +#define MTK_DP_TRANS_P0_34A4                           0x34a4
> +#define LANE_NUM_DP_TRANS_P0_MASK                              GENMASK(3, 2)
> +#define MTK_DP_TRANS_P0_3540                           0x3540
> +#define FEC_EN_DP_TRANS_P0_MASK                                        BIT(0)
> +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0                          BIT(3)
> +#define MTK_DP_TRANS_P0_3580                           0x3580
> +#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK               BIT(8)
> +#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK               BIT(9)
> +#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK               BIT(10)
> +#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK               BIT(11)
> +#define MTK_DP_TRANS_P0_35C8                           0x35c8
> +#define SW_IRQ_CLR_DP_TRANS_P0_MASK                            GENMASK(15, 0)
> +#define SW_IRQ_STATUS_DP_TRANS_P0_MASK                         GENMASK(15, 0)
> +#define MTK_DP_TRANS_P0_35D0                           0x35d0
> +#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK                   GENMASK(15, 0)
> +#define MTK_DP_TRANS_P0_35F0                           0x35f0
> +#define DP_TRANS_DUMMY_RW_0                                    BIT(3)
> +#define DP_TRANS_DUMMY_RW_0_MASK                               GENMASK(3, 2)
> +
> +/* offset: AUX_OFFSET (0x3600) */
> +#define MTK_DP_AUX_P0_360C                     0x360c
> +#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK                 GENMASK(12, 0)
> +#define AUX_TIMEOUT_THR_AUX_TX_P0_VAL                  0x1595
> +#define MTK_DP_AUX_P0_3614                     0x3614
> +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK               GENMASK(6, 0)
> +#define AUX_RX_UI_CNT_THR_AUX_FOR_26M                  13
> +#define MTK_DP_AUX_P0_3618                     0x3618
> +#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK                        BIT(9)
> +#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK       GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3620                     0x3620
> +#define AUX_RD_MODE_AUX_TX_P0_MASK                     BIT(9)
> +#define AUX_RX_FIFO_READ_PULSE_TX_P0                   BIT(8)
> +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK           GENMASK(7, 0)
> +#define MTK_DP_AUX_P0_3624                     0x3624
> +#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK            GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3628                     0x3628
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK                        GENMASK(9, 0)
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE             BIT(0)
> +#define MTK_DP_AUX_P0_362C                     0x362c
> +#define AUX_NO_LENGTH_AUX_TX_P0                                BIT(0)
> +#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK              BIT(1)
> +#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK               GENMASK(15, 2)
> +#define MTK_DP_AUX_P0_3630                     0x3630
> +#define AUX_TX_REQUEST_READY_AUX_TX_P0                 BIT(3)
> +#define MTK_DP_AUX_P0_3634                     0x3634
> +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK         GENMASK(15, 8)
> +#define AUX_TX_OVER_SAMPLE_RATE_FOR_26M                        25
> +#define MTK_DP_AUX_P0_3640                     0x3640
> +#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0         BIT(6)
> +#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0                BIT(5)
> +#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0                BIT(4)
> +#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0                  BIT(3)
> +#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0                 BIT(2)
> +#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0                 BIT(1)
> +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0                        BIT(0)
> +#define DP_AUX_P0_3640_VAL                             (AUX_400US_TIMEOUT_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_DATA_RECV_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_CMD_RECV_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
> +#define MTK_DP_AUX_P0_3644                     0x3644
> +#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK             GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3648                     0x3648
> +#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK         GENMASK(15, 0)
> +#define MTK_DP_AUX_P0_364C                     0x364c
> +#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK         GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3650                     0x3650
> +#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK                        GENMASK(15, 12)
> +#define PHY_FIFO_RST_AUX_TX_P0_MASK                    BIT(9)
> +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0                        BIT(8)
> +#define MTK_DP_AUX_P0_3658                     0x3658
> +#define AUX_TX_OV_EN_AUX_TX_P0_MASK                    BIT(0)
> +#define MTK_DP_AUX_P0_3690                     0x3690
> +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0               BIT(8)
> +#define MTK_DP_AUX_P0_3704                     0x3704
> +#define AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK    BIT(1)
> +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0              BIT(2)
> +#define MTK_DP_AUX_P0_3708                     0x3708
> +#define MTK_DP_AUX_P0_37C8                     0x37c8
> +#define MTK_ATOP_EN_AUX_TX_P0                          BIT(0)
> +
> +#endif /*_MTK_DP_REG_H_*/
> --
> 2.18.0
>

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

* Re: [PATCH v17 03/10] drm/mediatek: Add MT8195 Embedded DisplayPort driver
@ 2022-09-06  0:07     ` Chun-Kuang Hu
  0 siblings, 0 replies; 73+ messages in thread
From: Chun-Kuang Hu @ 2022-09-06  0:07 UTC (permalink / raw)
  To: Bo-Chen Chen
  Cc: Chun-Kuang Hu, Philipp Zabel, Daniel Vetter, Rob Herring,
	krzysztof.kozlowski+dt, Maxime Ripard, Thomas Zimmermann,
	Matthias Brugger, deller, David Airlie,
	Markus Schneider-Pargmann, Guillaume Ranquet, Jitao Shi,
	Chen-Yu Tsai, AngeloGioacchino Del Regno, CK Hu, liangxu.xu,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, linux-fbdev,
	Project_Global_Chrome_Upstream_Group

Hi, Bo-Chen:

Please help to fix the compile warning:

../drivers/gpu/drm/mediatek/mtk_dp.c: In function ‘mtk_dp_video_mute’:
../drivers/gpu/drm/mediatek/mtk_dp.c:947:23: warning: format ‘%x’
expects argument of type ‘unsigned int’, but argument 4 has type ‘long
unsigned int’ [-Wformat=]
  947 |  dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
      |                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../include/linux/dev_printk.h:129:27: note: in definition of macro ‘dev_printk’
  129 |   _dev_printk(level, dev, fmt, ##__VA_ARGS__);  \
      |                           ^~~
../include/linux/dev_printk.h:163:31: note: in expansion of macro ‘dev_fmt’
  163 |   dev_printk(KERN_DEBUG, dev, dev_fmt(fmt), ##__VA_ARGS__); \
      |                               ^~~~~~~
../drivers/gpu/drm/mediatek/mtk_dp.c:947:2: note: in expansion of
macro ‘dev_dbg’
  947 |  dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
      |  ^~~~~~~
../drivers/gpu/drm/mediatek/mtk_dp.c:947:36: note: format string is defined here
  947 |  dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
      |                                   ~^
      |                                    |
      |                                    unsigned int
      |                                   %lx

Regards,
Chun-Kuang.

Bo-Chen Chen <rex-bc.chen@mediatek.com> 於 2022年9月1日 週四 中午12:42寫道:
>
> From: Markus Schneider-Pargmann <msp@baylibre.com>
>
> This patch adds a embedded displayport driver for the MediaTek mt8195 SoC.
>
> It supports the MT8195, the embedded DisplayPort units. It offers
> DisplayPort 1.4 with up to 4 lanes.
>
> The driver creates a child device for the phy. The child device will
> never exist without the parent being active. As they are sharing a
> register range, the parent passes a regmap pointer to the child so that
> both can work with the same register range. The phy driver sets device
> data that is read by the parent to get the phy device that can be used
> to control the phy properties.
>
> This driver is based on an initial version by
> Jitao shi <jitao.shi@mediatek.com>
>
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/Kconfig      |    9 +
>  drivers/gpu/drm/mediatek/Makefile     |    2 +
>  drivers/gpu/drm/mediatek/mtk_dp.c     | 1999 +++++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_dp_reg.h |  305 ++++
>  4 files changed, 2315 insertions(+)
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h
>
> diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> index 2976d21e9a34..e66f4a3b6be0 100644
> --- a/drivers/gpu/drm/mediatek/Kconfig
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -21,6 +21,15 @@ config DRM_MEDIATEK
>           This driver provides kernel mode setting and
>           buffer management to userspace.
>
> +config DRM_MEDIATEK_DP
> +       tristate "DRM DPTX Support for MediaTek SoCs"
> +       depends on DRM_MEDIATEK
> +       select PHY_MTK_DP
> +       select DRM_DISPLAY_HELPER
> +       select DRM_DISPLAY_DP_HELPER
> +       help
> +         DRM/KMS Display Port driver for MediaTek SoCs.
> +
>  config DRM_MEDIATEK_HDMI
>         tristate "DRM HDMI Support for Mediatek SoCs"
>         depends on DRM_MEDIATEK
> diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> index 6e604a933ed0..3517d1c65cd7 100644
> --- a/drivers/gpu/drm/mediatek/Makefile
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -23,3 +23,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
>                           mtk_hdmi_ddc.o
>
>  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> +
> +obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> new file mode 100644
> index 000000000000..e2ec9b02b1aa
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -0,0 +1,1999 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019-2022 MediaTek Inc.
> + * Copyright (c) 2022 BayLibre
> + */
> +
> +#include <drm/display/drm_dp.h>
> +#include <drm/display/drm_dp_helper.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <linux/arm-smccc.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/media-bus-format.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/soc/mediatek/mtk_sip_svc.h>
> +#include <video/videomode.h>
> +
> +#include "mtk_dp_reg.h"
> +
> +#define MTK_DP_SIP_CONTROL_AARCH32     MTK_SIP_SMC_CMD(0x523)
> +#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE        (BIT(0) | BIT(5))
> +
> +#define MTK_DP_THREAD_CABLE_STATE_CHG  BIT(0)
> +#define MTK_DP_THREAD_HPD_EVENT                BIT(1)
> +
> +#define MTK_DP_4P1T 4
> +#define MTK_DP_HDE 2
> +#define MTK_DP_PIX_PER_ADDR 2
> +#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
> +#define MTK_DP_TBC_BUF_READ_START_ADDR 0x8
> +#define MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY 5
> +#define MTK_DP_TRAIN_DOWNSCALE_RETRY 10
> +
> +enum {
> +       MTK_DP_CAL_GLB_BIAS_TRIM = 0,
> +       MTK_DP_CAL_CLKTX_IMPSE,
> +       MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0,
> +       MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1,
> +       MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2,
> +       MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3,
> +       MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0,
> +       MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1,
> +       MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2,
> +       MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3,
> +       MTK_DP_CAL_MAX,
> +};
> +
> +struct mtk_dp_train_info {
> +       bool sink_ssc;
> +       bool cable_plugged_in;
> +       /* link_rate is in multiple of 0.27Gbps */
> +       int link_rate;
> +       int lane_count;
> +       unsigned int channel_eq_pattern;
> +};
> +
> +struct mtk_dp_info {
> +       enum dp_pixelformat format;
> +       struct videomode vm;
> +};
> +
> +struct mtk_dp_efuse_fmt {
> +       unsigned short idx;
> +       unsigned short shift;
> +       unsigned short mask;
> +       unsigned short min_val;
> +       unsigned short max_val;
> +       unsigned short default_val;
> +};
> +
> +struct mtk_dp {
> +       bool enabled;
> +       u8 max_lanes;
> +       u8 max_linkrate;
> +       u8 rx_cap[DP_RECEIVER_CAP_SIZE];
> +       u32 cal_data[MTK_DP_CAL_MAX];
> +       u32 irq_thread_handle;
> +       /* irq_thread_lock is used to protect irq_thread_handle */
> +       spinlock_t irq_thread_lock;
> +
> +       struct device *dev;
> +       struct drm_bridge bridge;
> +       struct drm_bridge *next_bridge;
> +       struct drm_connector *conn;
> +       struct drm_device *drm_dev;
> +       struct drm_dp_aux aux;
> +
> +       struct mtk_dp_info info;
> +       struct mtk_dp_train_info train_info;
> +
> +       struct platform_device *phy_dev;
> +       struct phy *phy;
> +       struct regmap *regs;
> +};
> +
> +static const struct mtk_dp_efuse_fmt mtk_dp_efuse_data[MTK_DP_CAL_MAX] = {
> +       [MTK_DP_CAL_GLB_BIAS_TRIM] = {
> +               .idx = 3,
> +               .shift = 27,
> +               .mask = 0x1f,
> +               .min_val = 1,
> +               .max_val = 0x1e,
> +               .default_val = 0xf,
> +       },
> +       [MTK_DP_CAL_CLKTX_IMPSE] = {
> +               .idx = 0,
> +               .shift = 9,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = {
> +               .idx = 2,
> +               .shift = 28,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = {
> +               .idx = 2,
> +               .shift = 20,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = {
> +               .idx = 2,
> +               .shift = 12,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = {
> +               .idx = 2,
> +               .shift = 4,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = {
> +               .idx = 2,
> +               .shift = 24,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = {
> +               .idx = 2,
> +               .shift = 16,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = {
> +               .idx = 2,
> +               .shift = 8,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +       [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = {
> +               .idx = 2,
> +               .shift = 0,
> +               .mask = 0xf,
> +               .min_val = 1,
> +               .max_val = 0xe,
> +               .default_val = 0x8,
> +       },
> +};
> +
> +static struct regmap_config mtk_dp_regmap_config = {
> +       .reg_bits = 32,
> +       .val_bits = 32,
> +       .reg_stride = 4,
> +       .max_register = SEC_OFFSET + 0x90,
> +       .name = "mtk-dp-registers",
> +};
> +
> +static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
> +{
> +       return container_of(b, struct mtk_dp, bridge);
> +}
> +
> +static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
> +{
> +       u32 read_val;
> +       int ret;
> +
> +       ret = regmap_read(mtk_dp->regs, offset, &read_val);
> +       if (ret) {
> +               dev_err(mtk_dp->dev, "Failed to read register 0x%x: %d\n",
> +                       offset, ret);
> +               return 0;
> +       }
> +
> +       return read_val;
> +}
> +
> +static int mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
> +{
> +       int ret = regmap_write(mtk_dp->regs, offset, val);
> +
> +       if (ret)
> +               dev_err(mtk_dp->dev,
> +                       "Failed to write register 0x%x with value 0x%x\n",
> +                       offset, val);
> +       return ret;
> +}
> +
> +static int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset,
> +                             u32 val, u32 mask)
> +{
> +       int ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
> +
> +       if (ret)
> +               dev_err(mtk_dp->dev,
> +                       "Failed to update register 0x%x with value 0x%x, mask 0x%x\n",
> +                       offset, val, mask);
> +       return ret;
> +}
> +
> +static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32 offset, u8 *buf,
> +                                   size_t length)
> +{
> +       int i;
> +
> +       /* 2 bytes per register */
> +       for (i = 0; i < length; i += 2) {
> +               u32 val = buf[i] | (i + 1 < length ? buf[i + 1] << 8 : 0);
> +
> +               if (mtk_dp_write(mtk_dp, offset + i * 2, val))
> +                       return;
> +       }
> +}
> +
> +static void mtk_dp_msa_bypass_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       u32 mask = HTOTAL_SEL_DP_ENC0_P0 | VTOTAL_SEL_DP_ENC0_P0 |
> +                  HSTART_SEL_DP_ENC0_P0 | VSTART_SEL_DP_ENC0_P0 |
> +                  HWIDTH_SEL_DP_ENC0_P0 | VHEIGHT_SEL_DP_ENC0_P0 |
> +                  HSP_SEL_DP_ENC0_P0 | HSW_SEL_DP_ENC0_P0 |
> +                  VSP_SEL_DP_ENC0_P0 | VSW_SEL_DP_ENC0_P0;
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, enable ? 0 : mask, mask);
> +}
> +
> +static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
> +{
> +       struct drm_display_mode mode;
> +       struct videomode *vm = &mtk_dp->info.vm;
> +
> +       drm_display_mode_from_videomode(vm, &mode);
> +
> +       /* horizontal */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010,
> +                          mode.htotal, HTOTAL_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
> +                          vm->hsync_len + vm->hback_porch,
> +                          HSTART_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> +                          vm->hsync_len, HSW_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
> +                          0, HSP_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020,
> +                          vm->hactive, HWIDTH_SW_DP_ENC0_P0_MASK);
> +
> +       /* vertical */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014,
> +                          mode.vtotal, VTOTAL_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
> +                          vm->vsync_len + vm->vback_porch,
> +                          VSTART_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> +                          vm->vsync_len, VSW_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
> +                          0, VSP_SW_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024,
> +                          vm->vactive, VHEIGHT_SW_DP_ENC0_P0_MASK);
> +
> +       /* horizontal */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064,
> +                          vm->hactive, HDE_NUM_LAST_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154,
> +                          mode.htotal, PGEN_HTOTAL_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
> +                          vm->hfront_porch,
> +                          PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C,
> +                          vm->hsync_len,
> +                          PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
> +                          vm->hback_porch + vm->hsync_len,
> +                          PGEN_HFDE_START_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164,
> +                          vm->hactive,
> +                          PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +
> +       /* vertical */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168,
> +                          mode.vtotal,
> +                          PGEN_VTOTAL_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
> +                          vm->vfront_porch,
> +                          PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170,
> +                          vm->vsync_len,
> +                          PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
> +                          vm->vback_porch + vm->vsync_len,
> +                          PGEN_VFDE_START_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178,
> +                          vm->vactive,
> +                          PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
> +}
> +
> +static int mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
> +                                  enum dp_pixelformat color_format)
> +{
> +       u32 val;
> +
> +       /* update MISC0 */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +                          color_format << DP_TEST_COLOR_FORMAT_SHIFT,
> +                          DP_TEST_COLOR_FORMAT_MASK);
> +
> +       switch (color_format) {
> +       case DP_PIXELFORMAT_YUV422:
> +               val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
> +               break;
> +       case DP_PIXELFORMAT_RGB:
> +               val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
> +               break;
> +       default:
> +               drm_warn(mtk_dp->drm_dev, "Unsupported color format: %d\n",
> +                        color_format);
> +               return -EINVAL;
> +       }
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +                          val, PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
> +       return 0;
> +}
> +
> +static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp)
> +{
> +       /* Only support 8 bits currently */
> +       /* Update MISC0 */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
> +                          DP_MSA_MISC_8_BPC, DP_TEST_BIT_DEPTH_MASK);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +                          VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT,
> +                          VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_config_mn_mode(struct mtk_dp *mtk_dp)
> +{
> +       /* 0: hw mode, 1: sw mode */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +                          0, VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32 val)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +                          val, SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
> +}
> +
> +static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
> +                          VIDEO_MN_GEN_EN_DP_ENC0_P0,
> +                          VIDEO_MN_GEN_EN_DP_ENC0_P0);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
> +                          SDP_DOWN_CNT_DP_ENC0_P0_VAL,
> +                          SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +                          SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL,
> +                          SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
> +                          VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL << 8,
> +                          VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
> +                          FIFO_READ_START_POINT_DP_ENC1_P0_VAL << 12,
> +                          FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
> +       mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368, DP_ENC1_P0_3368_VAL);
> +}
> +
> +static void mtk_dp_pg_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038,
> +                          enable ? VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK : 0,
> +                          VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
> +                          PGEN_PATTERN_SEL_VAL << 4, PGEN_PATTERN_SEL_MASK);
> +}
> +
> +static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640, DP_AUX_P0_3640_VAL);
> +}
> +
> +static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32 addr)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644,
> +                          cmd, MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648,
> +                          addr, MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C,
> +                          addr >> 16, MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_clear_fifo(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +                          MCU_ACK_TRAN_COMPLETE_AUX_TX_P0,
> +                          MCU_ACK_TRAN_COMPLETE_AUX_TX_P0 |
> +                          PHY_FIFO_RST_AUX_TX_P0_MASK |
> +                          MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +}
> +
> +static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
> +                          AUX_TX_REQUEST_READY_AUX_TX_P0,
> +                          AUX_TX_REQUEST_READY_AUX_TX_P0);
> +}
> +
> +static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> +                                      size_t length)
> +{
> +       mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf, length);
> +}
> +
> +static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
> +                                   size_t length, int read_delay)
> +{
> +       int read_pos;
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> +                          0, AUX_RD_MODE_AUX_TX_P0_MASK);
> +
> +       for (read_pos = 0; read_pos < length; read_pos++) {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
> +                                  AUX_RX_FIFO_READ_PULSE_TX_P0,
> +                                  AUX_RX_FIFO_READ_PULSE_TX_P0);
> +
> +               /* Hardware needs time to update the data */
> +               usleep_range(read_delay, read_delay * 2);
> +               buf[read_pos] = (u8)(mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3620) &
> +                                    AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK);
> +       }
> +}
> +
> +static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t length)
> +{
> +       if (length > 0) {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
> +                                  (length - 1) << 12,
> +                                  MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +                                  0,
> +                                  AUX_NO_LENGTH_AUX_TX_P0 |
> +                                  AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +                                  AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +       } else {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +                                  AUX_NO_LENGTH_AUX_TX_P0,
> +                                  AUX_NO_LENGTH_AUX_TX_P0 |
> +                                  AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +                                  AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +       }
> +}
> +
> +static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp, bool is_read)
> +{
> +       int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
> +
> +       while (--wait_reply) {
> +               u32 aux_irq_status;
> +
> +               if (is_read) {
> +                       u32 fifo_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3618);
> +
> +                       if (fifo_status &
> +                           (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
> +                            AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
> +                               return 0;
> +                       }
> +               }
> +
> +               aux_irq_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3640);
> +               if (aux_irq_status & AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
> +                       return 0;
> +
> +               if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0)
> +                       return -ETIMEDOUT;
> +
> +               /* Give the hardware a chance to reach completion before retrying */
> +               usleep_range(100, 500);
> +       }
> +
> +       return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool is_read, u8 cmd,
> +                                 u32 addr, u8 *buf, size_t length)
> +{
> +       int ret;
> +       u32 reply_cmd;
> +
> +       if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
> +                       (cmd == DP_AUX_NATIVE_READ && !length)))
> +               return -EINVAL;
> +
> +       if (!is_read)
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> +                                  AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0,
> +                                  AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0);
> +
> +       /* We need to clear fifo and irq before sending commands to the sink device. */
> +       mtk_dp_aux_clear_fifo(mtk_dp);
> +       mtk_dp_aux_irq_clear(mtk_dp);
> +
> +       mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
> +       mtk_dp_aux_set_length(mtk_dp, length);
> +
> +       if (!is_read) {
> +               if (length)
> +                       mtk_dp_aux_fill_write_fifo(mtk_dp, buf, length);
> +
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
> +                                  AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK,
> +                                  AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK);
> +       }
> +
> +       mtk_dp_aux_request_ready(mtk_dp);
> +
> +       /* Wait for feedback from sink device. */
> +       ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
> +
> +       reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
> +                   AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
> +
> +       if (ret || reply_cmd) {
> +               u32 phy_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3628) &
> +                                AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
> +               if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
> +                       drm_err(mtk_dp->drm_dev,
> +                               "AUX Rx Aux hang, need SW reset\n");
> +                       return -EIO;
> +               }
> +
> +               return -ETIMEDOUT;
> +       }
> +
> +       if (!length) {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
> +                                  0,
> +                                  AUX_NO_LENGTH_AUX_TX_P0 |
> +                                  AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
> +                                  AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
> +       } else if (is_read) {
> +               int read_delay;
> +
> +               if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
> +                   cmd == DP_AUX_I2C_READ)
> +                       read_delay = 500;
> +               else
> +                       read_delay = 100;
> +
> +               mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length, read_delay);
> +       }
> +
> +       return 0;
> +}
> +
> +static void mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int lane_num,
> +                                         int swing_val, int preemphasis)
> +{
> +       u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
> +
> +       dev_dbg(mtk_dp->dev,
> +               "link training: swing_val = 0x%x, pre-emphasis = 0x%x\n",
> +               swing_val, preemphasis);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +                          swing_val << (DP_TX0_VOLT_SWING_SHIFT + lane_shift),
> +                          DP_TX0_VOLT_SWING_MASK << lane_shift);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +                          preemphasis << (DP_TX0_PRE_EMPH_SHIFT + lane_shift),
> +                          DP_TX0_PRE_EMPH_MASK << lane_shift);
> +}
> +
> +static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
> +                          0,
> +                          DP_TX0_VOLT_SWING_MASK |
> +                          DP_TX1_VOLT_SWING_MASK |
> +                          DP_TX2_VOLT_SWING_MASK |
> +                          DP_TX3_VOLT_SWING_MASK |
> +                          DP_TX0_PRE_EMPH_MASK |
> +                          DP_TX1_PRE_EMPH_MASK |
> +                          DP_TX2_PRE_EMPH_MASK |
> +                          DP_TX3_PRE_EMPH_MASK);
> +}
> +
> +static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +       u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
> +                        SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
> +
> +       if (irq_status) {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
> +                                  irq_status, SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
> +                                  0, SW_IRQ_CLR_DP_TRANS_P0_MASK);
> +       }
> +
> +       return irq_status;
> +}
> +
> +static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
> +{
> +       u32 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
> +                         IRQ_STATUS_DP_TRANS_P0_MASK) >> 12;
> +
> +       if (irq_status) {
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +                                  irq_status, IRQ_CLR_DP_TRANS_P0_MASK);
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +                                  0, IRQ_CLR_DP_TRANS_P0_MASK);
> +       }
> +
> +       return irq_status;
> +}
> +
> +static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
> +                          enable ? 0 :
> +                          IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
> +                          IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
> +                          IRQ_MASK_DP_TRANS_P0_INT_IRQ,
> +                          IRQ_MASK_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
> +                          XTAL_FREQ_DP_TRANS_P0_DEFAULT,
> +                          XTAL_FREQ_DP_TRANS_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
> +                          FEC_CLOCK_EN_MODE_DP_TRANS_P0,
> +                          FEC_CLOCK_EN_MODE_DP_TRANS_P0);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
> +                          AUDIO_CH_SRC_SEL_DP_ENC0_P0,
> +                          AUDIO_CH_SRC_SEL_DP_ENC0_P0);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
> +                          0, SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK,
> +                          IRQ_MASK_AUX_TOP_IRQ, IRQ_MASK_AUX_TOP_IRQ);
> +}
> +
> +static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp *mtk_dp)
> +{
> +       u32 val;
> +       /* Debounce threshold */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +                          8, HPD_DEB_THD_DP_TRANS_P0_MASK);
> +
> +       val = (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
> +              HPD_INT_THD_DP_TRANS_P0_UPPER_1100US) << 4;
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +                          val, HPD_INT_THD_DP_TRANS_P0_MASK);
> +
> +       /*
> +        * Connect threshold 1.5ms + 5 x 0.1ms = 2ms
> +        * Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
> +        */
> +       val = (5 << 8) | (5 << 12);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
> +                          val,
> +                          HPD_DISC_THD_DP_TRANS_P0_MASK |
> +                          HPD_CONN_THD_DP_TRANS_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
> +                          HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
> +                          HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
> +{
> +       /* modify timeout threshold = 0x1595 */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C,
> +                          AUX_TIMEOUT_THR_AUX_TX_P0_VAL,
> +                          AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658,
> +                          0, AUX_TX_OV_EN_AUX_TX_P0_MASK);
> +       /* 25 for 26M */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
> +                          AUX_TX_OVER_SAMPLE_RATE_FOR_26M << 8,
> +                          AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
> +       /* 13 for 26M */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
> +                          AUX_RX_UI_CNT_THR_AUX_FOR_26M,
> +                          AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
> +                          MTK_ATOP_EN_AUX_TX_P0,
> +                          MTK_ATOP_EN_AUX_TX_P0);
> +}
> +
> +static void mtk_dp_initialize_digital_settings(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
> +                          0, VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
> +                          BS2BS_MODE_DP_ENC1_P0_VAL << 12,
> +                          BS2BS_MODE_DP_ENC1_P0_MASK);
> +
> +       /* dp tx encoder reset all sw */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +                          DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0,
> +                          DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
> +
> +       /* Wait for sw reset to complete */
> +       usleep_range(1000, 5000);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
> +                          0, DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
> +}
> +
> +static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> +                          DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0,
> +                          DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
> +
> +       /* Wait for sw reset to complete */
> +       usleep_range(1000, 5000);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
> +                          0, DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
> +}
> +
> +static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
> +                          lanes == 0 ? 0 : DP_TRANS_DUMMY_RW_0,
> +                          DP_TRANS_DUMMY_RW_0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +                          lanes, LANE_NUM_DP_ENC0_P0_MASK);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
> +                          lanes << 2, LANE_NUM_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
> +{
> +       const struct mtk_dp_efuse_fmt *fmt;
> +       struct device *dev = mtk_dp->dev;
> +       struct nvmem_cell *cell;
> +       u32 *cal_data = mtk_dp->cal_data;
> +       u32 *buf;
> +       int i;
> +       size_t len;
> +
> +       cell = nvmem_cell_get(dev, "dp_calibration_data");
> +       if (IS_ERR(cell)) {
> +               dev_warn(dev, "Failed to get nvmem cell dp_calibration_data\n");
> +               goto use_default_val;
> +       }
> +
> +       buf = (u32 *)nvmem_cell_read(cell, &len);
> +       nvmem_cell_put(cell);
> +
> +       if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
> +               dev_warn(dev, "Failed to read nvmem_cell_read\n");
> +
> +               if (!IS_ERR(buf))
> +                       kfree(buf);
> +
> +               goto use_default_val;
> +       }
> +
> +       for (i = 0; i < MTK_DP_CAL_MAX; i++) {
> +               fmt = &mtk_dp_efuse_data[i];
> +               cal_data[i] = (buf[fmt->idx] >> fmt->shift) & fmt->mask;
> +
> +               if (cal_data[i] < fmt->min_val || cal_data[i] > fmt->max_val) {
> +                       dev_warn(mtk_dp->dev, "Invalid efuse data, idx = %d\n", i);
> +                       kfree(buf);
> +                       goto use_default_val;
> +               }
> +       }
> +       kfree(buf);
> +
> +       return;
> +
> +use_default_val:
> +       dev_warn(mtk_dp->dev, "Use default calibration data\n");
> +       for (i = 0; i < MTK_DP_CAL_MAX; i++)
> +               cal_data[i] = mtk_dp_efuse_data[i].default_val;
> +}
> +
> +static void mtk_dp_set_calibration_data(struct mtk_dp *mtk_dp)
> +{
> +       u32 *cal_data = mtk_dp->cal_data;
> +
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
> +                          cal_data[MTK_DP_CAL_CLKTX_IMPSE] << 20,
> +                          RG_CKM_PT0_CKTX_IMPSEL);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
> +                          cal_data[MTK_DP_CAL_GLB_BIAS_TRIM] << 16,
> +                          RG_XTP_GLB_BIAS_INTR_CTRL);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] << 12,
> +                          RG_XTP_LN0_TX_IMPSEL_PMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] << 16,
> +                          RG_XTP_LN0_TX_IMPSEL_NMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] << 12,
> +                          RG_XTP_LN1_TX_IMPSEL_PMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] << 16,
> +                          RG_XTP_LN1_TX_IMPSEL_NMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] << 12,
> +                          RG_XTP_LN2_TX_IMPSEL_PMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] << 16,
> +                          RG_XTP_LN2_TX_IMPSEL_NMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] << 12,
> +                          RG_XTP_LN3_TX_IMPSEL_PMOS);
> +       mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
> +                          cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] << 16,
> +                          RG_XTP_LN3_TX_IMPSEL_NMOS);
> +}
> +
> +static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
> +                               u32 link_rate, int lane_count)
> +{
> +       int ret;
> +       union phy_configure_opts phy_opts = {
> +               .dp = {
> +                       .link_rate = drm_dp_bw_code_to_link_rate(link_rate) / 100,
> +                       .set_rate = 1,
> +                       .lanes = lane_count,
> +                       .set_lanes = 1,
> +                       .ssc = mtk_dp->train_info.sink_ssc,
> +               }
> +       };
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP,
> +                          DP_PWR_STATE_MASK);
> +
> +       ret = phy_configure(mtk_dp->phy, &phy_opts);
> +       if (ret)
> +               return ret;
> +
> +       mtk_dp_set_calibration_data(mtk_dp);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                          DP_PWR_STATE_BANDGAP_TPLL_LANE, DP_PWR_STATE_MASK);
> +
> +       return 0;
> +}
> +
> +static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
> +                 POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
> +                 POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
> +                 POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580,
> +                          enable ? val : 0, val);
> +}
> +
> +static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int pattern)
> +{
> +       /* TPS1 */
> +       if (pattern == 1)
> +               mtk_dp_set_idle_pattern(mtk_dp, false);
> +
> +       mtk_dp_update_bits(mtk_dp,
> +                          MTK_DP_TRANS_P0_3400,
> +                          pattern ? BIT(pattern - 1) << 12 : 0,
> +                          PATTERN1_EN_DP_TRANS_P0_MASK |
> +                          PATTERN2_EN_DP_TRANS_P0_MASK |
> +                          PATTERN3_EN_DP_TRANS_P0_MASK |
> +                          PATTERN4_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +                          ENHANCED_FRAME_EN_DP_ENC0_P0,
> +                          ENHANCED_FRAME_EN_DP_ENC0_P0);
> +}
> +
> +static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
> +                          enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
> +                          DP_SCR_EN_DP_TRANS_P0_MASK);
> +}
> +
> +static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       struct arm_smccc_res res;
> +       u32 val = VIDEO_MUTE_SEL_DP_ENC0_P0 |
> +                 (enable ? VIDEO_MUTE_SW_DP_ENC0_P0 : 0);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
> +                          val,
> +                          VIDEO_MUTE_SEL_DP_ENC0_P0 |
> +                          VIDEO_MUTE_SW_DP_ENC0_P0);
> +
> +       arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32,
> +                     MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable,
> +                     0, 0, 0, 0, 0, &res);
> +
> +       dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
> +               MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable, res.a0, res.a1);
> +}
> +
> +static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
> +                          0, SW_RST_B_PHYD);
> +
> +       /* Wait for power enable */
> +       usleep_range(10, 200);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
> +                          SW_RST_B_PHYD, SW_RST_B_PHYD);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                          DP_PWR_STATE_BANDGAP_TPLL, DP_PWR_STATE_MASK);
> +       mtk_dp_write(mtk_dp, MTK_DP_1040,
> +                    RG_DPAUX_RX_VALID_DEGLITCH_EN | RG_XTP_GLB_CKDET_EN |
> +                    RG_DPAUX_RX_EN);
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_0034, 0, DA_CKM_CKTX0_EN_FORCE_EN);
> +}
> +
> +static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_0034,
> +                          DA_CKM_CKTX0_EN_FORCE_EN, DA_CKM_CKTX0_EN_FORCE_EN);
> +
> +       /* Disable RX */
> +       mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
> +       mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
> +                    0x550 | FUSE_SEL | MEM_ISO_EN);
> +}
> +
> +static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp->train_info.link_rate = DP_LINK_BW_5_4;
> +       mtk_dp->train_info.lane_count = mtk_dp->max_lanes;
> +       mtk_dp->train_info.cable_plugged_in = false;
> +
> +       mtk_dp->info.format = DP_PIXELFORMAT_RGB;
> +       memset(&mtk_dp->info.vm, 0, sizeof(struct videomode));
> +}
> +
> +static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
> +{
> +       u32 sram_read_start = min_t(u32, MTK_DP_TBC_BUF_READ_START_ADDR,
> +                                   mtk_dp->info.vm.hactive /
> +                                   mtk_dp->train_info.lane_count /
> +                                   MTK_DP_4P1T / MTK_DP_HDE /
> +                                   MTK_DP_PIX_PER_ADDR);
> +       mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
> +       mtk_dp_setup_encoder(mtk_dp);
> +}
> +
> +static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_setup_tu(mtk_dp);
> +}
> +
> +static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes,
> +                                         u8 dpcd_adjust_req[2])
> +{
> +       int lane;
> +
> +       for (lane = 0; lane < lanes; ++lane) {
> +               u8 val;
> +               u8 swing;
> +               u8 preemphasis;
> +               int index = lane / 2;
> +               int shift = lane % 2 ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
> +
> +               swing = (dpcd_adjust_req[index] >> shift) &
> +                       DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
> +               preemphasis = ((dpcd_adjust_req[index] >> shift) &
> +                              DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
> +                             DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
> +               val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
> +                     preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +
> +               if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
> +                       val |= DP_TRAIN_MAX_SWING_REACHED;
> +               if (preemphasis == 3)
> +                       val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> +
> +               mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing, preemphasis);
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET + lane,
> +                                  val);
> +       }
> +}
> +
> +static void mtk_dp_pattern(struct mtk_dp *mtk_dp, bool is_tps1)
> +{
> +       int pattern;
> +       unsigned int aux_offset;
> +
> +       if (is_tps1) {
> +               pattern = 1;
> +               aux_offset = DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1;
> +       } else {
> +               aux_offset = mtk_dp->train_info.channel_eq_pattern;
> +
> +               switch (mtk_dp->train_info.channel_eq_pattern) {
> +               case DP_TRAINING_PATTERN_4:
> +                       pattern = 4;
> +                       break;
> +               case DP_TRAINING_PATTERN_3:
> +                       pattern = 3;
> +                       aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
> +                       break;
> +               case DP_TRAINING_PATTERN_2:
> +               default:
> +                       pattern = 2;
> +                       aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
> +                       break;
> +               }
> +       }
> +
> +       mtk_dp_train_set_pattern(mtk_dp, pattern);
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET, aux_offset);
> +}
> +
> +static int mtk_dp_train_setting(struct mtk_dp *mtk_dp, u8 target_link_rate,
> +                               u8 target_lane_count)
> +{
> +       int ret;
> +
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET, target_link_rate);
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
> +                          target_lane_count | DP_LANE_COUNT_ENHANCED_FRAME_EN);
> +
> +       if (mtk_dp->train_info.sink_ssc)
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
> +                                  DP_SPREAD_AMP_0_5);
> +
> +       mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
> +       ret = mtk_dp_phy_configure(mtk_dp, target_link_rate, target_lane_count);
> +       if (ret)
> +               return ret;
> +
> +       dev_dbg(mtk_dp->dev,
> +               "Link train target_link_rate = 0x%x, target_lane_count = 0x%x\n",
> +               target_link_rate, target_lane_count);
> +
> +       return 0;
> +}
> +
> +static int mtk_dp_train_cr(struct mtk_dp *mtk_dp, u8 target_lane_count)
> +{
> +       u8 lane_adjust[2] = {};
> +       u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +       u8 prev_lane_adjust = 0xff;
> +       int train_retries = 0;
> +       int voltage_retries = 0;
> +
> +       mtk_dp_pattern(mtk_dp, true);
> +
> +       /* In DP spec 1.4, the retry count of CR is defined as 10. */
> +       do {
> +               train_retries++;
> +               if (!mtk_dp->train_info.cable_plugged_in) {
> +                       mtk_dp_train_set_pattern(mtk_dp, 0);
> +                       return -ENODEV;
> +               }
> +
> +               drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
> +                                lane_adjust, sizeof(lane_adjust));
> +               mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
> +                                             lane_adjust);
> +
> +               drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
> +                                                      mtk_dp->rx_cap);
> +
> +               /* check link status from sink device */
> +               drm_dp_dpcd_read_link_status(&mtk_dp->aux, link_status);
> +               if (drm_dp_clock_recovery_ok(link_status,
> +                                            target_lane_count)) {
> +                       dev_dbg(mtk_dp->dev, "Link train CR pass\n");
> +                       return 0;
> +               }
> +
> +               /*
> +                * In DP spec 1.4, if current voltage level is the same
> +                * with previous voltage level, we need to retry 5 times.
> +                */
> +               if (prev_lane_adjust == link_status[4]) {
> +                       voltage_retries++;
> +                       /*
> +                        * Condition of CR fail:
> +                        * 1. Failed to pass CR using the same voltage
> +                        *    level over five times.
> +                        * 2. Failed to pass CR when the current voltage
> +                        *    level is the same with previous voltage
> +                        *    level and reach max voltage level (3).
> +                        */
> +                       if (voltage_retries > MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY ||
> +                           (prev_lane_adjust & DP_ADJUST_VOLTAGE_SWING_LANE0_MASK) == 3) {
> +                               dev_dbg(mtk_dp->dev, "Link train CR fail\n");
> +                               break;
> +                       }
> +               } else {
> +                       /*
> +                        * If the voltage level is changed, we need to
> +                        * re-calculate this retry count.
> +                        */
> +                       voltage_retries = 0;
> +               }
> +               prev_lane_adjust = link_status[4];
> +       } while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
> +
> +       /* Failed to train CR, and disable pattern. */
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +                          DP_TRAINING_PATTERN_DISABLE);
> +       mtk_dp_train_set_pattern(mtk_dp, 0);
> +
> +       return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_train_eq(struct mtk_dp *mtk_dp, u8 target_lane_count)
> +{
> +       u8 lane_adjust[2] = {};
> +       u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +       int train_retries = 0;
> +
> +       mtk_dp_pattern(mtk_dp, false);
> +
> +       do {
> +               train_retries++;
> +               if (!mtk_dp->train_info.cable_plugged_in) {
> +                       mtk_dp_train_set_pattern(mtk_dp, 0);
> +                       return -ENODEV;
> +               }
> +
> +               drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
> +                                lane_adjust, sizeof(lane_adjust));
> +               mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
> +                                             lane_adjust);
> +
> +               drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
> +                                                  mtk_dp->rx_cap);
> +
> +               /* check link status from sink device */
> +               drm_dp_dpcd_read_link_status(&mtk_dp->aux, link_status);
> +               if (drm_dp_channel_eq_ok(link_status, target_lane_count)) {
> +                       dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
> +
> +                       /* Training done, and disable pattern. */
> +                       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +                                          DP_TRAINING_PATTERN_DISABLE);
> +                       mtk_dp_train_set_pattern(mtk_dp, 0);
> +                       return 0;
> +               }
> +               dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
> +       } while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
> +
> +       /* Failed to train EQ, and disable pattern. */
> +       drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
> +                          DP_TRAINING_PATTERN_DISABLE);
> +       mtk_dp_train_set_pattern(mtk_dp, 0);
> +
> +       return -ETIMEDOUT;
> +}
> +
> +static int mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
> +{
> +       u8 val;
> +       ssize_t ret;
> +
> +       drm_dp_read_dpcd_caps(&mtk_dp->aux, mtk_dp->rx_cap);
> +
> +       if (drm_dp_tps4_supported(mtk_dp->rx_cap))
> +               mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_4;
> +       else if (drm_dp_tps3_supported(mtk_dp->rx_cap))
> +               mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_3;
> +       else
> +               mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_2;
> +
> +       mtk_dp->train_info.sink_ssc = drm_dp_max_downspread(mtk_dp->rx_cap);
> +
> +       ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
> +       if (ret < 1) {
> +               drm_err(mtk_dp->drm_dev, "Read mstm cap failed\n");
> +               return ret == 0 ? -EIO : ret;
> +       }
> +
> +       if (val & DP_MST_CAP) {
> +               /* Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 */
> +               ret = drm_dp_dpcd_readb(&mtk_dp->aux,
> +                                       DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
> +                                       &val);
> +               if (ret < 1) {
> +                       drm_err(mtk_dp->drm_dev, "Read irq vector failed\n");
> +                       return ret == 0 ? -EIO : ret;
> +               }
> +
> +               if (val)
> +                       drm_dp_dpcd_writeb(&mtk_dp->aux,
> +                                          DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
> +                                          val);
> +       }
> +
> +       return 0;
> +}
> +
> +static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
> +{
> +       phy_reset(mtk_dp->phy);
> +       mtk_dp_reset_swing_pre_emphasis(mtk_dp);
> +}
> +
> +static int mtk_dp_training(struct mtk_dp *mtk_dp)
> +{
> +       int ret;
> +       u8 lane_count, link_rate, train_limit, max_link_rate;
> +
> +       link_rate = min_t(u8, mtk_dp->max_linkrate,
> +                         mtk_dp->rx_cap[DP_MAX_LINK_RATE]);
> +       max_link_rate = link_rate;
> +       lane_count = min_t(u8, mtk_dp->max_lanes,
> +                          drm_dp_max_lane_count(mtk_dp->rx_cap));
> +
> +       /*
> +        * TPS are generated by the hardware pattern generator. From the
> +        * hardware setting we need to disable this scramble setting before
> +        * use the TPS pattern generator.
> +        */
> +       mtk_dp_training_set_scramble(mtk_dp, false);
> +
> +       for (train_limit = 6; train_limit > 0; train_limit--) {
> +               mtk_dp_train_change_mode(mtk_dp);
> +
> +               ret = mtk_dp_train_setting(mtk_dp, link_rate, lane_count);
> +               if (ret)
> +                       return ret;
> +
> +               ret = mtk_dp_train_cr(mtk_dp, lane_count);
> +               if (ret == -ENODEV) {
> +                       return ret;
> +               } else if (ret) {
> +                       /* reduce link rate */
> +                       switch (link_rate) {
> +                       case DP_LINK_BW_1_62:
> +                               lane_count = lane_count / 2;
> +                               link_rate = max_link_rate;
> +                               if (lane_count == 0)
> +                                       return -EIO;
> +                               break;
> +                       case DP_LINK_BW_2_7:
> +                               link_rate = DP_LINK_BW_1_62;
> +                               break;
> +                       case DP_LINK_BW_5_4:
> +                               link_rate = DP_LINK_BW_2_7;
> +                               break;
> +                       case DP_LINK_BW_8_1:
> +                               link_rate = DP_LINK_BW_5_4;
> +                               break;
> +                       default:
> +                               return -EINVAL;
> +                       };
> +                       continue;
> +               }
> +
> +               ret = mtk_dp_train_eq(mtk_dp, lane_count);
> +               if (ret == -ENODEV) {
> +                       return ret;
> +               } else if (ret) {
> +                       /* reduce lane count */
> +                       if (lane_count == 0)
> +                               return -EIO;
> +                       lane_count /= 2;
> +                       continue;
> +               }
> +
> +               /* if we can run to this, training is done. */
> +               break;
> +       }
> +
> +       if (train_limit == 0)
> +               return -ETIMEDOUT;
> +
> +       mtk_dp->train_info.link_rate = link_rate;
> +       mtk_dp->train_info.lane_count = lane_count;
> +
> +       /*
> +        * After training done, we need to output normal stream instead of TPS,
> +        * so we need to enable scramble.
> +        */
> +       mtk_dp_training_set_scramble(mtk_dp, true);
> +       mtk_dp_set_enhanced_frame_mode(mtk_dp);
> +
> +       return 0;
> +}
> +
> +static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
> +{
> +       /* the mute sequence is different between enable and disable */
> +       if (enable) {
> +               mtk_dp_msa_bypass_enable(mtk_dp, false);
> +               mtk_dp_pg_enable(mtk_dp, false);
> +               mtk_dp_set_tx_out(mtk_dp);
> +               mtk_dp_video_mute(mtk_dp, false);
> +       } else {
> +               mtk_dp_video_mute(mtk_dp, true);
> +               mtk_dp_pg_enable(mtk_dp, true);
> +               mtk_dp_msa_bypass_enable(mtk_dp, true);
> +       }
> +}
> +
> +static int mtk_dp_video_config(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_config_mn_mode(mtk_dp);
> +       mtk_dp_set_msa(mtk_dp);
> +       mtk_dp_set_color_depth(mtk_dp);
> +       return mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
> +}
> +
> +static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_set_idle_pattern(mtk_dp, true);
> +       mtk_dp_initialize_priv_data(mtk_dp);
> +
> +       mtk_dp_initialize_settings(mtk_dp);
> +       mtk_dp_initialize_aux_settings(mtk_dp);
> +       mtk_dp_initialize_digital_settings(mtk_dp);
> +
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
> +                          RX_REPLY_COMPLETE_MODE_AUX_TX_P0,
> +                          RX_REPLY_COMPLETE_MODE_AUX_TX_P0);
> +       mtk_dp_initialize_hpd_detect_settings(mtk_dp);
> +
> +       mtk_dp_digital_sw_reset(mtk_dp);
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
> +{
> +       struct mtk_dp *mtk_dp = dev;
> +       unsigned long flags;
> +       u32 status;
> +
> +       spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
> +       status = mtk_dp->irq_thread_handle;
> +       mtk_dp->irq_thread_handle = 0;
> +       spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
> +
> +       if (status & MTK_DP_THREAD_CABLE_STATE_CHG)
> +               drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
> +
> +       if (status & MTK_DP_THREAD_HPD_EVENT)
> +               dev_dbg(mtk_dp->dev, "Receive IRQ from sink devices\n");
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
> +{
> +       struct mtk_dp *mtk_dp = dev;
> +       bool cable_sta_chg = false;
> +       unsigned long flags;
> +       u32 irq_status = mtk_dp_swirq_get_clear(mtk_dp) |
> +                        mtk_dp_hwirq_get_clear(mtk_dp);
> +
> +       if (!irq_status)
> +               return IRQ_HANDLED;
> +
> +       spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
> +
> +       if (irq_status & MTK_DP_HPD_INTERRUPT)
> +               mtk_dp->irq_thread_handle |= MTK_DP_THREAD_HPD_EVENT;
> +
> +       /* Cable state is changed. */
> +       if (irq_status != MTK_DP_HPD_INTERRUPT) {
> +               mtk_dp->irq_thread_handle |= MTK_DP_THREAD_CABLE_STATE_CHG;
> +               cable_sta_chg = true;
> +       }
> +
> +       spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
> +
> +       if (cable_sta_chg) {
> +               if (!!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
> +                      HPD_DB_DP_TRANS_P0_MASK))
> +                       mtk_dp->train_info.cable_plugged_in = true;
> +               else
> +                       mtk_dp->train_info.cable_plugged_in = false;
> +       }
> +
> +       return IRQ_WAKE_THREAD;
> +}
> +
> +static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
> +                          struct platform_device *pdev)
> +{
> +       struct device_node *endpoint;
> +       struct device *dev = &pdev->dev;
> +       int ret;
> +       void __iomem *base;
> +       u32 linkrate;
> +       int len;
> +
> +       base = devm_platform_ioremap_resource(pdev, 0);
> +       if (IS_ERR(base))
> +               return PTR_ERR(base);
> +
> +       mtk_dp->regs = devm_regmap_init_mmio(dev, base, &mtk_dp_regmap_config);
> +       if (IS_ERR(mtk_dp->regs))
> +               return PTR_ERR(mtk_dp->regs);
> +
> +       endpoint = of_graph_get_endpoint_by_regs(pdev->dev.of_node, 1, -1);
> +       len = of_property_count_elems_of_size(endpoint,
> +                                             "data-lanes", sizeof(u32));
> +       if (len < 0 || len > 4 || len == 3) {
> +               dev_err(dev, "invalid data lane size: %d\n", len);
> +               return -EINVAL;
> +       }
> +
> +       mtk_dp->max_lanes = len;
> +
> +       ret = device_property_read_u32(dev, "max-linkrate-mhz", &linkrate);
> +       if (ret) {
> +               dev_err(dev, "failed to read max linkrate: %d\n", ret);
> +               return ret;
> +       }
> +
> +       mtk_dp->max_linkrate = drm_dp_link_rate_to_bw_code(linkrate * 100);
> +
> +       return 0;
> +}
> +
> +static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> +                                   struct drm_connector *connector)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       bool enabled = mtk_dp->enabled;
> +       struct edid *new_edid = NULL;
> +
> +       if (!enabled) {
> +               drm_bridge_chain_pre_enable(bridge);
> +
> +               /* power on aux */
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                                  DP_PWR_STATE_BANDGAP_TPLL_LANE,
> +                                  DP_PWR_STATE_MASK);
> +
> +               /* power on panel */
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> +               usleep_range(2000, 5000);
> +       }
> +
> +       new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> +
> +       /*
> +        * Parse capability here to let atomic_get_input_bus_fmts and
> +        * mode_valid use the capability to calculate sink bitrates.
> +        */
> +       if (mtk_dp_parse_capabilities(mtk_dp)) {
> +               drm_err(mtk_dp->drm_dev, "Can't parse capabilities\n");
> +               new_edid = NULL;
> +       }
> +
> +       if (!enabled) {
> +               /* power off panel */
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
> +               usleep_range(2000, 3000);
> +
> +               /* power off aux */
> +               mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                                  DP_PWR_STATE_BANDGAP_TPLL,
> +                                  DP_PWR_STATE_MASK);
> +
> +               drm_bridge_chain_post_disable(bridge);
> +       }
> +
> +       return new_edid;
> +}
> +
> +static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
> +                                  struct drm_dp_aux_msg *msg)
> +{
> +       struct mtk_dp *mtk_dp;
> +       bool is_read;
> +       u8 request;
> +       size_t accessed_bytes = 0;
> +       int ret;
> +
> +       mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
> +
> +       if (!mtk_dp->train_info.cable_plugged_in) {
> +               ret = -EAGAIN;
> +               goto err;
> +       }
> +
> +       switch (msg->request) {
> +       case DP_AUX_I2C_MOT:
> +       case DP_AUX_I2C_WRITE:
> +       case DP_AUX_NATIVE_WRITE:
> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
> +               request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
> +               is_read = false;
> +               break;
> +       case DP_AUX_I2C_READ:
> +       case DP_AUX_NATIVE_READ:
> +       case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
> +               request = msg->request;
> +               is_read = true;
> +               break;
> +       default:
> +               drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
> +                       msg->request);
> +               ret = -EINVAL;
> +               goto err;
> +       }
> +
> +       do {
> +               size_t to_access = min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
> +                                        msg->size - accessed_bytes);
> +
> +               ret = mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
> +                                            msg->address + accessed_bytes,
> +                                            msg->buffer + accessed_bytes,
> +                                            to_access);
> +
> +               if (ret) {
> +                       drm_info(mtk_dp->drm_dev,
> +                                "Failed to do AUX transfer: %d\n", ret);
> +                       goto err;
> +               }
> +               accessed_bytes += to_access;
> +       } while (accessed_bytes < msg->size);
> +
> +       msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
> +       return msg->size;
> +err:
> +       msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
> +       return ret;
> +}
> +
> +static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
> +{
> +       int ret;
> +
> +       ret = phy_init(mtk_dp->phy);
> +       if (ret) {
> +               dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
> +               return ret;
> +       }
> +
> +       mtk_dp_init_port(mtk_dp);
> +       mtk_dp_power_enable(mtk_dp);
> +
> +       return 0;
> +}
> +
> +static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
> +{
> +       mtk_dp_power_disable(mtk_dp);
> +       phy_exit(mtk_dp->phy);
> +}
> +
> +static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
> +                               enum drm_bridge_attach_flags flags)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       int ret;
> +
> +       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
> +               dev_err(mtk_dp->dev, "Driver does not provide a connector!");
> +               return -EINVAL;
> +       }
> +
> +       mtk_dp->aux.drm_dev = bridge->dev;
> +       ret = drm_dp_aux_register(&mtk_dp->aux);
> +       if (ret) {
> +               dev_err(mtk_dp->dev,
> +                       "failed to register DP AUX channel: %d\n", ret);
> +               return ret;
> +       }
> +
> +       ret = mtk_dp_poweron(mtk_dp);
> +       if (ret)
> +               goto err_aux_register;
> +
> +       if (mtk_dp->next_bridge) {
> +               ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
> +                                       &mtk_dp->bridge, flags);
> +               if (ret) {
> +                       drm_warn(mtk_dp->drm_dev,
> +                                "Failed to attach external bridge: %d\n", ret);
> +                       goto err_bridge_attach;
> +               }
> +       }
> +
> +       mtk_dp->drm_dev = bridge->dev;
> +
> +       mtk_dp_hwirq_enable(mtk_dp, true);
> +
> +       return 0;
> +
> +err_bridge_attach:
> +       mtk_dp_poweroff(mtk_dp);
> +err_aux_register:
> +       drm_dp_aux_unregister(&mtk_dp->aux);
> +       return ret;
> +}
> +
> +static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +       mtk_dp_hwirq_enable(mtk_dp, false);
> +       mtk_dp->drm_dev = NULL;
> +       mtk_dp_poweroff(mtk_dp);
> +       drm_dp_aux_unregister(&mtk_dp->aux);
> +}
> +
> +static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> +                                       struct drm_bridge_state *old_state)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       int ret;
> +
> +       mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
> +                                                               bridge->encoder);
> +       if (!mtk_dp->conn) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector is missing\n");
> +               return;
> +       }
> +
> +       /* power on aux */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                          DP_PWR_STATE_BANDGAP_TPLL_LANE,
> +                          DP_PWR_STATE_MASK);
> +
> +       if (mtk_dp->train_info.cable_plugged_in) {
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
> +               usleep_range(2000, 5000);
> +       }
> +
> +       /* Training */
> +       ret = mtk_dp_training(mtk_dp);
> +       if (ret) {
> +               drm_err(mtk_dp->drm_dev, "Training failed, %d\n", ret);
> +               goto power_off_aux;
> +       }
> +
> +       ret = mtk_dp_video_config(mtk_dp);
> +       if (ret)
> +               goto power_off_aux;
> +
> +       mtk_dp_video_enable(mtk_dp, true);
> +
> +       mtk_dp->enabled = true;
> +
> +       return;
> +power_off_aux:
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                          DP_PWR_STATE_BANDGAP_TPLL,
> +                          DP_PWR_STATE_MASK);
> +}
> +
> +static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> +                                        struct drm_bridge_state *old_state)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +
> +       mtk_dp->enabled = false;
> +       mtk_dp_video_enable(mtk_dp, false);
> +
> +       if (mtk_dp->train_info.cable_plugged_in) {
> +               drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
> +               usleep_range(2000, 3000);
> +       }
> +
> +       /* power off aux */
> +       mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +                          DP_PWR_STATE_BANDGAP_TPLL,
> +                          DP_PWR_STATE_MASK);
> +
> +       /* Ensure the sink is muted */
> +       msleep(20);
> +}
> +
> +static enum drm_mode_status
> +mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
> +                        const struct drm_display_info *info,
> +                        const struct drm_display_mode *mode)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       u32 bpp = info->color_formats & DRM_COLOR_FORMAT_YCBCR422 ? 16 : 24;
> +       u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
> +                             drm_dp_max_lane_count(mtk_dp->rx_cap),
> +                        drm_dp_bw_code_to_link_rate(mtk_dp->max_linkrate) *
> +                        mtk_dp->max_lanes);
> +
> +       if (rate < mode->clock * bpp / 8)
> +               return MODE_CLOCK_HIGH;
> +
> +       return MODE_OK;
> +}
> +
> +static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
> +                                                    struct drm_bridge_state *bridge_state,
> +                                                    struct drm_crtc_state *crtc_state,
> +                                                    struct drm_connector_state *conn_state,
> +                                                    unsigned int *num_output_fmts)
> +{
> +       u32 *output_fmts;
> +
> +       *num_output_fmts = 0;
> +       output_fmts = kmalloc(sizeof(*output_fmts), GFP_KERNEL);
> +       if (!output_fmts)
> +               return NULL;
> +       *num_output_fmts = 1;
> +       output_fmts[0] = MEDIA_BUS_FMT_FIXED;
> +       return output_fmts;
> +}
> +
> +static const u32 mt8195_input_fmts[] = {
> +       MEDIA_BUS_FMT_RGB888_1X24,
> +       MEDIA_BUS_FMT_YUV8_1X24,
> +       MEDIA_BUS_FMT_YUYV8_1X16,
> +};
> +
> +static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> +                                                   struct drm_bridge_state *bridge_state,
> +                                                   struct drm_crtc_state *crtc_state,
> +                                                   struct drm_connector_state *conn_state,
> +                                                   u32 output_fmt,
> +                                                   unsigned int *num_input_fmts)
> +{
> +       u32 *input_fmts;
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> +       struct drm_display_info *display_info =
> +               &conn_state->connector->display_info;
> +       u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
> +                             drm_dp_max_lane_count(mtk_dp->rx_cap),
> +                        drm_dp_bw_code_to_link_rate(mtk_dp->max_linkrate) *
> +                        mtk_dp->max_lanes);
> +
> +       *num_input_fmts = 0;
> +
> +       /*
> +        * If the linkrate is smaller than datarate of RGB888, larger than
> +        * datarate of YUV422 and sink device supports YUV422, we output YUV422
> +        * format. Use this condition, we can support more resolution.
> +        */
> +       if ((rate < (mode->clock * 24 / 8)) &&
> +           (rate > (mode->clock * 16 / 8)) &&
> +           (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) {
> +               input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL);
> +               if (!input_fmts)
> +                       return NULL;
> +               *num_input_fmts = 1;
> +               input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
> +       } else {
> +               input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts),
> +                                    sizeof(*input_fmts),
> +                                    GFP_KERNEL);
> +               if (!input_fmts)
> +                       return NULL;
> +
> +               *num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
> +               memcpy(input_fmts, mt8195_input_fmts, sizeof(mt8195_input_fmts));
> +       }
> +
> +       return input_fmts;
> +}
> +
> +static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
> +                                     struct drm_bridge_state *bridge_state,
> +                                     struct drm_crtc_state *crtc_state,
> +                                     struct drm_connector_state *conn_state)
> +{
> +       struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
> +       struct drm_crtc *crtc = conn_state->crtc;
> +       unsigned int input_bus_format;
> +
> +       input_bus_format = bridge_state->input_bus_cfg.format;
> +
> +       dev_dbg(mtk_dp->dev, "input format 0x%04x, output format 0x%04x\n",
> +               bridge_state->input_bus_cfg.format,
> +                bridge_state->output_bus_cfg.format);
> +
> +       if (input_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
> +               mtk_dp->info.format = DP_PIXELFORMAT_YUV422;
> +       else
> +               mtk_dp->info.format = DP_PIXELFORMAT_RGB;
> +
> +       if (!crtc) {
> +               drm_err(mtk_dp->drm_dev,
> +                       "Can't enable bridge as connector state doesn't have a crtc\n");
> +               return -EINVAL;
> +       }
> +
> +       drm_display_mode_to_videomode(&crtc_state->adjusted_mode, &mtk_dp->info.vm);
> +
> +       return 0;
> +}
> +
> +static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
> +       .atomic_check = mtk_dp_bridge_atomic_check,
> +       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> +       .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> +       .atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts,
> +       .atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts,
> +       .atomic_reset = drm_atomic_helper_bridge_reset,
> +       .attach = mtk_dp_bridge_attach,
> +       .detach = mtk_dp_bridge_detach,
> +       .atomic_enable = mtk_dp_bridge_atomic_enable,
> +       .atomic_disable = mtk_dp_bridge_atomic_disable,
> +       .mode_valid = mtk_dp_bridge_mode_valid,
> +       .get_edid = mtk_dp_get_edid,
> +};
> +
> +static int mtk_dp_probe(struct platform_device *pdev)
> +{
> +       struct mtk_dp *mtk_dp;
> +       struct device *dev = &pdev->dev;
> +       int ret, irq_num;
> +
> +       mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
> +       if (!mtk_dp)
> +               return -ENOMEM;
> +
> +       mtk_dp->dev = dev;
> +
> +       irq_num = platform_get_irq(pdev, 0);
> +       if (irq_num < 0)
> +               return dev_err_probe(dev, irq_num,
> +                                    "failed to request dp irq resource\n");
> +
> +       mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
> +       if (IS_ERR(mtk_dp->next_bridge))
> +               return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge),
> +                                    "Failed to get bridge\n");
> +
> +       ret = mtk_dp_dt_parse(mtk_dp, pdev);
> +       if (ret)
> +               return dev_err_probe(dev, ret, "Failed to parse dt\n");
> +
> +       drm_dp_aux_init(&mtk_dp->aux);
> +       mtk_dp->aux.name = "aux_mtk_dp";
> +       mtk_dp->aux.transfer = mtk_dp_aux_transfer;
> +
> +       spin_lock_init(&mtk_dp->irq_thread_lock);
> +
> +       ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
> +                                       mtk_dp_hpd_event_thread,
> +                                       IRQ_TYPE_LEVEL_HIGH, dev_name(dev),
> +                                       mtk_dp);
> +       if (ret)
> +               return dev_err_probe(dev, ret,
> +                                    "failed to request mediatek dptx irq\n");
> +
> +       platform_set_drvdata(pdev, mtk_dp);
> +
> +       mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
> +                                                       PLATFORM_DEVID_AUTO,
> +                                                       &mtk_dp->regs,
> +                                                       sizeof(struct regmap *));
> +       if (IS_ERR(mtk_dp->phy_dev))
> +               return dev_err_probe(dev, PTR_ERR(mtk_dp->phy_dev),
> +                                    "Failed to create device mediatek-dp-phy\n");
> +
> +       mtk_dp_get_calibration_data(mtk_dp);
> +
> +       mtk_dp->phy = devm_phy_get(&mtk_dp->phy_dev->dev, "dp");
> +
> +       if (IS_ERR(mtk_dp->phy)) {
> +               platform_device_unregister(mtk_dp->phy_dev);
> +               return dev_err_probe(dev, PTR_ERR(mtk_dp->phy),
> +                                    "Failed to get phy\n");
> +       }
> +
> +       mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
> +       mtk_dp->bridge.of_node = dev->of_node;
> +
> +       mtk_dp->bridge.ops =
> +               DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
> +       mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> +
> +       drm_bridge_add(&mtk_dp->bridge);
> +
> +       pm_runtime_enable(dev);
> +       pm_runtime_get_sync(dev);
> +
> +       return 0;
> +}
> +
> +static int mtk_dp_remove(struct platform_device *pdev)
> +{
> +       struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
> +
> +       pm_runtime_put(&pdev->dev);
> +       pm_runtime_disable(&pdev->dev);
> +       drm_bridge_remove(&mtk_dp->bridge);
> +       platform_device_unregister(mtk_dp->phy_dev);
> +
> +       return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int mtk_dp_suspend(struct device *dev)
> +{
> +       struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +       mtk_dp_power_disable(mtk_dp);
> +       mtk_dp_hwirq_enable(mtk_dp, false);
> +       pm_runtime_put_sync(dev);
> +
> +       return 0;
> +}
> +
> +static int mtk_dp_resume(struct device *dev)
> +{
> +       struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> +
> +       pm_runtime_get_sync(dev);
> +       mtk_dp_init_port(mtk_dp);
> +       mtk_dp_hwirq_enable(mtk_dp, true);
> +       mtk_dp_power_enable(mtk_dp);
> +
> +       return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
> +
> +static const struct of_device_id mtk_dp_of_match[] = {
> +       { .compatible = "mediatek,mt8195-edp-tx" },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
> +
> +struct platform_driver mtk_dp_driver = {
> +       .probe = mtk_dp_probe,
> +       .remove = mtk_dp_remove,
> +       .driver = {
> +               .name = "mediatek-drm-dp",
> +               .of_match_table = mtk_dp_of_match,
> +               .pm = &mtk_dp_pm_ops,
> +       },
> +};
> +
> +module_platform_driver(mtk_dp_driver);
> +
> +MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
> +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
> +MODULE_AUTHOR("Bo-Chen Chen <rex-bc.chen@mediatek.com>");
> +MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> new file mode 100644
> index 000000000000..3f01ba44871f
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> @@ -0,0 +1,305 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019-2022 MediaTek Inc.
> + * Copyright (c) 2022 BayLibre
> + */
> +#ifndef _MTK_DP_REG_H_
> +#define _MTK_DP_REG_H_
> +
> +#define SEC_OFFSET     0x4000
> +
> +#define MTK_DP_HPD_DISCONNECT          BIT(1)
> +#define MTK_DP_HPD_CONNECT             BIT(2)
> +#define MTK_DP_HPD_INTERRUPT           BIT(3)
> +
> +/* offset: 0x0 */
> +#define DP_PHY_GLB_BIAS_GEN_00         0x0
> +#define RG_XTP_GLB_BIAS_INTR_CTRL              GENMASK(20, 16)
> +#define DP_PHY_GLB_DPAUX_TX            0x8
> +#define RG_CKM_PT0_CKTX_IMPSEL                 GENMASK(23, 20)
> +#define MTK_DP_0034                    0x34
> +#define DA_XTP_GLB_CKDET_EN_FORCE_VAL          BIT(15)
> +#define DA_XTP_GLB_CKDET_EN_FORCE_EN           BIT(14)
> +#define DA_CKM_INTCKTX_EN_FORCE_VAL            BIT(13)
> +#define DA_CKM_INTCKTX_EN_FORCE_EN             BIT(12)
> +#define DA_CKM_CKTX0_EN_FORCE_VAL              BIT(11)
> +#define DA_CKM_CKTX0_EN_FORCE_EN               BIT(10)
> +#define DA_CKM_XTAL_CK_FORCE_VAL               BIT(9)
> +#define DA_CKM_XTAL_CK_FORCE_EN                        BIT(8)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_VAL           BIT(7)
> +#define DA_CKM_BIAS_LPF_EN_FORCE_EN            BIT(6)
> +#define DA_CKM_BIAS_EN_FORCE_VAL               BIT(5)
> +#define DA_CKM_BIAS_EN_FORCE_EN                        BIT(4)
> +#define DA_XTP_GLB_AVD10_ON_FORCE_VAL          BIT(3)
> +#define DA_XTP_GLB_AVD10_ON_FORCE              BIT(2)
> +#define DA_XTP_GLB_LDO_EN_FORCE_VAL            BIT(1)
> +#define DA_XTP_GLB_LDO_EN_FORCE_EN             BIT(0)
> +#define DP_PHY_LANE_TX_0               0x104
> +#define RG_XTP_LN0_TX_IMPSEL_PMOS              GENMASK(15, 12)
> +#define RG_XTP_LN0_TX_IMPSEL_NMOS              GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_1               0x204
> +#define RG_XTP_LN1_TX_IMPSEL_PMOS              GENMASK(15, 12)
> +#define RG_XTP_LN1_TX_IMPSEL_NMOS              GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_2               0x304
> +#define RG_XTP_LN2_TX_IMPSEL_PMOS              GENMASK(15, 12)
> +#define RG_XTP_LN2_TX_IMPSEL_NMOS              GENMASK(19, 16)
> +#define DP_PHY_LANE_TX_3               0x404
> +#define RG_XTP_LN3_TX_IMPSEL_PMOS              GENMASK(15, 12)
> +#define RG_XTP_LN3_TX_IMPSEL_NMOS              GENMASK(19, 16)
> +#define MTK_DP_1040                    0x1040
> +#define RG_DPAUX_RX_VALID_DEGLITCH_EN          BIT(2)
> +#define RG_XTP_GLB_CKDET_EN                    BIT(1)
> +#define RG_DPAUX_RX_EN                         BIT(0)
> +
> +/* offset: TOP_OFFSET (0x2000) */
> +#define MTK_DP_TOP_PWR_STATE           0x2000
> +#define DP_PWR_STATE_MASK                      GENMASK(1, 0)
> +#define DP_PWR_STATE_BANDGAP                   BIT(0)
> +#define DP_PWR_STATE_BANDGAP_TPLL              BIT(1)
> +#define DP_PWR_STATE_BANDGAP_TPLL_LANE         GENMASK(1, 0)
> +#define MTK_DP_TOP_SWING_EMP           0x2004
> +#define DP_TX0_VOLT_SWING_MASK                 GENMASK(1, 0)
> +#define DP_TX0_VOLT_SWING_SHIFT                        0
> +#define DP_TX0_PRE_EMPH_MASK                   GENMASK(3, 2)
> +#define DP_TX0_PRE_EMPH_SHIFT                  2
> +#define DP_TX1_VOLT_SWING_MASK                 GENMASK(9, 8)
> +#define DP_TX1_VOLT_SWING_SHIFT                        8
> +#define DP_TX1_PRE_EMPH_MASK                   GENMASK(11, 10)
> +#define DP_TX2_VOLT_SWING_MASK                 GENMASK(17, 16)
> +#define DP_TX2_PRE_EMPH_MASK                   GENMASK(19, 18)
> +#define DP_TX3_VOLT_SWING_MASK                 GENMASK(25, 24)
> +#define DP_TX3_PRE_EMPH_MASK                   GENMASK(27, 26)
> +#define MTK_DP_TOP_RESET_AND_PROBE     0x2020
> +#define SW_RST_B_PHYD                          BIT(4)
> +#define MTK_DP_TOP_IRQ_MASK            0x202c
> +#define IRQ_MASK_AUX_TOP_IRQ                   BIT(2)
> +#define MTK_DP_TOP_MEM_PD              0x2038
> +#define MEM_ISO_EN                             BIT(0)
> +#define FUSE_SEL                               BIT(2)
> +
> +/* offset: ENC0_OFFSET (0x3000) */
> +#define MTK_DP_ENC0_P0_3000                    0x3000
> +#define LANE_NUM_DP_ENC0_P0_MASK                       GENMASK(1, 0)
> +#define VIDEO_MUTE_SW_DP_ENC0_P0                       BIT(2)
> +#define VIDEO_MUTE_SEL_DP_ENC0_P0                      BIT(3)
> +#define ENHANCED_FRAME_EN_DP_ENC0_P0                   BIT(4)
> +#define MTK_DP_ENC0_P0_3004                    0x3004
> +#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK               BIT(8)
> +#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0           BIT(9)
> +#define MTK_DP_ENC0_P0_3010                    0x3010
> +#define HTOTAL_SW_DP_ENC0_P0_MASK                      GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3014                    0x3014
> +#define VTOTAL_SW_DP_ENC0_P0_MASK                      GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3018                    0x3018
> +#define HSTART_SW_DP_ENC0_P0_MASK                      GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_301C                    0x301c
> +#define VSTART_SW_DP_ENC0_P0_MASK                      GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3020                    0x3020
> +#define HWIDTH_SW_DP_ENC0_P0_MASK                      GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3024                    0x3024
> +#define VHEIGHT_SW_DP_ENC0_P0_MASK                     GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3028                    0x3028
> +#define HSW_SW_DP_ENC0_P0_MASK                         GENMASK(14, 0)
> +#define HSP_SW_DP_ENC0_P0_MASK                         BIT(15)
> +#define MTK_DP_ENC0_P0_302C                    0x302c
> +#define VSW_SW_DP_ENC0_P0_MASK                         GENMASK(14, 0)
> +#define VSP_SW_DP_ENC0_P0_MASK                         BIT(15)
> +#define MTK_DP_ENC0_P0_3030                    0x3030
> +#define HTOTAL_SEL_DP_ENC0_P0                          BIT(0)
> +#define VTOTAL_SEL_DP_ENC0_P0                          BIT(1)
> +#define HSTART_SEL_DP_ENC0_P0                          BIT(2)
> +#define VSTART_SEL_DP_ENC0_P0                          BIT(3)
> +#define HWIDTH_SEL_DP_ENC0_P0                          BIT(4)
> +#define VHEIGHT_SEL_DP_ENC0_P0                         BIT(5)
> +#define HSP_SEL_DP_ENC0_P0                             BIT(6)
> +#define HSW_SEL_DP_ENC0_P0                             BIT(7)
> +#define VSP_SEL_DP_ENC0_P0                             BIT(8)
> +#define VSW_SEL_DP_ENC0_P0                             BIT(9)
> +#define MTK_DP_ENC0_P0_3034                    0x3034
> +#define MTK_DP_ENC0_P0_3038                    0x3038
> +#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK               BIT(11)
> +#define MTK_DP_ENC0_P0_303C                    0x303c
> +#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK           GENMASK(5, 0)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK              GENMASK(10, 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT             (0 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT             (1 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT             (2 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT              (3 << 8)
> +#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT              (4 << 8)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK            GENMASK(14, 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB             (0 << 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422                (1 << 12)
> +#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420                (2 << 12)
> +#define VIDEO_MN_GEN_EN_DP_ENC0_P0                     BIT(15)
> +#define MTK_DP_ENC0_P0_3040                    0x3040
> +#define SDP_DOWN_CNT_DP_ENC0_P0_VAL                    0x20
> +#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK              GENMASK(11, 0)
> +#define MTK_DP_ENC0_P0_304C                    0x304c
> +#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK                        BIT(2)
> +#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK          BIT(8)
> +#define MTK_DP_ENC0_P0_3064                    0x3064
> +#define HDE_NUM_LAST_DP_ENC0_P0_MASK                   GENMASK(15, 0)
> +#define MTK_DP_ENC0_P0_3154                    0x3154
> +#define PGEN_HTOTAL_DP_ENC0_P0_MASK                    GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_3158                    0x3158
> +#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK              GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_315C                    0x315c
> +#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK         GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_3160                    0x3160
> +#define PGEN_HFDE_START_DP_ENC0_P0_MASK                        GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_3164                    0x3164
> +#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK         GENMASK(13, 0)
> +#define MTK_DP_ENC0_P0_3168                    0x3168
> +#define PGEN_VTOTAL_DP_ENC0_P0_MASK                    GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_316C                    0x316c
> +#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK              GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_3170                    0x3170
> +#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK         GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_3174                    0x3174
> +#define PGEN_VFDE_START_DP_ENC0_P0_MASK                        GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_3178                    0x3178
> +#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK         GENMASK(12, 0)
> +#define MTK_DP_ENC0_P0_31B0                    0x31b0
> +#define PGEN_PATTERN_SEL_VAL                           4
> +#define PGEN_PATTERN_SEL_MASK                          GENMASK(6, 4)
> +#define MTK_DP_ENC0_P0_31EC                    0x31ec
> +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0                    BIT(4)
> +#define ISRC1_HB3_DP_ENC0_P0_MASK                      GENMASK(15, 8)
> +
> +/* offset: ENC1_OFFSET (0x3200) */
> +#define MTK_DP_ENC1_P0_3300                    0x3300
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL             2
> +#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK            GENMASK(9, 8)
> +#define MTK_DP_ENC1_P0_3364                    0x3364
> +#define SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL          0x20
> +#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK    GENMASK(11, 0)
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_VAL           4
> +#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK          GENMASK(15, 12)
> +#define MTK_DP_ENC1_P0_3368                    0x3368
> +#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0       BIT(0)
> +#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0               BIT(4)
> +#define SDP_DP13_EN_DP_ENC1_P0                         BIT(8)
> +#define BS2BS_MODE_DP_ENC1_P0                          BIT(12)
> +#define BS2BS_MODE_DP_ENC1_P0_MASK                     GENMASK(13, 12)
> +#define BS2BS_MODE_DP_ENC1_P0_VAL                      1
> +#define DP_ENC1_P0_3368_VAL                            (VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0 | \
> +                                                        VIDEO_STABLE_CNT_THRD_DP_ENC1_P0 | \
> +                                                        SDP_DP13_EN_DP_ENC1_P0 | \
> +                                                        BS2BS_MODE_DP_ENC1_P0)
> +
> +/* offset: TRANS_OFFSET (0x3400) */
> +#define MTK_DP_TRANS_P0_3400                           0x3400
> +#define PATTERN1_EN_DP_TRANS_P0_MASK                           BIT(12)
> +#define PATTERN2_EN_DP_TRANS_P0_MASK                           BIT(13)
> +#define PATTERN3_EN_DP_TRANS_P0_MASK                           BIT(14)
> +#define PATTERN4_EN_DP_TRANS_P0_MASK                           BIT(15)
> +#define MTK_DP_TRANS_P0_3404                           0x3404
> +#define DP_SCR_EN_DP_TRANS_P0_MASK                             BIT(0)
> +#define MTK_DP_TRANS_P0_340C                           0x340c
> +#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0              BIT(13)
> +#define MTK_DP_TRANS_P0_3410                           0x3410
> +#define HPD_DEB_THD_DP_TRANS_P0_MASK                           GENMASK(3, 0)
> +#define HPD_INT_THD_DP_TRANS_P0_MASK                           GENMASK(7, 4)
> +#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US                    (2 << 4)
> +#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US                   (2 << 6)
> +#define HPD_DISC_THD_DP_TRANS_P0_MASK                          GENMASK(11, 8)
> +#define HPD_CONN_THD_DP_TRANS_P0_MASK                          GENMASK(15, 12)
> +#define MTK_DP_TRANS_P0_3414                           0x3414
> +#define HPD_DB_DP_TRANS_P0_MASK                                        BIT(2)
> +#define MTK_DP_TRANS_P0_3418                           0x3418
> +#define IRQ_CLR_DP_TRANS_P0_MASK                               GENMASK(3, 0)
> +#define IRQ_MASK_DP_TRANS_P0_MASK                              GENMASK(7, 4)
> +#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ                          (BIT(1) << 4)
> +#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ                          (BIT(2) << 4)
> +#define IRQ_MASK_DP_TRANS_P0_INT_IRQ                           (BIT(3) << 4)
> +#define IRQ_STATUS_DP_TRANS_P0_MASK                            GENMASK(15, 12)
> +#define MTK_DP_TRANS_P0_342C                           0x342c
> +#define XTAL_FREQ_DP_TRANS_P0_DEFAULT                          (BIT(0) | BIT(3) | BIT(5) | BIT(6))
> +#define XTAL_FREQ_DP_TRANS_P0_MASK                             GENMASK(7, 0)
> +#define MTK_DP_TRANS_P0_3430                           0x3430
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK                       GENMASK(1, 0)
> +#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT             BIT(1)
> +#define MTK_DP_TRANS_P0_34A4                           0x34a4
> +#define LANE_NUM_DP_TRANS_P0_MASK                              GENMASK(3, 2)
> +#define MTK_DP_TRANS_P0_3540                           0x3540
> +#define FEC_EN_DP_TRANS_P0_MASK                                        BIT(0)
> +#define FEC_CLOCK_EN_MODE_DP_TRANS_P0                          BIT(3)
> +#define MTK_DP_TRANS_P0_3580                           0x3580
> +#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK               BIT(8)
> +#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK               BIT(9)
> +#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK               BIT(10)
> +#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK               BIT(11)
> +#define MTK_DP_TRANS_P0_35C8                           0x35c8
> +#define SW_IRQ_CLR_DP_TRANS_P0_MASK                            GENMASK(15, 0)
> +#define SW_IRQ_STATUS_DP_TRANS_P0_MASK                         GENMASK(15, 0)
> +#define MTK_DP_TRANS_P0_35D0                           0x35d0
> +#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK                   GENMASK(15, 0)
> +#define MTK_DP_TRANS_P0_35F0                           0x35f0
> +#define DP_TRANS_DUMMY_RW_0                                    BIT(3)
> +#define DP_TRANS_DUMMY_RW_0_MASK                               GENMASK(3, 2)
> +
> +/* offset: AUX_OFFSET (0x3600) */
> +#define MTK_DP_AUX_P0_360C                     0x360c
> +#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK                 GENMASK(12, 0)
> +#define AUX_TIMEOUT_THR_AUX_TX_P0_VAL                  0x1595
> +#define MTK_DP_AUX_P0_3614                     0x3614
> +#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK               GENMASK(6, 0)
> +#define AUX_RX_UI_CNT_THR_AUX_FOR_26M                  13
> +#define MTK_DP_AUX_P0_3618                     0x3618
> +#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK                        BIT(9)
> +#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK       GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3620                     0x3620
> +#define AUX_RD_MODE_AUX_TX_P0_MASK                     BIT(9)
> +#define AUX_RX_FIFO_READ_PULSE_TX_P0                   BIT(8)
> +#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK           GENMASK(7, 0)
> +#define MTK_DP_AUX_P0_3624                     0x3624
> +#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK            GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3628                     0x3628
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK                        GENMASK(9, 0)
> +#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE             BIT(0)
> +#define MTK_DP_AUX_P0_362C                     0x362c
> +#define AUX_NO_LENGTH_AUX_TX_P0                                BIT(0)
> +#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK              BIT(1)
> +#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK               GENMASK(15, 2)
> +#define MTK_DP_AUX_P0_3630                     0x3630
> +#define AUX_TX_REQUEST_READY_AUX_TX_P0                 BIT(3)
> +#define MTK_DP_AUX_P0_3634                     0x3634
> +#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK         GENMASK(15, 8)
> +#define AUX_TX_OVER_SAMPLE_RATE_FOR_26M                        25
> +#define MTK_DP_AUX_P0_3640                     0x3640
> +#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0         BIT(6)
> +#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0                BIT(5)
> +#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0                BIT(4)
> +#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0                  BIT(3)
> +#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0                 BIT(2)
> +#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0                 BIT(1)
> +#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0                        BIT(0)
> +#define DP_AUX_P0_3640_VAL                             (AUX_400US_TIMEOUT_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_DATA_RECV_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_CMD_RECV_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0 | \
> +                                                        AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
> +#define MTK_DP_AUX_P0_3644                     0x3644
> +#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK             GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3648                     0x3648
> +#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK         GENMASK(15, 0)
> +#define MTK_DP_AUX_P0_364C                     0x364c
> +#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK         GENMASK(3, 0)
> +#define MTK_DP_AUX_P0_3650                     0x3650
> +#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK                        GENMASK(15, 12)
> +#define PHY_FIFO_RST_AUX_TX_P0_MASK                    BIT(9)
> +#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0                        BIT(8)
> +#define MTK_DP_AUX_P0_3658                     0x3658
> +#define AUX_TX_OV_EN_AUX_TX_P0_MASK                    BIT(0)
> +#define MTK_DP_AUX_P0_3690                     0x3690
> +#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0               BIT(8)
> +#define MTK_DP_AUX_P0_3704                     0x3704
> +#define AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK    BIT(1)
> +#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0              BIT(2)
> +#define MTK_DP_AUX_P0_3708                     0x3708
> +#define MTK_DP_AUX_P0_37C8                     0x37c8
> +#define MTK_ATOP_EN_AUX_TX_P0                          BIT(0)
> +
> +#endif /*_MTK_DP_REG_H_*/
> --
> 2.18.0
>

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

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

* Re: [PATCH v17 00/10] Add MT8195 DisplayPort driver
  2022-09-05 10:53     ` Dmitry Osipenko
  (?)
@ 2022-09-06  0:35       ` Chun-Kuang Hu
  -1 siblings, 0 replies; 73+ messages in thread
From: Chun-Kuang Hu @ 2022-09-06  0:35 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Chun-Kuang Hu, AngeloGioacchino Del Regno, DTML, linux-fbdev,
	Guillaume Ranquet, Jitao Shi, liangxu.xu, linux-kernel,
	DRI Development, Markus Schneider-Pargmann,
	Project_Global_Chrome_Upstream_Group,
	moderated list:ARM/Mediatek SoC support, Chen-Yu Tsai, Linux ARM,
	Dmitry Osipenko, Bo-Chen Chen, Philipp Zabel, Daniel Vetter,
	Rob Herring, krzysztof.kozlowski+dt, Maxime Ripard,
	Thomas Zimmermann, Matthias Brugger, deller, David Airlie

Hi, Dmitry:

Dmitry Osipenko <dmitry.osipenko@collabora.com> 於 2022年9月5日 週一 下午6:53寫道:
>
> On 9/4/22 15:59, Dmitry Osipenko wrote:
> > 01.09.2022 07:41, Bo-Chen Chen пишет:
> >> This patch is separated from v10 which is including dp driver, phy driver
> >> and dpintf driver. This series is only contained the DisplayPort driver.
> >>
> >> This series can be tested using 5.19-rc2 kernel and I test it in MT8195
> >> Tomato Chromebook. Modetest these modes:
> >
> > Applied to drm-misc-next, thanks!
>
> Hello Chun-Kuang Hu,
>
> Angelo told me today that you wanted to pick up the MTK driver patches
> and I applied them all to the drm-misc instead just of the "video/hdmi"
> patch. The series was fully reviewed and tested, so I had no doubts when
> applied all the patches.
>
> The applied patches can't be reverted, so if you have more changes
> prepared for the MTK driver, then please rebase them on top of the
> latest drm-misc-next.
>
> Apologizes for this confusion. Please let us know if we can help you.

OK, if this cannot be reverted, I could only accept this. Normally,
drm/mediatek patches would go though medaitek-drm-* branch. To prevent
any confusion, it's better to discuss before pick up.

Regards,
Chun-Kuang.

>
> --
> Best regards,
> Dmitry

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

* Re: [PATCH v17 00/10] Add MT8195 DisplayPort driver
@ 2022-09-06  0:35       ` Chun-Kuang Hu
  0 siblings, 0 replies; 73+ messages in thread
From: Chun-Kuang Hu @ 2022-09-06  0:35 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: linux-fbdev, DTML, David Airlie, DRI Development,
	krzysztof.kozlowski+dt, Dmitry Osipenko, deller,
	Project_Global_Chrome_Upstream_Group, Chen-Yu Tsai, Bo-Chen Chen,
	Chun-Kuang Hu, Jitao Shi, liangxu.xu, Markus Schneider-Pargmann,
	Rob Herring, moderated list:ARM/Mediatek SoC support,
	Matthias Brugger, Linux ARM, AngeloGioacchino Del Regno,
	Guillaume Ranquet, Thomas Zimmermann, linux-kernel

Hi, Dmitry:

Dmitry Osipenko <dmitry.osipenko@collabora.com> 於 2022年9月5日 週一 下午6:53寫道:
>
> On 9/4/22 15:59, Dmitry Osipenko wrote:
> > 01.09.2022 07:41, Bo-Chen Chen пишет:
> >> This patch is separated from v10 which is including dp driver, phy driver
> >> and dpintf driver. This series is only contained the DisplayPort driver.
> >>
> >> This series can be tested using 5.19-rc2 kernel and I test it in MT8195
> >> Tomato Chromebook. Modetest these modes:
> >
> > Applied to drm-misc-next, thanks!
>
> Hello Chun-Kuang Hu,
>
> Angelo told me today that you wanted to pick up the MTK driver patches
> and I applied them all to the drm-misc instead just of the "video/hdmi"
> patch. The series was fully reviewed and tested, so I had no doubts when
> applied all the patches.
>
> The applied patches can't be reverted, so if you have more changes
> prepared for the MTK driver, then please rebase them on top of the
> latest drm-misc-next.
>
> Apologizes for this confusion. Please let us know if we can help you.

OK, if this cannot be reverted, I could only accept this. Normally,
drm/mediatek patches would go though medaitek-drm-* branch. To prevent
any confusion, it's better to discuss before pick up.

Regards,
Chun-Kuang.

>
> --
> Best regards,
> Dmitry

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

* Re: [PATCH v17 00/10] Add MT8195 DisplayPort driver
@ 2022-09-06  0:35       ` Chun-Kuang Hu
  0 siblings, 0 replies; 73+ messages in thread
From: Chun-Kuang Hu @ 2022-09-06  0:35 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Chun-Kuang Hu, AngeloGioacchino Del Regno, DTML, linux-fbdev,
	Guillaume Ranquet, Jitao Shi, liangxu.xu, linux-kernel,
	DRI Development, Markus Schneider-Pargmann,
	Project_Global_Chrome_Upstream_Group,
	moderated list:ARM/Mediatek SoC support, Chen-Yu Tsai, Linux ARM,
	Dmitry Osipenko, Bo-Chen Chen, Philipp Zabel, Daniel Vetter,
	Rob Herring, krzysztof.kozlowski+dt, Maxime Ripard,
	Thomas Zimmermann, Matthias Brugger, deller, David Airlie

Hi, Dmitry:

Dmitry Osipenko <dmitry.osipenko@collabora.com> 於 2022年9月5日 週一 下午6:53寫道:
>
> On 9/4/22 15:59, Dmitry Osipenko wrote:
> > 01.09.2022 07:41, Bo-Chen Chen пишет:
> >> This patch is separated from v10 which is including dp driver, phy driver
> >> and dpintf driver. This series is only contained the DisplayPort driver.
> >>
> >> This series can be tested using 5.19-rc2 kernel and I test it in MT8195
> >> Tomato Chromebook. Modetest these modes:
> >
> > Applied to drm-misc-next, thanks!
>
> Hello Chun-Kuang Hu,
>
> Angelo told me today that you wanted to pick up the MTK driver patches
> and I applied them all to the drm-misc instead just of the "video/hdmi"
> patch. The series was fully reviewed and tested, so I had no doubts when
> applied all the patches.
>
> The applied patches can't be reverted, so if you have more changes
> prepared for the MTK driver, then please rebase them on top of the
> latest drm-misc-next.
>
> Apologizes for this confusion. Please let us know if we can help you.

OK, if this cannot be reverted, I could only accept this. Normally,
drm/mediatek patches would go though medaitek-drm-* branch. To prevent
any confusion, it's better to discuss before pick up.

Regards,
Chun-Kuang.

>
> --
> Best regards,
> Dmitry

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

end of thread, other threads:[~2022-09-06  0:37 UTC | newest]

Thread overview: 73+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-01  4:41 [PATCH v17 00/10] Add MT8195 DisplayPort driver Bo-Chen Chen
2022-09-01  4:41 ` Bo-Chen Chen
2022-09-01  4:41 ` Bo-Chen Chen
2022-09-01  4:41 ` [PATCH v17 01/10] dt-bindings: mediatek,dp: Add Display Port binding Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-01  4:41 ` [PATCH v17 02/10] video/hdmi: Add audio_infoframe packing for DP Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-04 14:56   ` Dmitry Osipenko
2022-09-04 14:56     ` Dmitry Osipenko
2022-09-01  4:41 ` [PATCH v17 03/10] drm/mediatek: Add MT8195 Embedded DisplayPort driver Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-02  7:17   ` CK Hu
2022-09-02  7:17     ` CK Hu
2022-09-02  7:17     ` CK Hu
2022-09-06  0:07   ` Chun-Kuang Hu
2022-09-06  0:07     ` Chun-Kuang Hu
2022-09-06  0:07     ` Chun-Kuang Hu
2022-09-01  4:41 ` [PATCH v17 04/10] drm/mediatek: dp: Add multiple bridge types support Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-02  7:22   ` CK Hu
2022-09-02  7:22     ` CK Hu
2022-09-02  7:22     ` CK Hu
2022-09-01  4:41 ` [PATCH v17 05/10] drm/mediatek: dp: Add multiple smc commands support Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-02  7:32   ` CK Hu
2022-09-02  7:32     ` CK Hu
2022-09-02  7:32     ` CK Hu
2022-09-01  4:41 ` [PATCH v17 06/10] drm/mediatek: dp: Add multiple calibration data formats support Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-02  7:38   ` CK Hu
2022-09-02  7:38     ` CK Hu
2022-09-02  7:38     ` CK Hu
2022-09-01  4:41 ` [PATCH v17 07/10] drm/mediatek: dp: Determine device of next_bridge Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-02  7:44   ` CK Hu
2022-09-02  7:44     ` CK Hu
2022-09-02  7:44     ` CK Hu
2022-09-01  4:41 ` [PATCH v17 08/10] drm/mediatek: dp: Add MT8195 External DisplayPort support Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-02  8:07   ` CK Hu
2022-09-02  8:07     ` CK Hu
2022-09-02  8:07     ` CK Hu
2022-09-01  4:41 ` [PATCH v17 09/10] drm/mediatek: dp: Add hpd debounce Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-02  8:12   ` CK Hu
2022-09-02  8:12     ` CK Hu
2022-09-02  8:12     ` CK Hu
2022-09-01  4:41 ` [PATCH v17 10/10] drm/mediatek: dp: Audio support for MT8195 Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-01  4:41   ` Bo-Chen Chen
2022-09-02  8:57   ` CK Hu
2022-09-02  8:57     ` CK Hu
2022-09-02  8:57     ` CK Hu
2022-09-01 10:58 ` [PATCH v17 00/10] Add MT8195 DisplayPort driver AngeloGioacchino Del Regno
2022-09-01 10:58   ` AngeloGioacchino Del Regno
2022-09-01 10:58   ` AngeloGioacchino Del Regno
2022-09-04 12:59 ` Dmitry Osipenko
2022-09-04 12:59   ` Dmitry Osipenko
2022-09-05 10:53   ` Dmitry Osipenko
2022-09-05 10:53     ` Dmitry Osipenko
2022-09-05 10:53     ` Dmitry Osipenko
2022-09-06  0:35     ` Chun-Kuang Hu
2022-09-06  0:35       ` Chun-Kuang Hu
2022-09-06  0:35       ` Chun-Kuang Hu

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.