mbox series

[v15,00/11] drm/mediatek: Add MT8195 DisplayPort driver

Message ID 20220727045035.32225-1-rex-bc.chen@mediatek.com
Headers show
Series drm/mediatek: Add MT8195 DisplayPort driver | expand

Message

Rex-BC Chen (陳柏辰) July 27, 2022, 4:50 a.m. UTC
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 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/

Bo-Chen Chen (3):
  drm/mediatek: Add retry to prevent misjudgment for sink devices
  drm/mediatek: set monitor to DP_SET_POWER_D3 to avoid garbage
  drm/mediatek: Use cached audio config when changing resolution

Guillaume Ranquet (4):
  drm/edid: Convert cea_sad helper struct to kernelDoc
  drm/edid: Add cea_sad helpers for freq/length
  drm/mediatek: Add MT8195 External DisplayPort support
  drm/mediatek: DP audio support for MT8195

Jitao Shi (1):
  drm/mediatek: 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         |  117 +
 drivers/gpu/drm/drm_edid.c                    |   63 +
 drivers/gpu/drm/mediatek/Kconfig              |    9 +
 drivers/gpu/drm/mediatek/Makefile             |    2 +
 drivers/gpu/drm/mediatek/mtk_dp.c             | 2854 +++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_dp_reg.h         |  545 ++++
 drivers/video/hdmi.c                          |   82 +-
 include/drm/display/drm_dp.h                  |    2 +
 include/drm/drm_edid.h                        |   26 +-
 include/linux/hdmi.h                          |    7 +-
 10 files changed, 3684 insertions(+), 23 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

Comments

AngeloGioacchino Del Regno July 27, 2022, 9:34 a.m. UTC | #1
Il 27/07/22 06:50, Bo-Chen Chen ha scritto:
> From: Guillaume Ranquet <granquet@baylibre.com>
> 
> This patch adds two helper functions that extract the frequency and word
> length from a struct cea_sad.
> 
> For these helper functions new defines are added that help translate the
> 'freq' and 'byte2' fields into real numbers.
> 
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>

Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
AngeloGioacchino Del Regno July 27, 2022, 9:35 a.m. UTC | #2
Il 27/07/22 06:50, Bo-Chen Chen ha scritto:
> 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>
AngeloGioacchino Del Regno July 27, 2022, 9:36 a.m. UTC | #3
Il 27/07/22 06:50, Bo-Chen Chen ha scritto:
> 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>

Tested-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
AngeloGioacchino Del Regno July 27, 2022, 9:38 a.m. UTC | #4
Il 27/07/22 06:50, Bo-Chen Chen ha scritto:
> From: Guillaume Ranquet <granquet@baylibre.com>
> 
> This patch adds 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>

Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
AngeloGioacchino Del Regno July 27, 2022, 9:40 a.m. UTC | #5
Il 27/07/22 06:50, Bo-Chen Chen ha scritto:
> For some DP dungles, we need to train more than onece to confirm that we
> don't misjudge the status of sink device.

Please fix the typos in your commit title and description.
title: misjudgment -> misjudgement
desc: dungles->dongles; onece->once

> 
> Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> ---
>   drivers/gpu/drm/mediatek/mtk_dp.c | 21 ++++++++++++++++++---
>   1 file changed, 18 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
> index ce817cb59445..80d7d6488105 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -42,6 +42,7 @@
>   #define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
>   #define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
>   #define MTK_DP_TRAIN_DOWNSCALE_RETRY 8
> +#define MTK_DP_TRAIN_CLEAR_RETRY 50
>   
>   struct mtk_dp_train_info {
>   	bool tps3;
> @@ -1431,11 +1432,25 @@ static int mtk_dp_video_config(struct mtk_dp *mtk_dp)
>   
>   static int mtk_dp_training(struct mtk_dp *mtk_dp)
>   {
> +	short max_retry = MTK_DP_TRAIN_CLEAR_RETRY;
>   	int ret;
>   
> -	ret = mtk_dp_train_start(mtk_dp);
> -	if (ret)
> -		return ret;
> +	/*
> +	 * We do retry to confirm that we don't misjudge the sink status.
> +	 * If it is still failed, we can confirm there are some issues for the
> +	 * sink device.
> +	 */
> +	do {
> +		ret = mtk_dp_train_start(mtk_dp);
> +		if (!ret)
> +			break;
> +	} while (--max_retry);
> +
> +	dev_info(mtk_dp->dev, "dp training clear retry times: %d\n",
> +		 MTK_DP_TRAIN_CLEAR_RETRY - max_retry);

dev_dbg() here.

...after which,

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

> +
> +	if (!max_retry)
> +		return -ETIMEDOUT;
>   
>   	ret = mtk_dp_video_config(mtk_dp);
>   	if (ret)
Rex-BC Chen (陳柏辰) July 28, 2022, 9:40 a.m. UTC | #6
On Wed, 2022-07-27 at 11:40 +0200, AngeloGioacchino Del Regno wrote:
> Il 27/07/22 06:50, Bo-Chen Chen ha scritto:
> > For some DP dungles, we need to train more than onece to confirm
> > that we
> > don't misjudge the status of sink device.
> 
> Please fix the typos in your commit title and description.
> title: misjudgment -> misjudgement
> desc: dungles->dongles; onece->once
> 
> > 
> > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> > ---
> >   drivers/gpu/drm/mediatek/mtk_dp.c | 21 ++++++++++++++++++---
> >   1 file changed, 18 insertions(+), 3 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> > b/drivers/gpu/drm/mediatek/mtk_dp.c
> > index ce817cb59445..80d7d6488105 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> > @@ -42,6 +42,7 @@
> >   #define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> >   #define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> >   #define MTK_DP_TRAIN_DOWNSCALE_RETRY 8
> > +#define MTK_DP_TRAIN_CLEAR_RETRY 50
> >   
> >   struct mtk_dp_train_info {
> >   	bool tps3;
> > @@ -1431,11 +1432,25 @@ static int mtk_dp_video_config(struct
> > mtk_dp *mtk_dp)
> >   
> >   static int mtk_dp_training(struct mtk_dp *mtk_dp)
> >   {
> > +	short max_retry = MTK_DP_TRAIN_CLEAR_RETRY;
> >   	int ret;
> >   
> > -	ret = mtk_dp_train_start(mtk_dp);
> > -	if (ret)
> > -		return ret;
> > +	/*
> > +	 * We do retry to confirm that we don't misjudge the sink
> > status.
> > +	 * If it is still failed, we can confirm there are some issues
> > for the
> > +	 * sink device.
> > +	 */
> > +	do {
> > +		ret = mtk_dp_train_start(mtk_dp);
> > +		if (!ret)
> > +			break;
> > +	} while (--max_retry);
> > +
> > +	dev_info(mtk_dp->dev, "dp training clear retry times: %d\n",
> > +		 MTK_DP_TRAIN_CLEAR_RETRY - max_retry);
> 
> dev_dbg() here.
> 
> ...after which,
> 
> Reviewed-by: AngeloGioacchino Del Regno <
> angelogioacchino.delregno@collabora.com>
> 

Hello Angelo,

Thanks for your review.
I will modify these in next version.

BRs,
Bo-Chen

> > +
> > +	if (!max_retry)
> > +		return -ETIMEDOUT;
> >   
> >   	ret = mtk_dp_video_config(mtk_dp);
> >   	if (ret)
> 
>
CK Hu (胡俊光) Aug. 2, 2022, 2:37 a.m. UTC | #7
Hi, Bo-Chen:

On Wed, 2022-07-27 at 12:50 +0800, Bo-Chen Chen wrote:
> From: Guillaume Ranquet <granquet@baylibre.com>
> 
> This patch adds 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     | 181 +++++++++++++++++++++---
> --
>  drivers/gpu/drm/mediatek/mtk_dp_reg.h |   1 +
>  2 files changed, 146 insertions(+), 36 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> b/drivers/gpu/drm/mediatek/mtk_dp.c
> index 06eeecedd49e..ce817cb59445 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> @@ -103,6 +103,11 @@ static struct regmap_config mtk_dp_regmap_config
> = {
>  	.name = "mtk-dp-registers",
>  };
>  
> +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
> +{
> +	return mtk_dp->next_bridge;

For external DP, it could also connect to a bridge IC which transfer DP
to HDMI or some interface else. Use the compatible to judge this edp or
dp.

> +}
> +
>  static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
>  {
>  	return container_of(b, struct mtk_dp, bridge);
> @@ -341,6 +346,19 @@ static void mtk_dp_pg_enable(struct mtk_dp
> *mtk_dp, bool enable)
>  			   4 << PGEN_PATTERN_SEL_SHIFT,
> PGEN_PATTERN_SEL_MASK);
>  }
>  
> +static bool mtk_dp_plug_state(struct mtk_dp *mtk_dp)
> +{
> +	return mtk_dp->train_info.cable_plugged_in;
> +}
> +
> +static bool mtk_dp_plug_state_avoid_pulse(struct mtk_dp *mtk_dp)
> +{
> +	bool ret;
> +
> +	return !(readx_poll_timeout(mtk_dp_plug_state, mtk_dp, ret,
> ret,
> +				    4000, 7 * 4000));
> +}
> +
>  static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
>  {
>  	mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640,
> @@ -778,35 +796,67 @@ static void mtk_dp_get_calibration_data(struct
> mtk_dp *mtk_dp)
>  		return;
>  	}
>  
> -	cal_data->glb_bias_trim =
> -		check_cal_data_valid(mtk_dp, 1, 0x1e,
> -				     (buf[3] >> 27) & 0x1f, 0xf);
> -	cal_data->clktx_impse =
> -		check_cal_data_valid(mtk_dp, 1, 0xe,
> -				     (buf[0] >> 9) & 0xf, 0x8);
> -	cal_data->ln_tx_impsel_pmos[0] =
> -		check_cal_data_valid(mtk_dp, 1, 0xe,
> -				     (buf[2] >> 28) & 0xf, 0x8);
> -	cal_data->ln_tx_impsel_nmos[0] =
> -		check_cal_data_valid(mtk_dp, 1, 0xe,
> -				     (buf[2] >> 24) & 0xf, 0x8);
> -	cal_data->ln_tx_impsel_pmos[1] =
> -		check_cal_data_valid(mtk_dp, 1, 0xe,
> -				     (buf[2] >> 20) & 0xf, 0x8);
> -	cal_data->ln_tx_impsel_nmos[1] =
> -		check_cal_data_valid(mtk_dp, 1, 0xe,
> -				     (buf[2] >> 16) & 0xf, 0x8);
> -	cal_data->ln_tx_impsel_pmos[2] =
> -		check_cal_data_valid(mtk_dp, 1, 0xe,
> -				     (buf[2] >> 12) & 0xf, 0x8);
> -	cal_data->ln_tx_impsel_nmos[2] =
> -		check_cal_data_valid(mtk_dp, 1, 0xe,
> -				     (buf[2] >> 8) & 0xf, 0x8);
> -	cal_data->ln_tx_impsel_pmos[3] =
> -		check_cal_data_valid(mtk_dp, 1, 0xe,
> -				     (buf[2] >> 4) & 0xf, 0x8);
> -	cal_data->ln_tx_impsel_nmos[3] =
> -		check_cal_data_valid(mtk_dp, 1, 0xe, buf[2] & 0xf,
> 0x8);
> +	if (mtk_dp_is_edp(mtk_dp)) {
> +		cal_data->glb_bias_trim =
> +			check_cal_data_valid(mtk_dp, 1, 0x1e,
> +					     (buf[3] >> 27) & 0x1f,
> 0xf);
> +		cal_data->clktx_impse =
> +			check_cal_data_valid(mtk_dp, 1, 0xe,
> +					     (buf[0] >> 9) & 0xf, 0x8);
> +		cal_data->ln_tx_impsel_pmos[0] =
> +			check_cal_data_valid(mtk_dp, 1, 0xe,
> +					     (buf[2] >> 28) & 0xf,
> 0x8);
> +		cal_data->ln_tx_impsel_nmos[0] =
> +			check_cal_data_valid(mtk_dp, 1, 0xe,
> +					     (buf[2] >> 24) & 0xf,
> 0x8);
> +		cal_data->ln_tx_impsel_pmos[1] =
> +			check_cal_data_valid(mtk_dp, 1, 0xe,
> +					     (buf[2] >> 20) & 0xf,
> 0x8);
> +		cal_data->ln_tx_impsel_nmos[1] =
> +			check_cal_data_valid(mtk_dp, 1, 0xe,
> +					     (buf[2] >> 16) & 0xf,
> 0x8);
> +		cal_data->ln_tx_impsel_pmos[2] =
> +			check_cal_data_valid(mtk_dp, 1, 0xe,
> +					     (buf[2] >> 12) & 0xf,
> 0x8);
> +		cal_data->ln_tx_impsel_nmos[2] =
> +			check_cal_data_valid(mtk_dp, 1, 0xe,
> +					     (buf[2] >> 8) & 0xf, 0x8);
> +		cal_data->ln_tx_impsel_pmos[3] =
> +			check_cal_data_valid(mtk_dp, 1, 0xe,
> +					     (buf[2] >> 4) & 0xf, 0x8);
> +		cal_data->ln_tx_impsel_nmos[3] =
> +			check_cal_data_valid(mtk_dp, 1, 0xe, buf[2] &
> 0xf, 0x8);
> +	} else {
> +		cal_data->glb_bias_trim =
> +			check_cal_data_valid(mtk_dp, 1, 0x1e,
> +					     (buf[0] >> 27) & 0x1f,
> 0xf);

Why not place glb_bias_trim value in nvmem buf[3] for both edp and dp
case? Place in the same nvmem position, the driver code would be
simple. 

> +		cal_data->clktx_impse =
> +			check_cal_data_valid(mtk_dp, 1, 0xe,
> +					     (buf[0] >> 13) & 0xf,
> 0x8);
> +		cal_data->ln_tx_impsel_pmos[0] =
> +			check_cal_data_valid(mtk_dp, 1, 0xe,
> +					     (buf[1] >> 28) & 0xf,
> 0x8);
> +		cal_data->ln_tx_impsel_nmos[0] =
> +			check_cal_data_valid(mtk_dp, 1, 0xe,
> +					     (buf[1] >> 24) & 0xf,
> 0x8);
> +		cal_data->ln_tx_impsel_pmos[1] =
> +			check_cal_data_valid(mtk_dp, 1, 0xe,
> +					     (buf[1] >> 20) & 0xf,
> 0x8);
> +		cal_data->ln_tx_impsel_nmos[1] =
> +			check_cal_data_valid(mtk_dp, 1, 0xe,
> +					     (buf[1] >> 16) & 0xf,
> 0x8);
> +		cal_data->ln_tx_impsel_pmos[2] =
> +			check_cal_data_valid(mtk_dp, 1, 0xe,
> +					     (buf[1] >> 12) & 0xf,
> 0x8);
> +		cal_data->ln_tx_impsel_nmos[2] =
> +			check_cal_data_valid(mtk_dp, 1, 0xe,
> +					     (buf[1] >> 8) & 0xf, 0x8);
> +		cal_data->ln_tx_impsel_pmos[3] =
> +			check_cal_data_valid(mtk_dp, 1, 0xe,
> +					     (buf[1] >> 4) & 0xf, 0x8);
> +		cal_data->ln_tx_impsel_nmos[3] =
> +			check_cal_data_valid(mtk_dp, 1, 0xe, buf[1] &
> 0xf, 0x8);

Why not place ln_tx_impsel_nmos and ln_tx_impsel_pmos value in nvmem
buf[2] for both edp and dp case? Place in the same nvmem position, the
driver code would be simple. 

> +	}
>  
>  	kfree(buf);
>  }
> @@ -926,7 +976,10 @@ static void mtk_dp_video_mute(struct mtk_dp
> *mtk_dp, bool enable)
>  			   VIDEO_MUTE_SEL_DP_ENC0_P0_MASK |
>  			   VIDEO_MUTE_SW_DP_ENC0_P0_MASK);
>  
> -	mtk_dp_sip_atf_call(mtk_dp, MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
> enable);
> +	mtk_dp_sip_atf_call(mtk_dp,
> +			    mtk_dp_is_edp(mtk_dp) ?
> +			    MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE :
> +			    MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable);

Use of_device_get_match_data to select MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE
or MTK_DP_SIP_ATF_VIDEO_UNMUTE.

>  }
>  
>  static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
> @@ -1226,6 +1279,9 @@ static int mtk_dp_parse_capabilities(struct
> mtk_dp *mtk_dp)
>  	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D0);
>  	usleep_range(2000, 5000);
>  
> +	if (!mtk_dp_plug_state(mtk_dp))
> +		return -ENODEV;
> +
>  	drm_dp_read_dpcd_caps(&mtk_dp->aux, mtk_dp->rx_cap);
>  
>  	train_info->link_rate = min_t(int, mtk_dp->max_linkrate,
> @@ -1277,6 +1333,9 @@ static int mtk_dp_train_start(struct mtk_dp
> *mtk_dp)
>  	u8 train_limit;
>  	u8 max_link_rate;
>  
> +	if (!mtk_dp_plug_state_avoid_pulse(mtk_dp))
> +		return -ENODEV;
> +
>  	link_rate = mtk_dp->rx_cap[1];
>  	lane_count = mtk_dp->rx_cap[2] & 0x1F;
>  
> @@ -1412,6 +1471,9 @@ static irqreturn_t mtk_dp_hpd_event_thread(int
> hpd, void *dev)
>  {
>  	struct mtk_dp *mtk_dp = dev;
>  
> +	dev_dbg(mtk_dp->dev, "drm_helper_hpd_irq_event\n");
> +	drm_helper_hpd_irq_event(mtk_dp->bridge.dev);

Because edp also have hpd interrupt, so I would like both edp and dp
have the same control flow for hdp. So move this to edp patch.

> +
>  	if (mtk_dp->train_info.hpd_inerrupt) {
>  		dev_dbg(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n");
>  		mtk_dp->train_info.hpd_inerrupt = false;
> @@ -1452,9 +1514,20 @@ static irqreturn_t mtk_dp_hpd_event(int hpd,
> void *dev)
>  		else
>  			train_info->cable_plugged_in = false;
>  
> -		mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> -				   DP_PWR_STATE_BANDGAP_TPLL_LANE,
> -				   DP_PWR_STATE_MASK);
> +		if (!train_info->cable_plugged_in) {
> +			mtk_dp_video_mute(mtk_dp, true);
> +
> +			mtk_dp_initialize_priv_data(mtk_dp);
> +			mtk_dp_set_idle_pattern(mtk_dp, true);
> +
> +			mtk_dp_update_bits(mtk_dp,
> MTK_DP_TOP_PWR_STATE,
> +					   DP_PWR_STATE_BANDGAP_TPLL,
> +					   DP_PWR_STATE_MASK);
> +		} else {
> +			mtk_dp_update_bits(mtk_dp,
> MTK_DP_TOP_PWR_STATE,
> +					   DP_PWR_STATE_BANDGAP_TPLL_LA
> NE,
> +					   DP_PWR_STATE_MASK);
> +		}
>  	}
>  
>  	return IRQ_HANDLED;
> @@ -1499,6 +1572,24 @@ 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;
> +	u8 sink_count = 0;
> +
> +	if (mtk_dp_is_edp(mtk_dp))
> +		return connector_status_connected;

Because edp also have hpd interrupt, so I would like both edp and dp
have the same control flow for hdp. So remove this.

> +
> +	if (mtk_dp_plug_state_avoid_pulse(mtk_dp)) {
> +		drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT,
> &sink_count);
> +		if (DP_GET_SINK_COUNT(sink_count))
> +			ret = connector_status_connected;
> +	}
> +
> +	return ret;
> +}
> +
>  static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
>  				    struct drm_connector *connector)
>  {
> @@ -1512,7 +1603,8 @@ static struct edid *mtk_dp_get_edid(struct
> drm_bridge *bridge,
>  	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);
> +	if (mtk_dp_plug_state(mtk_dp))
> +		new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);

Please explain why only dp has this problem. If edp also has this
problem, move this to edp patch.

Regards,
CK

>  
>  	if (!enabled)
>  		drm_bridge_chain_post_disable(bridge);
> @@ -1852,6 +1944,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)
> @@ -1873,9 +1966,15 @@ 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) {
> +		dev_info(dev,
> +			 "No panel connected in devicetree, continue as
> external DP\n");
> +		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");
> +	}
>  
>  	ret = mtk_dp_dt_parse(mtk_dp, pdev);
>  	if (ret)
> @@ -1918,7 +2017,10 @@ 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;
> +	if (mtk_dp_is_edp(mtk_dp))
> +		mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> +	else
> +		mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
>  
>  	drm_bridge_add(&mtk_dp->bridge);
>  
> @@ -1945,6 +2047,12 @@ static int mtk_dp_suspend(struct device *dev)
>  {
>  	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
>  
> +	if (mtk_dp_plug_state(mtk_dp)) {
> +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> DP_SET_POWER_D3);
> +		/* Ensure the sink is off before shutting down power */
> +		usleep_range(2000, 3000);
> +	}
> +
>  	mtk_dp_power_disable(mtk_dp);
>  
>  	mtk_dp_hwirq_enable(mtk_dp, false);
> @@ -1978,6 +2086,7 @@ 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" },
> +	{ .compatible = "mediatek,mt8195-dp-tx" },
>  	{},
>  };
>  MODULE_DEVICE_TABLE(of, 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 4ff8e9880dc9..59086eb82868 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> @@ -14,6 +14,7 @@
>  #define SEC_OFFSET	0x4000
>  
>  #define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE	(BIT(0) | BIT(5))
> +#define MTK_DP_SIP_ATF_VIDEO_UNMUTE	BIT(5)
>  
>  #define DP_PHY_GLB_BIAS_GEN_00		0
>  #define RG_XTP_GLB_BIAS_INTR_CTRL	GENMASK(20, 16)
CK Hu (胡俊光) Aug. 2, 2022, 2:56 a.m. UTC | #8
Hi, Bo-Chen:

On Wed, 2022-07-27 at 12:50 +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.
> 
> 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>
> ---

[snip]

> +
> +#define MTK_DP_ENC0_P0_31D0			(ENC0_OFFSET + 0x1D0)

MTK_DP_ENC0_P0_31D0 is uselese, so remove it.

> +#define VSC_EXT_CEA_HB0_DP_ENC0_P0_MASK		GENMASK(7, 0)
> +#define VSC_EXT_CEA_HB1_DP_ENC0_P0_MASK		GENMASK(15, 8)
> +#define VSC_EXT_CEA_HB1_DP_ENC0_P0_SHIFT	BIT(3)
> +
> +#define MTK_DP_ENC0_P0_31D4			(ENC0_OFFSET + 0x1D4)

Ditto.

> +#define VSC_EXT_CEA_HB2_DP_ENC0_P0_MASK		GENMASK(7, 0)
> +#define VSC_EXT_CEA_HB2_DP_ENC0_P0_SHIFT	0
> +#define VSC_EXT_CEA_HB3_DP_ENC0_P0_MASK		GENMASK(15, 8)
> +
> +#define MTK_DP_ENC0_P0_31D8			(ENC0_OFFSET + 0x1D8)

Ditto.

> +#define VSC_EXT_VESA_NUM_DP_ENC0_P0_MASK	GENMASK(5, 0)
> +#define VSC_EXT_VESA_NUM_DP_ENC0_P0_SHIFT	0
> +#define VSC_EXT_CEA_NUM_DP_ENC0_P0_MASK		GENMASK(13, 8)
> +#define VSC_EXT_CEA_NUM_DP_ENC0_P0_SHIFT	BIT(3)
> +
> +#define MTK_DP_ENC0_P0_31DC			(ENC0_OFFSET + 0x1DC)

Ditto.

Regards,
CK

> +#define HDR0_CFG_DP_ENC0_P0_MASK		GENMASK(7, 0)
> +#define HDR0_CFG_DP_ENC0_P0_SHIFT		0
> +#define MTK_DP_ENC0_P0_31E8			(ENC0_OFFSET + 0x1E8)
> +#define MTK_DP_ENC0_P0_31EC			(ENC0_OFFSET + 0x1EC)
> +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK	BIT(4)
> +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT	BIT(2)
> +#define ISRC1_HB3_DP_ENC0_P0_MASK		GENMASK(15, 8)
> +#define ISRC1_HB3_DP_ENC0_P0_SHIFT		BIT(3)
> +
CK Hu (胡俊光) Aug. 2, 2022, 5:09 a.m. UTC | #9
Hi, Bo-Chen:

On Wed, 2022-07-27 at 12:50 +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.
> 
> 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>
> ---

[snip]

> +
> +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev;
> +
> +	if (mtk_dp->train_info.hpd_inerrupt) {

When the thread is running, mtk_dp->train_info.hpd_inerrupt would be
true. So this checking is redundant.

> +		dev_dbg(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n");
> +		mtk_dp->train_info.hpd_inerrupt = false;
> +		mtk_dp_hpd_sink_event(mtk_dp);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
> +{
> +	struct mtk_dp *mtk_dp = dev;
> +	struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
> +	u32 irq_status;
> +
> +	irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS);
> +
> +	if (!irq_status)
> +		return IRQ_HANDLED;
> +
> +	if (irq_status & RGS_IRQ_STATUS_TRANSMITTER) {

Combine this if-checking with previous if-checking, it would be:

if (!(irq_status & RGS_IRQ_STATUS_TRANSMITTER))
	return IRQ_HANDLED;

> +		irq_status = mtk_dp_swirq_get_clear(mtk_dp) |
> +			     mtk_dp_hwirq_get_clear(mtk_dp);
> +
> +		if (!irq_status)
> +			return IRQ_HANDLED;
> +
> +		if (irq_status & MTK_DP_HPD_INTERRUPT)

Does this interrupt MTK_DP_HPD_INTERRUPT have any relation with
MTK_DP_HPD_CONNECT and MTK_DP_HPD_CONNECT? From the naming, I guess
that when MTK_DP_HPD_CONNECT happen, MTK_DP_HPD_INTERRUPT would also
happen. Either for MTK_DP_HPD_DISCONNECT. When would
MTK_DP_HPD_INTERRUPT happen but MTK_DP_HPD_CONNECT or
MTK_DP_HPD_DISCONNECT does not happen.

Regards,
CK

> +			train_info->hpd_inerrupt = true;
> +
> +		if (!(irq_status & MTK_DP_HPD_CONNECT ||
> +		      irq_status & MTK_DP_HPD_DISCONNECT))
> +			return IRQ_WAKE_THREAD;
> +
> +		if (!!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
> +		       HPD_DB_DP_TRANS_P0_MASK))
> +			train_info->cable_plugged_in = true;
> +		else
> +			train_info->cable_plugged_in = false;
> +
> +		mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> +				   DP_PWR_STATE_BANDGAP_TPLL_LANE,
> +				   DP_PWR_STATE_MASK);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
CK Hu (胡俊光) Aug. 2, 2022, 7:57 a.m. UTC | #10
Hi, Bo-Chen:

On Wed, 2022-07-27 at 12:50 +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.
> 
> 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>
> ---

[snip]

> +
> +static int mtk_dp_train_tps_2_3(struct mtk_dp *mtk_dp, u8
> target_linkrate,
> +				u8 target_lane_count, u8 *lane_adjust,
> +				int *status_control, u8
> *prev_lane_adjust)
> +{
> +	u8 val;
> +	u8 link_status[DP_LINK_STATUS_SIZE] = {};
> +
> +	if (*status_control == 1) {
> +		if (mtk_dp->train_info.tps4) {
> +			mtk_dp_train_set_pattern(mtk_dp, 4);
> +			val = DP_TRAINING_PATTERN_4;
> +		} else if (mtk_dp->train_info.tps3) {
> +			mtk_dp_train_set_pattern(mtk_dp, 3);
> +			val = DP_LINK_SCRAMBLING_DISABLE |
> +				DP_TRAINING_PATTERN_3;
> +		} else {
> +			mtk_dp_train_set_pattern(mtk_dp, 2);
> +			val = DP_LINK_SCRAMBLING_DISABLE |
> +				DP_TRAINING_PATTERN_2;
> +		}

Only one of tps2, tps3, tps4 would be process, and the priority is tps4
> tps3 > tps2, so I would like use one variable for this.

if support tps4, train_info.tps = 4.
else if support tps3, train_info.tps = 3.
else train_info.tps = 2.

And this part would be almost the same as the part in
mtk_dp_train_tps_1(), so separate this part to a function.

Regards,
CK


> +		drm_dp_dpcd_writeb(&mtk_dp->aux,
> +				   DP_TRAINING_PATTERN_SET, val);
> +		drm_dp_dpcd_read(&mtk_dp->aux,
> +				 DP_ADJUST_REQUEST_LANE0_1,
> lane_adjust,
> +				 sizeof(*lane_adjust) * 2);
> +
> +		mtk_dp_train_update_swing_pre(mtk_dp,
> +					      target_lane_count,
> lane_adjust);
> +		*status_control = 2;
> +	}
> +
> +	drm_dp_link_train_channel_eq_delay(&mtk_dp->aux, mtk_dp-
> >rx_cap);
> +
> +	drm_dp_dpcd_read_link_status(&mtk_dp->aux, link_status);
> +
> +	if (drm_dp_channel_eq_ok(link_status, target_lane_count)) {
> +		mtk_dp->train_info.eq_done = true;
> +		dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
> +		return 0;
> +	}
> +
> +	dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
> +
> +	if (*prev_lane_adjust != link_status[4])
> +		*prev_lane_adjust = link_status[4];
> +
> +	return -EAGAIN;
> +}
> +
Jani Nikula Aug. 2, 2022, 2:11 p.m. UTC | #11
On Wed, 27 Jul 2022, Bo-Chen Chen <rex-bc.chen@mediatek.com> wrote:
> From: Guillaume Ranquet <granquet@baylibre.com>
>
> This patch adds two helper functions that extract the frequency and word
> length from a struct cea_sad.
>
> For these helper functions new defines are added that help translate the
> 'freq' and 'byte2' fields into real numbers.

I think we should stop adding a plethora of functions that deal with
sads like this.

There should probably be *one* struct that contains all the details you
and everyone need extracted. There should be *one* function that fills
in all the details. The struct should be placed in
connector->display_info, and the function should be called *once* from
update_display_info().

Ideally, the function shouldn't even be exposed from drm_edid.c, but if
you still need to deal with situations where you don't call connector
update, maybe it needs to be exposed.

BR,
Jani.


>
> 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/drm_edid.c | 63 ++++++++++++++++++++++++++++++++++++++
>  include/drm/drm_edid.h     | 14 +++++++++
>  2 files changed, 77 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index bc43e1b32092..2a6f92da5ff3 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -4916,6 +4916,69 @@ int drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb)
>  }
>  EXPORT_SYMBOL(drm_edid_to_speaker_allocation);
>  
> +/**
> + * drm_cea_sad_get_sample_rate - Extract the sample rate from cea_sad
> + * @sad: Pointer to the cea_sad struct
> + *
> + * Extracts the cea_sad frequency field and returns the sample rate in Hz.
> + *
> + * Return: Sample rate in Hz or a negative errno if parsing failed.
> + */
> +int drm_cea_sad_get_sample_rate(const struct cea_sad *sad)
> +{
> +	switch (sad->freq) {
> +	case DRM_CEA_SAD_FREQ_32KHZ:
> +		return 32000;
> +	case DRM_CEA_SAD_FREQ_44KHZ:
> +		return 44100;
> +	case DRM_CEA_SAD_FREQ_48KHZ:
> +		return 48000;
> +	case DRM_CEA_SAD_FREQ_88KHZ:
> +		return 88200;
> +	case DRM_CEA_SAD_FREQ_96KHZ:
> +		return 96000;
> +	case DRM_CEA_SAD_FREQ_176KHZ:
> +		return 176400;
> +	case DRM_CEA_SAD_FREQ_192KHZ:
> +		return 192000;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +EXPORT_SYMBOL(drm_cea_sad_get_sample_rate);
> +
> +/**
> + * drm_cea_sad_get_uncompressed_word_length - Extract word length
> + * @sad: Pointer to the cea_sad struct
> + *
> + * Extracts the cea_sad byte2 field and returns the word length for an
> + * uncompressed stream.
> + *
> + * Note: This function may only be called for uncompressed audio.
> + *
> + * Return: Word length in bits or a negative errno if parsing failed.
> + */
> +int drm_cea_sad_get_uncompressed_word_length(const struct cea_sad *sad)
> +{
> +	if (sad->format != HDMI_AUDIO_CODING_TYPE_PCM) {
> +		DRM_WARN("Unable to get the uncompressed word length for format: %u\n",
> +			 sad->format);
> +		return -EINVAL;
> +	}
> +
> +	switch (sad->byte2) {
> +	case DRM_CEA_SAD_UNCOMPRESSED_WORD_16BIT:
> +		return 16;
> +	case DRM_CEA_SAD_UNCOMPRESSED_WORD_20BIT:
> +		return 20;
> +	case DRM_CEA_SAD_UNCOMPRESSED_WORD_24BIT:
> +		return 24;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +EXPORT_SYMBOL(drm_cea_sad_get_uncompressed_word_length);
> +
>  /**
>   * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync delay
>   * @connector: connector associated with the HDMI/DP sink
> diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
> index c2c43a4af681..779b710aed40 100644
> --- a/include/drm/drm_edid.h
> +++ b/include/drm/drm_edid.h
> @@ -373,6 +373,18 @@ struct cea_sad {
>  	u8 byte2;
>  };
>  
> +#define DRM_CEA_SAD_FREQ_32KHZ  BIT(0)
> +#define DRM_CEA_SAD_FREQ_44KHZ  BIT(1)
> +#define DRM_CEA_SAD_FREQ_48KHZ  BIT(2)
> +#define DRM_CEA_SAD_FREQ_88KHZ  BIT(3)
> +#define DRM_CEA_SAD_FREQ_96KHZ  BIT(4)
> +#define DRM_CEA_SAD_FREQ_176KHZ BIT(5)
> +#define DRM_CEA_SAD_FREQ_192KHZ BIT(6)
> +
> +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_16BIT BIT(0)
> +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_20BIT BIT(1)
> +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_24BIT BIT(2)
> +
>  struct drm_encoder;
>  struct drm_connector;
>  struct drm_connector_state;
> @@ -380,6 +392,8 @@ struct drm_display_mode;
>  
>  int drm_edid_to_sad(const struct edid *edid, struct cea_sad **sads);
>  int drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb);
> +int drm_cea_sad_get_sample_rate(const struct cea_sad *sad);
> +int drm_cea_sad_get_uncompressed_word_length(const struct cea_sad *sad);
>  int drm_av_sync_delay(struct drm_connector *connector,
>  		      const struct drm_display_mode *mode);
Rex-BC Chen (陳柏辰) Aug. 3, 2022, 5:47 a.m. UTC | #12
On Tue, 2022-08-02 at 10:37 +0800, CK Hu wrote:
> Hi, Bo-Chen:
> 
> On Wed, 2022-07-27 at 12:50 +0800, Bo-Chen Chen wrote:
> > From: Guillaume Ranquet <granquet@baylibre.com>
> > 
> > This patch adds 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     | 181 +++++++++++++++++++++-
> > --
> > --
> >  drivers/gpu/drm/mediatek/mtk_dp_reg.h |   1 +
> >  2 files changed, 146 insertions(+), 36 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> > b/drivers/gpu/drm/mediatek/mtk_dp.c
> > index 06eeecedd49e..ce817cb59445 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> > @@ -103,6 +103,11 @@ static struct regmap_config
> > mtk_dp_regmap_config
> > = {
> >  	.name = "mtk-dp-registers",
> >  };
> >  
> > +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp)
> > +{
> > +	return mtk_dp->next_bridge;
> 
> For external DP, it could also connect to a bridge IC which transfer
> DP
> to HDMI or some interface else. Use the compatible to judge this edp
> or
> dp.
> 

Hello CK,

ok, I will use of device data to do this.

> > +}
> > +
> >  static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
> >  {
> >  	return container_of(b, struct mtk_dp, bridge);
> > @@ -341,6 +346,19 @@ static void mtk_dp_pg_enable(struct mtk_dp
> > *mtk_dp, bool enable)
> >  			   4 << PGEN_PATTERN_SEL_SHIFT,
> > PGEN_PATTERN_SEL_MASK);
> >  }
> >  
> > +static bool mtk_dp_plug_state(struct mtk_dp *mtk_dp)
> > +{
> > +	return mtk_dp->train_info.cable_plugged_in;
> > +}
> > +
> > +static bool mtk_dp_plug_state_avoid_pulse(struct mtk_dp *mtk_dp)
> > +{
> > +	bool ret;
> > +
> > +	return !(readx_poll_timeout(mtk_dp_plug_state, mtk_dp, ret,
> > ret,
> > +				    4000, 7 * 4000));
> > +}
> > +
> >  static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
> >  {
> >  	mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640,
> > @@ -778,35 +796,67 @@ static void
> > mtk_dp_get_calibration_data(struct
> > mtk_dp *mtk_dp)
> >  		return;
> >  	}
> >  
> > -	cal_data->glb_bias_trim =
> > -		check_cal_data_valid(mtk_dp, 1, 0x1e,
> > -				     (buf[3] >> 27) & 0x1f, 0xf);
> > -	cal_data->clktx_impse =
> > -		check_cal_data_valid(mtk_dp, 1, 0xe,
> > -				     (buf[0] >> 9) & 0xf, 0x8);
> > -	cal_data->ln_tx_impsel_pmos[0] =
> > -		check_cal_data_valid(mtk_dp, 1, 0xe,
> > -				     (buf[2] >> 28) & 0xf, 0x8);
> > -	cal_data->ln_tx_impsel_nmos[0] =
> > -		check_cal_data_valid(mtk_dp, 1, 0xe,
> > -				     (buf[2] >> 24) & 0xf, 0x8);
> > -	cal_data->ln_tx_impsel_pmos[1] =
> > -		check_cal_data_valid(mtk_dp, 1, 0xe,
> > -				     (buf[2] >> 20) & 0xf, 0x8);
> > -	cal_data->ln_tx_impsel_nmos[1] =
> > -		check_cal_data_valid(mtk_dp, 1, 0xe,
> > -				     (buf[2] >> 16) & 0xf, 0x8);
> > -	cal_data->ln_tx_impsel_pmos[2] =
> > -		check_cal_data_valid(mtk_dp, 1, 0xe,
> > -				     (buf[2] >> 12) & 0xf, 0x8);
> > -	cal_data->ln_tx_impsel_nmos[2] =
> > -		check_cal_data_valid(mtk_dp, 1, 0xe,
> > -				     (buf[2] >> 8) & 0xf, 0x8);
> > -	cal_data->ln_tx_impsel_pmos[3] =
> > -		check_cal_data_valid(mtk_dp, 1, 0xe,
> > -				     (buf[2] >> 4) & 0xf, 0x8);
> > -	cal_data->ln_tx_impsel_nmos[3] =
> > -		check_cal_data_valid(mtk_dp, 1, 0xe, buf[2] & 0xf,
> > 0x8);
> > +	if (mtk_dp_is_edp(mtk_dp)) {
> > +		cal_data->glb_bias_trim =
> > +			check_cal_data_valid(mtk_dp, 1, 0x1e,
> > +					     (buf[3] >> 27) & 0x1f,
> > 0xf);
> > +		cal_data->clktx_impse =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe,
> > +					     (buf[0] >> 9) & 0xf, 0x8);
> > +		cal_data->ln_tx_impsel_pmos[0] =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe,
> > +					     (buf[2] >> 28) & 0xf,
> > 0x8);
> > +		cal_data->ln_tx_impsel_nmos[0] =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe,
> > +					     (buf[2] >> 24) & 0xf,
> > 0x8);
> > +		cal_data->ln_tx_impsel_pmos[1] =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe,
> > +					     (buf[2] >> 20) & 0xf,
> > 0x8);
> > +		cal_data->ln_tx_impsel_nmos[1] =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe,
> > +					     (buf[2] >> 16) & 0xf,
> > 0x8);
> > +		cal_data->ln_tx_impsel_pmos[2] =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe,
> > +					     (buf[2] >> 12) & 0xf,
> > 0x8);
> > +		cal_data->ln_tx_impsel_nmos[2] =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe,
> > +					     (buf[2] >> 8) & 0xf, 0x8);
> > +		cal_data->ln_tx_impsel_pmos[3] =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe,
> > +					     (buf[2] >> 4) & 0xf, 0x8);
> > +		cal_data->ln_tx_impsel_nmos[3] =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe, buf[2] &
> > 0xf, 0x8);
> > +	} else {
> > +		cal_data->glb_bias_trim =
> > +			check_cal_data_valid(mtk_dp, 1, 0x1e,
> > +					     (buf[0] >> 27) & 0x1f,
> > 0xf);
> 
> Why not place glb_bias_trim value in nvmem buf[3] for both edp and dp
> case? Place in the same nvmem position, the driver code would be
> simple. 
> 

These value are read from efuse data. do you mean change the order in
this dp driver?
Is so, I think it's not appropriate. It will be hard to debug if any
issue occurs.

> > +		cal_data->clktx_impse =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe,
> > +					     (buf[0] >> 13) & 0xf,
> > 0x8);
> > +		cal_data->ln_tx_impsel_pmos[0] =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe,
> > +					     (buf[1] >> 28) & 0xf,
> > 0x8);
> > +		cal_data->ln_tx_impsel_nmos[0] =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe,
> > +					     (buf[1] >> 24) & 0xf,
> > 0x8);
> > +		cal_data->ln_tx_impsel_pmos[1] =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe,
> > +					     (buf[1] >> 20) & 0xf,
> > 0x8);
> > +		cal_data->ln_tx_impsel_nmos[1] =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe,
> > +					     (buf[1] >> 16) & 0xf,
> > 0x8);
> > +		cal_data->ln_tx_impsel_pmos[2] =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe,
> > +					     (buf[1] >> 12) & 0xf,
> > 0x8);
> > +		cal_data->ln_tx_impsel_nmos[2] =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe,
> > +					     (buf[1] >> 8) & 0xf, 0x8);
> > +		cal_data->ln_tx_impsel_pmos[3] =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe,
> > +					     (buf[1] >> 4) & 0xf, 0x8);
> > +		cal_data->ln_tx_impsel_nmos[3] =
> > +			check_cal_data_valid(mtk_dp, 1, 0xe, buf[1] &
> > 0xf, 0x8);
> 
> Why not place ln_tx_impsel_nmos and ln_tx_impsel_pmos value in nvmem
> buf[2] for both edp and dp case? Place in the same nvmem position,
> the
> driver code would be simple. 
> 
> > +	}
> >  
> >  	kfree(buf);
> >  }
> > @@ -926,7 +976,10 @@ static void mtk_dp_video_mute(struct mtk_dp
> > *mtk_dp, bool enable)
> >  			   VIDEO_MUTE_SEL_DP_ENC0_P0_MASK |
> >  			   VIDEO_MUTE_SW_DP_ENC0_P0_MASK);
> >  
> > -	mtk_dp_sip_atf_call(mtk_dp, MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
> > enable);
> > +	mtk_dp_sip_atf_call(mtk_dp,
> > +			    mtk_dp_is_edp(mtk_dp) ?
> > +			    MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE :
> > +			    MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable);
> 
> Use of_device_get_match_data to select
> MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE
> or MTK_DP_SIP_ATF_VIDEO_UNMUTE.
> 

Do you mean add a new variable to control this in of device match data?

BRs,
Bo-Chen

> >  }
> >  
> >  static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
> > @@ -1226,6 +1279,9 @@ static int mtk_dp_parse_capabilities(struct
> > mtk_dp *mtk_dp)
> >  	drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> > DP_SET_POWER_D0);
> >  	usleep_range(2000, 5000);
> >  
> > +	if (!mtk_dp_plug_state(mtk_dp))
> > +		return -ENODEV;
> > +
> >  	drm_dp_read_dpcd_caps(&mtk_dp->aux, mtk_dp->rx_cap);
> >  
> >  	train_info->link_rate = min_t(int, mtk_dp->max_linkrate,
> > @@ -1277,6 +1333,9 @@ static int mtk_dp_train_start(struct mtk_dp
> > *mtk_dp)
> >  	u8 train_limit;
> >  	u8 max_link_rate;
> >  
> > +	if (!mtk_dp_plug_state_avoid_pulse(mtk_dp))
> > +		return -ENODEV;
> > +
> >  	link_rate = mtk_dp->rx_cap[1];
> >  	lane_count = mtk_dp->rx_cap[2] & 0x1F;
> >  
> > @@ -1412,6 +1471,9 @@ static irqreturn_t
> > mtk_dp_hpd_event_thread(int
> > hpd, void *dev)
> >  {
> >  	struct mtk_dp *mtk_dp = dev;
> >  
> > +	dev_dbg(mtk_dp->dev, "drm_helper_hpd_irq_event\n");
> > +	drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
> 
> Because edp also have hpd interrupt, so I would like both edp and dp
> have the same control flow for hdp. So move this to edp patch.
> 
> > +
> >  	if (mtk_dp->train_info.hpd_inerrupt) {
> >  		dev_dbg(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n");
> >  		mtk_dp->train_info.hpd_inerrupt = false;
> > @@ -1452,9 +1514,20 @@ static irqreturn_t mtk_dp_hpd_event(int hpd,
> > void *dev)
> >  		else
> >  			train_info->cable_plugged_in = false;
> >  
> > -		mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> > -				   DP_PWR_STATE_BANDGAP_TPLL_LANE,
> > -				   DP_PWR_STATE_MASK);
> > +		if (!train_info->cable_plugged_in) {
> > +			mtk_dp_video_mute(mtk_dp, true);
> > +
> > +			mtk_dp_initialize_priv_data(mtk_dp);
> > +			mtk_dp_set_idle_pattern(mtk_dp, true);
> > +
> > +			mtk_dp_update_bits(mtk_dp,
> > MTK_DP_TOP_PWR_STATE,
> > +					   DP_PWR_STATE_BANDGAP_TPLL,
> > +					   DP_PWR_STATE_MASK);
> > +		} else {
> > +			mtk_dp_update_bits(mtk_dp,
> > MTK_DP_TOP_PWR_STATE,
> > +					   DP_PWR_STATE_BANDGAP_TPLL_LA
> > NE,
> > +					   DP_PWR_STATE_MASK);
> > +		}
> >  	}
> >  
> >  	return IRQ_HANDLED;
> > @@ -1499,6 +1572,24 @@ 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;
> > +	u8 sink_count = 0;
> > +
> > +	if (mtk_dp_is_edp(mtk_dp))
> > +		return connector_status_connected;
> 
> Because edp also have hpd interrupt, so I would like both edp and dp
> have the same control flow for hdp. So remove this.
> 
> > +
> > +	if (mtk_dp_plug_state_avoid_pulse(mtk_dp)) {
> > +		drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT,
> > &sink_count);
> > +		if (DP_GET_SINK_COUNT(sink_count))
> > +			ret = connector_status_connected;
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> >  static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
> >  				    struct drm_connector *connector)
> >  {
> > @@ -1512,7 +1603,8 @@ static struct edid *mtk_dp_get_edid(struct
> > drm_bridge *bridge,
> >  	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);
> > +	if (mtk_dp_plug_state(mtk_dp))
> > +		new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
> 
> Please explain why only dp has this problem. If edp also has this
> problem, move this to edp patch.
> 
> Regards,
> CK
> 
> >  
> >  	if (!enabled)
> >  		drm_bridge_chain_post_disable(bridge);
> > @@ -1852,6 +1944,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)
> > @@ -1873,9 +1966,15 @@ 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) {
> > +		dev_info(dev,
> > +			 "No panel connected in devicetree, continue as
> > external DP\n");
> > +		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");
> > +	}
> >  
> >  	ret = mtk_dp_dt_parse(mtk_dp, pdev);
> >  	if (ret)
> > @@ -1918,7 +2017,10 @@ 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;
> > +	if (mtk_dp_is_edp(mtk_dp))
> > +		mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
> > +	else
> > +		mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
> >  
> >  	drm_bridge_add(&mtk_dp->bridge);
> >  
> > @@ -1945,6 +2047,12 @@ static int mtk_dp_suspend(struct device
> > *dev)
> >  {
> >  	struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
> >  
> > +	if (mtk_dp_plug_state(mtk_dp)) {
> > +		drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER,
> > DP_SET_POWER_D3);
> > +		/* Ensure the sink is off before shutting down power */
> > +		usleep_range(2000, 3000);
> > +	}
> > +
> >  	mtk_dp_power_disable(mtk_dp);
> >  
> >  	mtk_dp_hwirq_enable(mtk_dp, false);
> > @@ -1978,6 +2086,7 @@ 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" },
> > +	{ .compatible = "mediatek,mt8195-dp-tx" },
> >  	{},
> >  };
> >  MODULE_DEVICE_TABLE(of, 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 4ff8e9880dc9..59086eb82868 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> > +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
> > @@ -14,6 +14,7 @@
> >  #define SEC_OFFSET	0x4000
> >  
> >  #define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE	(BIT(0) | BIT(5))
> > +#define MTK_DP_SIP_ATF_VIDEO_UNMUTE	BIT(5)
> >  
> >  #define DP_PHY_GLB_BIAS_GEN_00		0
> >  #define RG_XTP_GLB_BIAS_INTR_CTRL	GENMASK(20, 16)
> 
>
Rex-BC Chen (陳柏辰) Aug. 5, 2022, 6:54 a.m. UTC | #13
On Tue, 2022-08-02 at 17:11 +0300, Jani Nikula wrote:
> On Wed, 27 Jul 2022, Bo-Chen Chen <rex-bc.chen@mediatek.com> wrote:
> > From: Guillaume Ranquet <granquet@baylibre.com>
> > 
> > This patch adds two helper functions that extract the frequency and
> > word
> > length from a struct cea_sad.
> > 
> > For these helper functions new defines are added that help
> > translate the
> > 'freq' and 'byte2' fields into real numbers.
> 
> I think we should stop adding a plethora of functions that deal with
> sads like this.
> 
> There should probably be *one* struct that contains all the details
> you
> and everyone need extracted. There should be *one* function that
> fills
> in all the details. The struct should be placed in
> connector->display_info, and the function should be called *once*
> from
> update_display_info().
> 
> Ideally, the function shouldn't even be exposed from drm_edid.c, but
> if
> you still need to deal with situations where you don't call connector
> update, maybe it needs to be exposed.
> 
> BR,
> Jani.
> 
> 

Hello Jani,

Thanks for your review.
After checking our patches, we found we will not use these two function
because we remove them in patch [11/11] drm/mediatek: Use cached audio
config when changing resolution.

I will drop [02/11] and [03/11].

Thanks!

BRs,
Bo-Chen

> > 
> > 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/drm_edid.c | 63
> > ++++++++++++++++++++++++++++++++++++++
> >  include/drm/drm_edid.h     | 14 +++++++++
> >  2 files changed, 77 insertions(+)
> > 
> > diff --git a/drivers/gpu/drm/drm_edid.c
> > b/drivers/gpu/drm/drm_edid.c
> > index bc43e1b32092..2a6f92da5ff3 100644
> > --- a/drivers/gpu/drm/drm_edid.c
> > +++ b/drivers/gpu/drm/drm_edid.c
> > @@ -4916,6 +4916,69 @@ int drm_edid_to_speaker_allocation(const
> > struct edid *edid, u8 **sadb)
> >  }
> >  EXPORT_SYMBOL(drm_edid_to_speaker_allocation);
> >  
> > +/**
> > + * drm_cea_sad_get_sample_rate - Extract the sample rate from
> > cea_sad
> > + * @sad: Pointer to the cea_sad struct
> > + *
> > + * Extracts the cea_sad frequency field and returns the sample
> > rate in Hz.
> > + *
> > + * Return: Sample rate in Hz or a negative errno if parsing
> > failed.
> > + */
> > +int drm_cea_sad_get_sample_rate(const struct cea_sad *sad)
> > +{
> > +	switch (sad->freq) {
> > +	case DRM_CEA_SAD_FREQ_32KHZ:
> > +		return 32000;
> > +	case DRM_CEA_SAD_FREQ_44KHZ:
> > +		return 44100;
> > +	case DRM_CEA_SAD_FREQ_48KHZ:
> > +		return 48000;
> > +	case DRM_CEA_SAD_FREQ_88KHZ:
> > +		return 88200;
> > +	case DRM_CEA_SAD_FREQ_96KHZ:
> > +		return 96000;
> > +	case DRM_CEA_SAD_FREQ_176KHZ:
> > +		return 176400;
> > +	case DRM_CEA_SAD_FREQ_192KHZ:
> > +		return 192000;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +EXPORT_SYMBOL(drm_cea_sad_get_sample_rate);
> > +
> > +/**
> > + * drm_cea_sad_get_uncompressed_word_length - Extract word length
> > + * @sad: Pointer to the cea_sad struct
> > + *
> > + * Extracts the cea_sad byte2 field and returns the word length
> > for an
> > + * uncompressed stream.
> > + *
> > + * Note: This function may only be called for uncompressed audio.
> > + *
> > + * Return: Word length in bits or a negative errno if parsing
> > failed.
> > + */
> > +int drm_cea_sad_get_uncompressed_word_length(const struct cea_sad
> > *sad)
> > +{
> > +	if (sad->format != HDMI_AUDIO_CODING_TYPE_PCM) {
> > +		DRM_WARN("Unable to get the uncompressed word length
> > for format: %u\n",
> > +			 sad->format);
> > +		return -EINVAL;
> > +	}
> > +
> > +	switch (sad->byte2) {
> > +	case DRM_CEA_SAD_UNCOMPRESSED_WORD_16BIT:
> > +		return 16;
> > +	case DRM_CEA_SAD_UNCOMPRESSED_WORD_20BIT:
> > +		return 20;
> > +	case DRM_CEA_SAD_UNCOMPRESSED_WORD_24BIT:
> > +		return 24;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +EXPORT_SYMBOL(drm_cea_sad_get_uncompressed_word_length);
> > +
> >  /**
> >   * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync
> > delay
> >   * @connector: connector associated with the HDMI/DP sink
> > diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
> > index c2c43a4af681..779b710aed40 100644
> > --- a/include/drm/drm_edid.h
> > +++ b/include/drm/drm_edid.h
> > @@ -373,6 +373,18 @@ struct cea_sad {
> >  	u8 byte2;
> >  };
> >  
> > +#define DRM_CEA_SAD_FREQ_32KHZ  BIT(0)
> > +#define DRM_CEA_SAD_FREQ_44KHZ  BIT(1)
> > +#define DRM_CEA_SAD_FREQ_48KHZ  BIT(2)
> > +#define DRM_CEA_SAD_FREQ_88KHZ  BIT(3)
> > +#define DRM_CEA_SAD_FREQ_96KHZ  BIT(4)
> > +#define DRM_CEA_SAD_FREQ_176KHZ BIT(5)
> > +#define DRM_CEA_SAD_FREQ_192KHZ BIT(6)
> > +
> > +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_16BIT BIT(0)
> > +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_20BIT BIT(1)
> > +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_24BIT BIT(2)
> > +
> >  struct drm_encoder;
> >  struct drm_connector;
> >  struct drm_connector_state;
> > @@ -380,6 +392,8 @@ struct drm_display_mode;
> >  
> >  int drm_edid_to_sad(const struct edid *edid, struct cea_sad
> > **sads);
> >  int drm_edid_to_speaker_allocation(const struct edid *edid, u8
> > **sadb);
> > +int drm_cea_sad_get_sample_rate(const struct cea_sad *sad);
> > +int drm_cea_sad_get_uncompressed_word_length(const struct cea_sad
> > *sad);
> >  int drm_av_sync_delay(struct drm_connector *connector,
> >  		      const struct drm_display_mode *mode);
> 
>
Rex-BC Chen (陳柏辰) Aug. 5, 2022, 7:10 a.m. UTC | #14
Hello all,

I will drop this modification becaus after we testing current drivers,
the issue described in commit message does not occur.

BRs,
Bo-Chen

On Thu, 2022-07-28 at 17:40 +0800, Rex-BC Chen wrote:
> On Wed, 2022-07-27 at 11:40 +0200, AngeloGioacchino Del Regno wrote:
> > Il 27/07/22 06:50, Bo-Chen Chen ha scritto:
> > > For some DP dungles, we need to train more than onece to confirm
> > > that we
> > > don't misjudge the status of sink device.
> > 
> > Please fix the typos in your commit title and description.
> > title: misjudgment -> misjudgement
> > desc: dungles->dongles; onece->once
> > 
> > > 
> > > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
> > > ---
> > >   drivers/gpu/drm/mediatek/mtk_dp.c | 21 ++++++++++++++++++---
> > >   1 file changed, 18 insertions(+), 3 deletions(-)
> > > 
> > > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c
> > > b/drivers/gpu/drm/mediatek/mtk_dp.c
> > > index ce817cb59445..80d7d6488105 100644
> > > --- a/drivers/gpu/drm/mediatek/mtk_dp.c
> > > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
> > > @@ -42,6 +42,7 @@
> > >   #define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3
> > >   #define MTK_DP_TBC_BUF_READ_START_ADDR 0x08
> > >   #define MTK_DP_TRAIN_DOWNSCALE_RETRY 8
> > > +#define MTK_DP_TRAIN_CLEAR_RETRY 50
> > >   
> > >   struct mtk_dp_train_info {
> > >   	bool tps3;
> > > @@ -1431,11 +1432,25 @@ static int mtk_dp_video_config(struct
> > > mtk_dp *mtk_dp)
> > >   
> > >   static int mtk_dp_training(struct mtk_dp *mtk_dp)
> > >   {
> > > +	short max_retry = MTK_DP_TRAIN_CLEAR_RETRY;
> > >   	int ret;
> > >   
> > > -	ret = mtk_dp_train_start(mtk_dp);
> > > -	if (ret)
> > > -		return ret;
> > > +	/*
> > > +	 * We do retry to confirm that we don't misjudge the sink
> > > status.
> > > +	 * If it is still failed, we can confirm there are some issues
> > > for the
> > > +	 * sink device.
> > > +	 */
> > > +	do {
> > > +		ret = mtk_dp_train_start(mtk_dp);
> > > +		if (!ret)
> > > +			break;
> > > +	} while (--max_retry);
> > > +
> > > +	dev_info(mtk_dp->dev, "dp training clear retry times: %d\n",
> > > +		 MTK_DP_TRAIN_CLEAR_RETRY - max_retry);
> > 
> > dev_dbg() here.
> > 
> > ...after which,
> > 
> > Reviewed-by: AngeloGioacchino Del Regno <
> > angelogioacchino.delregno@collabora.com>
> > 
> 
> Hello Angelo,
> 
> Thanks for your review.
> I will modify these in next version.
> 
> BRs,
> Bo-Chen
> 
> > > +
> > > +	if (!max_retry)
> > > +		return -ETIMEDOUT;
> > >   
> > >   	ret = mtk_dp_video_config(mtk_dp);
> > >   	if (ret)
> > 
> > 
> 
>
Jani Nikula Aug. 5, 2022, 7:54 a.m. UTC | #15
On Fri, 05 Aug 2022, Rex-BC Chen <rex-bc.chen@mediatek.com> wrote:
> On Tue, 2022-08-02 at 17:11 +0300, Jani Nikula wrote:
>> On Wed, 27 Jul 2022, Bo-Chen Chen <rex-bc.chen@mediatek.com> wrote:
>> > From: Guillaume Ranquet <granquet@baylibre.com>
>> > 
>> > This patch adds two helper functions that extract the frequency and
>> > word
>> > length from a struct cea_sad.
>> > 
>> > For these helper functions new defines are added that help
>> > translate the
>> > 'freq' and 'byte2' fields into real numbers.
>> 
>> I think we should stop adding a plethora of functions that deal with
>> sads like this.
>> 
>> There should probably be *one* struct that contains all the details
>> you
>> and everyone need extracted. There should be *one* function that
>> fills
>> in all the details. The struct should be placed in
>> connector->display_info, and the function should be called *once*
>> from
>> update_display_info().
>> 
>> Ideally, the function shouldn't even be exposed from drm_edid.c, but
>> if
>> you still need to deal with situations where you don't call connector
>> update, maybe it needs to be exposed.
>> 
>> BR,
>> Jani.
>> 
>> 
>
> Hello Jani,
>
> Thanks for your review.
> After checking our patches, we found we will not use these two function
> because we remove them in patch [11/11] drm/mediatek: Use cached audio
> config when changing resolution.
>
> I will drop [02/11] and [03/11].
>
> Thanks!

Thank you too! :)

BR,
Jani.


>
> BRs,
> Bo-Chen
>
>> > 
>> > 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/drm_edid.c | 63
>> > ++++++++++++++++++++++++++++++++++++++
>> >  include/drm/drm_edid.h     | 14 +++++++++
>> >  2 files changed, 77 insertions(+)
>> > 
>> > diff --git a/drivers/gpu/drm/drm_edid.c
>> > b/drivers/gpu/drm/drm_edid.c
>> > index bc43e1b32092..2a6f92da5ff3 100644
>> > --- a/drivers/gpu/drm/drm_edid.c
>> > +++ b/drivers/gpu/drm/drm_edid.c
>> > @@ -4916,6 +4916,69 @@ int drm_edid_to_speaker_allocation(const
>> > struct edid *edid, u8 **sadb)
>> >  }
>> >  EXPORT_SYMBOL(drm_edid_to_speaker_allocation);
>> >  
>> > +/**
>> > + * drm_cea_sad_get_sample_rate - Extract the sample rate from
>> > cea_sad
>> > + * @sad: Pointer to the cea_sad struct
>> > + *
>> > + * Extracts the cea_sad frequency field and returns the sample
>> > rate in Hz.
>> > + *
>> > + * Return: Sample rate in Hz or a negative errno if parsing
>> > failed.
>> > + */
>> > +int drm_cea_sad_get_sample_rate(const struct cea_sad *sad)
>> > +{
>> > +	switch (sad->freq) {
>> > +	case DRM_CEA_SAD_FREQ_32KHZ:
>> > +		return 32000;
>> > +	case DRM_CEA_SAD_FREQ_44KHZ:
>> > +		return 44100;
>> > +	case DRM_CEA_SAD_FREQ_48KHZ:
>> > +		return 48000;
>> > +	case DRM_CEA_SAD_FREQ_88KHZ:
>> > +		return 88200;
>> > +	case DRM_CEA_SAD_FREQ_96KHZ:
>> > +		return 96000;
>> > +	case DRM_CEA_SAD_FREQ_176KHZ:
>> > +		return 176400;
>> > +	case DRM_CEA_SAD_FREQ_192KHZ:
>> > +		return 192000;
>> > +	default:
>> > +		return -EINVAL;
>> > +	}
>> > +}
>> > +EXPORT_SYMBOL(drm_cea_sad_get_sample_rate);
>> > +
>> > +/**
>> > + * drm_cea_sad_get_uncompressed_word_length - Extract word length
>> > + * @sad: Pointer to the cea_sad struct
>> > + *
>> > + * Extracts the cea_sad byte2 field and returns the word length
>> > for an
>> > + * uncompressed stream.
>> > + *
>> > + * Note: This function may only be called for uncompressed audio.
>> > + *
>> > + * Return: Word length in bits or a negative errno if parsing
>> > failed.
>> > + */
>> > +int drm_cea_sad_get_uncompressed_word_length(const struct cea_sad
>> > *sad)
>> > +{
>> > +	if (sad->format != HDMI_AUDIO_CODING_TYPE_PCM) {
>> > +		DRM_WARN("Unable to get the uncompressed word length
>> > for format: %u\n",
>> > +			 sad->format);
>> > +		return -EINVAL;
>> > +	}
>> > +
>> > +	switch (sad->byte2) {
>> > +	case DRM_CEA_SAD_UNCOMPRESSED_WORD_16BIT:
>> > +		return 16;
>> > +	case DRM_CEA_SAD_UNCOMPRESSED_WORD_20BIT:
>> > +		return 20;
>> > +	case DRM_CEA_SAD_UNCOMPRESSED_WORD_24BIT:
>> > +		return 24;
>> > +	default:
>> > +		return -EINVAL;
>> > +	}
>> > +}
>> > +EXPORT_SYMBOL(drm_cea_sad_get_uncompressed_word_length);
>> > +
>> >  /**
>> >   * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync
>> > delay
>> >   * @connector: connector associated with the HDMI/DP sink
>> > diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
>> > index c2c43a4af681..779b710aed40 100644
>> > --- a/include/drm/drm_edid.h
>> > +++ b/include/drm/drm_edid.h
>> > @@ -373,6 +373,18 @@ struct cea_sad {
>> >  	u8 byte2;
>> >  };
>> >  
>> > +#define DRM_CEA_SAD_FREQ_32KHZ  BIT(0)
>> > +#define DRM_CEA_SAD_FREQ_44KHZ  BIT(1)
>> > +#define DRM_CEA_SAD_FREQ_48KHZ  BIT(2)
>> > +#define DRM_CEA_SAD_FREQ_88KHZ  BIT(3)
>> > +#define DRM_CEA_SAD_FREQ_96KHZ  BIT(4)
>> > +#define DRM_CEA_SAD_FREQ_176KHZ BIT(5)
>> > +#define DRM_CEA_SAD_FREQ_192KHZ BIT(6)
>> > +
>> > +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_16BIT BIT(0)
>> > +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_20BIT BIT(1)
>> > +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_24BIT BIT(2)
>> > +
>> >  struct drm_encoder;
>> >  struct drm_connector;
>> >  struct drm_connector_state;
>> > @@ -380,6 +392,8 @@ struct drm_display_mode;
>> >  
>> >  int drm_edid_to_sad(const struct edid *edid, struct cea_sad
>> > **sads);
>> >  int drm_edid_to_speaker_allocation(const struct edid *edid, u8
>> > **sadb);
>> > +int drm_cea_sad_get_sample_rate(const struct cea_sad *sad);
>> > +int drm_cea_sad_get_uncompressed_word_length(const struct cea_sad
>> > *sad);
>> >  int drm_av_sync_delay(struct drm_connector *connector,
>> >  		      const struct drm_display_mode *mode);
>> 
>> 
>
Rex-BC Chen (陳柏辰) Aug. 5, 2022, 8:22 a.m. UTC | #16
On Tue, 2022-08-02 at 13:09 +0800, CK Hu wrote:
> Hi, Bo-Chen:
> 
> On Wed, 2022-07-27 at 12:50 +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.
> > 
> > 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>
> > ---
> 
> [snip]
> 
> > +
> > +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
> > +{
> > +	struct mtk_dp *mtk_dp = dev;
> > +
> > +	if (mtk_dp->train_info.hpd_inerrupt) {
> 
> When the thread is running, mtk_dp->train_info.hpd_inerrupt would be
> true. So this checking is redundant.
> 

Hello CK,

ok, I will remove it.

> > +		dev_dbg(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n");
> > +		mtk_dp->train_info.hpd_inerrupt = false;
> > +		mtk_dp_hpd_sink_event(mtk_dp);
> > +	}
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
> > +{
> > +	struct mtk_dp *mtk_dp = dev;
> > +	struct mtk_dp_train_info *train_info = &mtk_dp->train_info;
> > +	u32 irq_status;
> > +
> > +	irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS);
> > +
> > +	if (!irq_status)
> > +		return IRQ_HANDLED;
> > +
> > +	if (irq_status & RGS_IRQ_STATUS_TRANSMITTER) {
> 
> Combine this if-checking with previous if-checking, it would be:
> 
> if (!(irq_status & RGS_IRQ_STATUS_TRANSMITTER))
> 	return IRQ_HANDLED;
> 

ok.

> > +		irq_status = mtk_dp_swirq_get_clear(mtk_dp) |
> > +			     mtk_dp_hwirq_get_clear(mtk_dp);
> > +
> > +		if (!irq_status)
> > +			return IRQ_HANDLED;
> > +
> > +		if (irq_status & MTK_DP_HPD_INTERRUPT)
> 
> Does this interrupt MTK_DP_HPD_INTERRUPT have any relation with
> MTK_DP_HPD_CONNECT and MTK_DP_HPD_CONNECT? From the naming, I guess
> that when MTK_DP_HPD_CONNECT happen, MTK_DP_HPD_INTERRUPT would also
> happen. Either for MTK_DP_HPD_DISCONNECT. When would
> MTK_DP_HPD_INTERRUPT happen but MTK_DP_HPD_CONNECT or
> MTK_DP_HPD_DISCONNECT does not happen.
> 

There is no relation for these status. They are individual.
MTK_DP_HPD_INTERRUPT is for interrupt from sink device. After receiving
this source device should do some actions.

MTK_DP_HPD_CONNECT and MTK_DP_HPD_DISCONNECT are for HPD status.

BRs,
Bo-Chen

> Regards,
> CK
> 
> > +			train_info->hpd_inerrupt = true;
> > +
> > +		if (!(irq_status & MTK_DP_HPD_CONNECT ||
> > +		      irq_status & MTK_DP_HPD_DISCONNECT))
> > +			return IRQ_WAKE_THREAD;
> > +
> > +		if (!!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
> > +		       HPD_DB_DP_TRANS_P0_MASK))
> > +			train_info->cable_plugged_in = true;
> > +		else
> > +			train_info->cable_plugged_in = false;
> > +
> > +		mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
> > +				   DP_PWR_STATE_BANDGAP_TPLL_LANE,
> > +				   DP_PWR_STATE_MASK);
> > +	}
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> 
>