@@ -107,6 +107,10 @@ Required properties:
- clocks: handles to fclk and pll clock
- clock-names: "fck", "sys_clk"
+Required properties if hdmi audio support is enabled:
+- dmas: DMA controller phandle for HDMI audio output
+- dma-names: "audio_tx"
+
Optional nodes:
- Video port for HDMI output
@@ -34,6 +34,16 @@
#include <linux/regulator/consumer.h>
#include <video/omapdss.h>
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
+#include <linux/dmaengine.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <uapi/sound/asound.h>
+#include <sound/asoundef.h>
+#include <sound/omap-pcm.h>
+#endif
+
#include "hdmi4_core.h"
#include "dss.h"
#include "dss_features.h"
@@ -52,6 +62,13 @@ static struct {
struct clk *sys_clk;
struct regulator *vdda_hdmi_dac_reg;
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
+ struct snd_dmaengine_dai_dma_data dma_data;
+ struct omap_dss_audio dss_audio;
+ struct snd_aes_iec958 iec;
+ struct snd_cea_861_aud_if cea;
+#endif
+
bool core_enabled;
struct omap_dss_device output;
@@ -510,7 +527,36 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev,
}
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
-static int hdmi_audio_enable(struct omap_dss_device *dssdev)
+static int hdmi_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+ /*
+ * Make sure that the period bytes are multiple of the DMA packet size.
+ * Largest packet size we use is 32 32-bit words = 128 bytes
+ */
+ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128);
+ if (ret < 0) {
+ dev_err(dai->dev, "could not apply constraint\n");
+ return ret;
+ }
+
+ mutex_lock(&hdmi.lock);
+ ret = hdmi_mode_has_audio(hdmi.cfg.cm.mode);
+ mutex_unlock(&hdmi.lock);
+ if (!ret) {
+ dev_err(dai->dev, "audio not supported\n");
+ return -ENODEV;
+ }
+
+ snd_soc_dai_set_dma_data(dai, substream, &hdmi.dma_data);
+
+ return 0;
+}
+
+static int hdmi_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
int r;
@@ -522,96 +568,137 @@ static int hdmi_audio_enable(struct omap_dss_device *dssdev)
}
r = hdmi_wp_audio_enable(&hdmi.wp, true);
- if (r)
- goto err;
-
- mutex_unlock(&hdmi.lock);
- return 0;
err:
mutex_unlock(&hdmi.lock);
return r;
}
-static void hdmi_audio_disable(struct omap_dss_device *dssdev)
-{
- hdmi_wp_audio_enable(&hdmi.wp, false);
-}
-
-static int hdmi_audio_start(struct omap_dss_device *dssdev)
-{
- return hdmi4_audio_start(&hdmi.core, &hdmi.wp);
-}
-
-static void hdmi_audio_stop(struct omap_dss_device *dssdev)
+static int hdmi_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
- hdmi4_audio_stop(&hdmi.core, &hdmi.wp);
-}
+ int err;
-static bool hdmi_audio_supported(struct omap_dss_device *dssdev)
-{
- bool r;
-
- mutex_lock(&hdmi.lock);
-
- r = hdmi_mode_has_audio(hdmi.cfg.cm.mode);
-
- mutex_unlock(&hdmi.lock);
- return r;
-}
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ hdmi.dma_data.maxburst = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ hdmi.dma_data.maxburst = 32;
+ break;
+ default:
+ dev_err(dai->dev, "format not supported!\n");
+ return -EINVAL;
+ }
-static int hdmi_audio_config(struct omap_dss_device *dssdev,
- struct omap_dss_audio *audio)
-{
- int r;
- u32 pclk = hdmi.cfg.timings.pixelclock;
+ hdmi.dss_audio.iec = &hdmi.iec;
+ hdmi.dss_audio.cea = &hdmi.cea;
+ err = hdmi_dss_audio_from_hw_params(params, &hdmi.dss_audio, dai);
+ if (err)
+ return err;
mutex_lock(&hdmi.lock);
if (!hdmi_mode_has_audio(hdmi.cfg.cm.mode)) {
- r = -EPERM;
+ err = -EPERM;
goto err;
}
- r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, audio, pclk);
- if (r)
- goto err;
-
- mutex_unlock(&hdmi.lock);
- return 0;
-
+ err = hdmi4_audio_config(&hdmi.core, &hdmi.wp, &hdmi.dss_audio,
+ hdmi.cfg.timings.pixelclock);
err:
mutex_unlock(&hdmi.lock);
- return r;
-}
-#else
-static int hdmi_audio_enable(struct omap_dss_device *dssdev)
-{
- return -EPERM;
+ return err;
+}
+
+static int hdmi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ int err = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ err = hdmi4_audio_start(&hdmi.core, &hdmi.wp);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ hdmi4_audio_stop(&hdmi.core, &hdmi.wp);
+ break;
+ default:
+ err = -EINVAL;
+ }
+ return err;
}
-static void hdmi_audio_disable(struct omap_dss_device *dssdev)
+static void hdmi_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
+ hdmi_wp_audio_enable(&hdmi.wp, false);
}
-static int hdmi_audio_start(struct omap_dss_device *dssdev)
+static const struct snd_soc_dai_ops hdmi_dai_ops = {
+ .startup = hdmi_dai_startup,
+ .hw_params = hdmi_dai_hw_params,
+ .prepare = hdmi_dai_prepare,
+ .trigger = hdmi_dai_trigger,
+ .shutdown = hdmi_dai_shutdown,
+};
+
+static struct snd_soc_dai_driver omap_hdmi_dai = {
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &hdmi_dai_ops,
+};
+
+static const struct snd_soc_component_driver omap_hdmi_component = {
+ .name = "omapdss_hdmi",
+};
+
+static int hdmi_audio_init(struct platform_device *pdev)
{
- return -EPERM;
+ struct resource *res;
+ int ret;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wp");
+ if (!res) {
+ dev_err(&pdev->dev, "Cannot obtain IORESOURCE_MEM l4\n");
+ return -ENODEV;
+ }
+ hdmi.dma_data.addr = res->start + HDMI_WP_AUDIO_DATA;
+ hdmi.dma_data.filter_data = "audio_tx";
+ hdmi.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+ ret = snd_soc_register_component(&pdev->dev, &omap_hdmi_component,
+ &omap_hdmi_dai, 1);
+ if (ret)
+ return ret;
+
+ return omap_pcm_platform_register(&pdev->dev);
}
-static void hdmi_audio_stop(struct omap_dss_device *dssdev)
+static void hdmi_audio_remove(struct platform_device *pdev)
{
+ snd_soc_unregister_component(&pdev->dev);
}
-static bool hdmi_audio_supported(struct omap_dss_device *dssdev)
+#else
+static int hdmi_audio_init(struct platform_device *pdev)
{
- return false;
+ return 0;
}
-static int hdmi_audio_config(struct omap_dss_device *dssdev,
- struct omap_dss_audio *audio)
+static void hdmi_audio_remove(struct platform_device *pdev)
{
- return -EPERM;
}
#endif
@@ -627,13 +714,6 @@ static const struct omapdss_hdmi_ops hdmi_ops = {
.get_timings = hdmi_display_get_timings,
.read_edid = hdmi_read_edid,
-
- .audio_enable = hdmi_audio_enable,
- .audio_disable = hdmi_audio_disable,
- .audio_start = hdmi_audio_start,
- .audio_stop = hdmi_audio_stop,
- .audio_supported = hdmi_audio_supported,
- .audio_config = hdmi_audio_config,
};
static void hdmi_init_output(struct platform_device *pdev)
@@ -732,6 +812,10 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
return r;
}
+ r = hdmi_audio_init(pdev);
+ if (r)
+ return r;
+
pm_runtime_enable(&pdev->dev);
hdmi_init_output(pdev);
@@ -745,6 +829,8 @@ static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
{
hdmi_uninit_output(pdev);
+ hdmi_audio_remove(pdev);
+
pm_runtime_disable(&pdev->dev);
return 0;
Integrate ASoC DAI component driver in to the OMAP4 hdmi driver. The implementation removes the callbacks for an external audio driver and the old external DAI driver does not work anymore after this patch. The patch also updates the relevant entry in ti,omap4-dss DT binding document. Signed-off-by: Jyri Sarha <jsarha@ti.com> --- .../devicetree/bindings/video/ti,omap4-dss.txt | 4 + drivers/video/fbdev/omap2/dss/hdmi4.c | 216 ++++++++++++++------ 2 files changed, 155 insertions(+), 65 deletions(-)