[PATCH v11 1/4] drm/msm/dp: do not initialize phy until plugin interrupt received

Kuogee Hsieh quic_khsieh at quicinc.com
Wed Jan 12 22:17:54 UTC 2022


On 1/12/2022 12:00 PM, Stephen Boyd wrote:
> Quoting Kuogee Hsieh (2022-01-11 10:43:23)
>> Current DP drivers have regulators, clocks, irq and phy are grouped
>> together within a function and executed not in a symmetric manner.
>> This increase difficulty of code maintenance and limited code scalability.
>> This patch divides the driver life cycle of operation into four states,
>> resume (including booting up), dongle plugin, dongle unplugged and suspend.
>> Regulators, core clocks and irq are grouped together and enabled at resume
>> (or booting up) so that the DP controller is armed and ready to receive HPD
>> plugin interrupts. HPD plugin interrupt is generated when a dongle plugs
>> into DUT (device under test). Once HPD plugin interrupt is received, DP
>> controller will initialize phy so that dpcd read/write will function and
>> following link training can be proceeded successfully. DP phy will be
>> disabled after main link is teared down at end of unplugged HPD interrupt
>> handle triggered by dongle unplugged out of DUT. Finally regulators, code
>> clocks and irq are disabled at corresponding suspension.

0) Please note that  dongles are behavior differently.

1) Apple dongle will generate plug-in interrupt only if no hdmi monitor 
atatched to dongle. it will generate irq-hpd interrupt once hdmi monitor 
connect to dongle later.

2) Apple dongle will generate plugged-in interrupt followed by irq-hpd 
interrupt if dongle has hdmi monitor attached when connects to DUT.

3) other dongle will not generate plug-in interrupt unless dongle has 
hdmi monitor attached when connects to DUT. It only generate plug-in 
interrupt only and no irq-hpd interrupt  generated on this case.

4) Note: phy_initialized only associated with plugged-in interrupt

5) irq-hpd interrupt must happen after plugged-in interrupt and before 
unplugged interrupt

I will fill up below question with Apple dongle case with the order of 
event happen timing.

> I'll write out the various scenarios
>
> #1
> 	dongle plugged in with HDMI cable attached
> 	driver probe

1) driver probe ==> core_initialized = false;    phy_initialized = false;
2) dp_display_host_init() ==> core_initialized = true;
3) generate plugged-in interrupt triggers handler
4) dp_display_phy_init() ==> phy_initialized = true;

> 	
>
> #2
> 	dongle unplugged
> 	driver probe

1) driver probe ==> core_initialized = false;    phy_initialized = false;
2) dp_display_host_init() ==> core_initialized = true;


>
> #3
> 	dongle plugged in without HDMI cable attached
> 	driver probe

1) driver probe ==> core_initialized = false;    phy_initialized = false;
2) dp_display_host_init() ==> core_initialized = true;
3) generate plug-in interrupt triggers handler
4) dp_display_phy_init() ==> phy_initialized = true;

Note: same as case #1
> #4
> 	driver probe
> 	dongle plugged in without HDMI cable attached

1) driver probe ==> core_initialized = false;    phy_initialized = false;
2) dp_display_host_init() ==> core_initialized = true;
3) dongle plugged in
4) generate plug-in interrupt triggers handler
5) dp_display_phy_init() ==> phy_initialized = true;

>
>
> #5
> 	driver probe
> 	dongle plugged in with HDMI cable attached

1) driver probe ==> core_initialized = false;    phy_initialized = false;
2) dp_display_host_init() ==> core_initialized = true;
3) dongle plugged in
4) generate plug-in interrupt trigger handler
5) dp_display_phy_init() ==> phy_initialized = true;

Note: same as case #4

>
> #6
> 	driver probe
> 	dongle plugged in
> 	suspend
> 	resume

1) driver probe ==> core_initialized = false;    phy_initialized = false;
2) dp_display_host_init() ==> core_initialized = true;
3) dongle plug in
4) generate plug-in interrupt triggers handler
5) dp_display_phy_init() ==> phy_initialized = true;
6) suspend
7) dp_display_host_deinit() ==> core_initialized = false;
8) dp_display_host_phy_exit() ==> phy_initialize = false;
9) resume
10) dp_display_host_init() ==> core_initialized = true;
11) generate plug-in interrupt
12) dp_display_phy_init() ==> phy_initialize = true;

>
> #7
> 	driver probe
> 	dongle plugged in
> 	suspend
> 	dongle unplugged
> 	resume

1) driver probe ==> core_initialized = false;    phy_initialized = false;
2) dp_display_host_init() ==> core_initialized = true;
3) dongle plugged in
4) generate plug-in interrupt triggers handler
5) dp_display_phy_init() ==> phy_initialized = true;
6) suspend
7) dp_display_host_deinit() ==> core_initialized = false;
8) dp_display_host_phy_exit() ==> phy_initialize = false;
9) dongle unplugged
10) resume
11) dp_display_host_init() ==> core_initialized = true;

#8
	driver probe
	dongle plugged in without HDMI cable attached
	suspend
	resume

1) driver probe ==> core_initialized = false;    phy_initialized = false;
2) dp_display_host_init() ==> core_initialized = true;
3) dongle plug in
4) generate plug-in interrupt triggers handler
5) dp_display_phy_init() ==> phy_initialized = true;
6) suspend
7) dp_display_host_deinit() ==> core_initialized = false;
8) dp_display_host_phy_exit() ==> phy_initialize = false;
9) resume
10) dp_display_host_init() ==> core_initialized = true;
11) generate plug-in interrupt
12) dp_display_phy_init() ==> phy_initialize = true;


NOTE: same case #6

#9
	driver probe
	dongle plugged in without HDMI cable attached
	suspend
	HDMI cable attached during suspend
	resume

1) driver probe ==> core_initialized = false;    phy_initialized = false;
2) dp_display_host_init() ==> core_initialized = true;
3) dongle plugged in
4) generate plug-in interrupt triggers handler
5) dp_display_phy_init() ==> phy_initialized = true;
6) suspend
7) dp_display_host_deinit() ==> core_initialized = false;
8) dp_display_host_phy_exit() ==> phy_initialize = false;
9) HDMI cable attached
10) resume
11) dp_display_host_init() ==> core_initialized = true;
12) generate plug-in interrupt
13) dp_display_phy_init() ==> phy_initialize = true;


What's the state of the phy and core initialized variable at the end of
each of these scenarios? Please fill out the truth table.

		  +-----------------+------------------------
                   |    false        |       true            |
		  +-----------------+------------------------
  phy_initialized  |                 |                       |
		  +-----------------+------------------------
  core_initialized |                 | #1,                   |
		  +-----------------+------------------------

I guess we also need eDP scenarios, but that's probably simpler

#10
	eDP panel connected
	driver probe

1) driver probe ==> core_initialized = false;    phy_initialized = false;
2) dp_display_host_init() ==> core_initialized = true;
3) generate plug-in interrupt triggers handler
4) dp_display_phy_init() ==> phy_initialized = true;



#11
	eDP panel disconnected
	driver probe

NOTE: eDP panel can not be disconnected

#12
	eDP panel disconnected
	driver probe
	suspend
	resume

NOTE: assume edp panel connected
1) driver probe ==> core_initialized = false;    phy_initialized = false;
2) dp_display_host_init() ==> core_initialized = true;
3) generate plug-in interrupt triggers handler
4) dp_display_phy_init() ==> phy_initialized = true;
5) suspend
6) dp_display_host_deinit() ==> core_initialized = false;
7) dp_display_host_phy_exit() ==> phy_initialize = false;
8) resume
9) dp_display_host_init() ==> core_initialized = true;
10) generate plug-in interrupt
11) dp_display_phy_init() ==> phy_initialize = true;

>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
>> index 7cc4d21..f6bb4bc 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>> @@ -83,6 +83,7 @@ struct dp_display_private {
>>
>>          /* state variables */
>>          bool core_initialized;
>> +       bool phy_initialized;
>>          bool hpd_irq_on;
>>          bool audio_supported;
>>
>> @@ -372,21 +373,46 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
>>          return rc;
>>   }
>>
>> -static void dp_display_host_init(struct dp_display_private *dp, int reset)
>> +static void dp_display_host_phy_init(struct dp_display_private *dp)
>>   {
>> -       bool flip = false;
>> +       DRM_DEBUG_DP("core_init=%d phy_init=%d\n",
>> +                       dp->core_initialized, dp->phy_initialized);
>>
>> +       if (!dp->phy_initialized) {
>> +               dp_ctrl_phy_init(dp->ctrl);
>> +               dp->phy_initialized = true;
>> +       }
>> +}
>> +
>> +static void dp_display_host_phy_exit(struct dp_display_private *dp)
>> +{
>> +       DRM_DEBUG_DP("core_init=%d phy_init=%d\n",
>> +                       dp->core_initialized, dp->phy_initialized);
>> +
>> +       if (dp->phy_initialized) {
>> +               dp_ctrl_phy_exit(dp->ctrl);
>> +               dp->phy_initialized = false;
>> +       }
>> +}
>> +
>> +static void dp_display_host_init(struct dp_display_private *dp)
>> +{
>>          DRM_DEBUG_DP("core_initialized=%d\n", dp->core_initialized);
>>          if (dp->core_initialized) {
>>                  DRM_DEBUG_DP("DP core already initialized\n");
>>                  return;
>>          }
>>
>> -       if (dp->usbpd->orientation == ORIENTATION_CC2)
>> -               flip = true;
>> +       dp_power_init(dp->power, false);
>> +       dp_ctrl_reset_irq_ctrl(dp->ctrl, true);
>> +
>> +       /*
>> +        * eDP is the embedded primary display and has its own phy
>> +        * initialize phy immediately
> Question still stands why we can't wait for hpd high from the eDP panel.
> Also, I think "has its own phy" means that it's not part of a combo
> USB+DP phy? Can you please clarify?
>
>> +        */
>> +       if (dp->dp_display.connector_type == DRM_MODE_CONNECTOR_eDP)
>> +               dp_display_host_phy_init(dp);
>>
>> -       dp_power_init(dp->power, flip);
>> -       dp_ctrl_host_init(dp->ctrl, flip, reset);
>>          dp_aux_init(dp->aux);
>>          dp->core_initialized = true;
>>   }
>> @@ -1306,20 +1330,23 @@ static int dp_pm_resume(struct device *dev)
>>          dp->hpd_state = ST_DISCONNECTED;
>>
>>          /* turn on dp ctrl/phy */
>> -       dp_display_host_init(dp, true);
>> +       dp_display_host_init(dp);
>>
>>          dp_catalog_ctrl_hpd_config(dp->catalog);
>>
>> -       /*
>> -        * set sink to normal operation mode -- D0
>> -        * before dpcd read
>> -        */
>> -       dp_link_psm_config(dp->link, &dp->panel->link_info, false);
>>
>>          if (dp_catalog_link_is_connected(dp->catalog)) {
>> +               /*
>> +                * set sink to normal operation mode -- D0
>> +                * before dpcd read
>> +                */
>> +               dp_display_host_phy_init(dp);
>> +               dp_link_psm_config(dp->link, &dp->panel->link_info, false);
>>                  sink_count = drm_dp_read_sink_count(dp->aux);
>>                  if (sink_count < 0)
>>                          sink_count = 0;
>> +
>> +               dp_display_host_phy_exit(dp);
> Why is the phy exited on resume when the link is still connected? Is
> this supposed to be done only when the sink_count is 0? And how does
> this interact with eDP where the phy is initialized by the call to
> dp_display_host_init() earlier in this function.
>
>>          }
>>
>>          dp->link->sink_count = sink_count;
>> @@ -1366,6 +1393,8 @@ static int dp_pm_suspend(struct device *dev)
>>                  dp_display_host_deinit(dp);
>>          }
>>
>> +       dp_display_host_phy_exit(dp);
>> +
>>          dp->hpd_state = ST_SUSPENDED;
>>
>>          /* host_init will be called at pm_resume */


More information about the dri-devel mailing list