Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f3a97491 authored by Ricardo Neri's avatar Ricardo Neri Committed by Tomi Valkeinen
Browse files

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: default avatarRicardo Neri <ricardo.neri@ti.com>
parent b7dea05a
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -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;
+42 −0
Original line number Diff line number Diff line
@@ -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)
+191 −2
Original line number Diff line number Diff line
@@ -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;