]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
OMAPDSS: HDMI: Implement DSS driver interface for audio
authorRicardo Neri <ricardo.neri@ti.com>
Thu, 10 May 2012 02:09:50 +0000 (21:09 -0500)
committerTomi Valkeinen <tomi.valkeinen@ti.com>
Fri, 11 May 2012 12:17:10 +0000 (15:17 +0300)
Implement the DSS device driver audio support interface in the HDMI
panel driver and generic driver. The implementation relies on the
IP-specific functions that are defined at DSS probe time.

A mixed locking strategy is used. The panel's mutex is used when
the state of the panel is queried as required by the audio functions.
The audio state is protected using a spinlock as users of DSS HDMI
audio functionality might start/stop audio while holding a spinlock.
The mutex and the spinlock are held and released as needed by each
individual function to protect the panel state and the audio state.

Although the panel's audio_start functions does not check whether
the panel is active, the audio _ENABLED state can be reached only
from audio_enable, which does check the state of the panel. Also,
if the panel is ever disabled, the audio state will transition
to _DISABLED. Transitions are always protected by the audio lock.

Signed-off-by: Ricardo Neri <ricardo.neri@ti.com>
drivers/video/omap2/dss/dss.h
drivers/video/omap2/dss/hdmi.c
drivers/video/omap2/dss/hdmi_panel.c

index d5cb19fe7e8bbe8130faaf4374945af4f460ac8a..4aa9529a47687062bac7dec2e3cba318c7a76f96 100644 (file)
@@ -464,6 +464,14 @@ int omapdss_hdmi_read_edid(u8 *buf, int len);
 bool omapdss_hdmi_detect(void);
 int hdmi_panel_init(void);
 void hdmi_panel_exit(void);
+#ifdef CONFIG_OMAP4_DSS_HDMI_AUDIO
+int hdmi_audio_enable(void);
+void hdmi_audio_disable(void);
+int hdmi_audio_start(void);
+void hdmi_audio_stop(void);
+bool hdmi_mode_has_audio(void);
+int hdmi_audio_config(struct omap_dss_audio *audio);
+#endif
 
 /* RFBI */
 int rfbi_init_platform_driver(void) __init;
index 09fdbf8714ea2577e11ba7b448d541e5a3a8cb49..8195c7166d200c8e575aba54f5e4f46c335df002 100644 (file)
@@ -654,6 +654,48 @@ int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts)
 
        return 0;
 }
+
+int hdmi_audio_enable(void)
+{
+       DSSDBG("audio_enable\n");
+
+       return hdmi.ip_data.ops->audio_enable(&hdmi.ip_data);
+}
+
+void hdmi_audio_disable(void)
+{
+       DSSDBG("audio_disable\n");
+
+       hdmi.ip_data.ops->audio_disable(&hdmi.ip_data);
+}
+
+int hdmi_audio_start(void)
+{
+       DSSDBG("audio_start\n");
+
+       return hdmi.ip_data.ops->audio_start(&hdmi.ip_data);
+}
+
+void hdmi_audio_stop(void)
+{
+       DSSDBG("audio_stop\n");
+
+       hdmi.ip_data.ops->audio_stop(&hdmi.ip_data);
+}
+
+bool hdmi_mode_has_audio(void)
+{
+       if (hdmi.ip_data.cfg.cm.mode == HDMI_HDMI)
+               return true;
+       else
+               return false;
+}
+
+int hdmi_audio_config(struct omap_dss_audio *audio)
+{
+       return hdmi.ip_data.ops->audio_config(&hdmi.ip_data, audio);
+}
+
 #endif
 
 static void __init hdmi_probe_pdata(struct platform_device *pdev)
index 5e215d6e8d0a849f1a34afc86b23652ebd24fe1c..1179e3c4b1c76565336b8e4a6041c5bc49da0964 100644 (file)
 static struct {
        /* This protects the panel ops, mainly when accessing the HDMI IP. */
        struct mutex lock;
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
+       /* This protects the audio ops, specifically. */
+       spinlock_t audio_lock;
+#endif
 } hdmi;
 
 
@@ -55,6 +59,162 @@ static void hdmi_panel_remove(struct omap_dss_device *dssdev)
 
 }
 
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
+static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev)
+{
+       unsigned long flags;
+       int r;
+
+       mutex_lock(&hdmi.lock);
+       spin_lock_irqsave(&hdmi.audio_lock, flags);
+
+       /* enable audio only if the display is active and supports audio */
+       if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE ||
+           !hdmi_mode_has_audio()) {
+               DSSERR("audio not supported or display is off\n");
+               r = -EPERM;
+               goto err;
+       }
+
+       r = hdmi_audio_enable();
+
+       if (!r)
+               dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
+
+err:
+       spin_unlock_irqrestore(&hdmi.audio_lock, flags);
+       mutex_unlock(&hdmi.lock);
+       return r;
+}
+
+static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&hdmi.audio_lock, flags);
+
+       hdmi_audio_disable();
+
+       dssdev->audio_state = OMAP_DSS_AUDIO_DISABLED;
+
+       spin_unlock_irqrestore(&hdmi.audio_lock, flags);
+}
+
+static int hdmi_panel_audio_start(struct omap_dss_device *dssdev)
+{
+       unsigned long flags;
+       int r;
+
+       spin_lock_irqsave(&hdmi.audio_lock, flags);
+       /*
+        * No need to check the panel state. It was checked when trasitioning
+        * to AUDIO_ENABLED.
+        */
+       if (dssdev->audio_state != OMAP_DSS_AUDIO_ENABLED) {
+               DSSERR("audio start from invalid state\n");
+               r = -EPERM;
+               goto err;
+       }
+
+       r = hdmi_audio_start();
+
+       if (!r)
+               dssdev->audio_state = OMAP_DSS_AUDIO_PLAYING;
+
+err:
+       spin_unlock_irqrestore(&hdmi.audio_lock, flags);
+       return r;
+}
+
+static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&hdmi.audio_lock, flags);
+
+       hdmi_audio_stop();
+       dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
+
+       spin_unlock_irqrestore(&hdmi.audio_lock, flags);
+}
+
+static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev)
+{
+       bool r = false;
+
+       mutex_lock(&hdmi.lock);
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+               goto err;
+
+       if (!hdmi_mode_has_audio())
+               goto err;
+
+       r = true;
+err:
+       mutex_unlock(&hdmi.lock);
+       return r;
+}
+
+static int hdmi_panel_audio_config(struct omap_dss_device *dssdev,
+               struct omap_dss_audio *audio)
+{
+       unsigned long flags;
+       int r;
+
+       mutex_lock(&hdmi.lock);
+       spin_lock_irqsave(&hdmi.audio_lock, flags);
+
+       /* config audio only if the display is active and supports audio */
+       if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE ||
+           !hdmi_mode_has_audio()) {
+               DSSERR("audio not supported or display is off\n");
+               r = -EPERM;
+               goto err;
+       }
+
+       r = hdmi_audio_config(audio);
+
+       if (!r)
+               dssdev->audio_state = OMAP_DSS_AUDIO_CONFIGURED;
+
+err:
+       spin_unlock_irqrestore(&hdmi.audio_lock, flags);
+       mutex_unlock(&hdmi.lock);
+       return r;
+}
+
+#else
+static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev)
+{
+       return -EPERM;
+}
+
+static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev)
+{
+}
+
+static int hdmi_panel_audio_start(struct omap_dss_device *dssdev)
+{
+       return -EPERM;
+}
+
+static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev)
+{
+}
+
+static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev)
+{
+       return false;
+}
+
+static int hdmi_panel_audio_config(struct omap_dss_device *dssdev,
+               struct omap_dss_audio *audio)
+{
+       return -EPERM;
+}
+#endif
+
 static int hdmi_panel_enable(struct omap_dss_device *dssdev)
 {
        int r = 0;
@@ -85,8 +245,15 @@ static void hdmi_panel_disable(struct omap_dss_device *dssdev)
 {
        mutex_lock(&hdmi.lock);
 
-       if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+       if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
+               /*
+                * TODO: notify audio users that the display was disabled. For
+                * now, disable audio locally to not break our audio state
+                * machine.
+                */
+               hdmi_panel_audio_disable(dssdev);
                omapdss_hdmi_display_disable(dssdev);
+       }
 
        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 
@@ -104,8 +271,13 @@ static int hdmi_panel_suspend(struct omap_dss_device *dssdev)
                goto err;
        }
 
-       dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
+       /*
+        * TODO: notify audio users that the display was suspended. For now,
+        * disable audio locally to not break our audio state machine.
+        */
+       hdmi_panel_audio_disable(dssdev);
 
+       dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
        omapdss_hdmi_display_disable(dssdev);
 
 err:
@@ -130,6 +302,7 @@ static int hdmi_panel_resume(struct omap_dss_device *dssdev)
                DSSERR("failed to power on\n");
                goto err;
        }
+       /* TODO: notify audio users that the panel resumed. */
 
        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 
@@ -156,6 +329,12 @@ static void hdmi_set_timings(struct omap_dss_device *dssdev,
 
        mutex_lock(&hdmi.lock);
 
+       /*
+        * TODO: notify audio users that there was a timings change. For
+        * now, disable audio locally to not break our audio state machine.
+        */
+       hdmi_panel_audio_disable(dssdev);
+
        dssdev->panel.timings = *timings;
        omapdss_hdmi_display_set_timing(dssdev);
 
@@ -235,6 +414,12 @@ static struct omap_dss_driver hdmi_driver = {
        .check_timings  = hdmi_check_timings,
        .read_edid      = hdmi_read_edid,
        .detect         = hdmi_detect,
+       .audio_enable   = hdmi_panel_audio_enable,
+       .audio_disable  = hdmi_panel_audio_disable,
+       .audio_start    = hdmi_panel_audio_start,
+       .audio_stop     = hdmi_panel_audio_stop,
+       .audio_supported        = hdmi_panel_audio_supported,
+       .audio_config   = hdmi_panel_audio_config,
        .driver                 = {
                .name   = "hdmi_panel",
                .owner  = THIS_MODULE,
@@ -245,6 +430,10 @@ int hdmi_panel_init(void)
 {
        mutex_init(&hdmi.lock);
 
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
+       spin_lock_init(&hdmi.audio_lock);
+#endif
+
        omap_dss_register_driver(&hdmi_driver);
 
        return 0;