diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_audio.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_audio.c | 95 |
1 files changed, 81 insertions, 14 deletions
diff --git a/drivers/gpu/drm/i915/intel_audio.c b/drivers/gpu/drm/i915/intel_audio.c index 5104c6bbd66f..bca4cc025d3d 100644 --- a/drivers/gpu/drm/i915/intel_audio.c +++ b/drivers/gpu/drm/i915/intel_audio.c @@ -21,14 +21,16 @@ * DEALINGS IN THE SOFTWARE. */ -#include <linux/kernel.h> #include <linux/component.h> +#include <linux/kernel.h> + +#include <drm/drm_edid.h> #include <drm/i915_component.h> #include <drm/intel_lpe_audio.h> -#include "intel_drv.h" -#include <drm/drm_edid.h> #include "i915_drv.h" +#include "intel_audio.h" +#include "intel_drv.h" /** * DOC: High Definition Audio over HDMI and Display Port @@ -741,27 +743,91 @@ void intel_init_audio_hooks(struct drm_i915_private *dev_priv) } } -static void i915_audio_component_get_power(struct device *kdev) +static void glk_force_audio_cdclk(struct drm_i915_private *dev_priv, + bool enable) { - intel_display_power_get(kdev_to_i915(kdev), POWER_DOMAIN_AUDIO); + struct drm_modeset_acquire_ctx ctx; + struct drm_atomic_state *state; + int ret; + + drm_modeset_acquire_init(&ctx, 0); + state = drm_atomic_state_alloc(&dev_priv->drm); + if (WARN_ON(!state)) + return; + + state->acquire_ctx = &ctx; + +retry: + to_intel_atomic_state(state)->cdclk.force_min_cdclk_changed = true; + to_intel_atomic_state(state)->cdclk.force_min_cdclk = + enable ? 2 * 96000 : 0; + + /* + * Protects dev_priv->cdclk.force_min_cdclk + * Need to lock this here in case we have no active pipes + * and thus wouldn't lock it during the commit otherwise. + */ + ret = drm_modeset_lock(&dev_priv->drm.mode_config.connection_mutex, + &ctx); + if (!ret) + ret = drm_atomic_commit(state); + + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + drm_modeset_backoff(&ctx); + goto retry; + } + + WARN_ON(ret); + + drm_atomic_state_put(state); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } -static void i915_audio_component_put_power(struct device *kdev) +static unsigned long i915_audio_component_get_power(struct device *kdev) { - intel_display_power_put_unchecked(kdev_to_i915(kdev), - POWER_DOMAIN_AUDIO); + struct drm_i915_private *dev_priv = kdev_to_i915(kdev); + intel_wakeref_t ret; + + /* Catch potential impedance mismatches before they occur! */ + BUILD_BUG_ON(sizeof(intel_wakeref_t) > sizeof(unsigned long)); + + ret = intel_display_power_get(dev_priv, POWER_DOMAIN_AUDIO); + + /* Force CDCLK to 2*BCLK as long as we need audio to be powered. */ + if (dev_priv->audio_power_refcount++ == 0) + if (IS_CANNONLAKE(dev_priv) || IS_GEMINILAKE(dev_priv)) + glk_force_audio_cdclk(dev_priv, true); + + return ret; +} + +static void i915_audio_component_put_power(struct device *kdev, + unsigned long cookie) +{ + struct drm_i915_private *dev_priv = kdev_to_i915(kdev); + + /* Stop forcing CDCLK to 2*BCLK if no need for audio to be powered. */ + if (--dev_priv->audio_power_refcount == 0) + if (IS_CANNONLAKE(dev_priv) || IS_GEMINILAKE(dev_priv)) + glk_force_audio_cdclk(dev_priv, false); + + intel_display_power_put(dev_priv, POWER_DOMAIN_AUDIO, cookie); } static void i915_audio_component_codec_wake_override(struct device *kdev, bool enable) { struct drm_i915_private *dev_priv = kdev_to_i915(kdev); + unsigned long cookie; u32 tmp; if (!IS_GEN(dev_priv, 9)) return; - i915_audio_component_get_power(kdev); + cookie = i915_audio_component_get_power(kdev); /* * Enable/disable generating the codec wake signal, overriding the @@ -779,7 +845,7 @@ static void i915_audio_component_codec_wake_override(struct device *kdev, usleep_range(1000, 1500); } - i915_audio_component_put_power(kdev); + i915_audio_component_put_power(kdev, cookie); } /* Get CDCLK in kHz */ @@ -850,12 +916,13 @@ static int i915_audio_component_sync_audio_rate(struct device *kdev, int port, struct i915_audio_component *acomp = dev_priv->audio_component; struct intel_encoder *encoder; struct intel_crtc *crtc; + unsigned long cookie; int err = 0; if (!HAS_DDI(dev_priv)) return 0; - i915_audio_component_get_power(kdev); + cookie = i915_audio_component_get_power(kdev); mutex_lock(&dev_priv->av_mutex); /* 1. get the pipe */ @@ -875,7 +942,7 @@ static int i915_audio_component_sync_audio_rate(struct device *kdev, int port, unlock: mutex_unlock(&dev_priv->av_mutex); - i915_audio_component_put_power(kdev); + i915_audio_component_put_power(kdev, cookie); return err; } @@ -980,7 +1047,7 @@ static const struct component_ops i915_audio_component_bind_ops = { * We ignore any error during registration and continue with reduced * functionality (i.e. without HDMI audio). */ -void i915_audio_component_init(struct drm_i915_private *dev_priv) +static void i915_audio_component_init(struct drm_i915_private *dev_priv) { int ret; @@ -1003,7 +1070,7 @@ void i915_audio_component_init(struct drm_i915_private *dev_priv) * Deregisters the audio component, breaking any existing binding to the * corresponding snd_hda_intel driver's master component. */ -void i915_audio_component_cleanup(struct drm_i915_private *dev_priv) +static void i915_audio_component_cleanup(struct drm_i915_private *dev_priv) { if (!dev_priv->audio_component_registered) return; |