]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/bridge/dw_hdmi.c
drm: bridge/dw_hdmi: add connector mode forcing
[karo-tx-linux.git] / drivers / gpu / drm / bridge / dw_hdmi.c
index dc1a382a96665a433ae6545a9abc8dc69d8f5fd5..7dcae9df509b9e67e94b3ed8881c4b2f38173b18 100644 (file)
@@ -126,7 +126,9 @@ struct dw_hdmi {
        bool sink_has_audio;
 
        struct mutex mutex;             /* for state below and previous_mode */
+       enum drm_connector_force force; /* mutex-protected force state */
        bool disabled;                  /* DRM has disabled our bridge */
+       bool bridge_is_on;              /* indicates the bridge is on */
 
        spinlock_t audio_lock;
        struct mutex audio_mutex;
@@ -1378,12 +1380,36 @@ static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi)
 
 static void dw_hdmi_poweron(struct dw_hdmi *hdmi)
 {
+       hdmi->bridge_is_on = true;
        dw_hdmi_setup(hdmi, &hdmi->previous_mode);
 }
 
 static void dw_hdmi_poweroff(struct dw_hdmi *hdmi)
 {
        dw_hdmi_phy_disable(hdmi);
+       hdmi->bridge_is_on = false;
+}
+
+static void dw_hdmi_update_power(struct dw_hdmi *hdmi)
+{
+       int force = hdmi->force;
+
+       if (hdmi->disabled) {
+               force = DRM_FORCE_OFF;
+       } else if (force == DRM_FORCE_UNSPECIFIED) {
+               if (hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD)
+                       force = DRM_FORCE_ON;
+               else
+                       force = DRM_FORCE_OFF;
+       }
+
+       if (force == DRM_FORCE_OFF) {
+               if (hdmi->bridge_is_on)
+                       dw_hdmi_poweroff(hdmi);
+       } else {
+               if (!hdmi->bridge_is_on)
+                       dw_hdmi_poweron(hdmi);
+       }
 }
 
 static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
@@ -1413,7 +1439,7 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
 
        mutex_lock(&hdmi->mutex);
        hdmi->disabled = true;
-       dw_hdmi_poweroff(hdmi);
+       dw_hdmi_update_power(hdmi);
        mutex_unlock(&hdmi->mutex);
 }
 
@@ -1422,8 +1448,8 @@ static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
        struct dw_hdmi *hdmi = bridge->driver_private;
 
        mutex_lock(&hdmi->mutex);
-       dw_hdmi_poweron(hdmi);
        hdmi->disabled = false;
+       dw_hdmi_update_power(hdmi);
        mutex_unlock(&hdmi->mutex);
 }
 
@@ -1438,6 +1464,11 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
        struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
                                             connector);
 
+       mutex_lock(&hdmi->mutex);
+       hdmi->force = DRM_FORCE_UNSPECIFIED;
+       dw_hdmi_update_power(hdmi);
+       mutex_unlock(&hdmi->mutex);
+
        return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
                connector_status_connected : connector_status_disconnected;
 }
@@ -1502,11 +1533,23 @@ static void dw_hdmi_connector_destroy(struct drm_connector *connector)
        drm_connector_cleanup(connector);
 }
 
+static void dw_hdmi_connector_force(struct drm_connector *connector)
+{
+       struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+                                            connector);
+
+       mutex_lock(&hdmi->mutex);
+       hdmi->force = connector->force;
+       dw_hdmi_update_power(hdmi);
+       mutex_unlock(&hdmi->mutex);
+}
+
 static struct drm_connector_funcs dw_hdmi_connector_funcs = {
        .dpms = drm_helper_connector_dpms,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .detect = dw_hdmi_connector_detect,
        .destroy = dw_hdmi_connector_destroy,
+       .force = dw_hdmi_connector_force,
 };
 
 static struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
@@ -1552,12 +1595,12 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
                if (phy_int_pol & HDMI_PHY_HPD) {
                        dev_dbg(hdmi->dev, "EVENT=plugin\n");
 
-                       if (!hdmi->disabled)
+                       if (!hdmi->disabled && !hdmi->force)
                                dw_hdmi_poweron(hdmi);
                } else {
                        dev_dbg(hdmi->dev, "EVENT=plugout\n");
 
-                       if (!hdmi->disabled)
+                       if (!hdmi->disabled && !hdmi->force)
                                dw_hdmi_poweroff(hdmi);
                }
                mutex_unlock(&hdmi->mutex);