summaryrefslogtreecommitdiff
path: root/sound/soc
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2012-05-23 01:37:59 +0200
committerTakashi Iwai <tiwai@suse.de>2012-05-23 01:37:59 +0200
commit599ed4b0aeceb6d45ea33f64639e745e3da72766 (patch)
treed1d68299eacda3b12097bd3a7d4e7c895e3d6a13 /sound/soc
parent85e184e4c3cd3e2285ceab91ff8f0cac094e8a85 (diff)
parentced47a3117ba084b404f3fc28de3ef6eaba1b911 (diff)
Merge branch 'for-3.5' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/asoc into topic/asoc
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/omap/Kconfig7
-rw-r--r--sound/soc/omap/Makefile4
-rw-r--r--sound/soc/omap/mcbsp.c115
-rw-r--r--sound/soc/omap/mcbsp.h8
-rw-r--r--sound/soc/omap/omap-abe-twl6040.c68
-rw-r--r--sound/soc/omap/omap-dmic.c8
-rw-r--r--sound/soc/omap/omap-hdmi-card.c87
-rw-r--r--sound/soc/omap/omap-hdmi.c238
-rw-r--r--sound/soc/omap/omap-hdmi.h4
-rw-r--r--sound/soc/omap/omap-mcbsp.c45
-rw-r--r--sound/soc/omap/omap-mcpdm.c8
-rw-r--r--sound/soc/omap/omap4-hdmi-card.c121
12 files changed, 489 insertions, 224 deletions
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 9ccfa5e1c11b..57a2fa751085 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -109,11 +109,12 @@ config SND_OMAP_SOC_OMAP_ABE_TWL6040
- PandaBoard (4430)
- PandaBoardES (4460)
-config SND_OMAP_SOC_OMAP4_HDMI
- tristate "SoC Audio support for Texas Instruments OMAP4 HDMI"
- depends on SND_OMAP_SOC && OMAP4_DSS_HDMI && OMAP2_DSS && ARCH_OMAP4
+config SND_OMAP_SOC_OMAP_HDMI
+ tristate "SoC Audio support for Texas Instruments OMAP HDMI"
+ depends on SND_OMAP_SOC && OMAP4_DSS_HDMI && OMAP2_DSS
select SND_OMAP_SOC_HDMI
select SND_SOC_OMAP_HDMI_CODEC
+ select OMAP4_DSS_HDMI_AUDIO
help
Say Y if you want to add support for SoC HDMI audio on Texas Instruments
OMAP4 chips
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index 1d656bce01d4..0e14dd322565 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -25,7 +25,7 @@ snd-soc-omap3pandora-objs := omap3pandora.o
snd-soc-omap3beagle-objs := omap3beagle.o
snd-soc-zoom2-objs := zoom2.o
snd-soc-igep0020-objs := igep0020.o
-snd-soc-omap4-hdmi-objs := omap4-hdmi-card.o
+snd-soc-omap-hdmi-card-objs := omap-hdmi-card.o
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o
@@ -41,4 +41,4 @@ obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o
obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o
obj-$(CONFIG_SND_OMAP_SOC_IGEP0020) += snd-soc-igep0020.o
-obj-$(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) += snd-soc-omap4-hdmi.o
+obj-$(CONFIG_SND_OMAP_SOC_OMAP_HDMI) += snd-soc-omap-hdmi-card.o
diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c
index e5f44440d1b9..34835e8a9160 100644
--- a/sound/soc/omap/mcbsp.c
+++ b/sound/soc/omap/mcbsp.c
@@ -109,6 +109,47 @@ static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp)
dev_dbg(mcbsp->dev, "***********************\n");
}
+static irqreturn_t omap_mcbsp_irq_handler(int irq, void *dev_id)
+{
+ struct omap_mcbsp *mcbsp = dev_id;
+ u16 irqst;
+
+ irqst = MCBSP_READ(mcbsp, IRQST);
+ dev_dbg(mcbsp->dev, "IRQ callback : 0x%x\n", irqst);
+
+ if (irqst & RSYNCERREN)
+ dev_err(mcbsp->dev, "RX Frame Sync Error!\n");
+ if (irqst & RFSREN)
+ dev_dbg(mcbsp->dev, "RX Frame Sync\n");
+ if (irqst & REOFEN)
+ dev_dbg(mcbsp->dev, "RX End Of Frame\n");
+ if (irqst & RRDYEN)
+ dev_dbg(mcbsp->dev, "RX Buffer Threshold Reached\n");
+ if (irqst & RUNDFLEN)
+ dev_err(mcbsp->dev, "RX Buffer Underflow!\n");
+ if (irqst & ROVFLEN)
+ dev_err(mcbsp->dev, "RX Buffer Overflow!\n");
+
+ if (irqst & XSYNCERREN)
+ dev_err(mcbsp->dev, "TX Frame Sync Error!\n");
+ if (irqst & XFSXEN)
+ dev_dbg(mcbsp->dev, "TX Frame Sync\n");
+ if (irqst & XEOFEN)
+ dev_dbg(mcbsp->dev, "TX End Of Frame\n");
+ if (irqst & XRDYEN)
+ dev_dbg(mcbsp->dev, "TX Buffer threshold Reached\n");
+ if (irqst & XUNDFLEN)
+ dev_err(mcbsp->dev, "TX Buffer Underflow!\n");
+ if (irqst & XOVFLEN)
+ dev_err(mcbsp->dev, "TX Buffer Overflow!\n");
+ if (irqst & XEMPTYEOFEN)
+ dev_dbg(mcbsp->dev, "TX Buffer empty at end of frame\n");
+
+ MCBSP_WRITE(mcbsp, IRQST, irqst);
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id)
{
struct omap_mcbsp *mcbsp_tx = dev_id;
@@ -176,6 +217,10 @@ void omap_mcbsp_config(struct omap_mcbsp *mcbsp,
/* Enable wakeup behavior */
if (mcbsp->pdata->has_wakeup)
MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN);
+
+ /* Enable TX/RX sync error interrupts by default */
+ if (mcbsp->irq)
+ MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN);
}
/**
@@ -489,23 +534,25 @@ int omap_mcbsp_request(struct omap_mcbsp *mcbsp)
MCBSP_WRITE(mcbsp, SPCR1, 0);
MCBSP_WRITE(mcbsp, SPCR2, 0);
- err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler,
- 0, "McBSP", (void *)mcbsp);
- if (err != 0) {
- dev_err(mcbsp->dev, "Unable to request TX IRQ %d "
- "for McBSP%d\n", mcbsp->tx_irq,
- mcbsp->id);
- goto err_clk_disable;
- }
+ if (mcbsp->irq) {
+ err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0,
+ "McBSP", (void *)mcbsp);
+ if (err != 0) {
+ dev_err(mcbsp->dev, "Unable to request IRQ\n");
+ goto err_clk_disable;
+ }
+ } else {
+ err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0,
+ "McBSP TX", (void *)mcbsp);
+ if (err != 0) {
+ dev_err(mcbsp->dev, "Unable to request TX IRQ\n");
+ goto err_clk_disable;
+ }
- if (mcbsp->rx_irq) {
- err = request_irq(mcbsp->rx_irq,
- omap_mcbsp_rx_irq_handler,
- 0, "McBSP", (void *)mcbsp);
+ err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0,
+ "McBSP RX", (void *)mcbsp);
if (err != 0) {
- dev_err(mcbsp->dev, "Unable to request RX IRQ %d "
- "for McBSP%d\n", mcbsp->rx_irq,
- mcbsp->id);
+ dev_err(mcbsp->dev, "Unable to request RX IRQ\n");
goto err_free_irq;
}
}
@@ -542,9 +589,16 @@ void omap_mcbsp_free(struct omap_mcbsp *mcbsp)
if (mcbsp->pdata->has_wakeup)
MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
- if (mcbsp->rx_irq)
+ /* Disable interrupt requests */
+ if (mcbsp->irq)
+ MCBSP_WRITE(mcbsp, IRQEN, 0);
+
+ if (mcbsp->irq) {
+ free_irq(mcbsp->irq, (void *)mcbsp);
+ } else {
free_irq(mcbsp->rx_irq, (void *)mcbsp);
- free_irq(mcbsp->tx_irq, (void *)mcbsp);
+ free_irq(mcbsp->tx_irq, (void *)mcbsp);
+ }
reg_cache = mcbsp->reg_cache;
@@ -754,7 +808,7 @@ THRESHOLD_PROP_BUILDER(max_tx_thres);
THRESHOLD_PROP_BUILDER(max_rx_thres);
static const char *dma_op_modes[] = {
- "element", "threshold", "frame",
+ "element", "threshold",
};
static ssize_t dma_op_mode_show(struct device *dev,
@@ -949,13 +1003,24 @@ int __devinit omap_mcbsp_init(struct platform_device *pdev)
else
mcbsp->phys_dma_base = res->start;
- mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx");
- mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx");
-
- /* From OMAP4 there will be a single irq line */
- if (mcbsp->tx_irq == -ENXIO) {
- mcbsp->tx_irq = platform_get_irq(pdev, 0);
- mcbsp->rx_irq = 0;
+ /*
+ * OMAP1, 2 uses two interrupt lines: TX, RX
+ * OMAP2430, OMAP3 SoC have combined IRQ line as well.
+ * OMAP4 and newer SoC only have the combined IRQ line.
+ * Use the combined IRQ if available since it gives better debugging
+ * possibilities.
+ */
+ mcbsp->irq = platform_get_irq_byname(pdev, "common");
+ if (mcbsp->irq == -ENXIO) {
+ mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx");
+
+ if (mcbsp->tx_irq == -ENXIO) {
+ mcbsp->irq = platform_get_irq(pdev, 0);
+ mcbsp->tx_irq = 0;
+ } else {
+ mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx");
+ mcbsp->irq = 0;
+ }
}
res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
diff --git a/sound/soc/omap/mcbsp.h b/sound/soc/omap/mcbsp.h
index a944fcc9073c..262a6152111f 100644
--- a/sound/soc/omap/mcbsp.h
+++ b/sound/soc/omap/mcbsp.h
@@ -217,17 +217,20 @@ enum {
/********************** McBSP DMA operating modes **************************/
#define MCBSP_DMA_MODE_ELEMENT 0
#define MCBSP_DMA_MODE_THRESHOLD 1
-#define MCBSP_DMA_MODE_FRAME 2
-/********************** McBSP WAKEUPEN bit definitions *********************/
+/********************** McBSP WAKEUPEN/IRQST/IRQEN bit definitions *********/
#define RSYNCERREN BIT(0)
#define RFSREN BIT(1)
#define REOFEN BIT(2)
#define RRDYEN BIT(3)
+#define RUNDFLEN BIT(4)
+#define ROVFLEN BIT(5)
#define XSYNCERREN BIT(7)
#define XFSXEN BIT(8)
#define XEOFEN BIT(9)
#define XRDYEN BIT(10)
+#define XUNDFLEN BIT(11)
+#define XOVFLEN BIT(12)
#define XEMPTYEOFEN BIT(14)
/* Clock signal muxing options */
@@ -295,6 +298,7 @@ struct omap_mcbsp {
int configured;
u8 free;
+ int irq;
int rx_irq;
int tx_irq;
diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c
index 93bb8eee22b3..9d93793d3077 100644
--- a/sound/soc/omap/omap-abe-twl6040.c
+++ b/sound/soc/omap/omap-abe-twl6040.c
@@ -40,6 +40,11 @@
#include "omap-pcm.h"
#include "../codecs/twl6040.h"
+struct abe_twl6040 {
+ int jack_detection; /* board can detect jack events */
+ int mclk_freq; /* MCLK frequency speed for twl6040 */
+};
+
static int omap_abe_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -47,13 +52,13 @@ static int omap_abe_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_card *card = codec->card;
- struct omap_abe_twl6040_data *pdata = dev_get_platdata(card->dev);
+ struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
int clk_id, freq;
int ret;
clk_id = twl6040_get_clk_id(rtd->codec);
if (clk_id == TWL6040_SYSCLK_SEL_HPPLL)
- freq = pdata->mclk_freq;
+ freq = priv->mclk_freq;
else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL)
freq = 32768;
else
@@ -128,6 +133,9 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Main Handset Mic", NULL),
SND_SOC_DAPM_MIC("Sub Handset Mic", NULL),
SND_SOC_DAPM_LINE("Line In", NULL),
+
+ /* Digital microphones */
+ SND_SOC_DAPM_MIC("Digital Mic", NULL),
};
static const struct snd_soc_dapm_route audio_map[] = {
@@ -173,6 +181,7 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_card *card = codec->card;
struct snd_soc_dapm_context *dapm = &codec->dapm;
struct omap_abe_twl6040_data *pdata = dev_get_platdata(card->dev);
+ struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
int hs_trim;
int ret = 0;
@@ -196,7 +205,7 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
TWL6040_HSF_TRIM_RIGHT(hs_trim));
/* Headset jack detection only if it is supported */
- if (pdata->jack_detection) {
+ if (priv->jack_detection) {
ret = snd_soc_jack_new(codec, "Headset Jack",
SND_JACK_HEADSET, &hs_jack);
if (ret)
@@ -210,10 +219,6 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
-static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = {
- SND_SOC_DAPM_MIC("Digital Mic", NULL),
-};
-
static const struct snd_soc_dapm_route dmic_audio_map[] = {
{"DMic", NULL, "Digital Mic"},
{"Digital Mic", NULL, "Digital Mic1 Bias"},
@@ -223,19 +228,13 @@ static int omap_abe_dmic_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
- int ret;
-
- ret = snd_soc_dapm_new_controls(dapm, dmic_dapm_widgets,
- ARRAY_SIZE(dmic_dapm_widgets));
- if (ret)
- return ret;
return snd_soc_dapm_add_routes(dapm, dmic_audio_map,
ARRAY_SIZE(dmic_audio_map));
}
/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link twl6040_dmic_dai[] = {
+static struct snd_soc_dai_link abe_twl6040_dai_links[] = {
{
.name = "TWL6040",
.stream_name = "TWL6040",
@@ -258,19 +257,6 @@ static struct snd_soc_dai_link twl6040_dmic_dai[] = {
},
};
-static struct snd_soc_dai_link twl6040_only_dai[] = {
- {
- .name = "TWL6040",
- .stream_name = "TWL6040",
- .cpu_dai_name = "omap-mcpdm",
- .codec_dai_name = "twl6040-legacy",
- .platform_name = "omap-pcm-audio",
- .codec_name = "twl6040-codec",
- .init = omap_abe_twl6040_init,
- .ops = &omap_abe_ops,
- },
-};
-
/* Audio machine driver */
static struct snd_soc_card omap_abe_card = {
.owner = THIS_MODULE,
@@ -285,6 +271,8 @@ static __devinit int omap_abe_probe(struct platform_device *pdev)
{
struct omap_abe_twl6040_data *pdata = dev_get_platdata(&pdev->dev);
struct snd_soc_card *card = &omap_abe_card;
+ struct abe_twl6040 *priv;
+ int num_links = 0;
int ret;
card->dev = &pdev->dev;
@@ -294,6 +282,10 @@ static __devinit int omap_abe_probe(struct platform_device *pdev)
return -ENODEV;
}
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+
if (pdata->card_name) {
card->name = pdata->card_name;
} else {
@@ -301,18 +293,24 @@ static __devinit int omap_abe_probe(struct platform_device *pdev)
return -ENODEV;
}
- if (!pdata->mclk_freq) {
+ priv->jack_detection = pdata->jack_detection;
+ priv->mclk_freq = pdata->mclk_freq;
+
+
+ if (!priv->mclk_freq) {
dev_err(&pdev->dev, "MCLK frequency missing\n");
return -ENODEV;
}
- if (pdata->has_dmic) {
- card->dai_link = twl6040_dmic_dai;
- card->num_links = ARRAY_SIZE(twl6040_dmic_dai);
- } else {
- card->dai_link = twl6040_only_dai;
- card->num_links = ARRAY_SIZE(twl6040_only_dai);
- }
+ if (pdata->has_dmic)
+ num_links = 2;
+ else
+ num_links = 1;
+
+ card->dai_link = abe_twl6040_dai_links;
+ card->num_links = num_links;
+
+ snd_soc_card_set_drvdata(card, priv);
ret = snd_soc_register_card(card);
if (ret)
diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c
index 4dcb5a7e40e8..75f5dca0e8d2 100644
--- a/sound/soc/omap/omap-dmic.c
+++ b/sound/soc/omap/omap-dmic.c
@@ -32,6 +32,7 @@
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
+#include <linux/of_device.h>
#include <plat/dma.h>
#include <sound/core.h>
@@ -528,10 +529,17 @@ static int __devexit asoc_dmic_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id omap_dmic_of_match[] = {
+ { .compatible = "ti,omap4-dmic", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, omap_dmic_of_match);
+
static struct platform_driver asoc_dmic_driver = {
.driver = {
.name = "omap-dmic",
.owner = THIS_MODULE,
+ .of_match_table = omap_dmic_of_match,
},
.probe = asoc_dmic_probe,
.remove = __devexit_p(asoc_dmic_remove),
diff --git a/sound/soc/omap/omap-hdmi-card.c b/sound/soc/omap/omap-hdmi-card.c
new file mode 100644
index 000000000000..eaa2ea0e3f81
--- /dev/null
+++ b/sound/soc/omap/omap-hdmi-card.c
@@ -0,0 +1,87 @@
+/*
+ * omap-hdmi-card.c
+ *
+ * OMAP ALSA SoC machine driver for TI OMAP HDMI
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Ricardo Neri <ricardo.neri@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <asm/mach-types.h>
+#include <video/omapdss.h>
+
+#define DRV_NAME "omap-hdmi-audio"
+
+static struct snd_soc_dai_link omap_hdmi_dai = {
+ .name = "HDMI",
+ .stream_name = "HDMI",
+ .cpu_dai_name = "omap-hdmi-audio-dai",
+ .platform_name = "omap-pcm-audio",
+ .codec_name = "hdmi-audio-codec",
+ .codec_dai_name = "omap-hdmi-hifi",
+};
+
+static struct snd_soc_card snd_soc_omap_hdmi = {
+ .name = "OMAPHDMI",
+ .owner = THIS_MODULE,
+ .dai_link = &omap_hdmi_dai,
+ .num_links = 1,
+};
+
+static __devinit int omap_hdmi_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_omap_hdmi;
+ int ret;
+
+ card->dev = &pdev->dev;
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+ card->dev = NULL;
+ return ret;
+ }
+ return 0;
+}
+
+static int __devexit omap_hdmi_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
+ card->dev = NULL;
+ return 0;
+}
+
+static struct platform_driver omap_hdmi_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = omap_hdmi_probe,
+ .remove = __devexit_p(omap_hdmi_remove),
+};
+
+module_platform_driver(omap_hdmi_driver);
+
+MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
+MODULE_DESCRIPTION("OMAP HDMI machine ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/omap/omap-hdmi.c b/sound/soc/omap/omap-hdmi.c
index 38e0defa7078..a08245d9203c 100644
--- a/sound/soc/omap/omap-hdmi.c
+++ b/sound/soc/omap/omap-hdmi.c
@@ -30,21 +30,28 @@
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
+#include <sound/asound.h>
+#include <sound/asoundef.h>
+#include <video/omapdss.h>
#include <plat/dma.h>
#include "omap-pcm.h"
#include "omap-hdmi.h"
-#define DRV_NAME "hdmi-audio-dai"
+#define DRV_NAME "omap-hdmi-audio-dai"
-static struct omap_pcm_dma_data omap_hdmi_dai_dma_params = {
- .name = "HDMI playback",
- .sync_mode = OMAP_DMA_SYNC_PACKET,
+struct hdmi_priv {
+ struct omap_pcm_dma_data dma_params;
+ struct omap_dss_audio dss_audio;
+ struct snd_aes_iec958 iec;
+ struct snd_cea_861_aud_if cea;
+ struct omap_dss_device *dssdev;
};
static int omap_hdmi_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
+ struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai);
int err;
/*
* Make sure that the period bytes are multiple of the DMA packet size.
@@ -52,46 +59,201 @@ static int omap_hdmi_dai_startup(struct snd_pcm_substream *substream,
*/
err = snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128);
- if (err < 0)
+ if (err < 0) {
+ dev_err(dai->dev, "could not apply constraint\n");
return err;
+ }
+ if (!priv->dssdev->driver->audio_supported(priv->dssdev)) {
+ dev_err(dai->dev, "audio not supported\n");
+ return -ENODEV;
+ }
return 0;
}
+static int omap_hdmi_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ return priv->dssdev->driver->audio_enable(priv->dssdev);
+}
+
static int omap_hdmi_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
+ struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai);
+ struct snd_aes_iec958 *iec = &priv->iec;
+ struct snd_cea_861_aud_if *cea = &priv->cea;
int err = 0;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
- omap_hdmi_dai_dma_params.packet_size = 16;
+ priv->dma_params.packet_size = 16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
- omap_hdmi_dai_dma_params.packet_size = 32;
+ priv->dma_params.packet_size = 32;
break;
default:
- err = -EINVAL;
+ dev_err(dai->dev, "format not supported!\n");
+ return -EINVAL;
}
- omap_hdmi_dai_dma_params.data_type = OMAP_DMA_DATA_TYPE_S32;
+ priv->dma_params.data_type = OMAP_DMA_DATA_TYPE_S32;
snd_soc_dai_set_dma_data(dai, substream,
- &omap_hdmi_dai_dma_params);
+ &priv->dma_params);
+
+ /*
+ * fill the IEC-60958 channel status word
+ */
+
+ /* specify IEC-60958-3 (commercial use) */
+ iec->status[0] &= ~IEC958_AES0_PROFESSIONAL;
+
+ /* specify that the audio is LPCM*/
+ iec->status[0] &= ~IEC958_AES0_NONAUDIO;
+
+ iec->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT;
+
+ iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE;
+
+ iec->status[0] |= IEC958_AES1_PRO_MODE_NOTID;
+
+ iec->status[1] = IEC958_AES1_CON_GENERAL;
+
+ iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC;
+
+ iec->status[2] |= IEC958_AES2_CON_CHANNEL_UNSPEC;
+
+ switch (params_rate(params)) {
+ case 32000:
+ iec->status[3] |= IEC958_AES3_CON_FS_32000;
+ break;
+ case 44100:
+ iec->status[3] |= IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ iec->status[3] |= IEC958_AES3_CON_FS_48000;
+ break;
+ case 88200:
+ iec->status[3] |= IEC958_AES3_CON_FS_88200;
+ break;
+ case 96000:
+ iec->status[3] |= IEC958_AES3_CON_FS_96000;
+ break;
+ case 176400:
+ iec->status[3] |= IEC958_AES3_CON_FS_176400;
+ break;
+ case 192000:
+ iec->status[3] |= IEC958_AES3_CON_FS_192000;
+ break;
+ default:
+ dev_err(dai->dev, "rate not supported!\n");
+ return -EINVAL;
+ }
+
+ /* specify the clock accuracy */
+ iec->status[3] |= IEC958_AES3_CON_CLOCK_1000PPM;
+
+ /*
+ * specify the word length. The same word length value can mean
+ * two different lengths. Hence, we need to specify the maximum
+ * word length as well.
+ */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ iec->status[4] |= IEC958_AES4_CON_WORDLEN_20_16;
+ iec->status[4] &= ~IEC958_AES4_CON_MAX_WORDLEN_24;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iec->status[4] |= IEC958_AES4_CON_WORDLEN_24_20;
+ iec->status[4] |= IEC958_AES4_CON_MAX_WORDLEN_24;
+ break;
+ default:
+ dev_err(dai->dev, "format not supported!\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Fill the CEA-861 audio infoframe (see spec for details)
+ */
+
+ cea->db1_ct_cc = (params_channels(params) - 1)
+ & CEA861_AUDIO_INFOFRAME_DB1CC;
+ cea->db1_ct_cc |= CEA861_AUDIO_INFOFRAME_DB1CT_FROM_STREAM;
+
+ cea->db2_sf_ss = CEA861_AUDIO_INFOFRAME_DB2SF_FROM_STREAM;
+ cea->db2_sf_ss |= CEA861_AUDIO_INFOFRAME_DB2SS_FROM_STREAM;
+
+ cea->db3 = 0; /* not used, all zeros */
+
+ /*
+ * The OMAP HDMI IP requires to use the 8-channel channel code when
+ * transmitting more than two channels.
+ */
+ if (params_channels(params) == 2)
+ cea->db4_ca = 0x0;
+ else
+ cea->db4_ca = 0x13;
+
+ cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED;
+ /* the expression is trivial but makes clear what we are doing */
+ cea->db5_dminh_lsv |= (0 & CEA861_AUDIO_INFOFRAME_DB5_LSV);
+
+ priv->dss_audio.iec = iec;
+ priv->dss_audio.cea = cea;
+
+ err = priv->dssdev->driver->audio_config(priv->dssdev,
+ &priv->dss_audio);
return err;
}
+static int omap_hdmi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai);
+ int err = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ err = priv->dssdev->driver->audio_start(priv->dssdev);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ priv->dssdev->driver->audio_stop(priv->dssdev);
+ break;
+ default:
+ err = -EINVAL;
+ }
+ return err;
+}
+
+static void omap_hdmi_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ priv->dssdev->driver->audio_disable(priv->dssdev);
+}
+
static const struct snd_soc_dai_ops omap_hdmi_dai_ops = {
.startup = omap_hdmi_dai_startup,
.hw_params = omap_hdmi_dai_hw_params,
+ .prepare = omap_hdmi_dai_prepare,
+ .trigger = omap_hdmi_dai_trigger,
+ .shutdown = omap_hdmi_dai_shutdown,
};
static struct snd_soc_dai_driver omap_hdmi_dai = {
.playback = {
.channels_min = 2,
- .channels_max = 2,
+ .channels_max = 8,
.rates = OMAP_HDMI_RATES,
.formats = OMAP_HDMI_FORMATS,
},
@@ -102,31 +264,77 @@ static __devinit int omap_hdmi_probe(struct platform_device *pdev)
{
int ret;
struct resource *hdmi_rsrc;
+ struct hdmi_priv *hdmi_data;
+ bool hdmi_dev_found = false;
+
+ hdmi_data = devm_kzalloc(&pdev->dev, sizeof(*hdmi_data), GFP_KERNEL);
+ if (hdmi_data == NULL) {
+ dev_err(&pdev->dev, "Cannot allocate memory for HDMI data\n");
+ return -ENOMEM;
+ }
hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!hdmi_rsrc) {
dev_err(&pdev->dev, "Cannot obtain IORESOURCE_MEM HDMI\n");
- return -EINVAL;
+ return -ENODEV;
}
- omap_hdmi_dai_dma_params.port_addr = hdmi_rsrc->start
+ hdmi_data->dma_params.port_addr = hdmi_rsrc->start
+ OMAP_HDMI_AUDIO_DMA_PORT;
hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!hdmi_rsrc) {
dev_err(&pdev->dev, "Cannot obtain IORESOURCE_DMA HDMI\n");
- return -EINVAL;
+ return -ENODEV;
}
- omap_hdmi_dai_dma_params.dma_req = hdmi_rsrc->start;
+ hdmi_data->dma_params.dma_req = hdmi_rsrc->start;
+ hdmi_data->dma_params.name = "HDMI playback";
+ hdmi_data->dma_params.sync_mode = OMAP_DMA_SYNC_PACKET;
+
+ /*
+ * TODO: We assume that there is only one DSS HDMI device. Future
+ * OMAP implementations may support more than one HDMI devices and
+ * we should provided separate audio support for all of them.
+ */
+ /* Find an HDMI device. */
+ for_each_dss_dev(hdmi_data->dssdev) {
+ omap_dss_get_device(hdmi_data->dssdev);
+ if (!hdmi_data->dssdev->driver) {
+ omap_dss_put_device(hdmi_data->dssdev);
+ continue;
+ }
+
+ if (hdmi_data->dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {
+ hdmi_dev_found = true;
+ break;
+ }
+ }
+
+ if (!hdmi_dev_found) {
+ dev_err(&pdev->dev, "no driver for HDMI display found\n");
+ return -ENODEV;
+ }
+
+ dev_set_drvdata(&pdev->dev, hdmi_data);
ret = snd_soc_register_dai(&pdev->dev, &omap_hdmi_dai);
+
return ret;
}
static int __devexit omap_hdmi_remove(struct platform_device *pdev)
{
+ struct hdmi_priv *hdmi_data = dev_get_drvdata(&pdev->dev);
+
snd_soc_unregister_dai(&pdev->dev);
+
+ if (hdmi_data == NULL) {
+ dev_err(&pdev->dev, "cannot obtain HDMi data\n");
+ return -ENODEV;
+ }
+
+ omap_dss_put_device(hdmi_data->dssdev);
return 0;
}
diff --git a/sound/soc/omap/omap-hdmi.h b/sound/soc/omap/omap-hdmi.h
index 34c298d5057e..6ad2bf4f2697 100644
--- a/sound/soc/omap/omap-hdmi.h
+++ b/sound/soc/omap/omap-hdmi.h
@@ -28,7 +28,9 @@
#define OMAP_HDMI_AUDIO_DMA_PORT 0x8c
#define OMAP_HDMI_RATES (SNDRV_PCM_RATE_32000 | \
- SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
#define OMAP_HDMI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE)
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 6912ac7cb625..1046083e90a0 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -71,18 +71,17 @@ static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream)
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
- /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
- if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD)
- /*
- * Configure McBSP threshold based on either:
- * packet_size, when the sDMA is in packet mode, or
- * based on the period size.
- */
- if (dma_data->packet_size)
- words = dma_data->packet_size;
- else
- words = snd_pcm_lib_period_bytes(substream) /
- (mcbsp->wlen / 8);
+ /*
+ * Configure McBSP threshold based on either:
+ * packet_size, when the sDMA is in packet mode, or based on the
+ * period size in THRESHOLD mode, otherwise use McBSP threshold = 1
+ * for mono streams.
+ */
+ if (dma_data->packet_size)
+ words = dma_data->packet_size;
+ else if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD)
+ words = snd_pcm_lib_period_bytes(substream) /
+ (mcbsp->wlen / 8);
else
words = 1;
@@ -139,13 +138,15 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
if (mcbsp->pdata->buffer_size) {
/*
* Rule for the buffer size. We should not allow
- * smaller buffer than the FIFO size to avoid underruns
+ * smaller buffer than the FIFO size to avoid underruns.
+ * This applies only for the playback stream.
*/
- snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
- omap_mcbsp_hwrule_min_buffersize,
- mcbsp,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ omap_mcbsp_hwrule_min_buffersize,
+ mcbsp,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
/* Make sure, that the period size is always even */
snd_pcm_hw_constraint_step(substream->runtime, 0,
@@ -230,6 +231,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
unsigned int format, div, framesize, master;
dma_data = &mcbsp->dma_data[substream->stream];
+ channels = params_channels(params);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -245,7 +247,6 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
}
if (mcbsp->pdata->buffer_size) {
dma_data->set_threshold = omap_mcbsp_set_threshold;
- /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
int period_words, max_thrsh;
@@ -283,6 +284,10 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
} else {
sync_mode = OMAP_DMA_SYNC_FRAME;
}
+ } else if (channels > 1) {
+ /* Use packet mode for non mono streams */
+ pkt_size = channels;
+ sync_mode = OMAP_DMA_SYNC_PACKET;
}
}
@@ -301,7 +306,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
regs->rcr1 &= ~(RFRLEN1(0x7f) | RWDLEN1(7));
regs->xcr1 &= ~(XFRLEN1(0x7f) | XWDLEN1(7));
format = mcbsp->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
- wpf = channels = params_channels(params);
+ wpf = channels;
if (channels == 2 && (format == SND_SOC_DAIFMT_I2S ||
format == SND_SOC_DAIFMT_LEFT_J)) {
/* Use dual-phase frames */
diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c
index 39705561131a..59d47ab5b15d 100644
--- a/sound/soc/omap/omap-mcpdm.c
+++ b/sound/soc/omap/omap-mcpdm.c
@@ -33,6 +33,7 @@
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
+#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -507,10 +508,17 @@ static int __devexit asoc_mcpdm_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id omap_mcpdm_of_match[] = {
+ { .compatible = "ti,omap4-mcpdm", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, omap_mcpdm_of_match);
+
static struct platform_driver asoc_mcpdm_driver = {
.driver = {
.name = "omap-mcpdm",
.owner = THIS_MODULE,
+ .of_match_table = omap_mcpdm_of_match,
},
.probe = asoc_mcpdm_probe,
diff --git a/sound/soc/omap/omap4-hdmi-card.c b/sound/soc/omap/omap4-hdmi-card.c
deleted file mode 100644
index 28d689b2714d..000000000000
--- a/sound/soc/omap/omap4-hdmi-card.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * omap4-hdmi-card.c
- *
- * OMAP ALSA SoC machine driver for TI OMAP4 HDMI
- * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
- * Author: Ricardo Neri <ricardo.neri@ti.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/module.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <asm/mach-types.h>
-#include <video/omapdss.h>
-
-#define DRV_NAME "omap4-hdmi-audio"
-
-static int omap4_hdmi_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- int i;
- struct omap_overlay_manager *mgr = NULL;
- struct device *dev = substream->pcm->card->dev;
-
- /* Find DSS HDMI device */
- for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) {
- mgr = omap_dss_get_overlay_manager(i);
- if (mgr && mgr->device
- && mgr->device->type == OMAP_DISPLAY_TYPE_HDMI)
- break;
- }
-
- if (i == omap_dss_get_num_overlay_managers()) {
- dev_err(dev, "HDMI display device not found!\n");
- return -ENODEV;
- }
-
- /* Make sure HDMI is power-on to avoid L3 interconnect errors */
- if (mgr->device->state != OMAP_DSS_DISPLAY_ACTIVE) {
- dev_err(dev, "HDMI display is not active!\n");
- return -EIO;
- }
-
- return 0;
-}
-
-static struct snd_soc_ops omap4_hdmi_dai_ops = {
- .hw_params = omap4_hdmi_dai_hw_params,
-};
-
-static struct snd_soc_dai_link omap4_hdmi_dai = {
- .name = "HDMI",
- .stream_name = "HDMI",
- .cpu_dai_name = "hdmi-audio-dai",
- .platform_name = "omap-pcm-audio",
- .codec_name = "omapdss_hdmi",
- .codec_dai_name = "hdmi-audio-codec",
- .ops = &omap4_hdmi_dai_ops,
-};
-
-static struct snd_soc_card snd_soc_omap4_hdmi = {
- .name = "OMAP4HDMI",
- .owner = THIS_MODULE,
- .dai_link = &omap4_hdmi_dai,
- .num_links = 1,
-};
-
-static __devinit int omap4_hdmi_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &snd_soc_omap4_hdmi;
- int ret;
-
- card->dev = &pdev->dev;
-
- ret = snd_soc_register_card(card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
- card->dev = NULL;
- return ret;
- }
- return 0;
-}
-
-static int __devexit omap4_hdmi_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- card->dev = NULL;
- return 0;
-}
-
-static struct platform_driver omap4_hdmi_driver = {
- .driver = {
- .name = "omap4-hdmi-audio",
- .owner = THIS_MODULE,
- },
- .probe = omap4_hdmi_probe,
- .remove = __devexit_p(omap4_hdmi_remove),
-};
-
-module_platform_driver(omap4_hdmi_driver);
-
-MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
-MODULE_DESCRIPTION("OMAP4 HDMI machine ASoC driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);