These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / video / fbdev / omap2 / dss / hdmi5.c
index 3f0b34a..b59ba79 100644 (file)
@@ -37,6 +37,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>
 
@@ -348,9 +349,24 @@ static int read_edid(u8 *buf, int len)
        return r;
 }
 
+static void hdmi_start_audio_stream(struct omap_hdmi *hd)
+{
+       REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
+       hdmi_wp_audio_enable(&hd->wp, true);
+       hdmi_wp_audio_core_req_enable(&hd->wp, true);
+}
+
+static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
+{
+       hdmi_wp_audio_core_req_enable(&hd->wp, false);
+       hdmi_wp_audio_enable(&hd->wp, false);
+       REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2);
+}
+
 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");
@@ -369,7 +385,21 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev)
                goto err0;
        }
 
+       if (hdmi.audio_configured) {
+               r = hdmi5_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;
@@ -381,17 +411,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);
 }
 
@@ -595,6 +627,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;
@@ -603,32 +637,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);
 
-       /* No-idle while playing audio, store the old value */
-       hd->wp_idlemode = REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2);
-       REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
+       spin_lock_irqsave(&hd->audio_playing_lock, flags);
 
-       hdmi_wp_audio_enable(&hd->wp, true);
-       hdmi_wp_audio_core_req_enable(&hd->wp, true);
+       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);
 
-       hdmi_wp_audio_core_req_enable(&hd->wp, false);
-       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;
 
-       /* Playback stopped, restore original idlemode */
-       REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2);
+       spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
 }
 
 static int hdmi_audio_config(struct device *dev,
@@ -647,6 +683,10 @@ static int hdmi_audio_config(struct device *dev,
        ret = hdmi5_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);
 
@@ -677,12 +717,18 @@ static int hdmi_audio_register(struct device *dev)
        if (IS_ERR(hdmi.audio_pdev))
                return PTR_ERR(hdmi.audio_pdev);
 
+       hdmi_runtime_get();
+       hdmi.wp_idlemode =
+               REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2);
+       hdmi_runtime_put();
+
        return 0;
 }
 
 /* HDMI HW IP initialisation */
-static int omapdss_hdmihw_probe(struct platform_device *pdev)
+static int hdmi5_bind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        int r;
        int irq;
 
@@ -690,6 +736,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);
@@ -748,8 +795,10 @@ err:
        return r;
 }
 
-static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
+static void hdmi5_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);
 
@@ -758,7 +807,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 hdmi5_component_ops = {
+       .bind   = hdmi5_bind,
+       .unbind = hdmi5_unbind,
+};
 
+static int hdmi5_probe(struct platform_device *pdev)
+{
+       return component_add(&pdev->dev, &hdmi5_component_ops);
+}
+
+static int hdmi5_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &hdmi5_component_ops);
        return 0;
 }
 
@@ -792,8 +855,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          = hdmi5_probe,
+       .remove         = hdmi5_remove,
        .driver         = {
                .name   = "omapdss_hdmi5",
                .pm     = &hdmi_pm_ops,
@@ -807,7 +870,7 @@ int __init hdmi5_init_platform_driver(void)
        return platform_driver_register(&omapdss_hdmihw_driver);
 }
 
-void __exit hdmi5_uninit_platform_driver(void)
+void hdmi5_uninit_platform_driver(void)
 {
        platform_driver_unregister(&omapdss_hdmihw_driver);
 }