OMAPDSS: HDMI: Implement DSS driver interface for audio
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>
This commit is contained in:
parent
b7dea05aec
commit
f3a97491f2
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue