These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / video / fbdev / omap2 / dss / hdmi4.c
index 916d479..94c8d55 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/clk.h>
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
+#include <linux/component.h>
 #include <video/omapdss.h>
 #include <sound/omap-hdmi-audio.h>
 
@@ -229,9 +230,9 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
 err_mgr_enable:
        hdmi_wp_video_stop(&hdmi.wp);
 err_vid_enable:
-err_phy_cfg:
        hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
 err_phy_pwr:
+err_phy_cfg:
 err_pll_cfg:
        dss_pll_disable(&hdmi.pll.pll);
 err_pll_enable:
@@ -320,9 +321,22 @@ static int read_edid(u8 *buf, int len)
        return r;
 }
 
+static void hdmi_start_audio_stream(struct omap_hdmi *hd)
+{
+       hdmi_wp_audio_enable(&hd->wp, true);
+       hdmi4_audio_start(&hd->core, &hd->wp);
+}
+
+static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
+{
+       hdmi4_audio_stop(&hd->core, &hd->wp);
+       hdmi_wp_audio_enable(&hd->wp, false);
+}
+
 static int hdmi_display_enable(struct omap_dss_device *dssdev)
 {
        struct omap_dss_device *out = &hdmi.output;
+       unsigned long flags;
        int r = 0;
 
        DSSDBG("ENTER hdmi_display_enable\n");
@@ -341,7 +355,21 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev)
                goto err0;
        }
 
+       if (hdmi.audio_configured) {
+               r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, &hdmi.audio_config,
+                                      hdmi.cfg.timings.pixelclock);
+               if (r) {
+                       DSSERR("Error restoring audio configuration: %d", r);
+                       hdmi.audio_abort_cb(&hdmi.pdev->dev);
+                       hdmi.audio_configured = false;
+               }
+       }
+
+       spin_lock_irqsave(&hdmi.audio_playing_lock, flags);
+       if (hdmi.audio_configured && hdmi.audio_playing)
+               hdmi_start_audio_stream(&hdmi);
        hdmi.display_enabled = true;
+       spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
 
        mutex_unlock(&hdmi.lock);
        return 0;
@@ -353,17 +381,19 @@ err0:
 
 static void hdmi_display_disable(struct omap_dss_device *dssdev)
 {
+       unsigned long flags;
+
        DSSDBG("Enter hdmi_display_disable\n");
 
        mutex_lock(&hdmi.lock);
 
-       if (hdmi.audio_pdev && hdmi.audio_abort_cb)
-               hdmi.audio_abort_cb(&hdmi.audio_pdev->dev);
+       spin_lock_irqsave(&hdmi.audio_playing_lock, flags);
+       hdmi_stop_audio_stream(&hdmi);
+       hdmi.display_enabled = false;
+       spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
 
        hdmi_power_off_full(dssdev);
 
-       hdmi.display_enabled = false;
-
        mutex_unlock(&hdmi.lock);
 }
 
@@ -567,6 +597,8 @@ static int hdmi_audio_shutdown(struct device *dev)
 
        mutex_lock(&hd->lock);
        hd->audio_abort_cb = NULL;
+       hd->audio_configured = false;
+       hd->audio_playing = false;
        mutex_unlock(&hd->lock);
 
        return 0;
@@ -575,25 +607,34 @@ static int hdmi_audio_shutdown(struct device *dev)
 static int hdmi_audio_start(struct device *dev)
 {
        struct omap_hdmi *hd = dev_get_drvdata(dev);
+       unsigned long flags;
 
        WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
-       WARN_ON(!hd->display_enabled);
 
-       hdmi_wp_audio_enable(&hd->wp, true);
-       hdmi4_audio_start(&hd->core, &hd->wp);
+       spin_lock_irqsave(&hd->audio_playing_lock, flags);
 
+       if (hd->display_enabled)
+               hdmi_start_audio_stream(hd);
+       hd->audio_playing = true;
+
+       spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
        return 0;
 }
 
 static void hdmi_audio_stop(struct device *dev)
 {
        struct omap_hdmi *hd = dev_get_drvdata(dev);
+       unsigned long flags;
 
        WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
-       WARN_ON(!hd->display_enabled);
 
-       hdmi4_audio_stop(&hd->core, &hd->wp);
-       hdmi_wp_audio_enable(&hd->wp, false);
+       spin_lock_irqsave(&hd->audio_playing_lock, flags);
+
+       if (hd->display_enabled)
+               hdmi_stop_audio_stream(hd);
+       hd->audio_playing = false;
+
+       spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
 }
 
 static int hdmi_audio_config(struct device *dev,
@@ -611,7 +652,10 @@ static int hdmi_audio_config(struct device *dev,
 
        ret = hdmi4_audio_config(&hd->core, &hd->wp, dss_audio,
                                 hd->cfg.timings.pixelclock);
-
+       if (!ret) {
+               hd->audio_configured = true;
+               hd->audio_config = *dss_audio;
+       }
 out:
        mutex_unlock(&hd->lock);
 
@@ -646,8 +690,9 @@ static int hdmi_audio_register(struct device *dev)
 }
 
 /* HDMI HW IP initialisation */
-static int omapdss_hdmihw_probe(struct platform_device *pdev)
+static int hdmi4_bind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        int r;
        int irq;
 
@@ -655,6 +700,7 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
        dev_set_drvdata(&pdev->dev, &hdmi);
 
        mutex_init(&hdmi.lock);
+       spin_lock_init(&hdmi.audio_playing_lock);
 
        if (pdev->dev.of_node) {
                r = hdmi_probe_of(pdev);
@@ -713,8 +759,10 @@ err:
        return r;
 }
 
-static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
+static void hdmi4_unbind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *pdev = to_platform_device(dev);
+
        if (hdmi.audio_pdev)
                platform_device_unregister(hdmi.audio_pdev);
 
@@ -723,7 +771,21 @@ static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
        hdmi_pll_uninit(&hdmi.pll);
 
        pm_runtime_disable(&pdev->dev);
+}
+
+static const struct component_ops hdmi4_component_ops = {
+       .bind   = hdmi4_bind,
+       .unbind = hdmi4_unbind,
+};
 
+static int hdmi4_probe(struct platform_device *pdev)
+{
+       return component_add(&pdev->dev, &hdmi4_component_ops);
+}
+
+static int hdmi4_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &hdmi4_component_ops);
        return 0;
 }
 
@@ -756,8 +818,8 @@ static const struct of_device_id hdmi_of_match[] = {
 };
 
 static struct platform_driver omapdss_hdmihw_driver = {
-       .probe          = omapdss_hdmihw_probe,
-       .remove         = __exit_p(omapdss_hdmihw_remove),
+       .probe          = hdmi4_probe,
+       .remove         = hdmi4_remove,
        .driver         = {
                .name   = "omapdss_hdmi",
                .pm     = &hdmi_pm_ops,
@@ -771,7 +833,7 @@ int __init hdmi4_init_platform_driver(void)
        return platform_driver_register(&omapdss_hdmihw_driver);
 }
 
-void __exit hdmi4_uninit_platform_driver(void)
+void hdmi4_uninit_platform_driver(void)
 {
        platform_driver_unregister(&omapdss_hdmihw_driver);
 }