drm/i915: Add atomic_get_property entrypoint for connectors (v2)
[deliverable/linux.git] / drivers / gpu / drm / i915 / intel_hdmi.c
index 29ec1535992d413fa5faf70c8192c02d6334b02c..995c5b261f4f0f5cf295de8b08c95840a36ca0fe 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/delay.h>
 #include <linux/hdmi.h>
 #include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_edid.h>
 #include "intel_drv.h"
@@ -166,6 +167,19 @@ static void g4x_write_infoframe(struct drm_encoder *encoder,
        POSTING_READ(VIDEO_DIP_CTL);
 }
 
+static bool g4x_infoframe_enabled(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
+       u32 val = I915_READ(VIDEO_DIP_CTL);
+
+       if (VIDEO_DIP_PORT(intel_dig_port->port) == (val & VIDEO_DIP_PORT_MASK))
+               return val & VIDEO_DIP_ENABLE;
+
+       return false;
+}
+
 static void ibx_write_infoframe(struct drm_encoder *encoder,
                                enum hdmi_infoframe_type type,
                                const void *frame, ssize_t len)
@@ -204,6 +218,17 @@ static void ibx_write_infoframe(struct drm_encoder *encoder,
        POSTING_READ(reg);
 }
 
+static bool ibx_infoframe_enabled(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       int reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
+       u32 val = I915_READ(reg);
+
+       return val & VIDEO_DIP_ENABLE;
+}
+
 static void cpt_write_infoframe(struct drm_encoder *encoder,
                                enum hdmi_infoframe_type type,
                                const void *frame, ssize_t len)
@@ -245,6 +270,17 @@ static void cpt_write_infoframe(struct drm_encoder *encoder,
        POSTING_READ(reg);
 }
 
+static bool cpt_infoframe_enabled(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       int reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
+       u32 val = I915_READ(reg);
+
+       return val & VIDEO_DIP_ENABLE;
+}
+
 static void vlv_write_infoframe(struct drm_encoder *encoder,
                                enum hdmi_infoframe_type type,
                                const void *frame, ssize_t len)
@@ -283,6 +319,17 @@ static void vlv_write_infoframe(struct drm_encoder *encoder,
        POSTING_READ(reg);
 }
 
+static bool vlv_infoframe_enabled(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       int reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe);
+       u32 val = I915_READ(reg);
+
+       return val & VIDEO_DIP_ENABLE;
+}
+
 static void hsw_write_infoframe(struct drm_encoder *encoder,
                                enum hdmi_infoframe_type type,
                                const void *frame, ssize_t len)
@@ -291,13 +338,13 @@ static void hsw_write_infoframe(struct drm_encoder *encoder,
        struct drm_device *dev = encoder->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
-       u32 ctl_reg = HSW_TVIDEO_DIP_CTL(intel_crtc->config.cpu_transcoder);
+       u32 ctl_reg = HSW_TVIDEO_DIP_CTL(intel_crtc->config->cpu_transcoder);
        u32 data_reg;
        int i;
        u32 val = I915_READ(ctl_reg);
 
        data_reg = hsw_infoframe_data_reg(type,
-                                         intel_crtc->config.cpu_transcoder,
+                                         intel_crtc->config->cpu_transcoder,
                                          dev_priv);
        if (data_reg == 0)
                return;
@@ -320,6 +367,18 @@ static void hsw_write_infoframe(struct drm_encoder *encoder,
        POSTING_READ(ctl_reg);
 }
 
+static bool hsw_infoframe_enabled(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       u32 ctl_reg = HSW_TVIDEO_DIP_CTL(intel_crtc->config->cpu_transcoder);
+       u32 val = I915_READ(ctl_reg);
+
+       return val & (VIDEO_DIP_ENABLE_AVI_HSW | VIDEO_DIP_ENABLE_SPD_HSW |
+                     VIDEO_DIP_ENABLE_VS_HSW);
+}
+
 /*
  * The data we write to the DIP data buffer registers is 1 byte bigger than the
  * HDMI infoframe size because of an ECC/reserved byte at position 3 (starting
@@ -378,7 +437,7 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder,
        }
 
        if (intel_hdmi->rgb_quant_range_selectable) {
-               if (intel_crtc->config.limited_color_range)
+               if (intel_crtc->config->limited_color_range)
                        frame.avi.quantization_range =
                                HDMI_QUANTIZATION_RANGE_LIMITED;
                else
@@ -614,7 +673,7 @@ static void hsw_set_infoframes(struct drm_encoder *encoder,
        struct drm_i915_private *dev_priv = encoder->dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
        struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
-       u32 reg = HSW_TVIDEO_DIP_CTL(intel_crtc->config.cpu_transcoder);
+       u32 reg = HSW_TVIDEO_DIP_CTL(intel_crtc->config->cpu_transcoder);
        u32 val = I915_READ(reg);
 
        assert_hdmi_port_disabled(intel_hdmi);
@@ -642,7 +701,7 @@ static void intel_hdmi_prepare(struct intel_encoder *encoder)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
        struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
-       struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode;
+       struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode;
        u32 hdmi_val;
 
        hdmi_val = SDVO_ENCODING_HDMI;
@@ -653,22 +712,14 @@ static void intel_hdmi_prepare(struct intel_encoder *encoder)
        if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
                hdmi_val |= SDVO_HSYNC_ACTIVE_HIGH;
 
-       if (crtc->config.pipe_bpp > 24)
+       if (crtc->config->pipe_bpp > 24)
                hdmi_val |= HDMI_COLOR_FORMAT_12bpc;
        else
                hdmi_val |= SDVO_COLOR_FORMAT_8bpc;
 
-       if (crtc->config.has_hdmi_sink)
+       if (crtc->config->has_hdmi_sink)
                hdmi_val |= HDMI_MODE_SELECT_HDMI;
 
-       if (crtc->config.has_audio) {
-               WARN_ON(!crtc->config.has_hdmi_sink);
-               DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n",
-                                pipe_name(crtc->pipe));
-               hdmi_val |= SDVO_AUDIO_ENABLE;
-               intel_write_eld(&encoder->base, adjusted_mode);
-       }
-
        if (HAS_PCH_CPT(dev))
                hdmi_val |= SDVO_PIPE_SEL_CPT(crtc->pipe);
        else if (IS_CHERRYVIEW(dev))
@@ -690,7 +741,7 @@ static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
        u32 tmp;
 
        power_domain = intel_display_port_power_domain(encoder);
-       if (!intel_display_power_enabled(dev_priv, power_domain))
+       if (!intel_display_power_is_enabled(dev_priv, power_domain))
                return false;
 
        tmp = I915_READ(intel_hdmi->hdmi_reg);
@@ -709,7 +760,7 @@ static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
 }
 
 static void intel_hdmi_get_config(struct intel_encoder *encoder,
-                                 struct intel_crtc_config *pipe_config)
+                                 struct intel_crtc_state *pipe_config)
 {
        struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
        struct drm_device *dev = encoder->base.dev;
@@ -732,6 +783,9 @@ static void intel_hdmi_get_config(struct intel_encoder *encoder,
        if (tmp & HDMI_MODE_SELECT_HDMI)
                pipe_config->has_hdmi_sink = true;
 
+       if (intel_hdmi->infoframe_enabled(&encoder->base))
+               pipe_config->has_infoframe = true;
+
        if (tmp & SDVO_AUDIO_ENABLE)
                pipe_config->has_audio = true;
 
@@ -739,7 +793,7 @@ static void intel_hdmi_get_config(struct intel_encoder *encoder,
            tmp & HDMI_COLOR_RANGE_16_235)
                pipe_config->limited_color_range = true;
 
-       pipe_config->adjusted_mode.flags |= flags;
+       pipe_config->base.adjusted_mode.flags |= flags;
 
        if ((tmp & SDVO_COLOR_FORMAT_MASK) == HDMI_COLOR_FORMAT_12bpc)
                dotclock = pipe_config->port_clock * 2 / 3;
@@ -749,7 +803,7 @@ static void intel_hdmi_get_config(struct intel_encoder *encoder,
        if (HAS_PCH_SPLIT(dev_priv->dev))
                ironlake_check_encoder_dotclock(pipe_config, dotclock);
 
-       pipe_config->adjusted_mode.crtc_clock = dotclock;
+       pipe_config->base.adjusted_mode.crtc_clock = dotclock;
 }
 
 static void intel_enable_hdmi(struct intel_encoder *encoder)
@@ -761,7 +815,7 @@ static void intel_enable_hdmi(struct intel_encoder *encoder)
        u32 temp;
        u32 enable_bits = SDVO_ENABLE;
 
-       if (intel_crtc->config.has_audio)
+       if (intel_crtc->config->has_audio)
                enable_bits |= SDVO_AUDIO_ENABLE;
 
        temp = I915_READ(intel_hdmi->hdmi_reg);
@@ -791,6 +845,13 @@ static void intel_enable_hdmi(struct intel_encoder *encoder)
                I915_WRITE(intel_hdmi->hdmi_reg, temp);
                POSTING_READ(intel_hdmi->hdmi_reg);
        }
+
+       if (intel_crtc->config->has_audio) {
+               WARN_ON(!intel_crtc->config->has_hdmi_sink);
+               DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n",
+                                pipe_name(intel_crtc->pipe));
+               intel_audio_codec_enable(encoder);
+       }
 }
 
 static void vlv_enable_hdmi(struct intel_encoder *encoder)
@@ -802,9 +863,13 @@ static void intel_disable_hdmi(struct intel_encoder *encoder)
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
+       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
        u32 temp;
        u32 enable_bits = SDVO_ENABLE | SDVO_AUDIO_ENABLE;
 
+       if (crtc->config->has_audio)
+               intel_audio_codec_disable(encoder);
+
        temp = I915_READ(intel_hdmi->hdmi_reg);
 
        /* HW workaround for IBX, we need to move the port to transcoder A
@@ -911,17 +976,20 @@ static bool hdmi_12bpc_possible(struct intel_crtc *crtc)
 }
 
 bool intel_hdmi_compute_config(struct intel_encoder *encoder,
-                              struct intel_crtc_config *pipe_config)
+                              struct intel_crtc_state *pipe_config)
 {
        struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
        struct drm_device *dev = encoder->base.dev;
-       struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
-       int clock_12bpc = pipe_config->adjusted_mode.crtc_clock * 3 / 2;
+       struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
+       int clock_12bpc = pipe_config->base.adjusted_mode.crtc_clock * 3 / 2;
        int portclock_limit = hdmi_portclock_limit(intel_hdmi, false);
        int desired_bpp;
 
        pipe_config->has_hdmi_sink = intel_hdmi->has_hdmi_sink;
 
+       if (pipe_config->has_hdmi_sink)
+               pipe_config->has_infoframe = true;
+
        if (intel_hdmi->color_range_auto) {
                /* See CEA-861-E - 5.1 Default Encoding Parameters */
                if (pipe_config->has_hdmi_sink &&
@@ -1185,12 +1253,12 @@ static void intel_hdmi_pre_enable(struct intel_encoder *encoder)
        struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
        struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
        struct drm_display_mode *adjusted_mode =
-               &intel_crtc->config.adjusted_mode;
+               &intel_crtc->config->base.adjusted_mode;
 
        intel_hdmi_prepare(encoder);
 
        intel_hdmi->set_infoframes(&encoder->base,
-                                  intel_crtc->config.has_hdmi_sink,
+                                  intel_crtc->config->has_hdmi_sink,
                                   adjusted_mode);
 }
 
@@ -1203,7 +1271,7 @@ static void vlv_hdmi_pre_enable(struct intel_encoder *encoder)
        struct intel_crtc *intel_crtc =
                to_intel_crtc(encoder->base.crtc);
        struct drm_display_mode *adjusted_mode =
-               &intel_crtc->config.adjusted_mode;
+               &intel_crtc->config->base.adjusted_mode;
        enum dpio_channel port = vlv_dport_to_channel(dport);
        int pipe = intel_crtc->pipe;
        u32 val;
@@ -1235,7 +1303,7 @@ static void vlv_hdmi_pre_enable(struct intel_encoder *encoder)
        mutex_unlock(&dev_priv->dpio_lock);
 
        intel_hdmi->set_infoframes(&encoder->base,
-                                  intel_crtc->config.has_hdmi_sink,
+                                  intel_crtc->config->has_hdmi_sink,
                                   adjusted_mode);
 
        intel_enable_hdmi(encoder);
@@ -1394,10 +1462,13 @@ static void chv_hdmi_post_disable(struct intel_encoder *encoder)
 static void chv_hdmi_pre_enable(struct intel_encoder *encoder)
 {
        struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
+       struct intel_hdmi *intel_hdmi = &dport->hdmi;
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc =
                to_intel_crtc(encoder->base.crtc);
+       struct drm_display_mode *adjusted_mode =
+               &intel_crtc->config->base.adjusted_mode;
        enum dpio_channel ch = vlv_dport_to_channel(dport);
        int pipe = intel_crtc->pipe;
        int data, i;
@@ -1405,6 +1476,15 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder)
 
        mutex_lock(&dev_priv->dpio_lock);
 
+       /* allow hardware to manage TX FIFO reset source */
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW11(ch));
+       val &= ~DPIO_LANEDESKEW_STRAP_OVRD;
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW11(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch));
+       val &= ~DPIO_LANEDESKEW_STRAP_OVRD;
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val);
+
        /* Deassert soft data lane reset*/
        val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch));
        val |= CHV_PCS_REQ_SOFTRESET_EN;
@@ -1441,12 +1521,26 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder)
        /* Clear calc init */
        val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch));
        val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
+       val &= ~(DPIO_PCS_TX1DEEMP_MASK | DPIO_PCS_TX2DEEMP_MASK);
+       val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5;
        vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val);
 
        val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch));
        val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
+       val &= ~(DPIO_PCS_TX1DEEMP_MASK | DPIO_PCS_TX2DEEMP_MASK);
+       val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5;
        vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val);
 
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW9(ch));
+       val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK);
+       val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000;
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW9(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW9(ch));
+       val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK);
+       val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000;
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW9(ch), val);
+
        /* FIXME: Program the support xxx V-dB */
        /* Use 800mV-0dB */
        for (i = 0; i < 4; i++) {
@@ -1499,6 +1593,10 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder)
 
        mutex_unlock(&dev_priv->dpio_lock);
 
+       intel_hdmi->set_infoframes(&encoder->base,
+                                  intel_crtc->config->has_hdmi_sink,
+                                  adjusted_mode);
+
        intel_enable_hdmi(encoder);
 
        vlv_wait_port_ready(dev_priv, dport);
@@ -1517,7 +1615,9 @@ static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
        .force = intel_hdmi_force,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = intel_hdmi_set_property,
+       .atomic_get_property = intel_connector_atomic_get_property,
        .destroy = intel_hdmi_destroy,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = {
@@ -1593,18 +1693,23 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
        if (IS_VALLEYVIEW(dev)) {
                intel_hdmi->write_infoframe = vlv_write_infoframe;
                intel_hdmi->set_infoframes = vlv_set_infoframes;
+               intel_hdmi->infoframe_enabled = vlv_infoframe_enabled;
        } else if (IS_G4X(dev)) {
                intel_hdmi->write_infoframe = g4x_write_infoframe;
                intel_hdmi->set_infoframes = g4x_set_infoframes;
+               intel_hdmi->infoframe_enabled = g4x_infoframe_enabled;
        } else if (HAS_DDI(dev)) {
                intel_hdmi->write_infoframe = hsw_write_infoframe;
                intel_hdmi->set_infoframes = hsw_set_infoframes;
+               intel_hdmi->infoframe_enabled = hsw_infoframe_enabled;
        } else if (HAS_PCH_IBX(dev)) {
                intel_hdmi->write_infoframe = ibx_write_infoframe;
                intel_hdmi->set_infoframes = ibx_set_infoframes;
+               intel_hdmi->infoframe_enabled = ibx_infoframe_enabled;
        } else {
                intel_hdmi->write_infoframe = cpt_write_infoframe;
                intel_hdmi->set_infoframes = cpt_set_infoframes;
+               intel_hdmi->infoframe_enabled = cpt_infoframe_enabled;
        }
 
        if (HAS_DDI(dev))
This page took 0.036944 seconds and 5 git commands to generate.