|
@@ -32,6 +32,10 @@
|
|
|
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;
|