OSDN Git Service

drm/i915/audio: set proper N/M in modeset
authorLibin Yang <libin.yang@linux.intel.com>
Tue, 25 Oct 2016 14:54:18 +0000 (17:54 +0300)
committerJani Nikula <jani.nikula@intel.com>
Wed, 26 Oct 2016 09:36:30 +0000 (12:36 +0300)
When modeset occurs and the LS_CLK is set to some special values in DP
mode, the N/M need to be set manually if audio is playing. Otherwise the
first several seconds may be silent in audio playback.

The relationship of Maud and Naud is expressed in the following
equation:

Maud/Naud = 512 * fs / f_LS_Clk

Please refer VESA DisplayPort Standard spec for details.

v2 by Jani:
- organize Maud/Naud table according to DP 1.4 spec
- add 64k and 128k audio rates
- update HSW_AUD_M_CTS_ENABLE register when Maud not found
- remove extra checks for port clock
- simplify Maud/Naud lookup
- reset patch author back to Libin

Cc: "Zhang, Keqiao" <keqiao.zhang@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: "Lin, Mengdong" <mengdong.lin@intel.com>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Libin Yang <libin.yang@linux.intel.com>
Signed-off-by: Jani Nikula <jani.nikula@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1477407258-30599-3-git-send-email-jani.nikula@intel.com
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_audio.c

index bdc7b35..542e570 100644 (file)
@@ -7370,6 +7370,13 @@ enum {
 #define _HSW_AUD_MISC_CTRL_B           0x65110
 #define HSW_AUD_MISC_CTRL(pipe)                _MMIO_PIPE(pipe, _HSW_AUD_MISC_CTRL_A, _HSW_AUD_MISC_CTRL_B)
 
+#define _HSW_AUD_M_CTS_ENABLE_A                0x65028
+#define _HSW_AUD_M_CTS_ENABLE_B                0x65128
+#define HSW_AUD_M_CTS_ENABLE(pipe)     _MMIO_PIPE(pipe, _HSW_AUD_M_CTS_ENABLE_A, _HSW_AUD_M_CTS_ENABLE_B)
+#define   AUD_M_CTS_M_VALUE_INDEX      (1 << 21)
+#define   AUD_M_CTS_M_PROG_ENABLE      (1 << 20)
+#define   AUD_CONFIG_M_MASK            0xfffff
+
 #define _HSW_AUD_DIP_ELD_CTRL_ST_A     0x650b4
 #define _HSW_AUD_DIP_ELD_CTRL_ST_B     0x651b4
 #define HSW_AUD_DIP_ELD_CTRL(pipe)     _MMIO_PIPE(pipe, _HSW_AUD_DIP_ELD_CTRL_ST_A, _HSW_AUD_DIP_ELD_CTRL_ST_B)
index 1a5c212..813fd74 100644 (file)
  * struct &i915_audio_component_audio_ops @audio_ops is called from i915 driver.
  */
 
+/* DP N/M table */
+#define LC_540M        540000
+#define LC_270M        270000
+#define LC_162M        162000
+
+struct dp_aud_n_m {
+       int sample_rate;
+       int clock;
+       u16 m;
+       u16 n;
+};
+
+/* Values according to DP 1.4 Table 2-104 */
+static const struct dp_aud_n_m dp_aud_n_m[] = {
+       { 32000, LC_162M, 1024, 10125 },
+       { 44100, LC_162M, 784, 5625 },
+       { 48000, LC_162M, 512, 3375 },
+       { 64000, LC_162M, 2048, 10125 },
+       { 88200, LC_162M, 1568, 5625 },
+       { 96000, LC_162M, 1024, 3375 },
+       { 128000, LC_162M, 4096, 10125 },
+       { 176400, LC_162M, 3136, 5625 },
+       { 192000, LC_162M, 2048, 3375 },
+       { 32000, LC_270M, 1024, 16875 },
+       { 44100, LC_270M, 784, 9375 },
+       { 48000, LC_270M, 512, 5625 },
+       { 64000, LC_270M, 2048, 16875 },
+       { 88200, LC_270M, 1568, 9375 },
+       { 96000, LC_270M, 1024, 5625 },
+       { 128000, LC_270M, 4096, 16875 },
+       { 176400, LC_270M, 3136, 9375 },
+       { 192000, LC_270M, 2048, 5625 },
+       { 32000, LC_540M, 1024, 33750 },
+       { 44100, LC_540M, 784, 18750 },
+       { 48000, LC_540M, 512, 11250 },
+       { 64000, LC_540M, 2048, 33750 },
+       { 88200, LC_540M, 1568, 18750 },
+       { 96000, LC_540M, 1024, 11250 },
+       { 128000, LC_540M, 4096, 33750 },
+       { 176400, LC_540M, 3136, 18750 },
+       { 192000, LC_540M, 2048, 11250 },
+};
+
+static const struct dp_aud_n_m *
+audio_config_dp_get_n_m(struct intel_crtc *intel_crtc, int rate)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(dp_aud_n_m); i++) {
+               if (rate == dp_aud_n_m[i].sample_rate &&
+                   intel_crtc->config->port_clock == dp_aud_n_m[i].clock)
+                       return &dp_aud_n_m[i];
+       }
+
+       return NULL;
+}
+
 static const struct {
        int clock;
        u32 config;
@@ -225,16 +282,43 @@ hsw_dp_audio_config_update(struct intel_crtc *intel_crtc, enum port port,
                           const struct drm_display_mode *adjusted_mode)
 {
        struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev);
+       struct i915_audio_component *acomp = dev_priv->audio_component;
+       int rate = acomp ? acomp->aud_sample_rate[port] : 0;
+       const struct dp_aud_n_m *nm = audio_config_dp_get_n_m(intel_crtc, rate);
        enum pipe pipe = intel_crtc->pipe;
        u32 tmp;
 
+       if (nm)
+               DRM_DEBUG_KMS("using Maud %u, Naud %u\n", nm->m, nm->n);
+       else
+               DRM_DEBUG_KMS("using automatic Maud, Naud\n");
+
        tmp = I915_READ(HSW_AUD_CFG(pipe));
        tmp &= ~AUD_CONFIG_N_VALUE_INDEX;
        tmp &= ~AUD_CONFIG_PIXEL_CLOCK_HDMI_MASK;
        tmp &= ~AUD_CONFIG_N_PROG_ENABLE;
        tmp |= AUD_CONFIG_N_VALUE_INDEX;
 
+       if (nm) {
+               tmp &= ~AUD_CONFIG_N_MASK;
+               tmp |= AUD_CONFIG_N(nm->n);
+               tmp |= AUD_CONFIG_N_PROG_ENABLE;
+       }
+
        I915_WRITE(HSW_AUD_CFG(pipe), tmp);
+
+       tmp = I915_READ(HSW_AUD_M_CTS_ENABLE(pipe));
+       tmp &= ~AUD_CONFIG_M_MASK;
+       tmp &= ~AUD_M_CTS_M_VALUE_INDEX;
+       tmp &= ~AUD_M_CTS_M_PROG_ENABLE;
+
+       if (nm) {
+               tmp |= nm->m;
+               tmp |= AUD_M_CTS_M_VALUE_INDEX;
+               tmp |= AUD_M_CTS_M_PROG_ENABLE;
+       }
+
+       I915_WRITE(HSW_AUD_M_CTS_ENABLE(pipe), tmp);
 }
 
 static void
@@ -266,6 +350,12 @@ hsw_hdmi_audio_config_update(struct intel_crtc *intel_crtc, enum port port,
        }
 
        I915_WRITE(HSW_AUD_CFG(pipe), tmp);
+
+       tmp = I915_READ(HSW_AUD_M_CTS_ENABLE(pipe));
+       tmp &= ~AUD_CONFIG_M_MASK;
+       tmp &= ~AUD_M_CTS_M_VALUE_INDEX;
+       tmp |= AUD_M_CTS_M_PROG_ENABLE;
+       I915_WRITE(HSW_AUD_M_CTS_ENABLE(pipe), tmp);
 }
 
 static void
@@ -686,7 +776,8 @@ static int i915_audio_component_sync_audio_rate(struct device *kdev, int port,
        /* 1. get the pipe */
        intel_encoder = get_saved_enc(dev_priv, port, pipe);
        if (!intel_encoder || !intel_encoder->base.crtc ||
-           intel_encoder->type != INTEL_OUTPUT_HDMI) {
+           (intel_encoder->type != INTEL_OUTPUT_HDMI &&
+            intel_encoder->type != INTEL_OUTPUT_DP)) {
                DRM_DEBUG_KMS("Not valid for port %c\n", port_name(port));
                err = -ENODEV;
                goto unlock;