glk_force_audio_cdclk() causes a regression: random screen blinking (caused by unexpected modesetting)
The glk_force_audio_cdclk() workaround, which was first introduced in commit 905801fe7237 ("drm/i915: Force 2*96 MHz cdclk on glk/cnl when audio power is enabled") (https://patchwork.freedesktop.org/patch/293205), causes a regression on Gemini Lake: if there is an external monitor connected to the laptop, then at random moments in time there is unexpected annoying blinking of both internal and external displays.
My initial debugging shows that this issue is related to runtime power management of audio. When the userspace audio stack becomes active after some time of inactivity (e.g. when starting or resuming an audio playback), runtime resume of audio happens, which triggers glk_force_audio_cdclk(), which changes CDCLK. Since there are 2 or more active displays, changing CDCLK requires modesetting (i.e. modeset skip implemented in https://patchwork.freedesktop.org/patch/294503 does not apply in this case). This modesetting causes an unexpected long blinking of the displays, which hurts the user experience a lot.
In other words, the problem is that glk_force_audio_cdclk() causes modesetting not only in cases when it is expected anyway (e.g. when plugging/unplugging the monitor, or when suspending/resuming the entire system) but also when it is completely unexpected by the user (due to runtime suspend/resume of the audio device).
To see this, I set up ftrace tracing for sof_suspend(), sof_resume() and glk_force_audio_cdclk() functions, and when the issue occurs, I can see the following trace:
cras-2453 [000] ...1. 449.093716: sof_resume <-__rpm_callback
cras-2453 [000] ...1. 449.093748: glk_force_audio_cdclk <-i915_audio_component_get_power
cras-2453 [001] ...1. 450.350903: glk_force_audio_cdclk <-i915_audio_component_put_power
and in DRM logs I see:
[ 449.079115] i915 0000:00:02.0: [drm:intel_modeset_calc_cdclk] Modeset required for cdclk change
[ 449.079120] i915 0000:00:02.0: [drm:intel_modeset_calc_cdclk] New cdclk calculated to be logical 316800 kHz, actual 316800 kHz
[ 449.079125] i915 0000:00:02.0: [drm:intel_modeset_calc_cdclk] New voltage level calculated to be logical 13, actual 13
[ 449.356819] i915 0000:00:02.0: [drm:intel_cdclk_dump_config] Changing CDCLK to 316800 kHz, VCO 633600 kHz, ref 19200 kHz, bypass 19200 kHz, voltage level 13
[ 450.335908] i915 0000:00:02.0: [drm:intel_modeset_calc_cdclk] Modeset required for cdclk change
[ 450.335913] i915 0000:00:02.0: [drm:intel_modeset_calc_cdclk] New cdclk calculated to be logical 158400 kHz, actual 158400 kHz
[ 450.335918] i915 0000:00:02.0: [drm:intel_modeset_calc_cdclk] New voltage level calculated to be logical 7, actual 7
[ 450.622657] i915 0000:00:02.0: [drm:intel_cdclk_dump_config] Changing CDCLK to 158400 kHz, VCO 633600 kHz, ref 19200 kHz, bypass 19200 kHz, voltage level 7
(whereas without an external monitor, it is almost the same, except that there is Can change cdclk cd2x divider with pipe A active
instead of Modeset required for cdclk change
, i.e modesetting is skipped, so the problem doesn't occur).
Removing glk_force_audio_cdclk() fixes this problem. (But I'm assuming that glk_force_audio_cdclk() addresses some real issue, so I'm not sure simply reverting it is a good idea.)
Also I tried enforcing "cdclk squasher" on GLK, i.e. adding .has_cdclk_squash = 1
to glk_display
, to force skipping modesetting (as per https://patchwork.freedesktop.org/patch/463648). This also fixes the problem, but obviously I'm not sure this is a safe thing to do either (AFAICS from the code, this squasher is not available in any SoC older than MTL).
Any suggestions what would be a proper way to fix this issue?
Device: HP Chromebook x360 14a
System: ChromeOS
Kernel version: 6.6.0-rc4 (and other kernels newer than 5.0)
External display: Dell P2421DC
External display connection: USB-C