Commit 8a9d4626 authored by Jyri Sarha's avatar Jyri Sarha Committed by Tomi Valkeinen

OMAPDSS: hdmi: Reconfigure and restart audio when display is enabled

Reconfigure and restart audio when display is enabled, if audio
playback was active before. This is needed in a situation when an
audio+video stream application opens the audio stream before the
video. When video stream is opened the display mode may change and
that aborts audio playback, because the display is momentarily
turned off.

The audio configuration is stored when it is successfully applied and
a boolean is set when the audio playback is started and unset when
stopped. This data is used to reconfigure the audio when display is
re-enabled. The audio playback is aborted if the reconfiguration fails.

A new spin lock is introduced in order to protect state variables
related to audio playback status. This is needed for the transition
from display enabled state (when audio start/stop commands can be
written to HW) to display disabled state (when audio start/stop
commands update only the hdmi.audio_playing variable) to always
serialize correctly with the start/stop audio commands. The already
existing mutex can not be used, because the audio start and stop
commands are executed in atomic context.

For example: when display is turned back on we take the spinlock and
we can be sure that the audio start/stop status will not change while
we update the HW according to hdmi.audio_playing state and set
hdmi.display_enabled to true. After releasing the lock
hdmi.display_enabled is true and all audio_start and audio_stop
commands write their stuff directly to HW.
Signed-off-by: default avatarJyri Sarha <jsarha@ti.com>
Signed-off-by: default avatarTomi Valkeinen <tomi.valkeinen@ti.com>
parent 1f93e4a9
...@@ -351,13 +351,20 @@ struct omap_hdmi { ...@@ -351,13 +351,20 @@ struct omap_hdmi {
struct regulator *vdda_reg; struct regulator *vdda_reg;
bool core_enabled; bool core_enabled;
bool display_enabled;
struct omap_dss_device output; struct omap_dss_device output;
struct platform_device *audio_pdev; struct platform_device *audio_pdev;
void (*audio_abort_cb)(struct device *dev); void (*audio_abort_cb)(struct device *dev);
int wp_idlemode; int wp_idlemode;
bool audio_configured;
struct omap_dss_audio audio_config;
/* This lock should be taken when booleans bellow are touched. */
spinlock_t audio_playing_lock;
bool audio_playing;
bool display_enabled;
}; };
#endif #endif
...@@ -321,9 +321,22 @@ static int read_edid(u8 *buf, int len) ...@@ -321,9 +321,22 @@ static int read_edid(u8 *buf, int len)
return r; 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) static int hdmi_display_enable(struct omap_dss_device *dssdev)
{ {
struct omap_dss_device *out = &hdmi.output; struct omap_dss_device *out = &hdmi.output;
unsigned long flags;
int r = 0; int r = 0;
DSSDBG("ENTER hdmi_display_enable\n"); DSSDBG("ENTER hdmi_display_enable\n");
...@@ -342,7 +355,21 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev) ...@@ -342,7 +355,21 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev)
goto err0; 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; hdmi.display_enabled = true;
spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
mutex_unlock(&hdmi.lock); mutex_unlock(&hdmi.lock);
return 0; return 0;
...@@ -354,17 +381,19 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev) ...@@ -354,17 +381,19 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev)
static void hdmi_display_disable(struct omap_dss_device *dssdev) static void hdmi_display_disable(struct omap_dss_device *dssdev)
{ {
unsigned long flags;
DSSDBG("Enter hdmi_display_disable\n"); DSSDBG("Enter hdmi_display_disable\n");
mutex_lock(&hdmi.lock); mutex_lock(&hdmi.lock);
if (hdmi.audio_pdev && hdmi.audio_abort_cb) spin_lock_irqsave(&hdmi.audio_playing_lock, flags);
hdmi.audio_abort_cb(&hdmi.audio_pdev->dev); hdmi_stop_audio_stream(&hdmi);
hdmi.display_enabled = false;
spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
hdmi_power_off_full(dssdev); hdmi_power_off_full(dssdev);
hdmi.display_enabled = false;
mutex_unlock(&hdmi.lock); mutex_unlock(&hdmi.lock);
} }
...@@ -568,6 +597,8 @@ static int hdmi_audio_shutdown(struct device *dev) ...@@ -568,6 +597,8 @@ static int hdmi_audio_shutdown(struct device *dev)
mutex_lock(&hd->lock); mutex_lock(&hd->lock);
hd->audio_abort_cb = NULL; hd->audio_abort_cb = NULL;
hd->audio_configured = false;
hd->audio_playing = false;
mutex_unlock(&hd->lock); mutex_unlock(&hd->lock);
return 0; return 0;
...@@ -576,25 +607,34 @@ static int hdmi_audio_shutdown(struct device *dev) ...@@ -576,25 +607,34 @@ static int hdmi_audio_shutdown(struct device *dev)
static int hdmi_audio_start(struct device *dev) static int hdmi_audio_start(struct device *dev)
{ {
struct omap_hdmi *hd = dev_get_drvdata(dev); struct omap_hdmi *hd = dev_get_drvdata(dev);
unsigned long flags;
WARN_ON(!hdmi_mode_has_audio(&hd->cfg)); WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
WARN_ON(!hd->display_enabled);
hdmi_wp_audio_enable(&hd->wp, true); spin_lock_irqsave(&hd->audio_playing_lock, flags);
hdmi4_audio_start(&hd->core, &hd->wp);
if (hd->display_enabled)
hdmi_start_audio_stream(hd);
hd->audio_playing = true;
spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
return 0; return 0;
} }
static void hdmi_audio_stop(struct device *dev) static void hdmi_audio_stop(struct device *dev)
{ {
struct omap_hdmi *hd = dev_get_drvdata(dev); struct omap_hdmi *hd = dev_get_drvdata(dev);
unsigned long flags;
WARN_ON(!hdmi_mode_has_audio(&hd->cfg)); WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
WARN_ON(!hd->display_enabled);
hdmi4_audio_stop(&hd->core, &hd->wp); spin_lock_irqsave(&hd->audio_playing_lock, flags);
hdmi_wp_audio_enable(&hd->wp, false);
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, static int hdmi_audio_config(struct device *dev,
...@@ -612,7 +652,10 @@ static int hdmi_audio_config(struct device *dev, ...@@ -612,7 +652,10 @@ static int hdmi_audio_config(struct device *dev,
ret = hdmi4_audio_config(&hd->core, &hd->wp, dss_audio, ret = hdmi4_audio_config(&hd->core, &hd->wp, dss_audio,
hd->cfg.timings.pixelclock); hd->cfg.timings.pixelclock);
if (!ret) {
hd->audio_configured = true;
hd->audio_config = *dss_audio;
}
out: out:
mutex_unlock(&hd->lock); mutex_unlock(&hd->lock);
...@@ -657,6 +700,7 @@ static int hdmi4_bind(struct device *dev, struct device *master, void *data) ...@@ -657,6 +700,7 @@ static int hdmi4_bind(struct device *dev, struct device *master, void *data)
dev_set_drvdata(&pdev->dev, &hdmi); dev_set_drvdata(&pdev->dev, &hdmi);
mutex_init(&hdmi.lock); mutex_init(&hdmi.lock);
spin_lock_init(&hdmi.audio_playing_lock);
if (pdev->dev.of_node) { if (pdev->dev.of_node) {
r = hdmi_probe_of(pdev); r = hdmi_probe_of(pdev);
......
...@@ -349,9 +349,24 @@ static int read_edid(u8 *buf, int len) ...@@ -349,9 +349,24 @@ static int read_edid(u8 *buf, int len)
return r; 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) static int hdmi_display_enable(struct omap_dss_device *dssdev)
{ {
struct omap_dss_device *out = &hdmi.output; struct omap_dss_device *out = &hdmi.output;
unsigned long flags;
int r = 0; int r = 0;
DSSDBG("ENTER hdmi_display_enable\n"); DSSDBG("ENTER hdmi_display_enable\n");
...@@ -370,7 +385,21 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev) ...@@ -370,7 +385,21 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev)
goto err0; 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; hdmi.display_enabled = true;
spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
mutex_unlock(&hdmi.lock); mutex_unlock(&hdmi.lock);
return 0; return 0;
...@@ -382,17 +411,19 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev) ...@@ -382,17 +411,19 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev)
static void hdmi_display_disable(struct omap_dss_device *dssdev) static void hdmi_display_disable(struct omap_dss_device *dssdev)
{ {
unsigned long flags;
DSSDBG("Enter hdmi_display_disable\n"); DSSDBG("Enter hdmi_display_disable\n");
mutex_lock(&hdmi.lock); mutex_lock(&hdmi.lock);
if (hdmi.audio_pdev && hdmi.audio_abort_cb) spin_lock_irqsave(&hdmi.audio_playing_lock, flags);
hdmi.audio_abort_cb(&hdmi.audio_pdev->dev); hdmi_stop_audio_stream(&hdmi);
hdmi.display_enabled = false;
spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
hdmi_power_off_full(dssdev); hdmi_power_off_full(dssdev);
hdmi.display_enabled = false;
mutex_unlock(&hdmi.lock); mutex_unlock(&hdmi.lock);
} }
...@@ -596,6 +627,8 @@ static int hdmi_audio_shutdown(struct device *dev) ...@@ -596,6 +627,8 @@ static int hdmi_audio_shutdown(struct device *dev)
mutex_lock(&hd->lock); mutex_lock(&hd->lock);
hd->audio_abort_cb = NULL; hd->audio_abort_cb = NULL;
hd->audio_configured = false;
hd->audio_playing = false;
mutex_unlock(&hd->lock); mutex_unlock(&hd->lock);
return 0; return 0;
...@@ -604,32 +637,34 @@ static int hdmi_audio_shutdown(struct device *dev) ...@@ -604,32 +637,34 @@ static int hdmi_audio_shutdown(struct device *dev)
static int hdmi_audio_start(struct device *dev) static int hdmi_audio_start(struct device *dev)
{ {
struct omap_hdmi *hd = dev_get_drvdata(dev); struct omap_hdmi *hd = dev_get_drvdata(dev);
unsigned long flags;
WARN_ON(!hdmi_mode_has_audio(&hd->cfg)); WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
WARN_ON(!hd->display_enabled);
/* No-idle while playing audio, store the old value */ spin_lock_irqsave(&hd->audio_playing_lock, flags);
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);
hdmi_wp_audio_enable(&hd->wp, true); if (hd->display_enabled)
hdmi_wp_audio_core_req_enable(&hd->wp, true); hdmi_start_audio_stream(hd);
hd->audio_playing = true;
spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
return 0; return 0;
} }
static void hdmi_audio_stop(struct device *dev) static void hdmi_audio_stop(struct device *dev)
{ {
struct omap_hdmi *hd = dev_get_drvdata(dev); struct omap_hdmi *hd = dev_get_drvdata(dev);
unsigned long flags;
WARN_ON(!hdmi_mode_has_audio(&hd->cfg)); WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
WARN_ON(!hd->display_enabled);
hdmi_wp_audio_core_req_enable(&hd->wp, false); spin_lock_irqsave(&hd->audio_playing_lock, flags);
hdmi_wp_audio_enable(&hd->wp, false);
if (hd->display_enabled)
hdmi_stop_audio_stream(hd);
hd->audio_playing = false;
/* Playback stopped, restore original idlemode */ spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2);
} }
static int hdmi_audio_config(struct device *dev, static int hdmi_audio_config(struct device *dev,
...@@ -648,6 +683,10 @@ static int hdmi_audio_config(struct device *dev, ...@@ -648,6 +683,10 @@ static int hdmi_audio_config(struct device *dev,
ret = hdmi5_audio_config(&hd->core, &hd->wp, dss_audio, ret = hdmi5_audio_config(&hd->core, &hd->wp, dss_audio,
hd->cfg.timings.pixelclock); hd->cfg.timings.pixelclock);
if (!ret) {
hd->audio_configured = true;
hd->audio_config = *dss_audio;
}
out: out:
mutex_unlock(&hd->lock); mutex_unlock(&hd->lock);
...@@ -678,6 +717,11 @@ static int hdmi_audio_register(struct device *dev) ...@@ -678,6 +717,11 @@ static int hdmi_audio_register(struct device *dev)
if (IS_ERR(hdmi.audio_pdev)) if (IS_ERR(hdmi.audio_pdev))
return PTR_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; return 0;
} }
...@@ -692,6 +736,7 @@ static int hdmi5_bind(struct device *dev, struct device *master, void *data) ...@@ -692,6 +736,7 @@ static int hdmi5_bind(struct device *dev, struct device *master, void *data)
dev_set_drvdata(&pdev->dev, &hdmi); dev_set_drvdata(&pdev->dev, &hdmi);
mutex_init(&hdmi.lock); mutex_init(&hdmi.lock);
spin_lock_init(&hdmi.audio_playing_lock);
if (pdev->dev.of_node) { if (pdev->dev.of_node) {
r = hdmi_probe_of(pdev); r = hdmi_probe_of(pdev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment