Skip to content

Commit

Permalink
OMAPDSS: HDMI: Implement DSS driver interface for audio
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Ricardo Neri authored and Tomi Valkeinen committed May 11, 2012
1 parent b7dea05 commit f3a9749
Show file tree
Hide file tree
Showing 3 changed files with 241 additions and 2 deletions.
8 changes: 8 additions & 0 deletions drivers/video/omap2/dss/dss.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
42 changes: 42 additions & 0 deletions drivers/video/omap2/dss/hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
193 changes: 191 additions & 2 deletions drivers/video/omap2/dss/hdmi_panel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;


Expand All @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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:
Expand All @@ -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;

Expand All @@ -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);

Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand Down

0 comments on commit f3a9749

Please sign in to comment.