drm/i915/hdmi: Add 'force_audio' property
[deliverable/linux.git] / drivers / gpu / drm / i915 / intel_hdmi.c
index 9fb9501f2d0751e0c863e575c987203de1b42e70..6c3b2ecd59d5d85fd6d14d186d4bc499198c6135 100644 (file)
@@ -42,6 +42,9 @@ struct intel_hdmi {
        u32 sdvox_reg;
        int ddc_bus;
        bool has_hdmi_sink;
+       bool has_audio;
+       int force_audio;
+       struct drm_property *force_audio_property;
 };
 
 static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder)
@@ -72,11 +75,12 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
        if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
                sdvox |= SDVO_HSYNC_ACTIVE_HIGH;
 
-       if (intel_hdmi->has_hdmi_sink) {
+       /* Required on CPT */
+       if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev))
+               sdvox |= HDMI_MODE_SELECT;
+
+       if (intel_hdmi->has_audio)
                sdvox |= SDVO_AUDIO_ENABLE;
-               if (HAS_PCH_CPT(dev))
-                       sdvox |= HDMI_MODE_SELECT;
-       }
 
        if (intel_crtc->pipe == 1) {
                if (HAS_PCH_CPT(dev))
@@ -154,6 +158,7 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
        enum drm_connector_status status = connector_status_disconnected;
 
        intel_hdmi->has_hdmi_sink = false;
+       intel_hdmi->has_audio = false;
        edid = drm_get_edid(connector,
                            &dev_priv->gmbus[intel_hdmi->ddc_bus].adapter);
 
@@ -161,11 +166,17 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
                if (edid->input & DRM_EDID_INPUT_DIGITAL) {
                        status = connector_status_connected;
                        intel_hdmi->has_hdmi_sink = drm_detect_hdmi_monitor(edid);
+                       intel_hdmi->has_audio = drm_detect_monitor_audio(edid);
                }
                connector->display_info.raw_edid = NULL;
                kfree(edid);
        }
 
+       if (status == connector_status_connected) {
+               if (intel_hdmi->force_audio)
+                       intel_hdmi->has_audio = intel_hdmi->force_audio > 0;
+       }
+
        return status;
 }
 
@@ -182,6 +193,46 @@ static int intel_hdmi_get_modes(struct drm_connector *connector)
                                   &dev_priv->gmbus[intel_hdmi->ddc_bus].adapter);
 }
 
+static int
+intel_hdmi_set_property(struct drm_connector *connector,
+                     struct drm_property *property,
+                     uint64_t val)
+{
+       struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
+       int ret;
+
+       ret = drm_connector_property_set_value(connector, property, val);
+       if (ret)
+               return ret;
+
+       if (property == intel_hdmi->force_audio_property) {
+               if (val == intel_hdmi->force_audio)
+                       return 0;
+
+               intel_hdmi->force_audio = val;
+
+               if (val > 0 && intel_hdmi->has_audio)
+                       return 0;
+               if (val < 0 && !intel_hdmi->has_audio)
+                       return 0;
+
+               intel_hdmi->has_audio = val > 0;
+               goto done;
+       }
+
+       return -EINVAL;
+
+done:
+       if (intel_hdmi->base.base.crtc) {
+               struct drm_crtc *crtc = intel_hdmi->base.base.crtc;
+               drm_crtc_helper_set_mode(crtc, &crtc->mode,
+                                        crtc->x, crtc->y,
+                                        crtc->fb);
+       }
+
+       return 0;
+}
+
 static void intel_hdmi_destroy(struct drm_connector *connector)
 {
        drm_sysfs_connector_remove(connector);
@@ -201,6 +252,7 @@ static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
        .dpms = drm_helper_connector_dpms,
        .detect = intel_hdmi_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
+       .set_property = intel_hdmi_set_property,
        .destroy = intel_hdmi_destroy,
 };
 
@@ -214,6 +266,20 @@ static const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
        .destroy = intel_encoder_destroy,
 };
 
+static void
+intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+
+       intel_hdmi->force_audio_property =
+               drm_property_create(dev, DRM_MODE_PROP_RANGE, "force_audio", 2);
+       if (intel_hdmi->force_audio_property) {
+               intel_hdmi->force_audio_property->values[0] = -1;
+               intel_hdmi->force_audio_property->values[1] = 1;
+               drm_connector_attach_property(connector, intel_hdmi->force_audio_property, 0);
+       }
+}
+
 void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -275,6 +341,8 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
 
        drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs);
 
+       intel_hdmi_add_properties(intel_hdmi, connector);
+
        intel_connector_attach_encoder(intel_connector, intel_encoder);
        drm_sysfs_connector_add(connector);
 
This page took 0.028802 seconds and 5 git commands to generate.