summaryrefslogtreecommitdiff
path: root/sound/soc/tegra
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/tegra')
-rw-r--r--sound/soc/tegra/Kconfig9
-rw-r--r--sound/soc/tegra/Makefile2
-rw-r--r--sound/soc/tegra/tegra_alc5632.c214
-rw-r--r--sound/soc/tegra/tegra_das.c66
-rw-r--r--sound/soc/tegra/tegra_i2s.c164
-rw-r--r--sound/soc/tegra/tegra_i2s.h1
-rw-r--r--sound/soc/tegra/tegra_pcm.c18
-rw-r--r--sound/soc/tegra/tegra_spdif.c14
-rw-r--r--sound/soc/tegra/tegra_wm8903.c193
-rw-r--r--sound/soc/tegra/trimslice.c41
10 files changed, 447 insertions, 275 deletions
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index c6af1fd707f5..ce1b773c351f 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -47,3 +47,12 @@ config SND_SOC_TEGRA_TRIMSLICE
help
Say Y or M here if you want to add support for SoC audio on the
TrimSlice platform.
+
+config SND_SOC_TEGRA_ALC5632
+ tristate "SoC Audio support for Tegra boards using an ALC5632 codec"
+ depends on SND_SOC_TEGRA && I2C
+ select SND_SOC_TEGRA_I2S
+ select SND_SOC_ALC5632
+ help
+ Say Y or M here if you want to add support for SoC audio on the
+ Toshiba AC100 netbook.
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index 4d943b3fe150..8e584b8fcfba 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -14,6 +14,8 @@ obj-$(CONFIG_SND_SOC_TEGRA_SPDIF) += snd-soc-tegra-spdif.o
# Tegra machine Support
snd-soc-tegra-wm8903-objs := tegra_wm8903.o
snd-soc-tegra-trimslice-objs := trimslice.o
+snd-soc-tegra-alc5632-objs := tegra_alc5632.o
obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o
obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o
+obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o
diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c
new file mode 100644
index 000000000000..4a0e805c4edd
--- /dev/null
+++ b/sound/soc/tegra/tegra_alc5632.c
@@ -0,0 +1,214 @@
+/*
+* tegra_alc5632.c -- Toshiba AC100(PAZ00) machine ASoC driver
+*
+* Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
+*
+* Authors: Leon Romanovsky <leon@leon.nu>
+* Andrey Danin <danindrey@mail.ru>
+* Marc Dietrich <marvin24@gmx.de>
+*
+* 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.
+*/
+
+#include <asm/mach-types.h>
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../codecs/alc5632.h"
+
+#include "tegra_das.h"
+#include "tegra_i2s.h"
+#include "tegra_pcm.h"
+#include "tegra_asoc_utils.h"
+
+#define DRV_NAME "tegra-alc5632"
+
+struct tegra_alc5632 {
+ struct tegra_asoc_utils_data util_data;
+};
+
+static int tegra_alc5632_asoc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_card *card = codec->card;
+ struct tegra_alc5632 *alc5632 = snd_soc_card_get_drvdata(card);
+ int srate, mclk;
+ int err;
+
+ srate = params_rate(params);
+ mclk = 512 * srate;
+
+ err = tegra_asoc_utils_set_rate(&alc5632->util_data, srate, mclk);
+ if (err < 0) {
+ dev_err(card->dev, "Can't configure clocks\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops tegra_alc5632_asoc_ops = {
+ .hw_params = tegra_alc5632_asoc_hw_params,
+};
+
+static struct snd_soc_jack tegra_alc5632_hs_jack;
+
+static struct snd_soc_jack_pin tegra_alc5632_hs_jack_pins[] = {
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headset Stereophone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+static const struct snd_soc_dapm_widget tegra_alc5632_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Int Spk", NULL),
+ SND_SOC_DAPM_HP("Headset Stereophone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route tegra_alc5632_audio_map[] = {
+ /* Internal Speaker */
+ {"Int Spk", NULL, "SPKOUT"},
+ {"Int Spk", NULL, "SPKOUTN"},
+
+ /* Headset Mic */
+ {"MIC1", NULL, "MICBIAS1"},
+ {"MICBIAS1", NULL, "Headset Mic"},
+
+ /* Headset Stereophone */
+ {"Headset Stereophone", NULL, "HPR"},
+ {"Headset Stereophone", NULL, "HPL"},
+};
+
+static const struct snd_kcontrol_new tegra_alc5632_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Int Spk"),
+};
+
+static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET,
+ &tegra_alc5632_hs_jack);
+ snd_soc_jack_add_pins(&tegra_alc5632_hs_jack,
+ ARRAY_SIZE(tegra_alc5632_hs_jack_pins),
+ tegra_alc5632_hs_jack_pins);
+
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
+
+ return 0;
+}
+
+static struct snd_soc_dai_link tegra_alc5632_dai = {
+ .name = "ALC5632",
+ .stream_name = "ALC5632 PCM",
+ .codec_name = "alc5632.0-001e",
+ .platform_name = "tegra-pcm-audio",
+ .cpu_dai_name = "tegra-i2s.0",
+ .codec_dai_name = "alc5632-hifi",
+ .init = tegra_alc5632_asoc_init,
+ .ops = &tegra_alc5632_asoc_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBS_CFS,
+};
+
+static struct snd_soc_card snd_soc_tegra_alc5632 = {
+ .name = "tegra-alc5632",
+ .owner = THIS_MODULE,
+ .dai_link = &tegra_alc5632_dai,
+ .num_links = 1,
+ .controls = tegra_alc5632_controls,
+ .num_controls = ARRAY_SIZE(tegra_alc5632_controls),
+ .dapm_widgets = tegra_alc5632_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra_alc5632_dapm_widgets),
+ .dapm_routes = tegra_alc5632_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tegra_alc5632_audio_map),
+ .fully_routed = true,
+};
+
+static __devinit int tegra_alc5632_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_tegra_alc5632;
+ struct tegra_alc5632 *alc5632;
+ int ret;
+
+ alc5632 = devm_kzalloc(&pdev->dev,
+ sizeof(struct tegra_alc5632), GFP_KERNEL);
+ if (!alc5632) {
+ dev_err(&pdev->dev, "Can't allocate tegra_alc5632\n");
+ return -ENOMEM;
+ }
+
+ ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev);
+ if (ret)
+ return ret;
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, alc5632);
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ tegra_asoc_utils_fini(&alc5632->util_data);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devexit tegra_alc5632_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct tegra_alc5632 *alc5632 = snd_soc_card_get_drvdata(card);
+
+ snd_soc_unregister_card(card);
+
+ tegra_asoc_utils_fini(&alc5632->util_data);
+
+ return 0;
+}
+
+static struct platform_driver tegra_alc5632_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = tegra_alc5632_probe,
+ .remove = __devexit_p(tegra_alc5632_remove),
+};
+module_platform_driver(tegra_alc5632_driver);
+
+MODULE_AUTHOR("Leon Romanovsky <leon@leon.nu>");
+MODULE_DESCRIPTION("Tegra+ALC5632 machine ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra_das.c b/sound/soc/tegra/tegra_das.c
index 3b55a44146af..3b3c1ba4d235 100644
--- a/sound/soc/tegra/tegra_das.c
+++ b/sound/soc/tegra/tegra_das.c
@@ -172,11 +172,11 @@ static int __devinit tegra_das_probe(struct platform_device *pdev)
if (das)
return -ENODEV;
- das = kzalloc(sizeof(struct tegra_das), GFP_KERNEL);
+ das = devm_kzalloc(&pdev->dev, sizeof(struct tegra_das), GFP_KERNEL);
if (!das) {
dev_err(&pdev->dev, "Can't allocate tegra_das\n");
ret = -ENOMEM;
- goto exit;
+ goto err;
}
das->dev = &pdev->dev;
@@ -184,22 +184,35 @@ static int __devinit tegra_das_probe(struct platform_device *pdev)
if (!res) {
dev_err(&pdev->dev, "No memory resource\n");
ret = -ENODEV;
- goto err_free;
+ goto err;
}
- region = request_mem_region(res->start, resource_size(res),
- pdev->name);
+ region = devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), pdev->name);
if (!region) {
dev_err(&pdev->dev, "Memory region already claimed\n");
ret = -EBUSY;
- goto err_free;
+ goto err;
}
- das->regs = ioremap(res->start, resource_size(res));
+ das->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!das->regs) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
- goto err_release;
+ goto err;
+ }
+
+ ret = tegra_das_connect_dap_to_dac(TEGRA_DAS_DAP_ID_1,
+ TEGRA_DAS_DAP_SEL_DAC1);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't set up DAS DAP connection\n");
+ goto err;
+ }
+ ret = tegra_das_connect_dac_to_dap(TEGRA_DAS_DAC_ID_1,
+ TEGRA_DAS_DAC_SEL_DAP1);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't set up DAS DAC connection\n");
+ goto err;
}
tegra_das_debug_add(das);
@@ -208,58 +221,41 @@ static int __devinit tegra_das_probe(struct platform_device *pdev)
return 0;
-err_release:
- release_mem_region(res->start, resource_size(res));
-err_free:
- kfree(das);
+err:
das = NULL;
-exit:
return ret;
}
static int __devexit tegra_das_remove(struct platform_device *pdev)
{
- struct resource *res;
-
if (!das)
return -ENODEV;
- platform_set_drvdata(pdev, NULL);
-
tegra_das_debug_remove(das);
- iounmap(das->regs);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
-
- kfree(das);
das = NULL;
return 0;
}
+static const struct of_device_id tegra_das_of_match[] __devinitconst = {
+ { .compatible = "nvidia,tegra20-das", },
+ {},
+};
+
static struct platform_driver tegra_das_driver = {
.probe = tegra_das_probe,
.remove = __devexit_p(tegra_das_remove),
.driver = {
.name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra_das_of_match,
},
};
-
-static int __init tegra_das_modinit(void)
-{
- return platform_driver_register(&tegra_das_driver);
-}
-module_init(tegra_das_modinit);
-
-static void __exit tegra_das_modexit(void)
-{
- platform_driver_unregister(&tegra_das_driver);
-}
-module_exit(tegra_das_modexit);
+module_platform_driver(tegra_das_driver);
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
MODULE_DESCRIPTION("Tegra DAS driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra_das_of_match);
diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c
index 6728fab8c411..33509de52540 100644
--- a/sound/soc/tegra/tegra_i2s.c
+++ b/sound/soc/tegra/tegra_i2s.c
@@ -36,13 +36,13 @@
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/io.h>
+#include <linux/of.h>
#include <mach/iomap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include "tegra_das.h"
#include "tegra_i2s.h"
#define DRV_NAME "tegra-i2s"
@@ -99,13 +99,11 @@ static const struct file_operations tegra_i2s_debug_fops = {
.release = single_release,
};
-static void tegra_i2s_debug_add(struct tegra_i2s *i2s, int id)
+static void tegra_i2s_debug_add(struct tegra_i2s *i2s)
{
- char name[] = DRV_NAME ".0";
-
- snprintf(name, sizeof(name), DRV_NAME".%1d", id);
- i2s->debug = debugfs_create_file(name, S_IRUGO, snd_soc_debugfs_root,
- i2s, &tegra_i2s_debug_fops);
+ i2s->debug = debugfs_create_file(i2s->dai.name, S_IRUGO,
+ snd_soc_debugfs_root, i2s,
+ &tegra_i2s_debug_fops);
}
static void tegra_i2s_debug_remove(struct tegra_i2s *i2s)
@@ -306,93 +304,54 @@ static int tegra_i2s_probe(struct snd_soc_dai *dai)
return 0;
}
-static struct snd_soc_dai_ops tegra_i2s_dai_ops = {
+static const struct snd_soc_dai_ops tegra_i2s_dai_ops = {
.set_fmt = tegra_i2s_set_fmt,
.hw_params = tegra_i2s_hw_params,
.trigger = tegra_i2s_trigger,
};
-static struct snd_soc_dai_driver tegra_i2s_dai[] = {
- {
- .name = DRV_NAME ".0",
- .probe = tegra_i2s_probe,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_96000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_96000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &tegra_i2s_dai_ops,
- .symmetric_rates = 1,
+static const struct snd_soc_dai_driver tegra_i2s_dai_template = {
+ .probe = tegra_i2s_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- {
- .name = DRV_NAME ".1",
- .probe = tegra_i2s_probe,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_96000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_96000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &tegra_i2s_dai_ops,
- .symmetric_rates = 1,
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
},
+ .ops = &tegra_i2s_dai_ops,
+ .symmetric_rates = 1,
};
static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev)
{
struct tegra_i2s * i2s;
struct resource *mem, *memregion, *dmareq;
+ u32 of_dma[2];
+ u32 dma_ch;
int ret;
- if ((pdev->id < 0) ||
- (pdev->id >= ARRAY_SIZE(tegra_i2s_dai))) {
- dev_err(&pdev->dev, "ID %d out of range\n", pdev->id);
- return -EINVAL;
- }
-
- /*
- * FIXME: Until a codec driver exists for the tegra DAS, hard-code a
- * 1:1 mapping between audio controllers and audio ports.
- */
- ret = tegra_das_connect_dap_to_dac(TEGRA_DAS_DAP_ID_1 + pdev->id,
- TEGRA_DAS_DAP_SEL_DAC1 + pdev->id);
- if (ret) {
- dev_err(&pdev->dev, "Can't set up DAP connection\n");
- return ret;
- }
- ret = tegra_das_connect_dac_to_dap(TEGRA_DAS_DAC_ID_1 + pdev->id,
- TEGRA_DAS_DAC_SEL_DAP1 + pdev->id);
- if (ret) {
- dev_err(&pdev->dev, "Can't set up DAC connection\n");
- return ret;
- }
-
- i2s = kzalloc(sizeof(struct tegra_i2s), GFP_KERNEL);
+ i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra_i2s), GFP_KERNEL);
if (!i2s) {
dev_err(&pdev->dev, "Can't allocate tegra_i2s\n");
ret = -ENOMEM;
- goto exit;
+ goto err;
}
dev_set_drvdata(&pdev->dev, i2s);
+ i2s->dai = tegra_i2s_dai_template;
+ i2s->dai.name = dev_name(&pdev->dev);
+
i2s->clk_i2s = clk_get(&pdev->dev, NULL);
if (IS_ERR(i2s->clk_i2s)) {
dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
ret = PTR_ERR(i2s->clk_i2s);
- goto err_free;
+ goto err;
}
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -404,104 +363,93 @@ static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev)
dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!dmareq) {
- dev_err(&pdev->dev, "No DMA resource\n");
- ret = -ENODEV;
- goto err_clk_put;
+ if (of_property_read_u32_array(pdev->dev.of_node,
+ "nvidia,dma-request-selector",
+ of_dma, 2) < 0) {
+ dev_err(&pdev->dev, "No DMA resource\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+ dma_ch = of_dma[1];
+ } else {
+ dma_ch = dmareq->start;
}
- memregion = request_mem_region(mem->start, resource_size(mem),
- DRV_NAME);
+ memregion = devm_request_mem_region(&pdev->dev, mem->start,
+ resource_size(mem), DRV_NAME);
if (!memregion) {
dev_err(&pdev->dev, "Memory region already claimed\n");
ret = -EBUSY;
goto err_clk_put;
}
- i2s->regs = ioremap(mem->start, resource_size(mem));
+ i2s->regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
if (!i2s->regs) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
- goto err_release;
+ goto err_clk_put;
}
i2s->capture_dma_data.addr = mem->start + TEGRA_I2S_FIFO2;
i2s->capture_dma_data.wrap = 4;
i2s->capture_dma_data.width = 32;
- i2s->capture_dma_data.req_sel = dmareq->start;
+ i2s->capture_dma_data.req_sel = dma_ch;
i2s->playback_dma_data.addr = mem->start + TEGRA_I2S_FIFO1;
i2s->playback_dma_data.wrap = 4;
i2s->playback_dma_data.width = 32;
- i2s->playback_dma_data.req_sel = dmareq->start;
+ i2s->playback_dma_data.req_sel = dma_ch;
i2s->reg_ctrl = TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED;
- ret = snd_soc_register_dai(&pdev->dev, &tegra_i2s_dai[pdev->id]);
+ ret = snd_soc_register_dai(&pdev->dev, &i2s->dai);
if (ret) {
dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
ret = -ENOMEM;
- goto err_unmap;
+ goto err_clk_put;
}
- tegra_i2s_debug_add(i2s, pdev->id);
+ tegra_i2s_debug_add(i2s);
return 0;
-err_unmap:
- iounmap(i2s->regs);
-err_release:
- release_mem_region(mem->start, resource_size(mem));
err_clk_put:
clk_put(i2s->clk_i2s);
-err_free:
- kfree(i2s);
-exit:
+err:
return ret;
}
static int __devexit tegra_i2s_platform_remove(struct platform_device *pdev)
{
struct tegra_i2s *i2s = dev_get_drvdata(&pdev->dev);
- struct resource *res;
snd_soc_unregister_dai(&pdev->dev);
tegra_i2s_debug_remove(i2s);
- iounmap(i2s->regs);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
-
clk_put(i2s->clk_i2s);
- kfree(i2s);
-
return 0;
}
+static const struct of_device_id tegra_i2s_of_match[] __devinitconst = {
+ { .compatible = "nvidia,tegra20-i2s", },
+ {},
+};
+
static struct platform_driver tegra_i2s_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
+ .of_match_table = tegra_i2s_of_match,
},
.probe = tegra_i2s_platform_probe,
.remove = __devexit_p(tegra_i2s_platform_remove),
};
-
-static int __init snd_tegra_i2s_init(void)
-{
- return platform_driver_register(&tegra_i2s_driver);
-}
-module_init(snd_tegra_i2s_init);
-
-static void __exit snd_tegra_i2s_exit(void)
-{
- platform_driver_unregister(&tegra_i2s_driver);
-}
-module_exit(snd_tegra_i2s_exit);
+module_platform_driver(tegra_i2s_driver);
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
MODULE_DESCRIPTION("Tegra I2S ASoC driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra_i2s_of_match);
diff --git a/sound/soc/tegra/tegra_i2s.h b/sound/soc/tegra/tegra_i2s.h
index 2b38a096f46c..15ce1e2e8bde 100644
--- a/sound/soc/tegra/tegra_i2s.h
+++ b/sound/soc/tegra/tegra_i2s.h
@@ -153,6 +153,7 @@
#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_TWELVE_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
struct tegra_i2s {
+ struct snd_soc_dai_driver dai;
struct clk *clk_i2s;
int clk_refs;
struct tegra_pcm_dma_params capture_dma_data;
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
index 436def1dfa39..c22431516ab2 100644
--- a/sound/soc/tegra/tegra_pcm.c
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -330,7 +330,6 @@ static u64 tegra_dma_mask = DMA_BIT_MASK(32);
static int tegra_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
- struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
@@ -339,14 +338,14 @@ static int tegra_pcm_new(struct snd_soc_pcm_runtime *rtd)
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = 0xffffffff;
- if (dai->driver->playback.channels_min) {
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
ret = tegra_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
goto err;
}
- if (dai->driver->capture.channels_min) {
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
ret = tegra_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
@@ -392,18 +391,7 @@ static struct platform_driver tegra_pcm_driver = {
.probe = tegra_pcm_platform_probe,
.remove = __devexit_p(tegra_pcm_platform_remove),
};
-
-static int __init snd_tegra_pcm_init(void)
-{
- return platform_driver_register(&tegra_pcm_driver);
-}
-module_init(snd_tegra_pcm_init);
-
-static void __exit snd_tegra_pcm_exit(void)
-{
- platform_driver_unregister(&tegra_pcm_driver);
-}
-module_exit(snd_tegra_pcm_exit);
+module_platform_driver(tegra_pcm_driver);
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
MODULE_DESCRIPTION("Tegra PCM ASoC driver");
diff --git a/sound/soc/tegra/tegra_spdif.c b/sound/soc/tegra/tegra_spdif.c
index dd11d0c63474..475428cf270e 100644
--- a/sound/soc/tegra/tegra_spdif.c
+++ b/sound/soc/tegra/tegra_spdif.c
@@ -226,7 +226,7 @@ static int tegra_spdif_probe(struct snd_soc_dai *dai)
return 0;
}
-static struct snd_soc_dai_ops tegra_spdif_dai_ops = {
+static const struct snd_soc_dai_ops tegra_spdif_dai_ops = {
.hw_params = tegra_spdif_hw_params,
.trigger = tegra_spdif_trigger,
};
@@ -352,17 +352,7 @@ static struct platform_driver tegra_spdif_driver = {
.remove = __devexit_p(tegra_spdif_platform_remove),
};
-static int __init snd_tegra_spdif_init(void)
-{
- return platform_driver_register(&tegra_spdif_driver);
-}
-module_init(snd_tegra_spdif_init);
-
-static void __exit snd_tegra_spdif_exit(void)
-{
- platform_driver_unregister(&tegra_spdif_driver);
-}
-module_exit(snd_tegra_spdif_exit);
+module_platform_driver(tegra_spdif_driver);
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
MODULE_DESCRIPTION("Tegra SPDIF ASoC driver");
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
index a81cf39257bf..566655e23b7d 100644
--- a/sound/soc/tegra/tegra_wm8903.c
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -34,6 +34,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/gpio.h>
+#include <linux/of_gpio.h>
#include <mach/tegra_wm8903_pdata.h>
@@ -59,8 +60,9 @@
#define GPIO_HP_DET BIT(4)
struct tegra_wm8903 {
+ struct tegra_wm8903_platform_data pdata;
+ struct platform_device *pcm_dev;
struct tegra_asoc_utils_data util_data;
- struct tegra_wm8903_platform_data *pdata;
int gpio_requested;
};
@@ -160,7 +162,7 @@ static int tegra_wm8903_event_int_spk(struct snd_soc_dapm_widget *w,
struct snd_soc_dapm_context *dapm = w->dapm;
struct snd_soc_card *card = dapm->card;
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
- struct tegra_wm8903_platform_data *pdata = machine->pdata;
+ struct tegra_wm8903_platform_data *pdata = &machine->pdata;
if (!(machine->gpio_requested & GPIO_SPKR_EN))
return 0;
@@ -177,7 +179,7 @@ static int tegra_wm8903_event_hp(struct snd_soc_dapm_widget *w,
struct snd_soc_dapm_context *dapm = w->dapm;
struct snd_soc_card *card = dapm->card;
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
- struct tegra_wm8903_platform_data *pdata = machine->pdata;
+ struct tegra_wm8903_platform_data *pdata = &machine->pdata;
if (!(machine->gpio_requested & GPIO_HP_MUTE))
return 0;
@@ -201,8 +203,8 @@ static const struct snd_soc_dapm_route harmony_audio_map[] = {
{"Int Spk", NULL, "RON"},
{"Int Spk", NULL, "LOP"},
{"Int Spk", NULL, "LON"},
- {"Mic Bias", NULL, "Mic Jack"},
- {"IN1L", NULL, "Mic Bias"},
+ {"Mic Jack", NULL, "MICBIAS"},
+ {"IN1L", NULL, "Mic Jack"},
};
static const struct snd_soc_dapm_route seaboard_audio_map[] = {
@@ -212,8 +214,8 @@ static const struct snd_soc_dapm_route seaboard_audio_map[] = {
{"Int Spk", NULL, "RON"},
{"Int Spk", NULL, "LOP"},
{"Int Spk", NULL, "LON"},
- {"Mic Bias", NULL, "Mic Jack"},
- {"IN1R", NULL, "Mic Bias"},
+ {"Mic Jack", NULL, "MICBIAS"},
+ {"IN1R", NULL, "Mic Jack"},
};
static const struct snd_soc_dapm_route kaen_audio_map[] = {
@@ -223,8 +225,8 @@ static const struct snd_soc_dapm_route kaen_audio_map[] = {
{"Int Spk", NULL, "RON"},
{"Int Spk", NULL, "LOP"},
{"Int Spk", NULL, "LON"},
- {"Mic Bias", NULL, "Mic Jack"},
- {"IN2R", NULL, "Mic Bias"},
+ {"Mic Jack", NULL, "MICBIAS"},
+ {"IN2R", NULL, "Mic Jack"},
};
static const struct snd_soc_dapm_route aebl_audio_map[] = {
@@ -232,8 +234,8 @@ static const struct snd_soc_dapm_route aebl_audio_map[] = {
{"Headphone Jack", NULL, "HPOUTL"},
{"Int Spk", NULL, "LINEOUTR"},
{"Int Spk", NULL, "LINEOUTL"},
- {"Mic Bias", NULL, "Mic Jack"},
- {"IN1R", NULL, "Mic Bias"},
+ {"Mic Jack", NULL, "MICBIAS"},
+ {"IN1R", NULL, "Mic Jack"},
};
static const struct snd_kcontrol_new tegra_wm8903_controls[] = {
@@ -246,9 +248,36 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_dapm_context *dapm = &codec->dapm;
struct snd_soc_card *card = codec->card;
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
- struct tegra_wm8903_platform_data *pdata = machine->pdata;
+ struct tegra_wm8903_platform_data *pdata = &machine->pdata;
+ struct device_node *np = card->dev->of_node;
int ret;
+ if (card->dev->platform_data) {
+ memcpy(pdata, card->dev->platform_data, sizeof(*pdata));
+ } else if (np) {
+ /*
+ * This part must be in init() rather than probe() in order to
+ * guarantee that the WM8903 has been probed, and hence its
+ * GPIO controller registered, which is a pre-condition for
+ * of_get_named_gpio() to be able to map the phandles in the
+ * properties to the controller node. Given this, all
+ * pdata handling is in init() for consistency.
+ */
+ pdata->gpio_spkr_en = of_get_named_gpio(np,
+ "nvidia,spkr-en-gpios", 0);
+ pdata->gpio_hp_mute = of_get_named_gpio(np,
+ "nvidia,hp-mute-gpios", 0);
+ pdata->gpio_hp_det = of_get_named_gpio(np,
+ "nvidia,hp-det-gpios", 0);
+ pdata->gpio_int_mic_en = of_get_named_gpio(np,
+ "nvidia,int-mic-en-gpios", 0);
+ pdata->gpio_ext_mic_en = of_get_named_gpio(np,
+ "nvidia,ext-mic-en-gpios", 0);
+ } else {
+ dev_err(card->dev, "No platform data supplied\n");
+ return -EINVAL;
+ }
+
if (gpio_is_valid(pdata->gpio_spkr_en)) {
ret = gpio_request(pdata->gpio_spkr_en, "spkr_en");
if (ret) {
@@ -316,28 +345,7 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
wm8903_mic_detect(codec, &tegra_wm8903_mic_jack, SND_JACK_MICROPHONE,
0);
- snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
-
- /* FIXME: Calculate automatically based on DAPM routes? */
- if (!machine_is_harmony())
- snd_soc_dapm_nc_pin(dapm, "IN1L");
- if (!machine_is_seaboard() && !machine_is_aebl())
- snd_soc_dapm_nc_pin(dapm, "IN1R");
- snd_soc_dapm_nc_pin(dapm, "IN2L");
- if (!machine_is_kaen())
- snd_soc_dapm_nc_pin(dapm, "IN2R");
- snd_soc_dapm_nc_pin(dapm, "IN3L");
- snd_soc_dapm_nc_pin(dapm, "IN3R");
-
- if (machine_is_aebl()) {
- snd_soc_dapm_nc_pin(dapm, "LON");
- snd_soc_dapm_nc_pin(dapm, "RON");
- snd_soc_dapm_nc_pin(dapm, "ROP");
- snd_soc_dapm_nc_pin(dapm, "LOP");
- } else {
- snd_soc_dapm_nc_pin(dapm, "LINEOUTR");
- snd_soc_dapm_nc_pin(dapm, "LINEOUTL");
- }
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
return 0;
}
@@ -355,6 +363,7 @@ static struct snd_soc_dai_link tegra_wm8903_dai = {
static struct snd_soc_card snd_soc_tegra_wm8903 = {
.name = "tegra-wm8903",
+ .owner = THIS_MODULE,
.dai_link = &tegra_wm8903_dai,
.num_links = 1,
@@ -362,51 +371,91 @@ static struct snd_soc_card snd_soc_tegra_wm8903 = {
.num_controls = ARRAY_SIZE(tegra_wm8903_controls),
.dapm_widgets = tegra_wm8903_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(tegra_wm8903_dapm_widgets),
+ .fully_routed = true,
};
static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &snd_soc_tegra_wm8903;
struct tegra_wm8903 *machine;
- struct tegra_wm8903_platform_data *pdata;
int ret;
- pdata = pdev->dev.platform_data;
- if (!pdata) {
+ if (!pdev->dev.platform_data && !pdev->dev.of_node) {
dev_err(&pdev->dev, "No platform data supplied\n");
return -EINVAL;
}
- machine = kzalloc(sizeof(struct tegra_wm8903), GFP_KERNEL);
+ machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm8903),
+ GFP_KERNEL);
if (!machine) {
dev_err(&pdev->dev, "Can't allocate tegra_wm8903 struct\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err;
}
-
- machine->pdata = pdata;
-
- ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
- if (ret)
- goto err_free_machine;
+ machine->pcm_dev = ERR_PTR(-EINVAL);
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, machine);
- if (machine_is_harmony()) {
- card->dapm_routes = harmony_audio_map;
- card->num_dapm_routes = ARRAY_SIZE(harmony_audio_map);
- } else if (machine_is_seaboard()) {
- card->dapm_routes = seaboard_audio_map;
- card->num_dapm_routes = ARRAY_SIZE(seaboard_audio_map);
- } else if (machine_is_kaen()) {
- card->dapm_routes = kaen_audio_map;
- card->num_dapm_routes = ARRAY_SIZE(kaen_audio_map);
+ if (pdev->dev.of_node) {
+ ret = snd_soc_of_parse_card_name(card, "nvidia,model");
+ if (ret)
+ goto err;
+
+ ret = snd_soc_of_parse_audio_routing(card,
+ "nvidia,audio-routing");
+ if (ret)
+ goto err;
+
+ tegra_wm8903_dai.codec_name = NULL;
+ tegra_wm8903_dai.codec_of_node = of_parse_phandle(
+ pdev->dev.of_node, "nvidia,audio-codec", 0);
+ if (!tegra_wm8903_dai.codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,audio-codec' missing or invalid\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ tegra_wm8903_dai.cpu_dai_name = NULL;
+ tegra_wm8903_dai.cpu_dai_of_node = of_parse_phandle(
+ pdev->dev.of_node, "nvidia,i2s-controller", 0);
+ if (!tegra_wm8903_dai.cpu_dai_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,i2s-controller' missing or invalid\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ machine->pcm_dev = platform_device_register_simple(
+ "tegra-pcm-audio", -1, NULL, 0);
+ if (IS_ERR(machine->pcm_dev)) {
+ dev_err(&pdev->dev,
+ "Can't instantiate tegra-pcm-audio\n");
+ ret = PTR_ERR(machine->pcm_dev);
+ goto err;
+ }
} else {
- card->dapm_routes = aebl_audio_map;
- card->num_dapm_routes = ARRAY_SIZE(aebl_audio_map);
+ if (machine_is_harmony()) {
+ card->dapm_routes = harmony_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(harmony_audio_map);
+ } else if (machine_is_seaboard()) {
+ card->dapm_routes = seaboard_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(seaboard_audio_map);
+ } else if (machine_is_kaen()) {
+ card->dapm_routes = kaen_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(kaen_audio_map);
+ } else {
+ card->dapm_routes = aebl_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(aebl_audio_map);
+ }
}
+ ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
+ if (ret)
+ goto err_unregister;
+
ret = snd_soc_register_card(card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
@@ -418,8 +467,10 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev)
err_fini_utils:
tegra_asoc_utils_fini(&machine->util_data);
-err_free_machine:
- kfree(machine);
+err_unregister:
+ if (!IS_ERR(machine->pcm_dev))
+ platform_device_unregister(machine->pcm_dev);
+err:
return ret;
}
@@ -427,7 +478,7 @@ static int __devexit tegra_wm8903_driver_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
- struct tegra_wm8903_platform_data *pdata = machine->pdata;
+ struct tegra_wm8903_platform_data *pdata = &machine->pdata;
if (machine->gpio_requested & GPIO_HP_DET)
snd_soc_jack_free_gpios(&tegra_wm8903_hp_jack,
@@ -446,35 +497,31 @@ static int __devexit tegra_wm8903_driver_remove(struct platform_device *pdev)
snd_soc_unregister_card(card);
tegra_asoc_utils_fini(&machine->util_data);
-
- kfree(machine);
+ if (!IS_ERR(machine->pcm_dev))
+ platform_device_unregister(machine->pcm_dev);
return 0;
}
+static const struct of_device_id tegra_wm8903_of_match[] __devinitconst = {
+ { .compatible = "nvidia,tegra-audio-wm8903", },
+ {},
+};
+
static struct platform_driver tegra_wm8903_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = &snd_soc_pm_ops,
+ .of_match_table = tegra_wm8903_of_match,
},
.probe = tegra_wm8903_driver_probe,
.remove = __devexit_p(tegra_wm8903_driver_remove),
};
-
-static int __init tegra_wm8903_modinit(void)
-{
- return platform_driver_register(&tegra_wm8903_driver);
-}
-module_init(tegra_wm8903_modinit);
-
-static void __exit tegra_wm8903_modexit(void)
-{
- platform_driver_unregister(&tegra_wm8903_driver);
-}
-module_exit(tegra_wm8903_modexit);
+module_platform_driver(tegra_wm8903_driver);
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
MODULE_DESCRIPTION("Tegra+WM8903 machine ASoC driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra_wm8903_of_match);
diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c
index b3a7efa6d960..2bdfc550cff8 100644
--- a/sound/soc/tegra/trimslice.c
+++ b/sound/soc/tegra/trimslice.c
@@ -115,18 +115,6 @@ static const struct snd_soc_dapm_route trimslice_audio_map[] = {
{"RLINEIN", NULL, "Line In"},
};
-static int trimslice_asoc_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_nc_pin(dapm, "LHPOUT");
- snd_soc_dapm_nc_pin(dapm, "RHPOUT");
- snd_soc_dapm_nc_pin(dapm, "MICIN");
-
- return 0;
-}
-
static struct snd_soc_dai_link trimslice_tlv320aic23_dai = {
.name = "TLV320AIC23",
.stream_name = "AIC23",
@@ -134,12 +122,12 @@ static struct snd_soc_dai_link trimslice_tlv320aic23_dai = {
.platform_name = "tegra-pcm-audio",
.cpu_dai_name = "tegra-i2s.0",
.codec_dai_name = "tlv320aic23-hifi",
- .init = trimslice_asoc_init,
.ops = &trimslice_asoc_ops,
};
static struct snd_soc_card snd_soc_trimslice = {
.name = "tegra-trimslice",
+ .owner = THIS_MODULE,
.dai_link = &trimslice_tlv320aic23_dai,
.num_links = 1,
@@ -147,6 +135,7 @@ static struct snd_soc_card snd_soc_trimslice = {
.num_dapm_widgets = ARRAY_SIZE(trimslice_dapm_widgets),
.dapm_routes = trimslice_audio_map,
.num_dapm_routes = ARRAY_SIZE(trimslice_audio_map),
+ .fully_routed = true,
};
static __devinit int tegra_snd_trimslice_probe(struct platform_device *pdev)
@@ -155,15 +144,17 @@ static __devinit int tegra_snd_trimslice_probe(struct platform_device *pdev)
struct tegra_trimslice *trimslice;
int ret;
- trimslice = kzalloc(sizeof(struct tegra_trimslice), GFP_KERNEL);
+ trimslice = devm_kzalloc(&pdev->dev, sizeof(struct tegra_trimslice),
+ GFP_KERNEL);
if (!trimslice) {
dev_err(&pdev->dev, "Can't allocate tegra_trimslice\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err;
}
ret = tegra_asoc_utils_init(&trimslice->util_data, &pdev->dev);
if (ret)
- goto err_free_trimslice;
+ goto err;
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
@@ -180,8 +171,7 @@ static __devinit int tegra_snd_trimslice_probe(struct platform_device *pdev)
err_fini_utils:
tegra_asoc_utils_fini(&trimslice->util_data);
-err_free_trimslice:
- kfree(trimslice);
+err:
return ret;
}
@@ -194,8 +184,6 @@ static int __devexit tegra_snd_trimslice_remove(struct platform_device *pdev)
tegra_asoc_utils_fini(&trimslice->util_data);
- kfree(trimslice);
-
return 0;
}
@@ -207,18 +195,7 @@ static struct platform_driver tegra_snd_trimslice_driver = {
.probe = tegra_snd_trimslice_probe,
.remove = __devexit_p(tegra_snd_trimslice_remove),
};
-
-static int __init snd_tegra_trimslice_init(void)
-{
- return platform_driver_register(&tegra_snd_trimslice_driver);
-}
-module_init(snd_tegra_trimslice_init);
-
-static void __exit snd_tegra_trimslice_exit(void)
-{
- platform_driver_unregister(&tegra_snd_trimslice_driver);
-}
-module_exit(snd_tegra_trimslice_exit);
+module_platform_driver(tegra_snd_trimslice_driver);
MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
MODULE_DESCRIPTION("Trimslice machine ASoC driver");