[Freedreno] [PATCH v11 1/4] drm/msm/dp: do not initialize phy until plugin interrupt received
Kuogee Hsieh
quic_khsieh at quicinc.com
Thu Jan 13 17:51:42 UTC 2022
On 1/12/2022 8:13 PM, Stephen Boyd wrote:
> Quoting Kuogee Hsieh (2022-01-12 14:17:54)
>> 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.
> Ok. The test scenarios can be reworded in terms of plugin irq and
> irq-hpd if that makes it easier.
>
>> 4) Note: phy_initialized only associated with plugged-in interrupt
>>
>> 5) irq-hpd interrupt must happen after plugged-in interrupt and before
>> unplugged interrupt
> More precisely it's that plugged-in interrupt must be handled before
> irq-hpd but plugged-in and irq-hpd can both be pending at the device
> concurrently unless they're masked and unmasked in some particular
> order. I thought the driver ensures that only irq-hpd is unmasked once
> the plugged in irq is handled. Can you confirm? Similarly, unplugged irq
> is unmasked after plugged in irq is handled, but irq-hpd and unplugged
> can both be pending if the irq handler is delayed?
we went through this before,
1) plugin_handler ==> do plug staffs + unmask irq_hpd at end of handler
2) unplug_handler ==> mask both plugin and irq_hpd + do unplug staffs +
unmask plugin irq at end of handler
3) priority order of handling irq when multiple irqs pending ==> plugin
-> irq_hpd -> unplug
>
>> 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;
> Why is the order of operations swapped? During probe core_initialized
> is done first and then phy_initialized but then on suspend
> core_initialized is done first again before phy_initialized. That's
> asymmetric.
ok, will fix this
>
>> 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
> I think this is more like "dp_display_config_hpd() is called by hpd
> kthread"?
yes, correct as below (eDP will call phy_init immediately
1) driver probe ==> core_initialized = false; phy_initialized = false;
2) dp_display_config_hpd()
3) dp_display_host_init() ==> core_initialized = true;
4) dp_display_host_phy_init() ==> phy_initialize = true
5) 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
> The panel can certainly be disconnected in the sense that the ribbon
> cable to the panel is busted or not working properly. That's what this
> scenario is for.
1) driver probe ==> core_initialized = false; phy_initialized = false;
2) dp_display_config_hpd()
3) dp_display_host_init() ==> core_initialized = true;
4) dp_display_host_phy_init() ==> phy_initialize = true
1) driver probe ==> core_initialized = false; phy_initialized = false;
>> #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;
1) driver probe ==> core_initialized = false; phy_initialized = false;
2) dp_display_config_hpd()
3) dp_display_host_init() ==> core_initialized = true;
4) dp_display_host_phy_init() ==> phy_initialize = true
5) generate plug-in interrupt triggers handler
6) dp_display_phy_init() ==> phy_initialized = true;
7) suspend
8) dp_display_host_deinit() ==> core_initialized = false;
9) dp_display_host_phy_exit() ==> phy_initialize = false;
10) resume
11) dp_display_host_init() ==> core_initialized = true;
11) dp_display_phy_init() ==> phy_initialize = true;
12) generate plug-in interrupt triggers handler
> Thanks. It really helps to see the various scenarios.
>
>>>> 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;
> Any response to the above two comments?
More information about the Freedreno
mailing list