[alsa-devel] [PATCH 00/17] ASoC: tegra: Add Tegra30 support
From: Stephen Warren swarren@nvidia.com
This series adds basic support for 16-bit stereo on Tegra30. This will be enabled for the Cardhu board in a separate series to arch/arm/mach-tegra.
I don't believe this depends on anything that won't be in 3.4. The related arch/arm/mach-tegra series will have some dependencies.
There are a couple patches in here which also touch arch/arm/mach-tegra. I believe it should be fine to apply these to the ASoC tree without issue (I don't anticpate conflicts) but as you (Mark) mentioned, putting them into a separate branch just in case might be a good idea.
Stephen Warren (17): ASoC: tegra: remove open-coded clk reference counting ASoC: tegra: ensure clocks are enabled when touching registers ASoC: tegra: remove unnecessary includes ASoC: tegra: add description to a couple Kconfig options ASoC: tegra: fix Kconfig SND_SOC_TEGRA_ALC5632 indentation ASoC: tegra: fix some checkpatch warnings ASoC: tegra: introduce separate Kconfig variable for DAS driver ASoC: tegra: make Tegra20 drivers depend on Tegra20 support ASoC: tegra: sort Makefile into common and per-SoC files ASoC: tegra: rename Tegra20-specific driver files ASoC: tegra: complete Tegra->Tegra20 renaming ASoC: tegra: utils: add support for Tegra30 devices ASoC: tegra: set a sensible initial clock rate ASoC: tegra: add tegra30-ahub driver ASoC: tegra: add tegra30-i2s driver ASoC: tegra: add Kconfig and Makefile support for Tegra30 ASoC: tegra+wm8903 machine: support Tegra30
.../bindings/sound/tegra-audio-wm8903.txt | 8 +- .../devicetree/bindings/sound/tegra30-ahub.txt | 18 + .../devicetree/bindings/sound/tegra30-i2s.txt | 12 + arch/arm/mach-tegra/board-dt-tegra20.c | 6 +- arch/arm/mach-tegra/devices.c | 6 +- arch/arm/mach-tegra/tegra2_clocks.c | 4 +- sound/soc/tegra/Kconfig | 61 ++- sound/soc/tegra/Makefile | 18 +- sound/soc/tegra/tegra20_das.c | 261 +++++++++ sound/soc/tegra/tegra20_das.h | 135 +++++ sound/soc/tegra/tegra20_i2s.c | 462 +++++++++++++++ sound/soc/tegra/tegra20_i2s.h | 165 ++++++ sound/soc/tegra/tegra20_spdif.c | 369 ++++++++++++ sound/soc/tegra/tegra20_spdif.h | 472 ++++++++++++++++ sound/soc/tegra/tegra30_ahub.c | 593 ++++++++++++++++++++ sound/soc/tegra/tegra30_ahub.h | 489 ++++++++++++++++ sound/soc/tegra/tegra30_i2s.c | 538 ++++++++++++++++++ sound/soc/tegra/tegra30_i2s.h | 242 ++++++++ sound/soc/tegra/tegra_alc5632.c | 6 +- sound/soc/tegra/tegra_asoc_utils.c | 35 +- sound/soc/tegra/tegra_asoc_utils.h | 10 +- sound/soc/tegra/tegra_das.c | 261 --------- sound/soc/tegra/tegra_das.h | 135 ----- sound/soc/tegra/tegra_i2s.c | 464 --------------- sound/soc/tegra/tegra_i2s.h | 166 ------ sound/soc/tegra/tegra_spdif.c | 369 ------------ sound/soc/tegra/tegra_spdif.h | 473 ---------------- sound/soc/tegra/tegra_wm8903.c | 39 +- sound/soc/tegra/trimslice.c | 10 +- 29 files changed, 3896 insertions(+), 1931 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/tegra30-ahub.txt create mode 100644 Documentation/devicetree/bindings/sound/tegra30-i2s.txt create mode 100644 sound/soc/tegra/tegra20_das.c create mode 100644 sound/soc/tegra/tegra20_das.h create mode 100644 sound/soc/tegra/tegra20_i2s.c create mode 100644 sound/soc/tegra/tegra20_i2s.h create mode 100644 sound/soc/tegra/tegra20_spdif.c create mode 100644 sound/soc/tegra/tegra20_spdif.h create mode 100644 sound/soc/tegra/tegra30_ahub.c create mode 100644 sound/soc/tegra/tegra30_ahub.h create mode 100644 sound/soc/tegra/tegra30_i2s.c create mode 100644 sound/soc/tegra/tegra30_i2s.h delete mode 100644 sound/soc/tegra/tegra_das.c delete mode 100644 sound/soc/tegra/tegra_das.h delete mode 100644 sound/soc/tegra/tegra_i2s.c delete mode 100644 sound/soc/tegra/tegra_i2s.h delete mode 100644 sound/soc/tegra/tegra_spdif.c delete mode 100644 sound/soc/tegra/tegra_spdif.h
From: Stephen Warren swarren@nvidia.com
clk_enable/disable() already reference count the enable calls, so there's no need for the callers to do the same.
Signed-off-by: Stephen Warren swarren@nvidia.com --- sound/soc/tegra/tegra_i2s.c | 14 ++++---------- sound/soc/tegra/tegra_i2s.h | 1 - sound/soc/tegra/tegra_spdif.c | 8 ++------ sound/soc/tegra/tegra_spdif.h | 1 - 4 files changed, 6 insertions(+), 18 deletions(-)
diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c index 546e29b..0a874fb 100644 --- a/sound/soc/tegra/tegra_i2s.c +++ b/sound/soc/tegra/tegra_i2s.c @@ -220,8 +220,7 @@ static int tegra_i2s_hw_params(struct snd_pcm_substream *substream, if (i2sclock % (2 * srate)) reg |= TEGRA_I2S_TIMING_NON_SYM_ENABLE;
- if (!i2s->clk_refs) - clk_enable(i2s->clk_i2s); + clk_enable(i2s->clk_i2s);
tegra_i2s_write(i2s, TEGRA_I2S_TIMING, reg);
@@ -229,8 +228,7 @@ static int tegra_i2s_hw_params(struct snd_pcm_substream *substream, TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS | TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS);
- if (!i2s->clk_refs) - clk_disable(i2s->clk_i2s); + clk_disable(i2s->clk_i2s);
return 0; } @@ -268,9 +266,7 @@ static int tegra_i2s_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: - if (!i2s->clk_refs) - clk_enable(i2s->clk_i2s); - i2s->clk_refs++; + clk_enable(i2s->clk_i2s); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) tegra_i2s_start_playback(i2s); else @@ -283,9 +279,7 @@ static int tegra_i2s_trigger(struct snd_pcm_substream *substream, int cmd, tegra_i2s_stop_playback(i2s); else tegra_i2s_stop_capture(i2s); - i2s->clk_refs--; - if (!i2s->clk_refs) - clk_disable(i2s->clk_i2s); + clk_disable(i2s->clk_i2s); break; default: return -EINVAL; diff --git a/sound/soc/tegra/tegra_i2s.h b/sound/soc/tegra/tegra_i2s.h index 15ce1e2..c08e019 100644 --- a/sound/soc/tegra/tegra_i2s.h +++ b/sound/soc/tegra/tegra_i2s.h @@ -155,7 +155,6 @@ struct tegra_i2s { struct snd_soc_dai_driver dai; struct clk *clk_i2s; - int clk_refs; struct tegra_pcm_dma_params capture_dma_data; struct tegra_pcm_dma_params playback_dma_data; void __iomem *regs; diff --git a/sound/soc/tegra/tegra_spdif.c b/sound/soc/tegra/tegra_spdif.c index cd836cb..3426633 100644 --- a/sound/soc/tegra/tegra_spdif.c +++ b/sound/soc/tegra/tegra_spdif.c @@ -196,18 +196,14 @@ static int tegra_spdif_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: - if (!spdif->clk_refs) - clk_enable(spdif->clk_spdif_out); - spdif->clk_refs++; + clk_enable(spdif->clk_spdif_out); tegra_spdif_start_playback(spdif); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: tegra_spdif_stop_playback(spdif); - spdif->clk_refs--; - if (!spdif->clk_refs) - clk_disable(spdif->clk_spdif_out); + clk_disable(spdif->clk_spdif_out); break; default: return -EINVAL; diff --git a/sound/soc/tegra/tegra_spdif.h b/sound/soc/tegra/tegra_spdif.h index 2e03db4..f5fc712 100644 --- a/sound/soc/tegra/tegra_spdif.h +++ b/sound/soc/tegra/tegra_spdif.h @@ -462,7 +462,6 @@
struct tegra_spdif { struct clk *clk_spdif_out; - int clk_refs; struct tegra_pcm_dma_params capture_dma_data; struct tegra_pcm_dma_params playback_dma_data; void __iomem *regs;
From: Stephen Warren swarren@nvidia.com
Debugfs files could be accessed any time, so explicitly enable clocks when reading registers to generate debugfs file content.
Signed-off-by: Stephen Warren swarren@nvidia.com --- sound/soc/tegra/tegra_i2s.c | 4 ++++ sound/soc/tegra/tegra_spdif.c | 4 ++++ 2 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c index 0a874fb..8aafc53 100644 --- a/sound/soc/tegra/tegra_i2s.c +++ b/sound/soc/tegra/tegra_i2s.c @@ -79,11 +79,15 @@ static int tegra_i2s_show(struct seq_file *s, void *unused) struct tegra_i2s *i2s = s->private; int i;
+ clk_enable(i2s->clk_i2s); + for (i = 0; i < ARRAY_SIZE(regs); i++) { u32 val = tegra_i2s_read(i2s, regs[i].offset); seq_printf(s, "%s = %08x\n", regs[i].name, val); }
+ clk_disable(i2s->clk_i2s); + return 0; }
diff --git a/sound/soc/tegra/tegra_spdif.c b/sound/soc/tegra/tegra_spdif.c index 3426633..e16c4ff 100644 --- a/sound/soc/tegra/tegra_spdif.c +++ b/sound/soc/tegra/tegra_spdif.c @@ -79,11 +79,15 @@ static int tegra_spdif_show(struct seq_file *s, void *unused) struct tegra_spdif *spdif = s->private; int i;
+ clk_enable(spdif->clk_spdif_out); + for (i = 0; i < ARRAY_SIZE(regs); i++) { u32 val = tegra_spdif_read(spdif, regs[i].offset); seq_printf(s, "%s = %08x\n", regs[i].name, val); }
+ clk_disable(spdif->clk_spdif_out); + return 0; }
On Fri, Mar 30, 2012 at 05:07:17PM -0600, Stephen Warren wrote:
From: Stephen Warren swarren@nvidia.com
Debugfs files could be accessed any time, so explicitly enable clocks when reading registers to generate debugfs file content.
Applied, thanks. People keep muttering about regmap support for memory mapped devices and while there's issues to resolve doing it for things like this it might be worthwhile...
From: Stephen Warren swarren@nvidia.com
These include aren't needed, and some of the files are about to be renamed.
Signed-off-by: Stephen Warren swarren@nvidia.com --- sound/soc/tegra/tegra_alc5632.c | 3 --- sound/soc/tegra/tegra_wm8903.c | 3 --- sound/soc/tegra/trimslice.c | 3 --- 3 files changed, 0 insertions(+), 9 deletions(-)
diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c index 396181c..32de700 100644 --- a/sound/soc/tegra/tegra_alc5632.c +++ b/sound/soc/tegra/tegra_alc5632.c @@ -29,9 +29,6 @@
#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" diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index fbd1335..2f9e9ff 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -46,9 +46,6 @@
#include "../codecs/wm8903.h"
-#include "tegra_das.h" -#include "tegra_i2s.h" -#include "tegra_pcm.h" #include "tegra_asoc_utils.h"
#define DRV_NAME "tegra-snd-wm8903" diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c index 6be9e0f..8884667 100644 --- a/sound/soc/tegra/trimslice.c +++ b/sound/soc/tegra/trimslice.c @@ -38,9 +38,6 @@
#include "../codecs/tlv320aic23.h"
-#include "tegra_das.h" -#include "tegra_i2s.h" -#include "tegra_pcm.h" #include "tegra_asoc_utils.h"
#define DRV_NAME "tegra-snd-trimslice"
On Fri, Mar 30, 2012 at 05:07:18PM -0600, Stephen Warren wrote:
From: Stephen Warren swarren@nvidia.com
These include aren't needed, and some of the files are about to be renamed.
Applied, thanks.
From: Stephen Warren swarren@nvidia.com
Even though these options are typically only selected by other options, it's still useful to give them a brief description.
Signed-off-by: Stephen Warren swarren@nvidia.com --- sound/soc/tegra/Kconfig | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index ce1b773..634734e 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -5,7 +5,7 @@ config SND_SOC_TEGRA Say Y or M here if you want support for SoC audio on Tegra.
config SND_SOC_TEGRA_I2S - tristate + tristate "Tegra I2S driver" depends on SND_SOC_TEGRA help Say Y or M if you want to add support for codecs attached to the @@ -13,7 +13,7 @@ config SND_SOC_TEGRA_I2S machine drivers to support below.
config SND_SOC_TEGRA_SPDIF - tristate + tristate "Tegra SPDIF driver" depends on SND_SOC_TEGRA default m help
On Fri, Mar 30, 2012 at 05:07:19PM -0600, Stephen Warren wrote:
From: Stephen Warren swarren@nvidia.com
Even though these options are typically only selected by other options, it's still useful to give them a brief description.
Why do you find these short descriptions useful? They'll cause Kconfig to prompt for them and...
- tristate
- tristate "Tegra I2S driver" depends on SND_SOC_TEGRA help Say Y or M if you want to add support for codecs attached to the
...there's already a description in the long text anyway?
On 03/31/2012 01:48 PM, Mark Brown wrote:
On Fri, Mar 30, 2012 at 05:07:19PM -0600, Stephen Warren wrote:
From: Stephen Warren swarren@nvidia.com
Even though these options are typically only selected by other options, it's still useful to give them a brief description.
Why do you find these short descriptions useful? They'll cause Kconfig to prompt for them and...
Honestly, the downstream patches for Tegra30 support that I was cherry-picking had descriptions for the Tegra30 options, so I needed to resolve the discrepancy
When doing that, I figured why hide those options, even if they're typically auto-selected? Perhaps some out-of-tree ASoC machine driver needs those modules built.
Still, I can go either way; do you want me to drop this, and remove the equivalent descriptions from the Tegra30 patches?
- tristate
- tristate "Tegra I2S driver" depends on SND_SOC_TEGRA help Say Y or M if you want to add support for codecs attached to the
...there's already a description in the long text anyway?
On Mon, Apr 02, 2012 at 10:32:31AM -0600, Stephen Warren wrote:
When doing that, I figured why hide those options, even if they're typically auto-selected? Perhaps some out-of-tree ASoC machine driver needs those modules built.
I've never seen anyone actually build out of tree ASoC modules, everyone always patches their machine drivers into their kernel.
Still, I can go either way; do you want me to drop this, and remove the equivalent descriptions from the Tegra30 patches?
Drop them (or fix all other platforms to have descriptions but my preference is to drop).
From: Stephen Warren swarren@nvidia.com
Indent with TABs to be consistent with the rest of the file.
Signed-off-by: Stephen Warren swarren@nvidia.com --- sound/soc/tegra/Kconfig | 14 +++++++------- 1 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 634734e..e42ca2e 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -49,10 +49,10 @@ config SND_SOC_TEGRA_TRIMSLICE 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. + 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.
On Fri, Mar 30, 2012 at 05:07:20PM -0600, Stephen Warren wrote:
From: Stephen Warren swarren@nvidia.com
Indent with TABs to be consistent with the rest of the file.
Applied, thanks.
From: Stephen Warren swarren@nvidia.com
ERROR: trailing whitespace ERROR: code indent should use tabs where possible WARNING: please, no spaces at the start of a line ERROR: "foo * bar" should be "foo *bar"
Signed-off-by: Stephen Warren swarren@nvidia.com --- sound/soc/tegra/tegra_das.h | 2 +- sound/soc/tegra/tegra_i2s.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/sound/soc/tegra/tegra_das.h b/sound/soc/tegra/tegra_das.h index 2c96c7b..1810cd1 100644 --- a/sound/soc/tegra/tegra_das.h +++ b/sound/soc/tegra/tegra_das.h @@ -94,7 +94,7 @@ struct tegra_das { * DAS: Digital audio switch (HW module controlled by this driver) * DAP: Digital audio port (port/pins on Tegra device) * DAC: Digital audio controller (e.g. I2S or AC97 controller elsewhere) - * + * * The Tegra DAS is a mux/cross-bar which can connect each DAP to a specific * DAC, or another DAP. When DAPs are connected, one must be the master and * one the slave. Each DAC allows selection of a specific DAP for input, to diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c index 8aafc53..86f5ca4 100644 --- a/sound/soc/tegra/tegra_i2s.c +++ b/sound/soc/tegra/tegra_i2s.c @@ -148,7 +148,7 @@ static int tegra_i2s_set_fmt(struct snd_soc_dai *dai, return -EINVAL; }
- i2s->reg_ctrl &= ~(TEGRA_I2S_CTRL_BIT_FORMAT_MASK | + i2s->reg_ctrl &= ~(TEGRA_I2S_CTRL_BIT_FORMAT_MASK | TEGRA_I2S_CTRL_LRCK_MASK); switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_A: @@ -182,7 +182,7 @@ static int tegra_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct device *dev = substream->pcm->card->dev; + struct device *dev = substream->pcm->card->dev; struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); u32 reg; int ret, sample_size, srate, i2sclock, bitcnt; @@ -294,7 +294,7 @@ static int tegra_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
static int tegra_i2s_probe(struct snd_soc_dai *dai) { - struct tegra_i2s * i2s = snd_soc_dai_get_drvdata(dai); + struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
dai->capture_dma_data = &i2s->capture_dma_data; dai->playback_dma_data = &i2s->playback_dma_data; @@ -328,7 +328,7 @@ static const struct snd_soc_dai_driver tegra_i2s_dai_template = {
static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev) { - struct tegra_i2s * i2s; + struct tegra_i2s *i2s; struct resource *mem, *memregion, *dmareq; u32 of_dma[2]; u32 dma_ch;
On Fri, Mar 30, 2012 at 05:07:21PM -0600, Stephen Warren wrote:
From: Stephen Warren swarren@nvidia.com
ERROR: trailing whitespace ERROR: code indent should use tabs where possible WARNING: please, no spaces at the start of a line ERROR: "foo * bar" should be "foo *bar"
Applied, thanks.
From: Stephen Warren swarren@nvidia.com
This is mainly for symmetry with a future Tegra30 driver, where the equivalent of the DAS (the AHUB) is useful separately from the I2S driver.
Signed-off-by: Stephen Warren swarren@nvidia.com --- sound/soc/tegra/Kconfig | 9 +++++++++ sound/soc/tegra/Makefile | 2 +- 2 files changed, 10 insertions(+), 1 deletions(-)
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index e42ca2e..87fb90d 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -4,9 +4,18 @@ config SND_SOC_TEGRA help Say Y or M here if you want support for SoC audio on Tegra.
+config SND_SOC_TEGRA_DAS + tristate "Tegra Digital Audio Switch driver" + depends on SND_SOC_TEGRA + help + Say Y or M if you want to add support for the Tegra DAS module. + You will also need to select the individual machine drivers to + support below. + config SND_SOC_TEGRA_I2S tristate "Tegra I2S driver" depends on SND_SOC_TEGRA + select SND_SOC_TEGRA_DAS help Say Y or M if you want to add support for codecs attached to the Tegra I2S interface. You will also need to select the individual diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 8e584b8..6d5d81e 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -6,7 +6,7 @@ snd-soc-tegra-spdif-objs := tegra_spdif.o snd-soc-tegra-utils-objs += tegra_asoc_utils.o
obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o -obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-das.o +obj-$(CONFIG_SND_SOC_TEGRA_DAS) += snd-soc-tegra-das.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o obj-$(CONFIG_SND_SOC_TEGRA_I2S) += snd-soc-tegra-i2s.o obj-$(CONFIG_SND_SOC_TEGRA_SPDIF) += snd-soc-tegra-spdif.o
On Fri, Mar 30, 2012 at 05:07:22PM -0600, Stephen Warren wrote:
From: Stephen Warren swarren@nvidia.com
This is mainly for symmetry with a future Tegra30 driver, where the equivalent of the DAS (the AHUB) is useful separately from the I2S driver.
Applied, thanks.
From: Stephen Warren swarren@nvidia.com
Without this, the Tegra20 drivers can be built into a kernel that's built only for Tegra30.
Signed-off-by: Stephen Warren swarren@nvidia.com --- sound/soc/tegra/Kconfig | 12 ++++++------ 1 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 87fb90d..3aec43d 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -6,7 +6,7 @@ config SND_SOC_TEGRA
config SND_SOC_TEGRA_DAS tristate "Tegra Digital Audio Switch driver" - depends on SND_SOC_TEGRA + depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC help Say Y or M if you want to add support for the Tegra DAS module. You will also need to select the individual machine drivers to @@ -14,7 +14,7 @@ config SND_SOC_TEGRA_DAS
config SND_SOC_TEGRA_I2S tristate "Tegra I2S driver" - depends on SND_SOC_TEGRA + depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC select SND_SOC_TEGRA_DAS help Say Y or M if you want to add support for codecs attached to the @@ -23,7 +23,7 @@ config SND_SOC_TEGRA_I2S
config SND_SOC_TEGRA_SPDIF tristate "Tegra SPDIF driver" - depends on SND_SOC_TEGRA + depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC default m help Say Y or M if you want to add support for the SPDIF interface. @@ -41,7 +41,7 @@ config SND_SOC_TEGRA_WM8903 tristate "SoC Audio support for Tegra boards using a WM8903 codec" depends on SND_SOC_TEGRA && I2C depends on MACH_HAS_SND_SOC_TEGRA_WM8903 - select SND_SOC_TEGRA_I2S + select SND_SOC_TEGRA_I2S if ARCH_TEGRA_2x_SOC select SND_SOC_WM8903 help Say Y or M here if you want to add support for SoC audio on Tegra @@ -51,7 +51,7 @@ config SND_SOC_TEGRA_WM8903 config SND_SOC_TEGRA_TRIMSLICE tristate "SoC Audio support for TrimSlice board" depends on SND_SOC_TEGRA && MACH_TRIMSLICE && I2C - select SND_SOC_TEGRA_I2S + select SND_SOC_TEGRA_I2S if ARCH_TEGRA_2x_SOC select SND_SOC_TLV320AIC23 help Say Y or M here if you want to add support for SoC audio on the @@ -60,7 +60,7 @@ config SND_SOC_TEGRA_TRIMSLICE 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_TEGRA_I2S if ARCH_TEGRA_2x_SOC select SND_SOC_ALC5632 help Say Y or M here if you want to add support for SoC audio on the
From: Stephen Warren swarren@nvidia.com
The DAS, I2S, and SPDIF drivers are Tegra20-specific. Group these together so that when Tegra30-specific equivalents are added later, the file ordering makes sense.
Signed-off-by: Stephen Warren swarren@nvidia.com --- sound/soc/tegra/Makefile | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 6d5d81e..714d360 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -1,13 +1,13 @@ # Tegra platform Support -snd-soc-tegra-das-objs := tegra_das.o snd-soc-tegra-pcm-objs := tegra_pcm.o +snd-soc-tegra-utils-objs += tegra_asoc_utils.o +snd-soc-tegra-das-objs := tegra_das.o snd-soc-tegra-i2s-objs := tegra_i2s.o snd-soc-tegra-spdif-objs := tegra_spdif.o -snd-soc-tegra-utils-objs += tegra_asoc_utils.o
+obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o obj-$(CONFIG_SND_SOC_TEGRA_DAS) += snd-soc-tegra-das.o -obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o obj-$(CONFIG_SND_SOC_TEGRA_I2S) += snd-soc-tegra-i2s.o obj-$(CONFIG_SND_SOC_TEGRA_SPDIF) += snd-soc-tegra-spdif.o
On Fri, Mar 30, 2012 at 05:07:24PM -0600, Stephen Warren wrote:
From: Stephen Warren swarren@nvidia.com
The DAS, I2S, and SPDIF drivers are Tegra20-specific. Group these together so that when Tegra30-specific equivalents are added later, the file ordering makes sense.
Applied, thanks.
From: Stephen Warren swarren@nvidia.com
Tegra30 has some additional clocks that need to be manipulated, names some clocks differently, runs PLLs at different base rates, etc. The utility code needs to handle this.
Signed-off-by: Stephen Warren swarren@nvidia.com --- sound/soc/tegra/tegra_alc5632.c | 3 ++- sound/soc/tegra/tegra_asoc_utils.c | 29 ++++++++++++++++++++++++----- sound/soc/tegra/tegra_asoc_utils.h | 10 ++++++++-- sound/soc/tegra/tegra_wm8903.c | 3 ++- sound/soc/tegra/trimslice.c | 3 ++- 5 files changed, 38 insertions(+), 10 deletions(-)
diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c index 32de700..6151487 100644 --- a/sound/soc/tegra/tegra_alc5632.c +++ b/sound/soc/tegra/tegra_alc5632.c @@ -210,7 +210,8 @@ static __devinit int tegra_alc5632_probe(struct platform_device *pdev)
tegra_alc5632_dai.platform_of_node = tegra_alc5632_dai.cpu_dai_of_node;
- ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev); + ret = tegra_asoc_utils_init(&alc5632->util_data, + TEGRA_ASOC_UTILS_SOC_TEGRA20, &pdev->dev); if (ret) goto err;
diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c index f8428e4..7e5c412 100644 --- a/sound/soc/tegra/tegra_asoc_utils.c +++ b/sound/soc/tegra/tegra_asoc_utils.c @@ -2,7 +2,7 @@ * tegra_asoc_utils.c - Harmony machine ASoC driver * * Author: Stephen Warren swarren@nvidia.com - * Copyright (C) 2010 - NVIDIA, Inc. + * Copyright (C) 2010,2012 - NVIDIA, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -40,7 +40,10 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, case 22050: case 44100: case 88200: - new_baseclock = 56448000; + if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20) + new_baseclock = 56448000; + else + new_baseclock = 564480000; break; case 8000: case 16000: @@ -48,7 +51,10 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, case 48000: case 64000: case 96000: - new_baseclock = 73728000; + if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20) + new_baseclock = 73728000; + else + new_baseclock = 552960000; break; default: return -EINVAL; @@ -78,7 +84,7 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, return err; }
- /* Don't set cdev1 rate; its locked to pll_a_out0 */ + /* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */
err = clk_enable(data->clk_pll_a); if (err) { @@ -106,10 +112,20 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate);
int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, + enum tegra_asoc_utils_soc soc, struct device *dev) { int ret;
+ switch (soc) { + case TEGRA_ASOC_UTILS_SOC_TEGRA20: + case TEGRA_ASOC_UTILS_SOC_TEGRA30: + break; + default: + return -EINVAL; + } + + data->soc = soc; data->dev = dev;
data->clk_pll_a = clk_get_sys(NULL, "pll_a"); @@ -126,7 +142,10 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, goto err_put_pll_a; }
- data->clk_cdev1 = clk_get_sys(NULL, "cdev1"); + if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20) + data->clk_cdev1 = clk_get_sys(NULL, "cdev1"); + else + data->clk_cdev1 = clk_get_sys("extern1", NULL); if (IS_ERR(data->clk_cdev1)) { dev_err(data->dev, "Can't retrieve clk cdev1\n"); ret = PTR_ERR(data->clk_cdev1); diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h index 4818195..5e65641 100644 --- a/sound/soc/tegra/tegra_asoc_utils.h +++ b/sound/soc/tegra/tegra_asoc_utils.h @@ -2,7 +2,7 @@ * tegra_asoc_utils.h - Definitions for Tegra DAS driver * * Author: Stephen Warren swarren@nvidia.com - * Copyright (C) 2010 - NVIDIA, Inc. + * Copyright (C) 2010,2012 - NVIDIA, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -26,8 +26,14 @@ struct clk; struct device;
+enum tegra_asoc_utils_soc { + TEGRA_ASOC_UTILS_SOC_TEGRA20, + TEGRA_ASOC_UTILS_SOC_TEGRA30, +}; + struct tegra_asoc_utils_data { struct device *dev; + enum tegra_asoc_utils_soc soc; struct clk *clk_pll_a; struct clk *clk_pll_a_out0; struct clk *clk_cdev1; @@ -38,8 +44,8 @@ struct tegra_asoc_utils_data { int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, int mclk); int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, + enum tegra_asoc_utils_soc soc, struct device *dev); void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data);
#endif - diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index 0b0df49..d2ac5cc 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -442,7 +442,8 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) } }
- ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); + ret = tegra_asoc_utils_init(&machine->util_data, + TEGRA_ASOC_UTILS_SOC_TEGRA20, &pdev->dev); if (ret) goto err;
diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c index 0fd115e..487c52f 100644 --- a/sound/soc/tegra/trimslice.c +++ b/sound/soc/tegra/trimslice.c @@ -149,7 +149,8 @@ static __devinit int tegra_snd_trimslice_probe(struct platform_device *pdev) goto err; }
- ret = tegra_asoc_utils_init(&trimslice->util_data, &pdev->dev); + ret = tegra_asoc_utils_init(&trimslice->util_data, + TEGRA_ASOC_UTILS_SOC_TEGRA20, &pdev->dev); if (ret) goto err;
On Fri, Mar 30, 2012 at 05:07:27PM -0600, Stephen Warren wrote:
- ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev);
- ret = tegra_asoc_utils_init(&alc5632->util_data,
TEGRA_ASOC_UTILS_SOC_TEGRA20, &pdev->dev);
Would it not be more sensible to do cpu_is_() calls in the utils code rather than have the board say? It'd possibly also end up looking nicer in the code and may mean the compiler can figure out not to include cases that can't be taken in the current build.
On 03/31/2012 02:20 PM, Mark Brown wrote:
On Fri, Mar 30, 2012 at 05:07:27PM -0600, Stephen Warren wrote:
- ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev);
- ret = tegra_asoc_utils_init(&alc5632->util_data, +
TEGRA_ASOC_UTILS_SOC_TEGRA20, &pdev->dev);
Would it not be more sensible to do cpu_is_() calls in the utils code rather than have the board say? It'd possibly also end up looking nicer in the code and may mean the compiler can figure out not to include cases that can't be taken in the current build.
We had floated the idea of adding cpu_is_*() (Well, soc_is_*()) macros for Tegra in the past, and received the message that was a bad idea; things should instead be driven by compatible flag values or similar. See http://www.spinics.net/lists/linux-tegra/msg02250.html. That said, I see many cpu_is_*() calls in arch/arm, so perhaps we should revisit this.
On Sat, Mar 31, 2012 at 07:57:12PM -0600, Stephen Warren wrote:
On 03/31/2012 02:20 PM, Mark Brown wrote:
On Fri, Mar 30, 2012 at 05:07:27PM -0600, Stephen Warren wrote:
- ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev);
- ret = tegra_asoc_utils_init(&alc5632->util_data, +
TEGRA_ASOC_UTILS_SOC_TEGRA20, &pdev->dev);
Would it not be more sensible to do cpu_is_() calls in the utils code rather than have the board say? It'd possibly also end up looking nicer in the code and may mean the compiler can figure out not to include cases that can't be taken in the current build.
We had floated the idea of adding cpu_is_*() (Well, soc_is_*()) macros for Tegra in the past, and received the message that was a bad idea; things should instead be driven by compatible flag values or similar.
Or look at the binding, or something. Having the machine drivers need to translate the bindings into these defines just feels clunky.
See http://www.spinics.net/lists/linux-tegra/msg02250.html. That said, I see many cpu_is_*() calls in arch/arm, so perhaps we should revisit this.
Many of those might get some pushback in this day and age.
On 04/01/2012 04:28 AM, Mark Brown wrote:
On Sat, Mar 31, 2012 at 07:57:12PM -0600, Stephen Warren wrote:
On 03/31/2012 02:20 PM, Mark Brown wrote:
On Fri, Mar 30, 2012 at 05:07:27PM -0600, Stephen Warren wrote:
- ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev);
- ret = tegra_asoc_utils_init(&alc5632->util_data, +
TEGRA_ASOC_UTILS_SOC_TEGRA20, &pdev->dev);
Would it not be more sensible to do cpu_is_() calls in the utils code rather than have the board say? It'd possibly also end up looking nicer in the code and may mean the compiler can figure out not to include cases that can't be taken in the current build.
We had floated the idea of adding cpu_is_*() (Well, soc_is_*()) macros for Tegra in the past, and received the message that was a bad idea; things should instead be driven by compatible flag values or similar.
Or look at the binding, or something. Having the machine drivers need to translate the bindings into these defines just feels clunky.
I guess the utility code can just call of_machine_is_compatible() during probe, and so not require the machine driver to pass the value in. That doesn't feel /too/ gross, and avoids having to export any new soc_is() or similar APIs for Tegra.
Does that seem reasonable to you?
In the longer run, I suspect the clock naming differences that are keyed off this flag to be handled by clock properties in DT, but that relies on common clock bindings being in place first. I'm not sure if it makes sense to put the PLL rate information into the DT too, or not.
See http://www.spinics.net/lists/linux-tegra/msg02250.html. That said, I see many cpu_is_*() calls in arch/arm, so perhaps we should revisit this.
Many of those might get some pushback in this day and age.
On Mon, Apr 02, 2012 at 11:44:36AM -0600, Stephen Warren wrote:
I guess the utility code can just call of_machine_is_compatible() during probe, and so not require the machine driver to pass the value in. That doesn't feel /too/ gross, and avoids having to export any new soc_is() or similar APIs for Tegra.
Does that seem reasonable to you?
I guess, yes.
From: Stephen Warren swarren@nvidia.com
Initialize the audio clock tree appropriately for some reasonable rate. This makes sure the PLLs etc. are actually programmed to something reasonable when the audio driver is loaded.
Signed-off-by: Stephen Warren swarren@nvidia.com --- sound/soc/tegra/tegra_asoc_utils.c | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c index 7e5c412..22397d4 100644 --- a/sound/soc/tegra/tegra_asoc_utils.c +++ b/sound/soc/tegra/tegra_asoc_utils.c @@ -152,8 +152,14 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, goto err_put_pll_a_out0; }
+ ret = tegra_asoc_utils_set_rate(data, 48000, 256 * 48000); + if (ret) + goto err_put_cdev1; + return 0;
+err_put_cdev1: + clk_put(data->clk_cdev1); err_put_pll_a_out0: clk_put(data->clk_pll_a_out0); err_put_pll_a:
On Fri, Mar 30, 2012 at 05:07:28PM -0600, Stephen Warren wrote:
From: Stephen Warren swarren@nvidia.com
Initialize the audio clock tree appropriately for some reasonable rate. This makes sure the PLLs etc. are actually programmed to something reasonable when the audio driver is loaded.
- ret = tegra_asoc_utils_set_rate(data, 48000, 256 * 48000);
Applied, though 48kHz is a somewhat surprising choice for the default CPU audio rate - the overwhelming majority of audio played winds up being 44.1kHz. Still, it's just going to get changed as required.
On 03/31/2012 02:17 PM, Mark Brown wrote:
On Fri, Mar 30, 2012 at 05:07:28PM -0600, Stephen Warren wrote:
From: Stephen Warren swarren@nvidia.com
Initialize the audio clock tree appropriately for some reasonable rate. This makes sure the PLLs etc. are actually programmed to something reasonable when the audio driver is loaded.
- ret = tegra_asoc_utils_set_rate(data, 48000, 256 * 48000);
Applied, though 48kHz is a somewhat surprising choice for the default CPU audio rate - the overwhelming majority of audio played winds up being 44.1kHz. Still, it's just going to get changed as required.
I may as well change this to be consistent with the Tegra-side patch. I note you said "applied", but I don't actually see it in your kernel.org repo.
On Sat, Mar 31, 2012 at 08:07:18PM -0600, Stephen Warren wrote:
On 03/31/2012 02:17 PM, Mark Brown wrote:
Applied, though 48kHz is a somewhat surprising choice for the default CPU audio rate - the overwhelming majority of audio played winds up being 44.1kHz. Still, it's just going to get changed as required.
I may as well change this to be consistent with the Tegra-side patch. I note you said "applied", but I don't actually see it in your kernel.org repo.
It was there, probably forgot to push it out last night. But I just dropped it since you're about to replace it and I just rebased onto -rc1.
From: Stephen Warren swarren@nvidia.com
The AHUB (Audio Hub) is a mux/crossbar which links all audio-related devices except the HDA controller on Tegra30. The devices include the DMA FIFOs, DAM (Digital Audio Mixers), I2S controllers, and SPDIF controller. Audio data may be routed between these devices in various combinations as required by board design/application.
Includes a squashed bugfix from Nikesh Oswal noswal@nvidia.com Includes squashed bugfixes from Sumit Bhattacharya sumitb@nvidia.com
Signed-off-by: Stephen Warren swarren@nvidia.com --- .../devicetree/bindings/sound/tegra30-ahub.txt | 18 + sound/soc/tegra/tegra30_ahub.c | 593 ++++++++++++++++++++ sound/soc/tegra/tegra30_ahub.h | 489 ++++++++++++++++ 3 files changed, 1100 insertions(+), 0 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/tegra30-ahub.txt create mode 100644 sound/soc/tegra/tegra30_ahub.c create mode 100644 sound/soc/tegra/tegra30_ahub.h
diff --git a/Documentation/devicetree/bindings/sound/tegra30-ahub.txt b/Documentation/devicetree/bindings/sound/tegra30-ahub.txt new file mode 100644 index 0000000..06cfabe --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tegra30-ahub.txt @@ -0,0 +1,18 @@ +NVIDIA Tegra30 AHUB (Audio Hub) + +Required properties: +- compatible : "nvidia,tegra30-ahub" +- reg : Should contain the register physical address and length for each of + the AHUB's APBIF registers and the AHUB's own registers. +- interrupts : Should contain AHUB interrupt +- nvidia,dma-request-selector : The Tegra DMA controller's phandle and + request selector for the first APBIF channel. + +Example: + +ahub@70080000 { + compatible = "nvidia,tegra30-ahub"; + reg = <0x70080000 0x80 0x70080200 0x100>; + interrupts = < 0 103 0x04 >; + nvidia,dma-request-selector = <&apbdma 1>; +}; diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c new file mode 100644 index 0000000..067876f --- /dev/null +++ b/sound/soc/tegra/tegra30_ahub.c @@ -0,0 +1,593 @@ +/* + * tegra30_ahub.c - Tegra30 AHUB driver + * + * Copyright (c) 2011,2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see http://www.gnu.org/licenses/. + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/of.h> +#include <mach/clk.h> +#include <mach/dma.h> +#include <mach/iomap.h> +#include <sound/soc.h> +#include "tegra30_ahub.h" + +#define DRV_NAME "tegra30-ahub" + +static struct tegra30_ahub *ahub; +int probed = -EPROBE_DEFER; + +static inline void tegra30_apbif_write(u32 reg, u32 val) +{ + __raw_writel(val, ahub->apbif_regs + reg); +} + +static inline u32 tegra30_apbif_read(u32 reg) +{ + return __raw_readl(ahub->apbif_regs + reg); +} + +static inline void tegra30_audio_write(u32 reg, u32 val) +{ + __raw_writel(val, ahub->audio_regs + reg); +} + +static inline u32 tegra30_audio_read(u32 reg) +{ + return __raw_readl(ahub->audio_regs + reg); +} + +int tegra30_ahub_is_probed(void) +{ + return probed; +} +EXPORT_SYMBOL(tegra30_ahub_is_probed); + +/* + * clk_apbif isn't required for a theoretical I2S<->I2S configuration where + * no PCM data is read from or sent to memory. However, that's an unlikely + * use-case, and not something the rest of the driver supports right now, so + * we'll just treat the two clocks as one for now. + * + * These functions should not be a plain ref-count. Instead, each active stream + * contributes some requirement to the minimum clock rate, so starting or + * stopping streams should dynamically adjust the clock as required. However, + * this is not yet implemented. + */ +void tegra30_ahub_enable_clocks(void) +{ + clk_enable(ahub->clk_d_audio); + clk_enable(ahub->clk_apbif); +} +EXPORT_SYMBOL(tegra30_ahub_enable_clocks); + +void tegra30_ahub_disable_clocks(void) +{ + clk_disable(ahub->clk_apbif); + clk_disable(ahub->clk_d_audio); +} +EXPORT_SYMBOL(tegra30_ahub_disable_clocks); + +#ifdef CONFIG_DEBUG_FS +static inline u32 tegra30_ahub_read(u32 space, u32 reg) +{ + if (space == 0) + return tegra30_apbif_read(reg); + else + return tegra30_audio_read(reg); +} + +static int tegra30_ahub_show(struct seq_file *s, void *unused) +{ +#define REG(space, r) { space, r, 0, 1, #r } +#define ARR(space, r) { space, r, r##_STRIDE, r##_COUNT, #r } + static const struct { + int space; + u32 offset; + u32 stride; + u32 count; + const char *name; + } regs[] = { + ARR(0, TEGRA30_AHUB_CHANNEL_CTRL), + ARR(0, TEGRA30_AHUB_CHANNEL_CLEAR), + ARR(0, TEGRA30_AHUB_CHANNEL_STATUS), + ARR(0, TEGRA30_AHUB_CIF_TX_CTRL), + ARR(0, TEGRA30_AHUB_CIF_RX_CTRL), + REG(0, TEGRA30_AHUB_CONFIG_LINK_CTRL), + REG(0, TEGRA30_AHUB_MISC_CTRL), + REG(0, TEGRA30_AHUB_APBDMA_LIVE_STATUS), + REG(0, TEGRA30_AHUB_I2S_LIVE_STATUS), + ARR(0, TEGRA30_AHUB_DAM_LIVE_STATUS), + REG(0, TEGRA30_AHUB_SPDIF_LIVE_STATUS), + REG(0, TEGRA30_AHUB_I2S_INT_MASK), + REG(0, TEGRA30_AHUB_DAM_INT_MASK), + REG(0, TEGRA30_AHUB_SPDIF_INT_MASK), + REG(0, TEGRA30_AHUB_APBIF_INT_MASK), + REG(0, TEGRA30_AHUB_I2S_INT_STATUS), + REG(0, TEGRA30_AHUB_DAM_INT_STATUS), + REG(0, TEGRA30_AHUB_SPDIF_INT_STATUS), + REG(0, TEGRA30_AHUB_APBIF_INT_STATUS), + REG(0, TEGRA30_AHUB_I2S_INT_SOURCE), + REG(0, TEGRA30_AHUB_DAM_INT_SOURCE), + REG(0, TEGRA30_AHUB_SPDIF_INT_SOURCE), + REG(0, TEGRA30_AHUB_APBIF_INT_SOURCE), + REG(0, TEGRA30_AHUB_I2S_INT_SET), + REG(0, TEGRA30_AHUB_DAM_INT_SET), + REG(0, TEGRA30_AHUB_SPDIF_INT_SET), + REG(0, TEGRA30_AHUB_APBIF_INT_SET), + ARR(1, TEGRA30_AHUB_AUDIO_RX), + }; +#undef ARRG +#undef REG + + int i, j; + + tegra30_ahub_enable_clocks(); + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + if (regs[i].count > 1) { + for (j = 0; j < regs[i].count; j++) { + u32 reg = regs[i].offset + (j * regs[i].stride); + u32 val = tegra30_ahub_read(regs[i].space, reg); + seq_printf(s, "%s[%d] = %08x\n", regs[i].name, + j, val); + } + } else { + u32 val = tegra30_ahub_read(regs[i].space, + regs[i].offset); + seq_printf(s, "%s = %08x\n", regs[i].name, val); + } + } + + tegra30_ahub_disable_clocks(); + + return 0; +} + +static int tegra30_ahub_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, tegra30_ahub_show, inode->i_private); +} + +static const struct file_operations tegra30_ahub_debug_fops = { + .open = tegra30_ahub_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void tegra30_ahub_debug_add(struct tegra30_ahub *ahub) +{ + ahub->debug = debugfs_create_file(DRV_NAME, S_IRUGO, + snd_soc_debugfs_root, ahub, + &tegra30_ahub_debug_fops); +} + +static void tegra30_ahub_debug_remove(struct tegra30_ahub *ahub) +{ + if (ahub->debug) + debugfs_remove(ahub->debug); +} +#else +static inline void tegra30_ahub_debug_add(struct tegra30_ahub *ahub) +{ +} + +static inline void tegra30_ahub_debug_remove(struct tegra30_ahub *ahub) +{ +} +#endif + +int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif, + unsigned long *fiforeg, + unsigned long *reqsel) +{ + int channel; + u32 reg, val; + + channel = find_first_zero_bit(ahub->rx_usage, + TEGRA30_AHUB_CHANNEL_CTRL_COUNT); + if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT) + return -EBUSY; + + __set_bit(channel, ahub->rx_usage); + + *rxcif = TEGRA30_AHUB_RXCIF_APBIF_RX0 + channel; + *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_RXFIFO + + (channel * TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE); + *reqsel = ahub->dma_sel + channel; + + tegra30_ahub_enable_clocks(); + + reg = TEGRA30_AHUB_CHANNEL_CTRL + + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); + val = tegra30_apbif_read(reg); + val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK | + TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK); + val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT) | + TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN | + TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16; + tegra30_apbif_write(reg, val); + + reg = TEGRA30_AHUB_CIF_RX_CTRL + + (channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE); + val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | + (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | + (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | + TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 | + TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 | + TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX; + tegra30_apbif_write(reg, val); + + tegra30_ahub_disable_clocks(); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_rx_fifo); + +int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif) +{ + int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; + int reg, val; + + tegra30_ahub_enable_clocks(); + + reg = TEGRA30_AHUB_CHANNEL_CTRL + + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); + val = tegra30_apbif_read(reg); + val |= TEGRA30_AHUB_CHANNEL_CTRL_RX_EN; + tegra30_apbif_write(reg, val); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_enable_rx_fifo); + +int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif) +{ + int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; + int reg, val; + + reg = TEGRA30_AHUB_CHANNEL_CTRL + + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); + val = tegra30_apbif_read(reg); + val &= ~TEGRA30_AHUB_CHANNEL_CTRL_RX_EN; + tegra30_apbif_write(reg, val); + + tegra30_ahub_disable_clocks(); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_disable_rx_fifo); + +int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif) +{ + int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; + + __clear_bit(channel, ahub->rx_usage); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_free_rx_fifo); + +int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif, + unsigned long *fiforeg, + unsigned long *reqsel) +{ + int channel; + u32 reg, val; + + channel = find_first_zero_bit(ahub->tx_usage, + TEGRA30_AHUB_CHANNEL_CTRL_COUNT); + if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT) + return -EBUSY; + + __set_bit(channel, ahub->tx_usage); + + *txcif = TEGRA30_AHUB_TXCIF_APBIF_TX0 + channel; + *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_TXFIFO + + (channel * TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE); + *reqsel = ahub->dma_sel + channel; + + tegra30_ahub_enable_clocks(); + + reg = TEGRA30_AHUB_CHANNEL_CTRL + + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); + val = tegra30_apbif_read(reg); + val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK | + TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK); + val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT) | + TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN | + TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16; + tegra30_apbif_write(reg, val); + + reg = TEGRA30_AHUB_CIF_TX_CTRL + + (channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE); + val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | + (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | + (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | + TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 | + TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 | + TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX; + tegra30_apbif_write(reg, val); + + tegra30_ahub_disable_clocks(); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_tx_fifo); + +int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif) +{ + int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; + int reg, val; + + tegra30_ahub_enable_clocks(); + + reg = TEGRA30_AHUB_CHANNEL_CTRL + + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); + val = tegra30_apbif_read(reg); + val |= TEGRA30_AHUB_CHANNEL_CTRL_TX_EN; + tegra30_apbif_write(reg, val); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_enable_tx_fifo); + +int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif) +{ + int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; + int reg, val; + + reg = TEGRA30_AHUB_CHANNEL_CTRL + + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); + val = tegra30_apbif_read(reg); + val &= ~TEGRA30_AHUB_CHANNEL_CTRL_TX_EN; + tegra30_apbif_write(reg, val); + + tegra30_ahub_disable_clocks(); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_disable_tx_fifo); + +int tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif) +{ + int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; + + __clear_bit(channel, ahub->tx_usage); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_free_tx_fifo); + +int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif, + enum tegra30_ahub_txcif txcif) +{ + int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; + int reg; + + tegra30_ahub_enable_clocks(); + + reg = TEGRA30_AHUB_AUDIO_RX + + (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); + tegra30_audio_write(reg, 1 << txcif); + + tegra30_ahub_disable_clocks(); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_set_rx_cif_source); + +int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif) +{ + int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; + int reg; + + tegra30_ahub_enable_clocks(); + + reg = TEGRA30_AHUB_AUDIO_RX + + (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); + tegra30_audio_write(reg, 0); + + tegra30_ahub_disable_clocks(); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra30_ahub_unset_rx_cif_source); + +static const char * const configlink_clocks[] __devinitconst = { + "i2s0", + "i2s1", + "i2s2", + "i2s3", + "i2s4", + "dam0", + "dam1", + "dam2", + "spdif_in", +}; + +static int __devinit tegra30_ahub_probe(struct platform_device *pdev) +{ + struct clk *clk; + int i; + struct resource *res0, *res1, *region; + u32 of_dma[2]; + int ret = 0; + + if (ahub) + return -ENODEV; + + /* + * The AHUB hosts a register bus: the "configlink". For this to + * operate correctly, all devices on this bus must be out of reset. + * Ensure that here. Drivers for devices on this bus call + * tegra30_ahub_is_probed() during both their own probe, and because + * those drivers call into numerous tegra30_ahub_*() functions which + * won't operate correctly until the AHUB probe() has completed. + */ + for (i = 0; i < ARRAY_SIZE(configlink_clocks); i++) { + clk = clk_get_sys(NULL, configlink_clocks[i]); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Can't get clock %s\n", + configlink_clocks[i]); + ret = PTR_ERR(clk); + goto err; + } + tegra_periph_reset_deassert(clk); + clk_put(clk); + } + + ahub = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_ahub), + GFP_KERNEL); + if (!ahub) { + dev_err(&pdev->dev, "Can't allocate tegra30_ahub\n"); + ret = -ENOMEM; + goto err; + } + ahub->dev = &pdev->dev; + + ahub->clk_d_audio = clk_get(&pdev->dev, "d_audio"); + if (IS_ERR(ahub->clk_d_audio)) { + dev_err(&pdev->dev, "Can't retrieve ahub d_audio clock\n"); + ret = PTR_ERR(ahub->clk_d_audio); + goto err; + } + + ahub->clk_apbif = clk_get(&pdev->dev, "apbif"); + if (IS_ERR(ahub->clk_apbif)) { + dev_err(&pdev->dev, "Can't retrieve ahub apbif clock\n"); + ret = PTR_ERR(ahub->clk_apbif); + goto err_clk_put_d_audio; + } + + if (of_property_read_u32_array(pdev->dev.of_node, + "nvidia,dma-request-selector", + of_dma, 2) < 0) { + dev_err(&pdev->dev, + "Missing property nvidia,dma-request-selector\n"); + ret = -ENODEV; + goto err_clk_put_d_audio; + } + ahub->dma_sel = of_dma[1]; + + res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res0) { + dev_err(&pdev->dev, "No memory 0 resource\n"); + ret = -ENODEV; + goto err_clk_put_apbif; + } + + region = devm_request_mem_region(&pdev->dev, res0->start, + resource_size(res0), DRV_NAME); + if (!region) { + dev_err(&pdev->dev, "Memory region 0 already claimed\n"); + ret = -EBUSY; + goto err_clk_put_apbif; + } + + ahub->apbif_regs = devm_ioremap(&pdev->dev, res0->start, + resource_size(res0)); + if (!ahub->apbif_regs) { + dev_err(&pdev->dev, "ioremap 0 failed\n"); + ret = -ENOMEM; + goto err_clk_put_apbif; + } + + ahub->apbif_addr = res0->start; + + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res1) { + dev_err(&pdev->dev, "No memory 1 resource\n"); + ret = -ENODEV; + goto err_clk_put_apbif; + } + + region = devm_request_mem_region(&pdev->dev, res1->start, + resource_size(res1), DRV_NAME); + if (!region) { + dev_err(&pdev->dev, "Memory region 1 already claimed\n"); + ret = -EBUSY; + goto err_clk_put_apbif; + } + + ahub->audio_regs = devm_ioremap(&pdev->dev, res1->start, + resource_size(res1)); + if (!ahub->audio_regs) { + dev_err(&pdev->dev, "ioremap 1 failed\n"); + ret = -ENOMEM; + goto err_clk_put_apbif; + } + + tegra30_ahub_debug_add(ahub); + + platform_set_drvdata(pdev, ahub); + + probed = 0; + return 0; + +err_clk_put_apbif: + clk_put(ahub->clk_apbif); +err_clk_put_d_audio: + clk_put(ahub->clk_d_audio); + ahub = 0; +err: + if (ret != -EPROBE_DEFER) + probed = -ENODEV; + return ret; +} + +static int __devexit tegra30_ahub_remove(struct platform_device *pdev) +{ + if (!ahub) + return -ENODEV; + + tegra30_ahub_debug_remove(ahub); + + clk_put(ahub->clk_apbif); + clk_put(ahub->clk_d_audio); + + ahub = 0; + + return 0; +} + +static const struct of_device_id tegra30_ahub_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra30-ahub", }, + {}, +}; + +static struct platform_driver tegra30_ahub_driver = { + .probe = tegra30_ahub_probe, + .remove = __devexit_p(tegra30_ahub_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = tegra30_ahub_of_match, + }, +}; +module_platform_driver(tegra30_ahub_driver); + +MODULE_AUTHOR("Stephen Warren swarren@nvidia.com"); +MODULE_DESCRIPTION("Tegra30 AHUB driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra30_ahub.h b/sound/soc/tegra/tegra30_ahub.h new file mode 100644 index 0000000..4c23cd7 --- /dev/null +++ b/sound/soc/tegra/tegra30_ahub.h @@ -0,0 +1,489 @@ +/* + * tegra30_ahub.h - Definitions for Tegra30 AHUB driver + * + * Copyright (c) 2011,2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see http://www.gnu.org/licenses/. + */ + +#ifndef __TEGRA30_AHUB_H__ +#define __TEGRA30_AHUB_H__ + +/* Fields in *_CIF_RX/TX_CTRL; used by AHUB FIFOs, and all other audio modules */ + +#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT 28 +#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US 0xf +#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK (TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) + +/* Channel count minus 1 */ +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT 24 +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US 7 +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK (TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) + +/* Channel count minus 1 */ +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT 16 +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US 7 +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK (TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) + +#define TEGRA30_AUDIOCIF_BITS_4 0 +#define TEGRA30_AUDIOCIF_BITS_8 1 +#define TEGRA30_AUDIOCIF_BITS_12 2 +#define TEGRA30_AUDIOCIF_BITS_16 3 +#define TEGRA30_AUDIOCIF_BITS_20 4 +#define TEGRA30_AUDIOCIF_BITS_24 5 +#define TEGRA30_AUDIOCIF_BITS_28 6 +#define TEGRA30_AUDIOCIF_BITS_32 7 + +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT 12 +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_MASK (7 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_4 (TEGRA30_AUDIOCIF_BITS_4 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_8 (TEGRA30_AUDIOCIF_BITS_8 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_12 (TEGRA30_AUDIOCIF_BITS_12 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 (TEGRA30_AUDIOCIF_BITS_16 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_20 (TEGRA30_AUDIOCIF_BITS_20 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_24 (TEGRA30_AUDIOCIF_BITS_24 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_28 (TEGRA30_AUDIOCIF_BITS_28 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_32 (TEGRA30_AUDIOCIF_BITS_32 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) + +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT 8 +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_MASK (7 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_4 (TEGRA30_AUDIOCIF_BITS_4 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_8 (TEGRA30_AUDIOCIF_BITS_8 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_12 (TEGRA30_AUDIOCIF_BITS_12 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 (TEGRA30_AUDIOCIF_BITS_16 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_20 (TEGRA30_AUDIOCIF_BITS_20 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_24 (TEGRA30_AUDIOCIF_BITS_24 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_28 (TEGRA30_AUDIOCIF_BITS_28 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_32 (TEGRA30_AUDIOCIF_BITS_32 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) + +#define TEGRA30_AUDIOCIF_EXPAND_ZERO 0 +#define TEGRA30_AUDIOCIF_EXPAND_ONE 1 +#define TEGRA30_AUDIOCIF_EXPAND_LFSR 2 + +#define TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT 6 +#define TEGRA30_AUDIOCIF_CTRL_EXPAND_MASK (3 << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_EXPAND_ZERO (TEGRA30_AUDIOCIF_EXPAND_ZERO << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_EXPAND_ONE (TEGRA30_AUDIOCIF_EXPAND_ONE << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_EXPAND_LFSR (TEGRA30_AUDIOCIF_EXPAND_LFSR << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) + +#define TEGRA30_AUDIOCIF_STEREO_CONV_CH0 0 +#define TEGRA30_AUDIOCIF_STEREO_CONV_CH1 1 +#define TEGRA30_AUDIOCIF_STEREO_CONV_AVG 2 + +#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT 4 +#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_MASK (3 << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_CH0 (TEGRA30_AUDIOCIF_STEREO_CONV_CH0 << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_CH1 (TEGRA30_AUDIOCIF_STEREO_CONV_CH1 << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_AVG (TEGRA30_AUDIOCIF_STEREO_CONV_AVG << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) + +#define TEGRA30_AUDIOCIF_CTRL_REPLICATE 3 + +#define TEGRA30_AUDIOCIF_DIRECTION_TX 0 +#define TEGRA30_AUDIOCIF_DIRECTION_RX 1 + +#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT 2 +#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_MASK (1 << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX (TEGRA30_AUDIOCIF_DIRECTION_TX << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX (TEGRA30_AUDIOCIF_DIRECTION_RX << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) + +#define TEGRA30_AUDIOCIF_TRUNCATE_ROUND 0 +#define TEGRA30_AUDIOCIF_TRUNCATE_CHOP 1 + +#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT 1 +#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_MASK (1 << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_ROUND (TEGRA30_AUDIOCIF_TRUNCATE_ROUND << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_CHOP (TEGRA30_AUDIOCIF_TRUNCATE_CHOP << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) + +#define TEGRA30_AUDIOCIF_MONO_CONV_ZERO 0 +#define TEGRA30_AUDIOCIF_MONO_CONV_COPY 1 + +#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT 0 +#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_MASK (1 << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_ZERO (TEGRA30_AUDIOCIF_MONO_CONV_ZERO << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT) +#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_COPY (TEGRA30_AUDIOCIF_MONO_CONV_COPY << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT) + +/* Registers within TEGRA30_AUDIO_CLUSTER_BASE */ + +/* TEGRA30_AHUB_CHANNEL_CTRL */ + +#define TEGRA30_AHUB_CHANNEL_CTRL 0x0 +#define TEGRA30_AHUB_CHANNEL_CTRL_STRIDE 0x20 +#define TEGRA30_AHUB_CHANNEL_CTRL_COUNT 4 +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_EN (1 << 31) +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_EN (1 << 30) +#define TEGRA30_AHUB_CHANNEL_CTRL_LOOPBACK (1 << 29) + +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT 16 +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK_US 0xff +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK (TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK_US << TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT) + +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT 8 +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK_US 0xff +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK (TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK_US << TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT) + +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN (1 << 6) + +#define TEGRA30_PACK_8_4 2 +#define TEGRA30_PACK_16 3 + +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT 4 +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK_US 3 +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK (TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK_US << TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT) +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_8_4 (TEGRA30_PACK_8_4 << TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT) +#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16 (TEGRA30_PACK_16 << TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT) + +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN (1 << 2) + +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT 0 +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK_US 3 +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK (TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK_US << TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT) +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_8_4 (TEGRA30_PACK_8_4 << TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT) +#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16 (TEGRA30_PACK_16 << TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT) + +/* TEGRA30_AHUB_CHANNEL_CLEAR */ + +#define TEGRA30_AHUB_CHANNEL_CLEAR 0x4 +#define TEGRA30_AHUB_CHANNEL_CLEAR_STRIDE 0x20 +#define TEGRA30_AHUB_CHANNEL_CLEAR_COUNT 4 +#define TEGRA30_AHUB_CHANNEL_CLEAR_TX_SOFT_RESET (1 << 31) +#define TEGRA30_AHUB_CHANNEL_CLEAR_RX_SOFT_RESET (1 << 30) + +/* TEGRA30_AHUB_CHANNEL_STATUS */ + +#define TEGRA30_AHUB_CHANNEL_STATUS 0x8 +#define TEGRA30_AHUB_CHANNEL_STATUS_STRIDE 0x20 +#define TEGRA30_AHUB_CHANNEL_STATUS_COUNT 4 +#define TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_SHIFT 24 +#define TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_MASK_US 0xff +#define TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_MASK (TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_MASK_US << TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_SHIFT) +#define TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_SHIFT 16 +#define TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_MASK_US 0xff +#define TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_MASK (TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_MASK_US << TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_SHIFT) +#define TEGRA30_AHUB_CHANNEL_STATUS_TX_TRIG (1 << 1) +#define TEGRA30_AHUB_CHANNEL_STATUS_RX_TRIG (1 << 0) + +/* TEGRA30_AHUB_CHANNEL_TXFIFO */ + +#define TEGRA30_AHUB_CHANNEL_TXFIFO 0xc +#define TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE 0x20 +#define TEGRA30_AHUB_CHANNEL_TXFIFO_COUNT 4 + +/* TEGRA30_AHUB_CHANNEL_RXFIFO */ + +#define TEGRA30_AHUB_CHANNEL_RXFIFO 0x10 +#define TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE 0x20 +#define TEGRA30_AHUB_CHANNEL_RXFIFO_COUNT 4 + +/* TEGRA30_AHUB_CIF_TX_CTRL */ + +#define TEGRA30_AHUB_CIF_TX_CTRL 0x14 +#define TEGRA30_AHUB_CIF_TX_CTRL_STRIDE 0x20 +#define TEGRA30_AHUB_CIF_TX_CTRL_COUNT 4 +/* Uses field from TEGRA30_AUDIOCIF_CTRL_* */ + +/* TEGRA30_AHUB_CIF_RX_CTRL */ + +#define TEGRA30_AHUB_CIF_RX_CTRL 0x18 +#define TEGRA30_AHUB_CIF_RX_CTRL_STRIDE 0x20 +#define TEGRA30_AHUB_CIF_RX_CTRL_COUNT 4 +/* Uses field from TEGRA30_AUDIOCIF_CTRL_* */ + +/* TEGRA30_AHUB_CONFIG_LINK_CTRL */ + +#define TEGRA30_AHUB_CONFIG_LINK_CTRL 0x80 +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_SHIFT 28 +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_MASK_US 0xf +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_MASK (TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_MASK_US << TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_SHIFT) +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_SHIFT 16 +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_MASK_US 0xfff +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_MASK (TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_MASK_US << TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_SHIFT) +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_SHIFT 4 +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_MASK_US 0xfff +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_MASK (TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_MASK_US << TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_SHIFT) +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_CG_EN (1 << 2) +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_CLEAR_TIMEOUT_CNTR (1 << 1) +#define TEGRA30_AHUB_CONFIG_LINK_CTRL_SOFT_RESET (1 << 0) + +/* TEGRA30_AHUB_MISC_CTRL */ + +#define TEGRA30_AHUB_MISC_CTRL 0x84 +#define TEGRA30_AHUB_MISC_CTRL_AUDIO_ACTIVE (1 << 31) +#define TEGRA30_AHUB_MISC_CTRL_AUDIO_CG_EN (1 << 8) +#define TEGRA30_AHUB_MISC_CTRL_AUDIO_OBS_SEL_SHIFT 0 +#define TEGRA30_AHUB_MISC_CTRL_AUDIO_OBS_SEL_MASK (0x1f << TEGRA30_AHUB_MISC_CTRL_AUDIO_OBS_SEL_SHIFT) + +/* TEGRA30_AHUB_APBDMA_LIVE_STATUS */ + +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS 0x88 +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_RX_CIF_FIFO_FULL (1 << 31) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_TX_CIF_FIFO_FULL (1 << 30) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_RX_CIF_FIFO_FULL (1 << 29) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_TX_CIF_FIFO_FULL (1 << 28) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_RX_CIF_FIFO_FULL (1 << 27) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_TX_CIF_FIFO_FULL (1 << 26) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_RX_CIF_FIFO_FULL (1 << 25) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_TX_CIF_FIFO_FULL (1 << 24) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_RX_CIF_FIFO_EMPTY (1 << 23) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_TX_CIF_FIFO_EMPTY (1 << 22) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_RX_CIF_FIFO_EMPTY (1 << 21) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_TX_CIF_FIFO_EMPTY (1 << 20) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_RX_CIF_FIFO_EMPTY (1 << 19) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_TX_CIF_FIFO_EMPTY (1 << 18) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_RX_CIF_FIFO_EMPTY (1 << 17) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_TX_CIF_FIFO_EMPTY (1 << 16) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_RX_DMA_FIFO_FULL (1 << 15) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_TX_DMA_FIFO_FULL (1 << 14) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_RX_DMA_FIFO_FULL (1 << 13) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_TX_DMA_FIFO_FULL (1 << 12) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_RX_DMA_FIFO_FULL (1 << 11) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_TX_DMA_FIFO_FULL (1 << 10) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_RX_DMA_FIFO_FULL (1 << 9) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_TX_DMA_FIFO_FULL (1 << 8) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_RX_DMA_FIFO_EMPTY (1 << 7) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_TX_DMA_FIFO_EMPTY (1 << 6) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_RX_DMA_FIFO_EMPTY (1 << 5) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_TX_DMA_FIFO_EMPTY (1 << 4) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_RX_DMA_FIFO_EMPTY (1 << 3) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_TX_DMA_FIFO_EMPTY (1 << 2) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_RX_DMA_FIFO_EMPTY (1 << 1) +#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_TX_DMA_FIFO_EMPTY (1 << 0) + +/* TEGRA30_AHUB_I2S_LIVE_STATUS */ + +#define TEGRA30_AHUB_I2S_LIVE_STATUS 0x8c +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_RX_FIFO_FULL (1 << 29) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_TX_FIFO_FULL (1 << 28) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_RX_FIFO_FULL (1 << 27) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_TX_FIFO_FULL (1 << 26) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_RX_FIFO_FULL (1 << 25) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_TX_FIFO_FULL (1 << 24) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_RX_FIFO_FULL (1 << 23) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_TX_FIFO_FULL (1 << 22) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_FULL (1 << 21) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_FULL (1 << 20) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_RX_FIFO_ENABLED (1 << 19) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_TX_FIFO_ENABLED (1 << 18) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_RX_FIFO_ENABLED (1 << 17) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_TX_FIFO_ENABLED (1 << 16) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_RX_FIFO_ENABLED (1 << 15) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_TX_FIFO_ENABLED (1 << 14) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_RX_FIFO_ENABLED (1 << 13) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_TX_FIFO_ENABLED (1 << 12) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_ENABLED (1 << 11) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_ENABLED (1 << 10) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_RX_FIFO_EMPTY (1 << 9) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_TX_FIFO_EMPTY (1 << 8) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_RX_FIFO_EMPTY (1 << 7) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_TX_FIFO_EMPTY (1 << 6) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_RX_FIFO_EMPTY (1 << 5) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_TX_FIFO_EMPTY (1 << 4) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_RX_FIFO_EMPTY (1 << 3) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_TX_FIFO_EMPTY (1 << 2) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_EMPTY (1 << 1) +#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_EMPTY (1 << 0) + +/* TEGRA30_AHUB_DAM0_LIVE_STATUS */ + +#define TEGRA30_AHUB_DAM_LIVE_STATUS 0x90 +#define TEGRA30_AHUB_DAM_LIVE_STATUS_STRIDE 0x8 +#define TEGRA30_AHUB_DAM_LIVE_STATUS_COUNT 3 +#define TEGRA30_AHUB_DAM_LIVE_STATUS_TX_ENABLED (1 << 26) +#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX1_ENABLED (1 << 25) +#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX0_ENABLED (1 << 24) +#define TEGRA30_AHUB_DAM_LIVE_STATUS_TXFIFO_FULL (1 << 15) +#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX1FIFO_FULL (1 << 9) +#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX0FIFO_FULL (1 << 8) +#define TEGRA30_AHUB_DAM_LIVE_STATUS_TXFIFO_EMPTY (1 << 7) +#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX1FIFO_EMPTY (1 << 1) +#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX0FIFO_EMPTY (1 << 0) + +/* TEGRA30_AHUB_SPDIF_LIVE_STATUS */ + +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS 0xa8 +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_TX_ENABLED (1 << 11) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_RX_ENABLED (1 << 10) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_TX_ENABLED (1 << 9) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_RX_ENABLED (1 << 8) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_TXFIFO_FULL (1 << 7) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_RXFIFO_FULL (1 << 6) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_TXFIFO_FULL (1 << 5) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_RXFIFO_FULL (1 << 4) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_TXFIFO_EMPTY (1 << 3) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_RXFIFO_EMPTY (1 << 2) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_TXFIFO_EMPTY (1 << 1) +#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_RXFIFO_EMPTY (1 << 0) + +/* TEGRA30_AHUB_I2S_INT_MASK */ + +#define TEGRA30_AHUB_I2S_INT_MASK 0xb0 + +/* TEGRA30_AHUB_DAM_INT_MASK */ + +#define TEGRA30_AHUB_DAM_INT_MASK 0xb4 + +/* TEGRA30_AHUB_SPDIF_INT_MASK */ + +#define TEGRA30_AHUB_SPDIF_INT_MASK 0xbc + +/* TEGRA30_AHUB_APBIF_INT_MASK */ + +#define TEGRA30_AHUB_APBIF_INT_MASK 0xc0 + +/* TEGRA30_AHUB_I2S_INT_STATUS */ + +#define TEGRA30_AHUB_I2S_INT_STATUS 0xc8 + +/* TEGRA30_AHUB_DAM_INT_STATUS */ + +#define TEGRA30_AHUB_DAM_INT_STATUS 0xcc + +/* TEGRA30_AHUB_SPDIF_INT_STATUS */ + +#define TEGRA30_AHUB_SPDIF_INT_STATUS 0xd4 + +/* TEGRA30_AHUB_APBIF_INT_STATUS */ + +#define TEGRA30_AHUB_APBIF_INT_STATUS 0xd8 + +/* TEGRA30_AHUB_I2S_INT_SOURCE */ + +#define TEGRA30_AHUB_I2S_INT_SOURCE 0xe0 + +/* TEGRA30_AHUB_DAM_INT_SOURCE */ + +#define TEGRA30_AHUB_DAM_INT_SOURCE 0xe4 + +/* TEGRA30_AHUB_SPDIF_INT_SOURCE */ + +#define TEGRA30_AHUB_SPDIF_INT_SOURCE 0xec + +/* TEGRA30_AHUB_APBIF_INT_SOURCE */ + +#define TEGRA30_AHUB_APBIF_INT_SOURCE 0xf0 + +/* TEGRA30_AHUB_I2S_INT_SET */ + +#define TEGRA30_AHUB_I2S_INT_SET 0xf8 + +/* TEGRA30_AHUB_DAM_INT_SET */ + +#define TEGRA30_AHUB_DAM_INT_SET 0xfc + +/* TEGRA30_AHUB_SPDIF_INT_SET */ + +#define TEGRA30_AHUB_SPDIF_INT_SET 0x100 + +/* TEGRA30_AHUB_APBIF_INT_SET */ + +#define TEGRA30_AHUB_APBIF_INT_SET 0x104 + +/* Registers within TEGRA30_AHUB_BASE */ + +#define TEGRA30_AHUB_AUDIO_RX 0x0 +#define TEGRA30_AHUB_AUDIO_RX_STRIDE 0x4 +#define TEGRA30_AHUB_AUDIO_RX_COUNT 17 +/* This register repeats once for each entry in enum tegra30_ahub_rxcif */ +/* The fields in this register are 1 bit per entry in tegra30_ahub_txcif */ + +/* + * Terminology: + * AHUB: Audio Hub; a cross-bar switch between the audio devices: DMA FIFOs, + * I2S controllers, SPDIF controllers, and DAMs. + * XBAR: The core cross-bar component of the AHUB. + * CIF: Client Interface; the HW module connecting an audio device to the + * XBAR. + * DAM: Digital Audio Mixer: A HW module that mixes multiple audio streams, + * possibly including sample-rate conversion. + * + * Each TX CIF transmits data into the XBAR. Each RX CIF can receive audio + * transmitted by a particular TX CIF. + * + * This driver is currently very simplistic; many HW features are not + * exposed; DAMs are not supported, only 16-bit stereo audio is supported, + * etc. + */ + +enum tegra30_ahub_txcif { + TEGRA30_AHUB_TXCIF_APBIF_TX0, + TEGRA30_AHUB_TXCIF_APBIF_TX1, + TEGRA30_AHUB_TXCIF_APBIF_TX2, + TEGRA30_AHUB_TXCIF_APBIF_TX3, + TEGRA30_AHUB_TXCIF_I2S0_TX0, + TEGRA30_AHUB_TXCIF_I2S1_TX0, + TEGRA30_AHUB_TXCIF_I2S2_TX0, + TEGRA30_AHUB_TXCIF_I2S3_TX0, + TEGRA30_AHUB_TXCIF_I2S4_TX0, + TEGRA30_AHUB_TXCIF_DAM0_TX0, + TEGRA30_AHUB_TXCIF_DAM1_TX0, + TEGRA30_AHUB_TXCIF_DAM2_TX0, + TEGRA30_AHUB_TXCIF_SPDIF_TX0, + TEGRA30_AHUB_TXCIF_SPDIF_TX1, +}; + +enum tegra30_ahub_rxcif { + TEGRA30_AHUB_RXCIF_APBIF_RX0, + TEGRA30_AHUB_RXCIF_APBIF_RX1, + TEGRA30_AHUB_RXcIF_APBIF_RX2, + TEGRA30_AHUB_RXCIF_APBIF_RX3, + TEGRA30_AHUB_RXCIF_I2S0_RX0, + TEGRA30_AHUB_RXCIF_I2S1_RX0, + TEGRA30_AHUB_RXCIF_I2S2_RX0, + TEGRA30_AHUB_RXCIF_I2S3_RX0, + TEGRA30_AHUB_RXCIF_I2S4_RX0, + TEGRA30_AHUB_RXCIF_DAM0_RX0, + TEGRA30_AHUB_RXCIF_DAM0_RX1, + TEGRA30_AHUB_RXCIF_DAM1_RX0, + TEGRA30_AHUB_RXCIF_DAM2_RX1, + TEGRA30_AHUB_RXCIF_DAM3_RX0, + TEGRA30_AHUB_RXCIF_DAM3_RX1, + TEGRA30_AHUB_RXCIF_SPDIF_RX0, + TEGRA30_AHUB_RXCIF_SPDIF_RX1, +}; + +extern int tegra30_ahub_is_probed(void); + +extern void tegra30_ahub_enable_clocks(void); +extern void tegra30_ahub_disable_clocks(void); + +extern int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif, + unsigned long *fiforeg, + unsigned long *reqsel); +extern int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif); +extern int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif); +extern int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif); + +extern int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif, + unsigned long *fiforeg, + unsigned long *reqsel); +extern int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif); +extern int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif); +extern int tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif); + +extern int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif, + enum tegra30_ahub_txcif txcif); +extern int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif); + +struct tegra30_ahub { + struct device *dev; + struct clk *clk_d_audio; + struct clk *clk_apbif; + int dma_sel; + resource_size_t apbif_addr; + void __iomem *apbif_regs; + void __iomem *audio_regs; + DECLARE_BITMAP(rx_usage, TEGRA30_AHUB_CHANNEL_CTRL_COUNT); + DECLARE_BITMAP(tx_usage, TEGRA30_AHUB_CHANNEL_CTRL_COUNT); + struct dentry *debug; +}; + +#endif
On Fri, Mar 30, 2012 at 05:07:29PM -0600, Stephen Warren wrote:
+int tegra30_ahub_is_probed(void) +{
- return probed;
+} +EXPORT_SYMBOL(tegra30_ahub_is_probed);
This looks like a fairly clear sign that this should be a CODEC driver, or possibly the DMA driver - if nothing else even if you didn't change anything else about the implementation it'd ensure that you didn't need to open code this probe checking.
All your EXPORT_SYMBOLs should really be EXPORT_SYMBOL_GPL as you're using the ASoC APIs.
+/*
- clk_apbif isn't required for a theoretical I2S<->I2S configuration where
- no PCM data is read from or sent to memory. However, that's an unlikely
- use-case, and not something the rest of the driver supports right now, so
- we'll just treat the two clocks as one for now.
I rather suspect you'll find your system architects view that as a resonable use case for things like voice calls where the audio for the call would go from the baseband to the CODEC or bluetooth without going into main memory. Of course many CODECs can take this path directly allowing the CPU to be powered off so...
On 03/31/2012 02:14 PM, Mark Brown wrote:
On Fri, Mar 30, 2012 at 05:07:29PM -0600, Stephen Warren wrote:
+int tegra30_ahub_is_probed(void) +{
- return probed;
+} +EXPORT_SYMBOL(tegra30_ahub_is_probed);
This looks like a fairly clear sign that this should be a CODEC driver,
Certainly it should be a CODEC in the long-term, and most likely the Tegra20 DAS driver too.
My aim here was to get something basic in place before I looked into all the "SoC DSP" and separate FE/BE stuuf, and how to express the AHUB and DAS as codecs.
or possibly the DMA driver - if nothing else even if you didn't change anything else about the implementation it'd ensure that you didn't need to open code this probe checking.
Maybe just because I haven't looked into this at all, but I'm not sure how this would solve the probing issue; I can see that the machine driver wouldn't (ASoC rather than pdev) probe until the AHUB driver had (pdev) probed, but how would that affect the dependent device probes? I suppose it'd work out if the dependent devices didn't touch HW until their ASoC probes?
All your EXPORT_SYMBOLs should really be EXPORT_SYMBOL_GPL as you're using the ASoC APIs.
Yes, just a think-o (like a typo)
+/*
- clk_apbif isn't required for a theoretical I2S<->I2S configuration where
- no PCM data is read from or sent to memory. However, that's an unlikely
- use-case, and not something the rest of the driver supports right now, so
- we'll just treat the two clocks as one for now.
I rather suspect you'll find your system architects view that as a resonable use case for things like voice calls where the audio for the call would go from the baseband to the CODEC or bluetooth without going into main memory. Of course many CODECs can take this path directly allowing the CPU to be powered off so...
Yes, the roots of this patch were actually written long ago before I was really aware of that use-case. I should probably update the comment.
On Sat, Mar 31, 2012 at 08:04:58PM -0600, Stephen Warren wrote:
On 03/31/2012 02:14 PM, Mark Brown wrote:
This looks like a fairly clear sign that this should be a CODEC driver,
My aim here was to get something basic in place before I looked into all the "SoC DSP" and separate FE/BE stuuf, and how to express the AHUB and DAS as codecs.
The DAS might be more suited to the soc-pcm model given that there's more of a tight coupling between what goes in and what comes out (from what I remember).
or possibly the DMA driver - if nothing else even if you didn't change anything else about the implementation it'd ensure that you didn't need to open code this probe checking.
Maybe just because I haven't looked into this at all, but I'm not sure how this would solve the probing issue; I can see that the machine driver wouldn't (ASoC rather than pdev) probe until the AHUB driver had (pdev) probed, but how would that affect the dependent device probes? I suppose it'd work out if the dependent devices didn't touch HW until their ASoC probes?
Yes, exactly - allocate the resources in the platform driver probes, register with ASoC and then only touch the hardware when ASoC tells you the whole card is ready.
On 04/01/2012 04:31 AM, Mark Brown wrote:
On Sat, Mar 31, 2012 at 08:04:58PM -0600, Stephen Warren wrote:
On 03/31/2012 02:14 PM, Mark Brown wrote:
This looks like a fairly clear sign that this should be a CODEC driver,
My aim here was to get something basic in place before I looked into all the "SoC DSP" and separate FE/BE stuuf, and how to express the AHUB and DAS as codecs.
The DAS might be more suited to the soc-pcm model given that there's more of a tight coupling between what goes in and what comes out (from what I remember).
or possibly the DMA driver - if nothing else even if you didn't change anything else about the implementation it'd ensure that you didn't need to open code this probe checking.
Maybe just because I haven't looked into this at all, but I'm not sure how this would solve the probing issue; I can see that the machine driver wouldn't (ASoC rather than pdev) probe until the AHUB driver had (pdev) probed, but how would that affect the dependent device probes? I suppose it'd work out if the dependent devices didn't touch HW until their ASoC probes?
Yes, exactly - allocate the resources in the platform driver probes, register with ASoC and then only touch the hardware when ASoC tells you the whole card is ready.
Thinking about this more, I guess the AHUB driver should be a bus driver; in HW, it implements a register bus and the I2S, SPDIF, and DAM devices' registers are accessed through that bus. That would solve the probing order issues in a way that's much more directly modeled on the hardware. Relying on the separate pdev and ASoC probes for the child devices seems a little bit like relying on an implementation detail.
Does this sound like a good idea to you?
I imagine doing this through device-tree upstream is fairly easy (just like I2C for example), but haven't looked in detail yet.
The thing that makes me shy away from this is when our downstream kernels want to pick up v3.5, and perhaps don't want to use DT exclusively yet, how would they do the same bus/child thing with platform devices in board files? Perhaps they don't need to, and can just register the AHUB pdev first, and child devices later, and ignore the bus' existence.
Now, whether to represent the AHUB as a codec or not, and whether the machine driver's DT binding needs an AHUB node is an orthogonal issue, I think.
On Mon, Apr 02, 2012 at 11:27:52AM -0600, Stephen Warren wrote:
On 04/01/2012 04:31 AM, Mark Brown wrote:
Yes, exactly - allocate the resources in the platform driver probes, register with ASoC and then only touch the hardware when ASoC tells you the whole card is ready.
Thinking about this more, I guess the AHUB driver should be a bus driver; in HW, it implements a register bus and the I2S, SPDIF, and DAM devices' registers are accessed through that bus. That would solve the probing order issues in a way that's much more directly modeled on the hardware. Relying on the separate pdev and ASoC probes for the child devices seems a little bit like relying on an implementation detail.
Does this sound like a good idea to you?
Yeah, I'd actually written "mfd" in one of the earlier drafts of my original mail but then deleted it as I figured it'd already be that way if it could be supported :/
The thing that makes me shy away from this is when our downstream kernels want to pick up v3.5, and perhaps don't want to use DT exclusively yet, how would they do the same bus/child thing with platform devices in board files? Perhaps they don't need to, and can just register the AHUB pdev first, and child devices later, and ignore the bus' existence.
You can happily specify parent/child relationships with static registrations, just set the parent pointers appropriately and ensure things get registered in the right order.
From: Stephen Warren swarren@nvidia.com
This provides an ASoC DAI interface for Tegra 30's I2S controller.
Includes a squashed bugfix from Sumit Bhattacharya sumitb@nvidia.com
Signed-off-by: Stephen Warren swarren@nvidia.com --- .../devicetree/bindings/sound/tegra30-i2s.txt | 12 + sound/soc/tegra/tegra30_i2s.c | 538 ++++++++++++++++++++ sound/soc/tegra/tegra30_i2s.h | 242 +++++++++ 3 files changed, 792 insertions(+), 0 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/tegra30-i2s.txt create mode 100644 sound/soc/tegra/tegra30_i2s.c create mode 100644 sound/soc/tegra/tegra30_i2s.h
diff --git a/Documentation/devicetree/bindings/sound/tegra30-i2s.txt b/Documentation/devicetree/bindings/sound/tegra30-i2s.txt new file mode 100644 index 0000000..ee0aa8f --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tegra30-i2s.txt @@ -0,0 +1,12 @@ +NVIDIA Tegra30 I2S controller + +Required properties: +- compatible : "nvidia,tegra30-i2s" +- reg : Should contain I2S registers location and length + +Example: + +i2s@70002800 { + compatible = "nvidia,tegra30-i2s"; + reg = <0x70080300 0x100>; +}; diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c new file mode 100644 index 0000000..b8f51c3 --- /dev/null +++ b/sound/soc/tegra/tegra30_i2s.c @@ -0,0 +1,538 @@ +/* + * tegra30_i2s.c - Tegra30 I2S driver + * + * Author: Stephen Warren swarren@nvidia.com + * Copyright (c) 2010-2012, NVIDIA CORPORATION. All rights reserved. + * + * Based on code copyright/by: + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * Scott Peterson speterson@nvidia.com + * + * Copyright (C) 2010 Google, Inc. + * Iliyan Malchev malchev@google.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see http://www.gnu.org/licenses/. + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#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 "tegra30_ahub.h" +#include "tegra30_i2s.h" + +#define DRV_NAME "tegra30-i2s" + +static inline void tegra30_i2s_write(struct tegra30_i2s *i2s, u32 reg, u32 val) +{ + __raw_writel(val, i2s->regs + reg); +} + +static inline u32 tegra30_i2s_read(struct tegra30_i2s *i2s, u32 reg) +{ + return __raw_readl(i2s->regs + reg); +} + +static void tegra30_i2s_enable_clocks(struct tegra30_i2s *i2s) +{ + tegra30_ahub_enable_clocks(); + clk_enable(i2s->clk_i2s); +} + +static void tegra30_i2s_disable_clocks(struct tegra30_i2s *i2s) +{ + clk_disable(i2s->clk_i2s); + tegra30_ahub_disable_clocks(); +} + +#ifdef CONFIG_DEBUG_FS +static int tegra30_i2s_show(struct seq_file *s, void *unused) +{ +#define REG(r) { r, #r } + static const struct { + int offset; + const char *name; + } regs[] = { + REG(TEGRA30_I2S_CTRL), + REG(TEGRA30_I2S_TIMING), + REG(TEGRA30_I2S_OFFSET), + REG(TEGRA30_I2S_CH_CTRL), + REG(TEGRA30_I2S_SLOT_CTRL), + REG(TEGRA30_I2S_CIF_TX_CTRL), + REG(TEGRA30_I2S_CIF_RX_CTRL), + REG(TEGRA30_I2S_FLOWCTL), + REG(TEGRA30_I2S_TX_STEP), + REG(TEGRA30_I2S_FLOW_STATUS), + REG(TEGRA30_I2S_FLOW_TOTAL), + REG(TEGRA30_I2S_FLOW_OVER), + REG(TEGRA30_I2S_FLOW_UNDER), + REG(TEGRA30_I2S_LCOEF_1_4_0), + REG(TEGRA30_I2S_LCOEF_1_4_1), + REG(TEGRA30_I2S_LCOEF_1_4_2), + REG(TEGRA30_I2S_LCOEF_1_4_3), + REG(TEGRA30_I2S_LCOEF_1_4_4), + REG(TEGRA30_I2S_LCOEF_1_4_5), + REG(TEGRA30_I2S_LCOEF_2_4_0), + REG(TEGRA30_I2S_LCOEF_2_4_1), + REG(TEGRA30_I2S_LCOEF_2_4_2), + }; +#undef REG + + struct tegra30_i2s *i2s = s->private; + int i; + + tegra30_i2s_enable_clocks(i2s); + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + u32 val = tegra30_i2s_read(i2s, regs[i].offset); + seq_printf(s, "%s = %08x\n", regs[i].name, val); + } + + tegra30_i2s_disable_clocks(i2s); + + return 0; +} + +static int tegra30_i2s_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, tegra30_i2s_show, inode->i_private); +} + +static const struct file_operations tegra30_i2s_debug_fops = { + .open = tegra30_i2s_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void tegra30_i2s_debug_add(struct tegra30_i2s *i2s) +{ + i2s->debug = debugfs_create_file(i2s->dai.name, S_IRUGO, + snd_soc_debugfs_root, i2s, + &tegra30_i2s_debug_fops); +} + +static void tegra30_i2s_debug_remove(struct tegra30_i2s *i2s) +{ + if (i2s->debug) + debugfs_remove(i2s->debug); +} +#else +static inline void tegra30_i2s_debug_add(struct tegra30_i2s *i2s, int id) +{ +} + +static inline void tegra30_i2s_debug_remove(struct tegra30_i2s *i2s) +{ +} +#endif + +int tegra30_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); + int ret; + + tegra30_i2s_enable_clocks(i2s); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = tegra30_ahub_allocate_tx_fifo(&i2s->txcif, + &i2s->playback_dma_data.addr, + &i2s->playback_dma_data.req_sel); + i2s->playback_dma_data.wrap = 4; + i2s->playback_dma_data.width = 32; + tegra30_ahub_set_rx_cif_source( + TEGRA30_AHUB_RXCIF_I2S0_RX0 + i2s->cif_id, + i2s->txcif); + } else { + ret = tegra30_ahub_allocate_rx_fifo(&i2s->rxcif, + &i2s->capture_dma_data.addr, + &i2s->capture_dma_data.req_sel); + i2s->capture_dma_data.wrap = 4; + i2s->capture_dma_data.width = 32; + tegra30_ahub_set_rx_cif_source(i2s->rxcif, + TEGRA30_AHUB_TXCIF_I2S0_TX0 + i2s->cif_id); + } + + tegra30_i2s_disable_clocks(i2s); + + return ret; +} + +void tegra30_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + tegra30_i2s_enable_clocks(i2s); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + tegra30_ahub_unset_rx_cif_source( + TEGRA30_AHUB_RXCIF_I2S0_RX0 + i2s->cif_id); + tegra30_ahub_free_tx_fifo(i2s->txcif); + } else { + tegra30_ahub_unset_rx_cif_source(i2s->rxcif); + tegra30_ahub_free_rx_fifo(i2s->rxcif); + } + + tegra30_i2s_disable_clocks(i2s); +} + +static int tegra30_i2s_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_MASTER_ENABLE; + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_MASTER_ENABLE; + break; + case SND_SOC_DAIFMT_CBM_CFM: + break; + default: + return -EINVAL; + } + + i2s->reg_ctrl &= ~(TEGRA30_I2S_CTRL_FRAME_FORMAT_MASK | + TEGRA30_I2S_CTRL_LRCK_MASK); + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC; + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_L_LOW; + break; + case SND_SOC_DAIFMT_DSP_B: + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC; + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_R_LOW; + break; + case SND_SOC_DAIFMT_I2S: + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK; + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_L_LOW; + break; + case SND_SOC_DAIFMT_RIGHT_J: + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK; + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_L_LOW; + break; + case SND_SOC_DAIFMT_LEFT_J: + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK; + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_L_LOW; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct device *dev = substream->pcm->card->dev; + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); + u32 val; + int ret, sample_size, srate, i2sclock, bitcnt; + + if (params_channels(params) != 2) + return -EINVAL; + + i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_BIT_SIZE_MASK; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_BIT_SIZE_16; + sample_size = 16; + break; + default: + return -EINVAL; + } + + srate = params_rate(params); + + /* Final "* 2" required by Tegra hardware */ + i2sclock = srate * params_channels(params) * sample_size * 2; + + bitcnt = (i2sclock / (2 * srate)) - 1; + if (bitcnt < 0 || bitcnt > TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US) + return -EINVAL; + + ret = clk_set_rate(i2s->clk_i2s, i2sclock); + if (ret) { + dev_err(dev, "Can't set I2S clock rate: %d\n", ret); + return ret; + } + + tegra30_i2s_enable_clocks(i2s); + + val = bitcnt << TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT; + + if (i2sclock % (2 * srate)) + val |= TEGRA30_I2S_TIMING_NON_SYM_ENABLE; + + tegra30_i2s_write(i2s, TEGRA30_I2S_TIMING, val); + + val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | + (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | + (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | + TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 | + TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + val |= TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX; + tegra30_i2s_write(i2s, TEGRA30_I2S_CIF_RX_CTRL, val); + } else { + val |= TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX; + tegra30_i2s_write(i2s, TEGRA30_I2S_CIF_TX_CTRL, val); + } + + val = (1 << TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT) | + (1 << TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT); + tegra30_i2s_write(i2s, TEGRA30_I2S_OFFSET, val); + + tegra30_i2s_disable_clocks(i2s); + + return 0; +} + +static void tegra30_i2s_start_playback(struct tegra30_i2s *i2s) +{ + tegra30_ahub_enable_tx_fifo(i2s->txcif); + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_XFER_EN_TX; + tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl); +} + +static void tegra30_i2s_stop_playback(struct tegra30_i2s *i2s) +{ + tegra30_ahub_disable_tx_fifo(i2s->txcif); + i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_TX; + tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl); +} + +static void tegra30_i2s_start_capture(struct tegra30_i2s *i2s) +{ + tegra30_ahub_enable_rx_fifo(i2s->rxcif); + i2s->reg_ctrl |= TEGRA30_I2S_CTRL_XFER_EN_RX; + tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl); +} + +static void tegra30_i2s_stop_capture(struct tegra30_i2s *i2s) +{ + tegra30_ahub_disable_rx_fifo(i2s->rxcif); + i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_RX; + tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl); +} + +static int tegra30_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + tegra30_i2s_enable_clocks(i2s); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tegra30_i2s_start_playback(i2s); + else + tegra30_i2s_start_capture(i2s); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tegra30_i2s_stop_playback(i2s); + else + tegra30_i2s_stop_capture(i2s); + tegra30_i2s_disable_clocks(i2s); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tegra30_i2s_probe(struct snd_soc_dai *dai) +{ + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + dai->capture_dma_data = &i2s->capture_dma_data; + dai->playback_dma_data = &i2s->playback_dma_data; + + return 0; +} + +static struct snd_soc_dai_ops tegra30_i2s_dai_ops = { + .startup = tegra30_i2s_startup, + .shutdown = tegra30_i2s_shutdown, + .set_fmt = tegra30_i2s_set_fmt, + .hw_params = tegra30_i2s_hw_params, + .trigger = tegra30_i2s_trigger, +}; + +static const struct snd_soc_dai_driver tegra30_i2s_dai_template = { + .probe = tegra30_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 = &tegra30_i2s_dai_ops, + .symmetric_rates = 1, +}; + +static __devinit int tegra30_i2s_platform_probe(struct platform_device *pdev) +{ + struct tegra30_i2s *i2s; + struct resource *mem, *memregion; + int ret; + + ret = tegra30_ahub_is_probed(); + if (ret < 0) + return ret; + + i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_i2s), GFP_KERNEL); + if (!i2s) { + dev_err(&pdev->dev, "Can't allocate tegra30_i2s\n"); + ret = -ENOMEM; + goto err; + } + dev_set_drvdata(&pdev->dev, i2s); + + i2s->dai = tegra30_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; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No memory resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + + /* + * FIXME: This is gross. This should come from a DT property, but I'd + * like to defer that until we have a better idea of how the AHUB and + * its clients fit into ASoC's BE/FE split and codec support. + * + * Base addreses are 0x70080n00, where n is 3..7 for I2S 0..4. + */ + i2s->cif_id = ((mem->start & 0xf00) >> 8) - 3; + + 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 = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); + if (!i2s->regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_clk_put; + } + + 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_clk_put; + } + + ret = tegra_pcm_platform_register(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); + goto err_unregister_dai; + } + + tegra30_i2s_debug_add(i2s); + + return 0; + +err_unregister_dai: + snd_soc_unregister_dai(&pdev->dev); +err_clk_put: + clk_put(i2s->clk_i2s); +err: + return ret; +} + +static int __devexit tegra30_i2s_platform_remove(struct platform_device *pdev) +{ + struct tegra30_i2s *i2s = dev_get_drvdata(&pdev->dev); + + tegra_pcm_platform_unregister(&pdev->dev); + snd_soc_unregister_dai(&pdev->dev); + + tegra30_i2s_debug_remove(i2s); + + clk_put(i2s->clk_i2s); + + return 0; +} + +static const struct of_device_id tegra30_i2s_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra30-i2s", }, + {}, +}; + +static struct platform_driver tegra30_i2s_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = tegra30_i2s_of_match, + }, + .probe = tegra30_i2s_platform_probe, + .remove = __devexit_p(tegra30_i2s_platform_remove), +}; +module_platform_driver(tegra30_i2s_driver); + +MODULE_AUTHOR("Stephen Warren swarren@nvidia.com"); +MODULE_DESCRIPTION("Tegra30 I2S ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra30_i2s_of_match); diff --git a/sound/soc/tegra/tegra30_i2s.h b/sound/soc/tegra/tegra30_i2s.h new file mode 100644 index 0000000..193e081 --- /dev/null +++ b/sound/soc/tegra/tegra30_i2s.h @@ -0,0 +1,242 @@ +/* + * tegra30_i2s.h - Definitions for Tegra30 I2S driver + * + * Copyright (c) 2011,2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see http://www.gnu.org/licenses/. + */ + +#ifndef __TEGRA30_I2S_H__ +#define __TEGRA30_I2S_H__ + +#include "tegra_pcm.h" + +/* Register offsets from TEGRA30_I2S*_BASE */ + +#define TEGRA30_I2S_CTRL 0x0 +#define TEGRA30_I2S_TIMING 0x4 +#define TEGRA30_I2S_OFFSET 0x08 +#define TEGRA30_I2S_CH_CTRL 0x0c +#define TEGRA30_I2S_SLOT_CTRL 0x10 +#define TEGRA30_I2S_CIF_RX_CTRL 0x14 +#define TEGRA30_I2S_CIF_TX_CTRL 0x18 +#define TEGRA30_I2S_FLOWCTL 0x1c +#define TEGRA30_I2S_TX_STEP 0x20 +#define TEGRA30_I2S_FLOW_STATUS 0x24 +#define TEGRA30_I2S_FLOW_TOTAL 0x28 +#define TEGRA30_I2S_FLOW_OVER 0x2c +#define TEGRA30_I2S_FLOW_UNDER 0x30 +#define TEGRA30_I2S_LCOEF_1_4_0 0x34 +#define TEGRA30_I2S_LCOEF_1_4_1 0x38 +#define TEGRA30_I2S_LCOEF_1_4_2 0x3c +#define TEGRA30_I2S_LCOEF_1_4_3 0x40 +#define TEGRA30_I2S_LCOEF_1_4_4 0x44 +#define TEGRA30_I2S_LCOEF_1_4_5 0x48 +#define TEGRA30_I2S_LCOEF_2_4_0 0x4c +#define TEGRA30_I2S_LCOEF_2_4_1 0x50 +#define TEGRA30_I2S_LCOEF_2_4_2 0x54 + +/* Fields in TEGRA30_I2S_CTRL */ + +#define TEGRA30_I2S_CTRL_XFER_EN_TX (1 << 31) +#define TEGRA30_I2S_CTRL_XFER_EN_RX (1 << 30) +#define TEGRA30_I2S_CTRL_CG_EN (1 << 29) +#define TEGRA30_I2S_CTRL_SOFT_RESET (1 << 28) +#define TEGRA30_I2S_CTRL_TX_FLOWCTL_EN (1 << 27) + +#define TEGRA30_I2S_CTRL_OBS_SEL_SHIFT 24 +#define TEGRA30_I2S_CTRL_OBS_SEL_MASK (7 << TEGRA30_I2S_CTRL_OBS_SEL_SHIFT) + +#define TEGRA30_I2S_FRAME_FORMAT_LRCK 0 +#define TEGRA30_I2S_FRAME_FORMAT_FSYNC 1 + +#define TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT 12 +#define TEGRA30_I2S_CTRL_FRAME_FORMAT_MASK (7 << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT) +#define TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK (TEGRA30_I2S_FRAME_FORMAT_LRCK << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT) +#define TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC (TEGRA30_I2S_FRAME_FORMAT_FSYNC << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT) + +#define TEGRA30_I2S_CTRL_MASTER_ENABLE (1 << 10) + +#define TEGRA30_I2S_LRCK_LEFT_LOW 0 +#define TEGRA30_I2S_LRCK_RIGHT_LOW 1 + +#define TEGRA30_I2S_CTRL_LRCK_SHIFT 9 +#define TEGRA30_I2S_CTRL_LRCK_MASK (1 << TEGRA30_I2S_CTRL_LRCK_SHIFT) +#define TEGRA30_I2S_CTRL_LRCK_L_LOW (TEGRA30_I2S_LRCK_LEFT_LOW << TEGRA30_I2S_CTRL_LRCK_SHIFT) +#define TEGRA30_I2S_CTRL_LRCK_R_LOW (TEGRA30_I2S_LRCK_RIGHT_LOW << TEGRA30_I2S_CTRL_LRCK_SHIFT) + +#define TEGRA30_I2S_CTRL_LPBK_ENABLE (1 << 8) + +#define TEGRA30_I2S_BIT_CODE_LINEAR 0 +#define TEGRA30_I2S_BIT_CODE_ULAW 1 +#define TEGRA30_I2S_BIT_CODE_ALAW 2 + +#define TEGRA30_I2S_CTRL_BIT_CODE_SHIFT 4 +#define TEGRA30_I2S_CTRL_BIT_CODE_MASK (3 << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_CODE_LINEAR (TEGRA30_I2S_BIT_CODE_LINEAR << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_CODE_ULAW (TEGRA30_I2S_BIT_CODE_ULAW << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_CODE_ALAW (TEGRA30_I2S_BIT_CODE_ALAW << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT) + +#define TEGRA30_I2S_BITS_8 1 +#define TEGRA30_I2S_BITS_12 2 +#define TEGRA30_I2S_BITS_16 3 +#define TEGRA30_I2S_BITS_20 4 +#define TEGRA30_I2S_BITS_24 5 +#define TEGRA30_I2S_BITS_28 6 +#define TEGRA30_I2S_BITS_32 7 + +/* Sample container size; see {RX,TX}_MASK field in CH_CTRL below */ +#define TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT 0 +#define TEGRA30_I2S_CTRL_BIT_SIZE_MASK (7 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_SIZE_8 (TEGRA30_I2S_BITS_8 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_SIZE_12 (TEGRA30_I2S_BITS_12 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_SIZE_16 (TEGRA30_I2S_BITS_16 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_SIZE_20 (TEGRA30_I2S_BITS_20 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_SIZE_24 (TEGRA30_I2S_BITS_24 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_SIZE_28 (TEGRA30_I2S_BITS_28 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA30_I2S_CTRL_BIT_SIZE_32 (TEGRA30_I2S_BITS_32 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT) + +/* Fields in TEGRA30_I2S_TIMING */ + +#define TEGRA30_I2S_TIMING_NON_SYM_ENABLE (1 << 12) +#define TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT 0 +#define TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US 0x7fff +#define TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK (TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT) + +/* Fields in TEGRA30_I2S_OFFSET */ + +#define TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT 16 +#define TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_MASK_US 0x7ff +#define TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_MASK (TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_MASK_US << TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT) +#define TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT 0 +#define TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_MASK_US 0x7ff +#define TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_MASK (TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_MASK_US << TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT) + +/* Fields in TEGRA30_I2S_CH_CTRL */ + +/* (FSYNC width - 1) in bit clocks */ +#define TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_SHIFT 24 +#define TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK_US 0xff +#define TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK (TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK_US << TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_SHIFT) + +#define TEGRA30_I2S_HIGHZ_NO 0 +#define TEGRA30_I2S_HIGHZ_YES 1 +#define TEGRA30_I2S_HIGHZ_ON_HALF_BIT_CLK 2 + +#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT 12 +#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_MASK (3 << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT) +#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_NO (TEGRA30_I2S_HIGHZ_NO << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT) +#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_YES (TEGRA30_I2S_HIGHZ_YES << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT) +#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_ON_HALF_BIT_CLK (TEGRA30_I2S_HIGHZ_ON_HALF_BIT_CLK << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT) + +#define TEGRA30_I2S_MSB_FIRST 0 +#define TEGRA30_I2S_LSB_FIRST 1 + +#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT 10 +#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_MASK (1 << TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT) +#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_MSB_FIRST (TEGRA30_I2S_MSB_FIRST << TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT) +#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_LSB_FIRST (TEGRA30_I2S_LSB_FIRST << TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT) +#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT 9 +#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_MASK (1 << TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT) +#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_MSB_FIRST (TEGRA30_I2S_MSB_FIRST << TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT) +#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_LSB_FIRST (TEGRA30_I2S_LSB_FIRST << TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT) + +#define TEGRA30_I2S_POS_EDGE 0 +#define TEGRA30_I2S_NEG_EDGE 1 + +#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT 8 +#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_MASK (1 << TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT) +#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_POS_EDGE (TEGRA30_I2S_POS_EDGE << TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT) +#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_NEG_EDGE (TEGRA30_I2S_NEG_EDGE << TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT) + +/* Sample size is # bits from BIT_SIZE minus this field */ +#define TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_SHIFT 4 +#define TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_MASK_US 7 +#define TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_MASK (TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_MASK_US << TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_SHIFT) + +#define TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_SHIFT 0 +#define TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_MASK_US 7 +#define TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_MASK (TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_MASK_US << TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_SHIFT) + +/* Fields in TEGRA30_I2S_SLOT_CTRL */ + +/* Number of slots in frame, minus 1 */ +#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT 16 +#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK_US 7 +#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK (TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_MASK_US << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_SHIFT) + +/* TDM mode slot enable bitmask */ +#define TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT 8 +#define TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_MASK (0xff << TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT) + +#define TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT 0 +#define TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_MASK (0xff << TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT) + +/* Fields in TEGRA30_I2S_CIF_RX_CTRL */ +/* Uses field from TEGRA30_AUDIOCIF_CTRL_* in tegra30_ahub.h */ + +/* Fields in TEGRA30_I2S_CIF_TX_CTRL */ +/* Uses field from TEGRA30_AUDIOCIF_CTRL_* in tegra30_ahub.h */ + +/* Fields in TEGRA30_I2S_FLOWCTL */ + +#define TEGRA30_I2S_FILTER_LINEAR 0 +#define TEGRA30_I2S_FILTER_QUAD 1 + +#define TEGRA30_I2S_FLOWCTL_FILTER_SHIFT 31 +#define TEGRA30_I2S_FLOWCTL_FILTER_MASK (1 << TEGRA30_I2S_FLOWCTL_FILTER_SHIFT) +#define TEGRA30_I2S_FLOWCTL_FILTER_LINEAR (TEGRA30_I2S_FILTER_LINEAR << TEGRA30_I2S_FLOWCTL_FILTER_SHIFT) +#define TEGRA30_I2S_FLOWCTL_FILTER_QUAD (TEGRA30_I2S_FILTER_QUAD << TEGRA30_I2S_FLOWCTL_FILTER_SHIFT) + +/* Fields in TEGRA30_I2S_TX_STEP */ + +#define TEGRA30_I2S_TX_STEP_SHIFT 0 +#define TEGRA30_I2S_TX_STEP_MASK_US 0xffff +#define TEGRA30_I2S_TX_STEP_MASK (TEGRA30_I2S_TX_STEP_MASK_US << TEGRA30_I2S_TX_STEP_SHIFT) + +/* Fields in TEGRA30_I2S_FLOW_STATUS */ + +#define TEGRA30_I2S_FLOW_STATUS_UNDERFLOW (1 << 31) +#define TEGRA30_I2S_FLOW_STATUS_OVERFLOW (1 << 30) +#define TEGRA30_I2S_FLOW_STATUS_MONITOR_INT_EN (1 << 4) +#define TEGRA30_I2S_FLOW_STATUS_COUNTER_CLR (1 << 3) +#define TEGRA30_I2S_FLOW_STATUS_MONITOR_CLR (1 << 2) +#define TEGRA30_I2S_FLOW_STATUS_COUNTER_EN (1 << 1) +#define TEGRA30_I2S_FLOW_STATUS_MONITOR_EN (1 << 0) + +/* + * There are no fields in TEGRA30_I2S_FLOW_TOTAL, TEGRA30_I2S_FLOW_OVER, + * TEGRA30_I2S_FLOW_UNDER; they are counters taking the whole register. + */ + +/* Fields in TEGRA30_I2S_LCOEF_* */ + +#define TEGRA30_I2S_LCOEF_COEF_SHIFT 0 +#define TEGRA30_I2S_LCOEF_COEF_MASK_US 0xffff +#define TEGRA30_I2S_LCOEF_COEF_MASK (TEGRA30_I2S_LCOEF_COEF_MASK_US << TEGRA30_I2S_LCOEF_COEF_SHIFT) + +struct tegra30_i2s { + struct snd_soc_dai_driver dai; + int cif_id; + struct clk *clk_i2s; + int clk_refs; + enum tegra30_ahub_rxcif rxcif; + struct tegra_pcm_dma_params capture_dma_data; + enum tegra30_ahub_txcif txcif; + struct tegra_pcm_dma_params playback_dma_data; + void __iomem *regs; + struct dentry *debug; + u32 reg_ctrl; +}; + +#endif
On Fri, Mar 30, 2012 at 05:07:30PM -0600, Stephen Warren wrote:
+#ifdef CONFIG_DEBUG_FS +static int tegra30_i2s_show(struct seq_file *s, void *unused) +{
Abstraction please - this is open coded in several of your drivers.
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
ret = tegra30_ahub_allocate_tx_fifo(&i2s->txcif,
&i2s->playback_dma_data.addr,
&i2s->playback_dma_data.req_sel);
i2s->playback_dma_data.wrap = 4;
i2s->playback_dma_data.width = 32;
tegra30_ahub_set_rx_cif_source(
TEGRA30_AHUB_RXCIF_I2S0_RX0 + i2s->cif_id,
i2s->txcif);
So. This is all fairly straightforward, simple and non-invasive for CPU<->I2S streams but obviously it's locking out any loopbacks within the AHUB which is a bit sad.
Looking at this it seems like all that's required is to propagate stream events back up the chain and do the routing, there doesn't seem to be much other interaction between the AHUB and the interfaces?
On 03/31/2012 03:34 PM, Mark Brown wrote:
On Fri, Mar 30, 2012 at 05:07:30PM -0600, Stephen Warren wrote:
+#ifdef CONFIG_DEBUG_FS +static int tegra30_i2s_show(struct seq_file *s, void *unused) +{
Abstraction please - this is open coded in several of your drivers.
There's very little code here; unifying it would require passing the clock enable/disable and register read functions to any common code, which would end up making everything rather more complex. Sometimes there are multiple register read functions for different chunks of the address space. I guess if we did have regmap for MMIO, these debugfs files would pretty much come for free. Is that what you're looking for here?
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
ret = tegra30_ahub_allocate_tx_fifo(&i2s->txcif,
&i2s->playback_dma_data.addr,
&i2s->playback_dma_data.req_sel);
i2s->playback_dma_data.wrap = 4;
i2s->playback_dma_data.width = 32;
tegra30_ahub_set_rx_cif_source(
TEGRA30_AHUB_RXCIF_I2S0_RX0 + i2s->cif_id,
i2s->txcif);
So. This is all fairly straightforward, simple and non-invasive for CPU<->I2S streams but obviously it's locking out any loopbacks within the AHUB which is a bit sad.
Yes, I certainly foresee the way the DAIs interact with AHUB changing when adding more advanced features, such as if representing AHUB as a CODEC.
Our downstream kernels do have some code that sets up loopbacks between I2S modules, and it's all derived from these patches as a starting point. The downstream solution probably isn't upstreamable though; it wasn't written with upstream approaches in mind:-(
Looking at this it seems like all that's required is to propagate stream events back up the chain and do the routing, there doesn't seem to be much other interaction between the AHUB and the interfaces?
I suspect that's pretty much what representing AHUB as a CODEC would turn into, or very similar anyway. Is it OK to get the basic functionality in first and then refactor this, or would you prefer going straight to the complete solution?
On Sat, Mar 31, 2012 at 08:17:10PM -0600, Stephen Warren wrote:
On 03/31/2012 03:34 PM, Mark Brown wrote:
+#ifdef CONFIG_DEBUG_FS +static int tegra30_i2s_show(struct seq_file *s, void *unused) +{
Abstraction please - this is open coded in several of your drivers.
There's very little code here; unifying it would require passing the clock enable/disable and register read functions to any common code, which would end up making everything rather more complex. Sometimes
If you move the clock management into runtime PM that'd provide a convenient abstraction for keeping the clocks and whatever else is needed on without much complexity. There's some other (non-ASoC) drivers that do this, it works pretty well.
there are multiple register read functions for different chunks of the address space. I guess if we did have regmap for MMIO, these debugfs files would pretty much come for free. Is that what you're looking for here?
That's one of the ideas, yes. It'd need some work to make it happen (mainly due to the use of mutexes), or you could mark things that need to be attacked from register context
Looking at this it seems like all that's required is to propagate stream events back up the chain and do the routing, there doesn't seem to be much other interaction between the AHUB and the interfaces?
I suspect that's pretty much what representing AHUB as a CODEC would turn into, or very similar anyway. Is it OK to get the basic
Yes, the above question was aimed at trying to figure out which way of representing the device would work best - looking at this code it seems like a CODEC.
functionality in first and then refactor this, or would you prefer going straight to the complete solution?
It seems best to at least hook things in so machine drivers know about the audio hub (in their DT bindings if nothing else) even if the actual implementation is something like you've got now. That way any churn is limited to the CPU drivers as much as possible.
From: Stephen Warren swarren@nvidia.com
This adds Kconfig options for the Tegra30 AHUB and I2S controller, and updates the Tegra+WM8903 machine driver Kconfig to select those.
Includes a squashed bugfix from Sumit Bhattacharya sumitb@nvidia.com
Signed-off-by: Stephen Warren swarren@nvidia.com --- sound/soc/tegra/Kconfig | 18 ++++++++++++++++++ sound/soc/tegra/Makefile | 4 ++++ 2 files changed, 22 insertions(+), 0 deletions(-)
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 63603d0..cdec4be 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -30,6 +30,23 @@ config SND_SOC_TEGRA20_SPDIF You will also need to select the individual machine drivers to support below.
+config SND_SOC_TEGRA30_AHUB + tristate "Tegra30 Audio Hub driver" + depends on SND_SOC_TEGRA && ARCH_TEGRA_3x_SOC + help + Say Y or M if you want to add support for the Tegra20 AHUB module. + You will also need to select the individual machine drivers to + support below. + +config SND_SOC_TEGRA30_I2S + tristate "Tegra30 I2S driver" + depends on SND_SOC_TEGRA && ARCH_TEGRA_3x_SOC + select SND_SOC_TEGRA30_AHUB + help + Say Y or M if you want to add support for codecs attached to the + Tegra30 I2S interface. You will also need to select the individual + machine drivers to support below. + config MACH_HAS_SND_SOC_TEGRA_WM8903 bool help @@ -42,6 +59,7 @@ config SND_SOC_TEGRA_WM8903 depends on SND_SOC_TEGRA && I2C depends on MACH_HAS_SND_SOC_TEGRA_WM8903 select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC + select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC select SND_SOC_WM8903 help Say Y or M here if you want to add support for SoC audio on Tegra diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 4726b90..98704b4 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -4,12 +4,16 @@ snd-soc-tegra-utils-objs += tegra_asoc_utils.o snd-soc-tegra20-das-objs := tegra20_das.o snd-soc-tegra20-i2s-objs := tegra20_i2s.o snd-soc-tegra20-spdif-objs := tegra20_spdif.o +snd-soc-tegra30-ahub-objs := tegra30_ahub.o +snd-soc-tegra30-i2s-objs := tegra30_i2s.o
obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o obj-$(CONFIG_SND_SOC_TEGRA20_DAS) += snd-soc-tegra20-das.o obj-$(CONFIG_SND_SOC_TEGRA20_I2S) += snd-soc-tegra20-i2s.o obj-$(CONFIG_SND_SOC_TEGRA20_SPDIF) += snd-soc-tegra20-spdif.o +obj-$(CONFIG_SND_SOC_TEGRA30_AHUB) += snd-soc-tegra30-ahub.o +obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o
# Tegra machine Support snd-soc-tegra-wm8903-objs := tegra_wm8903.o
From: Stephen Warren swarren@nvidia.com
Modify the Tegra+WM8903 machine driver to add and document device tree binding enhancements required to feed the Tegra20-vs-Tegra30 flag to the Tegra ASoC utilities.
Signed-off-by: Stephen Warren swarren@nvidia.com --- .../bindings/sound/tegra-audio-wm8903.txt | 8 +++-- sound/soc/tegra/tegra_wm8903.c | 33 +++++++++++++++---- 2 files changed, 31 insertions(+), 10 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/tegra-audio-wm8903.txt b/Documentation/devicetree/bindings/sound/tegra-audio-wm8903.txt index d5b0da8..a8b6900 100644 --- a/Documentation/devicetree/bindings/sound/tegra-audio-wm8903.txt +++ b/Documentation/devicetree/bindings/sound/tegra-audio-wm8903.txt @@ -1,7 +1,9 @@ NVIDIA Tegra audio complex
Required properties: -- compatible : "nvidia,tegra-audio-wm8903" +- compatible : "nvidia,tegra-audio-wm8903" (Tegra20), + "nvidia,tegra20-audio-wm8903" (Tegra20), or + "nvidia,tegra30-audio-wm8903" (Tegra30). - nvidia,model : The user-visible name of this sound complex. - nvidia,audio-routing : A list of the connections between audio components. Each entry is a pair of strings, the first being the connection's sink, @@ -46,8 +48,8 @@ Optional properties: Example:
sound { - compatible = "nvidia,tegra-audio-wm8903-harmony", - "nvidia,tegra-audio-wm8903" + compatible = "nvidia,tegra20-audio-wm8903-harmony", + "nvidia,tegra20-audio-wm8903" nvidia,model = "tegra-wm8903-harmony";
nvidia,audio-routing = diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index d2ac5cc..5574453 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_device.h> #include <linux/of_gpio.h>
#include <mach/tegra_wm8903_pdata.h> @@ -370,8 +371,26 @@ static struct snd_soc_card snd_soc_tegra_wm8903 = { .fully_routed = true, };
+static const struct of_device_id tegra_wm8903_of_match[] __devinitconst = { + { + .compatible = "nvidia,tegra30-audio-wm8903", + .data = (void *)TEGRA_ASOC_UTILS_SOC_TEGRA30 + }, + { + .compatible = "nvidia,tegra-audio-wm8903", + .data = (void *)TEGRA_ASOC_UTILS_SOC_TEGRA20 + }, + { + .compatible = "nvidia,tegra20-audio-wm8903", + .data = (void *)TEGRA_ASOC_UTILS_SOC_TEGRA20 + }, + {}, +}; + static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) { + const struct of_device_id *match; + enum tegra_asoc_utils_soc soc; struct snd_soc_card *card = &snd_soc_tegra_wm8903; struct tegra_wm8903 *machine; int ret; @@ -381,6 +400,12 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) return -EINVAL; }
+ match = of_match_device(tegra_wm8903_of_match, &pdev->dev); + if (match) + soc = (enum tegra_asoc_utils_soc)match->data; + else + soc = TEGRA_ASOC_UTILS_SOC_TEGRA20; + machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm8903), GFP_KERNEL); if (!machine) { @@ -442,8 +467,7 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) } }
- ret = tegra_asoc_utils_init(&machine->util_data, - TEGRA_ASOC_UTILS_SOC_TEGRA20, &pdev->dev); + ret = tegra_asoc_utils_init(&machine->util_data, soc, &pdev->dev); if (ret) goto err;
@@ -489,11 +513,6 @@ static int __devexit tegra_wm8903_driver_remove(struct platform_device *pdev) 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,
On Fri, Mar 30, 2012 at 05:07:15PM -0600, Stephen Warren wrote:
From: Stephen Warren swarren@nvidia.com
This series adds basic support for 16-bit stereo on Tegra30. This will be enabled for the Cardhu board in a separate series to arch/arm/mach-tegra.
Applied, thanks.
On 03/31/2012 01:44 PM, Mark Brown wrote:
On Fri, Mar 30, 2012 at 05:07:15PM -0600, Stephen Warren wrote:
From: Stephen Warren swarren@nvidia.com
This series adds basic support for 16-bit stereo on Tegra30. This will be enabled for the Cardhu board in a separate series to arch/arm/mach-tegra.
Applied, thanks.
I assume this "applied" refers to PATCH 01/17 not PATCH 00? Looks that way in your git tree at least.
participants (2)
-
Mark Brown
-
Stephen Warren