drm: bridge/dw_hdmi: avoid enabling interface in mode_set
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / bridge / dw_hdmi.c
index 0ed8c49f1c6ed8ca41b18d40ef6adaf0beb4b696..8efd61781f89e3e85edf70c564d99ee6e0983fcc 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/hdmi.h>
 #include <linux/mutex.h>
 #include <linux/of_device.h>
+#include <linux/spinlock.h>
 
 #include <drm/drm_of.h>
 #include <drm/drmP.h>
@@ -81,7 +82,6 @@ static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
 };
 
 struct hdmi_vmode {
-       bool mdvi;
        bool mdataenablepolarity;
 
        unsigned int mpixelclock;
@@ -122,9 +122,15 @@ struct dw_hdmi {
 
        struct i2c_adapter *ddc;
        void __iomem *regs;
+       bool sink_is_hdmi;
+       bool sink_has_audio;
 
+       spinlock_t audio_lock;
        struct mutex audio_mutex;
        unsigned int sample_rate;
+       unsigned int audio_cts;
+       unsigned int audio_n;
+       bool audio_enable;
        int ratio;
 
        void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
@@ -346,7 +352,11 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
        dev_dbg(hdmi->dev, "%s: samplerate=%ukHz ratio=%d pixelclk=%luMHz N=%d cts=%d\n",
                __func__, sample_rate, ratio, pixel_clk, n, cts);
 
-       hdmi_set_cts_n(hdmi, cts, n);
+       spin_lock_irq(&hdmi->audio_lock);
+       hdmi->audio_n = n;
+       hdmi->audio_cts = cts;
+       hdmi_set_cts_n(hdmi, cts, hdmi->audio_enable ? n : 0);
+       spin_unlock_irq(&hdmi->audio_lock);
 }
 
 static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi)
@@ -375,6 +385,28 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate);
 
+void dw_hdmi_audio_enable(struct dw_hdmi *hdmi)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&hdmi->audio_lock, flags);
+       hdmi->audio_enable = true;
+       hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+       spin_unlock_irqrestore(&hdmi->audio_lock, flags);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_audio_enable);
+
+void dw_hdmi_audio_disable(struct dw_hdmi *hdmi)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&hdmi->audio_lock, flags);
+       hdmi->audio_enable = false;
+       hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
+       spin_unlock_irqrestore(&hdmi->audio_lock, flags);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_audio_disable);
+
 /*
  * this submodule is responsible for the video data synchronization.
  * for example, for RGB 4:4:4 input, the data map is defined as
@@ -882,11 +914,10 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
 static int dw_hdmi_phy_init(struct dw_hdmi *hdmi)
 {
        int i, ret;
-       bool cscon = false;
+       bool cscon;
 
        /*check csc whether needed activated in HDMI mode */
-       cscon = (is_color_space_conversion(hdmi) &&
-                       !hdmi->hdmi_data.video_mode.mdvi);
+       cscon = hdmi->sink_is_hdmi && is_color_space_conversion(hdmi);
 
        /* HDMI Phy spec says to do the phy initialization sequence twice */
        for (i = 0; i < 2; i++) {
@@ -1062,9 +1093,9 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
                HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
                HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE;
 
-       inv_val |= (vmode->mdvi ?
-               HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE :
-               HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE);
+       inv_val |= hdmi->sink_is_hdmi ?
+               HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE :
+               HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE;
 
        hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
 
@@ -1191,10 +1222,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
 
        if (!hdmi->vic) {
                dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n");
-               hdmi->hdmi_data.video_mode.mdvi = true;
        } else {
                dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic);
-               hdmi->hdmi_data.video_mode.mdvi = false;
        }
 
        if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
@@ -1205,18 +1234,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
        else
                hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
 
-       if ((hdmi->vic == 10) || (hdmi->vic == 11) ||
-           (hdmi->vic == 12) || (hdmi->vic == 13) ||
-           (hdmi->vic == 14) || (hdmi->vic == 15) ||
-           (hdmi->vic == 25) || (hdmi->vic == 26) ||
-           (hdmi->vic == 27) || (hdmi->vic == 28) ||
-           (hdmi->vic == 29) || (hdmi->vic == 30) ||
-           (hdmi->vic == 35) || (hdmi->vic == 36) ||
-           (hdmi->vic == 37) || (hdmi->vic == 38))
-               hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1;
-       else
-               hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
-
+       hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
        hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
 
        /* TODO: Get input format from IPU (via FB driver interface) */
@@ -1240,18 +1258,22 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
        /* HDMI Initialization Step B.3 */
        dw_hdmi_enable_video_path(hdmi);
 
-       /* not for DVI mode */
-       if (hdmi->hdmi_data.video_mode.mdvi) {
-               dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
-       } else {
-               dev_dbg(hdmi->dev, "%s CEA mode\n", __func__);
+       if (hdmi->sink_has_audio) {
+               dev_dbg(hdmi->dev, "sink has audio support\n");
 
                /* HDMI Initialization Step E - Configure audio */
                hdmi_clk_regenerator_update_pixel_clock(hdmi);
                hdmi_enable_audio_clk(hdmi);
+       }
+
+       /* not for DVI mode */
+       if (hdmi->sink_is_hdmi) {
+               dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
 
                /* HDMI Initialization Step F - Configure AVI InfoFrame */
                hdmi_config_AVI(hdmi, mode);
+       } else {
+               dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
        }
 
        hdmi_video_packetize(hdmi);
@@ -1260,7 +1282,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
        hdmi_tx_hdcp_config(hdmi);
 
        dw_hdmi_clear_overflow(hdmi);
-       if (hdmi->cable_plugin && !hdmi->hdmi_data.video_mode.mdvi)
+       if (hdmi->cable_plugin && hdmi->sink_is_hdmi)
                hdmi_enable_overflow_interrupts(hdmi);
 
        return 0;
@@ -1353,8 +1375,6 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
 {
        struct dw_hdmi *hdmi = bridge->driver_private;
 
-       dw_hdmi_setup(hdmi, mode);
-
        /* Store the display mode for plugin/DKMS poweron events */
        memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
 }
@@ -1410,6 +1430,8 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
                dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
                        edid->width_cm, edid->height_cm);
 
+               hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+               hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
                drm_mode_connector_update_edid_property(connector, edid);
                ret = drm_add_edid_modes(connector, edid);
                kfree(edid);
@@ -1428,6 +1450,10 @@ dw_hdmi_connector_mode_valid(struct drm_connector *connector,
                                           struct dw_hdmi, connector);
        enum drm_mode_status mode_status = MODE_OK;
 
+       /* We don't support double-clocked modes */
+       if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+               return MODE_BAD;
+
        if (hdmi->plat_data->mode_valid)
                mode_status = hdmi->plat_data->mode_valid(connector, mode);
 
@@ -1577,6 +1603,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
        hdmi->encoder = encoder;
 
        mutex_init(&hdmi->audio_mutex);
+       spin_lock_init(&hdmi->audio_lock);
 
        of_property_read_u32(np, "reg-io-width", &val);