From c448303e86c970cca4833bd9480c08f09b948b40 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 16 Mar 2012 16:56:44 +0800 Subject: ASoC: fsl: add imx-sgtl5000 machine driver This is the initial imx-sgtl5000 machine driver support with only playback dai link implemented. More features can be added on top of it later. It's a device tree only machine driver working with fsl_ssi driver. Signed-off-by: Shawn Guo Acked-by: Sascha Hauer Acked-by: Timur Tabi Signed-off-by: Mark Brown --- .../bindings/sound/imx-audio-sgtl5000.txt | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt new file mode 100644 index 000000000000..421a374790b3 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt @@ -0,0 +1,24 @@ +Freescale i.MX audio complex with SGTL5000 codec + +Required properties: +- compatible : "fsl,imx-audio-sgtl5000" +- model : The user-visible name of this sound complex +- ssi-controller : The phandle of the i.MX SSI controller +- audio-codec : The phandle of the SGTL5000 audio codec +- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX) +- mux-ext-port : The external port of the i.MX audio muxer + +Note: The AUDMUX port numbering should start at 1, which is consistent with +hardware manual. + +Example: + +sound { + compatible = "fsl,imx51-babbage-sgtl5000", + "fsl,imx-audio-sgtl5000"; + model = "imx51-babbage-sgtl5000"; + ssi-controller = <&ssi1>; + audio-codec = <&sgtl5000>; + mux-int-port = <1>; + mux-ext-port = <3>; +}; -- cgit v1.2.3-59-g8ed1b From 172b4c5c8afdb7471d9b03fc96a6b6455a49e19d Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 30 Mar 2012 00:13:03 +0800 Subject: ASoC: fsl: add audio routing for imx-sgtl5000 Add DAPM widgets and audio routing support for imx-sgtl5000 machine driver. Signed-off-by: Shawn Guo Signed-off-by: Mark Brown --- .../bindings/sound/imx-audio-sgtl5000.txt | 25 ++++++++++++++++++++++ sound/soc/fsl/imx-sgtl5000.c | 13 +++++++++++ 2 files changed, 38 insertions(+) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt index 421a374790b3..d09b4e31461c 100644 --- a/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt +++ b/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt @@ -5,6 +5,27 @@ Required properties: - model : The user-visible name of this sound complex - ssi-controller : The phandle of the i.MX SSI controller - audio-codec : The phandle of the SGTL5000 audio codec +- audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names could be power + supplies, SGTL5000 pins, and the jacks on the board: + + Power supplies: + * Mic Bias + + SGTL5000 pins: + * MIC_IN + * LINE_IN + * HP_OUT + * LINE_OUT + + Board connectors: + * Mic Jack + * Line In Jack + * Headphone Jack + * Line Out Jack + * Ext Spk + - mux-int-port : The internal port of the i.MX audio muxer (AUDMUX) - mux-ext-port : The external port of the i.MX audio muxer @@ -19,6 +40,10 @@ sound { model = "imx51-babbage-sgtl5000"; ssi-controller = <&ssi1>; audio-codec = <&sgtl5000>; + audio-routing = + "Mic Jack", "Mic Bias", + "MIC_IN", "Mic Bias", + "Headphone Jack", "HP_OUT"; mux-int-port = <1>; mux-ext-port = <3>; }; diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index 3786b61b4fea..e1a7441ec133 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -45,6 +45,14 @@ static int imx_sgtl5000_dai_init(struct snd_soc_pcm_runtime *rtd) return 0; } +static const struct snd_soc_dapm_widget imx_sgtl5000_dapm_widgets[] = { + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_LINE("Line In Jack", NULL), + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Line Out Jack", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), +}; + static int __devinit imx_sgtl5000_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -127,10 +135,15 @@ static int __devinit imx_sgtl5000_probe(struct platform_device *pdev) data->card.dev = &pdev->dev; ret = snd_soc_of_parse_card_name(&data->card, "model"); + if (ret) + return ret; + ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); if (ret) return ret; data->card.num_links = 1; data->card.dai_link = &data->dai; + data->card.dapm_widgets = imx_sgtl5000_dapm_widgets; + data->card.num_dapm_widgets = ARRAY_SIZE(imx_sgtl5000_dapm_widgets); ret = snd_soc_register_card(&data->card); if (ret) { -- cgit v1.2.3-59-g8ed1b From be944d42ccc125f1b200e7a4185af5bb87865190 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 10 Apr 2012 16:31:59 -0600 Subject: ASoC: tegra: add tegra30-ahub driver 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 Includes squashed bugfixes from Sumit Bhattacharya Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- .../bindings/sound/nvidia,tegra30-ahub.txt | 32 ++ sound/soc/tegra/tegra30_ahub.c | 631 +++++++++++++++++++++ sound/soc/tegra/tegra30_ahub.h | 483 ++++++++++++++++ 3 files changed, 1146 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra30-ahub.txt create mode 100644 sound/soc/tegra/tegra30_ahub.c create mode 100644 sound/soc/tegra/tegra30_ahub.h (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra30-ahub.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra30-ahub.txt new file mode 100644 index 000000000000..1ac7b1642186 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra30-ahub.txt @@ -0,0 +1,32 @@ +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. +- ranges : The bus address mapping for the configlink register bus. + Can be empty since the mapping is 1:1. +- #address-cells : For the configlink bus. Should be <1>; +- #size-cells : For the configlink bus. Should be <1>. + +AHUB client modules need to specify the IDs of their CIFs (Client InterFaces). +For RX CIFs, the numbers indicate the register number within AHUB routing +register space (APBIF 0..3 RX, I2S 0..5 RX, DAM 0..2 RX 0..1, SPDIF RX 0..1). +For TX CIFs, the numbers indicate the bit position within the AHUB routing +registers (APBIF 0..3 TX, I2S 0..5 TX, DAM 0..2 TX, SPDIF TX 0..1). + +Example: + +ahub@70080000 { + compatible = "nvidia,tegra30-ahub"; + reg = <0x70080000 0x200 0x70080200 0x100>; + interrupts = < 0 103 0x04 >; + nvidia,dma-request-selector = <&apbdma 1>; + + ranges; + #address-cells = <1>; + #size-cells = <1>; +}; diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c new file mode 100644 index 000000000000..57cd419f743e --- /dev/null +++ b/sound/soc/tegra/tegra30_ahub.c @@ -0,0 +1,631 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tegra30_ahub.h" + +#define DRV_NAME "tegra30-ahub" + +static struct tegra30_ahub *ahub; + +static inline void tegra30_apbif_write(u32 reg, u32 val) +{ + regmap_write(ahub->regmap_apbif, reg, val); +} + +static inline u32 tegra30_apbif_read(u32 reg) +{ + u32 val; + regmap_read(ahub->regmap_apbif, reg, &val); + return val; +} + +static inline void tegra30_audio_write(u32 reg, u32 val) +{ + regmap_write(ahub->regmap_ahub, reg, val); +} + +static int tegra30_ahub_runtime_suspend(struct device *dev) +{ + regcache_cache_only(ahub->regmap_apbif, true); + regcache_cache_only(ahub->regmap_ahub, true); + + clk_disable(ahub->clk_apbif); + clk_disable(ahub->clk_d_audio); + + return 0; +} + +/* + * clk_apbif isn't required for an I2S<->I2S configuration where no PCM data + * is read from or sent to memory. However, that's 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. + */ +static int tegra30_ahub_runtime_resume(struct device *dev) +{ + int ret; + + ret = clk_enable(ahub->clk_d_audio); + if (ret) { + dev_err(dev, "clk_enable d_audio failed: %d\n", ret); + return ret; + } + ret = clk_enable(ahub->clk_apbif); + if (ret) { + dev_err(dev, "clk_enable apbif failed: %d\n", ret); + clk_disable(ahub->clk_d_audio); + return ret; + } + + regcache_cache_only(ahub->regmap_apbif, false); + regcache_cache_only(ahub->regmap_ahub, false); + + return 0; +} + +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; + + 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); + + 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; + + 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); + + 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; + + 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); + + 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; + + 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); + + 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; + + reg = TEGRA30_AHUB_AUDIO_RX + + (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); + tegra30_audio_write(reg, 1 << txcif); + + 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; + + reg = TEGRA30_AHUB_AUDIO_RX + + (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); + tegra30_audio_write(reg, 0); + + 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", +}; + +struct of_dev_auxdata ahub_auxdata[] __devinitdata = { + OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080300, "tegra30-i2s.0", NULL), + OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080400, "tegra30-i2s.1", NULL), + OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080500, "tegra30-i2s.2", NULL), + OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080600, "tegra30-i2s.3", NULL), + OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080700, "tegra30-i2s.4", NULL), + {} +}; + +#define LAST_REG(name) \ + (TEGRA30_AHUB_##name + \ + (TEGRA30_AHUB_##name##_STRIDE * TEGRA30_AHUB_##name##_COUNT) - 4) + +#define REG_IN_ARRAY(reg, name) \ + ((reg >= TEGRA30_AHUB_##name) && \ + (reg <= LAST_REG(name) && \ + (!((reg - TEGRA30_AHUB_##name) % TEGRA30_AHUB_##name##_STRIDE)))) + +static bool tegra30_ahub_apbif_wr_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA30_AHUB_CONFIG_LINK_CTRL: + case TEGRA30_AHUB_MISC_CTRL: + case TEGRA30_AHUB_APBDMA_LIVE_STATUS: + case TEGRA30_AHUB_I2S_LIVE_STATUS: + case TEGRA30_AHUB_SPDIF_LIVE_STATUS: + case TEGRA30_AHUB_I2S_INT_MASK: + case TEGRA30_AHUB_DAM_INT_MASK: + case TEGRA30_AHUB_SPDIF_INT_MASK: + case TEGRA30_AHUB_APBIF_INT_MASK: + case TEGRA30_AHUB_I2S_INT_STATUS: + case TEGRA30_AHUB_DAM_INT_STATUS: + case TEGRA30_AHUB_SPDIF_INT_STATUS: + case TEGRA30_AHUB_APBIF_INT_STATUS: + case TEGRA30_AHUB_I2S_INT_SOURCE: + case TEGRA30_AHUB_DAM_INT_SOURCE: + case TEGRA30_AHUB_SPDIF_INT_SOURCE: + case TEGRA30_AHUB_APBIF_INT_SOURCE: + case TEGRA30_AHUB_I2S_INT_SET: + case TEGRA30_AHUB_DAM_INT_SET: + case TEGRA30_AHUB_SPDIF_INT_SET: + case TEGRA30_AHUB_APBIF_INT_SET: + return true; + default: + break; + }; + + if (REG_IN_ARRAY(reg, CHANNEL_CTRL) || + REG_IN_ARRAY(reg, CHANNEL_CLEAR) || + REG_IN_ARRAY(reg, CHANNEL_STATUS) || + REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || + REG_IN_ARRAY(reg, CHANNEL_RXFIFO) || + REG_IN_ARRAY(reg, CIF_TX_CTRL) || + REG_IN_ARRAY(reg, CIF_RX_CTRL) || + REG_IN_ARRAY(reg, DAM_LIVE_STATUS)) + return true; + + return false; +} + +static bool tegra30_ahub_apbif_volatile_reg(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case TEGRA30_AHUB_CONFIG_LINK_CTRL: + case TEGRA30_AHUB_MISC_CTRL: + case TEGRA30_AHUB_APBDMA_LIVE_STATUS: + case TEGRA30_AHUB_I2S_LIVE_STATUS: + case TEGRA30_AHUB_SPDIF_LIVE_STATUS: + case TEGRA30_AHUB_I2S_INT_STATUS: + case TEGRA30_AHUB_DAM_INT_STATUS: + case TEGRA30_AHUB_SPDIF_INT_STATUS: + case TEGRA30_AHUB_APBIF_INT_STATUS: + case TEGRA30_AHUB_I2S_INT_SET: + case TEGRA30_AHUB_DAM_INT_SET: + case TEGRA30_AHUB_SPDIF_INT_SET: + case TEGRA30_AHUB_APBIF_INT_SET: + return true; + default: + break; + }; + + if (REG_IN_ARRAY(reg, CHANNEL_CLEAR) || + REG_IN_ARRAY(reg, CHANNEL_STATUS) || + REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || + REG_IN_ARRAY(reg, CHANNEL_RXFIFO) || + REG_IN_ARRAY(reg, DAM_LIVE_STATUS)) + return true; + + return false; +} + +static bool tegra30_ahub_apbif_precious_reg(struct device *dev, + unsigned int reg) +{ + if (REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || + REG_IN_ARRAY(reg, CHANNEL_RXFIFO)) + return true; + + return false; +} + +static const struct regmap_config tegra30_ahub_apbif_regmap_config = { + .name = "apbif", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = TEGRA30_AHUB_APBIF_INT_SET, + .writeable_reg = tegra30_ahub_apbif_wr_rd_reg, + .readable_reg = tegra30_ahub_apbif_wr_rd_reg, + .volatile_reg = tegra30_ahub_apbif_volatile_reg, + .precious_reg = tegra30_ahub_apbif_precious_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static bool tegra30_ahub_ahub_wr_rd_reg(struct device *dev, unsigned int reg) +{ + if (REG_IN_ARRAY(reg, AUDIO_RX)) + return true; + + return false; +} + +static const struct regmap_config tegra30_ahub_ahub_regmap_config = { + .name = "ahub", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = LAST_REG(AUDIO_RX), + .writeable_reg = tegra30_ahub_ahub_wr_rd_reg, + .readable_reg = tegra30_ahub_ahub_wr_rd_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static int __devinit tegra30_ahub_probe(struct platform_device *pdev) +{ + struct clk *clk; + int i; + struct resource *res0, *res1, *region; + u32 of_dma[2]; + void __iomem *regs_apbif, *regs_ahub; + 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. + */ + 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; + } + dev_set_drvdata(&pdev->dev, ahub); + + 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 apbif memory 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, "request region apbif failed\n"); + ret = -EBUSY; + goto err_clk_put_apbif; + } + ahub->apbif_addr = res0->start; + + regs_apbif = devm_ioremap(&pdev->dev, res0->start, + resource_size(res0)); + if (!regs_apbif) { + dev_err(&pdev->dev, "ioremap apbif failed\n"); + ret = -ENOMEM; + goto err_clk_put_apbif; + } + + ahub->regmap_apbif = devm_regmap_init_mmio(&pdev->dev, regs_apbif, + &tegra30_ahub_apbif_regmap_config); + if (IS_ERR(ahub->regmap_apbif)) { + dev_err(&pdev->dev, "apbif regmap init failed\n"); + ret = PTR_ERR(ahub->regmap_apbif); + goto err_clk_put_apbif; + } + regcache_cache_only(ahub->regmap_apbif, true); + + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res1) { + dev_err(&pdev->dev, "No ahub memory 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, "request region ahub failed\n"); + ret = -EBUSY; + goto err_clk_put_apbif; + } + + regs_ahub = devm_ioremap(&pdev->dev, res1->start, + resource_size(res1)); + if (!regs_ahub) { + dev_err(&pdev->dev, "ioremap ahub failed\n"); + ret = -ENOMEM; + goto err_clk_put_apbif; + } + + ahub->regmap_ahub = devm_regmap_init_mmio(&pdev->dev, regs_ahub, + &tegra30_ahub_ahub_regmap_config); + if (IS_ERR(ahub->regmap_ahub)) { + dev_err(&pdev->dev, "ahub regmap init failed\n"); + ret = PTR_ERR(ahub->regmap_ahub); + goto err_clk_put_apbif; + } + regcache_cache_only(ahub->regmap_ahub, true); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = tegra30_ahub_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + of_platform_populate(pdev->dev.of_node, NULL, ahub_auxdata, + &pdev->dev); + + return 0; + +err_pm_disable: + pm_runtime_disable(&pdev->dev); +err_clk_put_apbif: + clk_put(ahub->clk_apbif); +err_clk_put_d_audio: + clk_put(ahub->clk_d_audio); + ahub = 0; +err: + return ret; +} + +static int __devexit tegra30_ahub_remove(struct platform_device *pdev) +{ + if (!ahub) + return -ENODEV; + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra30_ahub_runtime_suspend(&pdev->dev); + + 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 const struct dev_pm_ops tegra30_ahub_pm_ops __devinitconst = { + SET_RUNTIME_PM_OPS(tegra30_ahub_runtime_suspend, + tegra30_ahub_runtime_resume, NULL) +}; + +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, + .pm = &tegra30_ahub_pm_ops, + }, +}; +module_platform_driver(tegra30_ahub_driver); + +MODULE_AUTHOR("Stephen Warren "); +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 000000000000..e690e2eecc92 --- /dev/null +++ b/sound/soc/tegra/tegra30_ahub.h @@ -0,0 +1,483 @@ +/* + * 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 . + */ + +#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_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; + struct regmap *regmap_apbif; + struct regmap *regmap_ahub; + DECLARE_BITMAP(rx_usage, TEGRA30_AHUB_CHANNEL_CTRL_COUNT); + DECLARE_BITMAP(tx_usage, TEGRA30_AHUB_CHANNEL_CTRL_COUNT); +}; + +#endif -- cgit v1.2.3-59-g8ed1b From 4fb0384f3dc68da10cf3f134c45efc6ab14f71df Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 10 Apr 2012 16:32:00 -0600 Subject: ASoC: tegra: add tegra30-i2s driver This provides an ASoC DAI interface for Tegra 30's I2S controller. Includes a squashed bugfix from Sumit Bhattacharya Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- .../bindings/sound/nvidia,tegra30-i2s.txt | 15 + sound/soc/tegra/tegra30_i2s.c | 536 +++++++++++++++++++++ sound/soc/tegra/tegra30_i2s.h | 242 ++++++++++ 3 files changed, 793 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra30-i2s.txt create mode 100644 sound/soc/tegra/tegra30_i2s.c create mode 100644 sound/soc/tegra/tegra30_i2s.h (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra30-i2s.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra30-i2s.txt new file mode 100644 index 000000000000..dfa6c037124a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra30-i2s.txt @@ -0,0 +1,15 @@ +NVIDIA Tegra30 I2S controller + +Required properties: +- compatible : "nvidia,tegra30-i2s" +- reg : Should contain I2S registers location and length +- nvidia,ahub-cif-ids : The list of AHUB CIF IDs for this port, rx (playback) + first, tx (capture) second. See nvidia,tegra30-ahub.txt for values. + +Example: + +i2s@70002800 { + compatible = "nvidia,tegra30-i2s"; + reg = <0x70080300 0x100>; + nvidia,ahub-cif-ids = <4 4>; +}; diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c new file mode 100644 index 000000000000..8596032985dc --- /dev/null +++ b/sound/soc/tegra/tegra30_i2s.c @@ -0,0 +1,536 @@ +/* + * tegra30_i2s.c - Tegra30 I2S driver + * + * Author: Stephen Warren + * Copyright (c) 2010-2012, NVIDIA CORPORATION. All rights reserved. + * + * Based on code copyright/by: + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * Scott Peterson + * + * Copyright (C) 2010 Google, Inc. + * Iliyan Malchev + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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) +{ + regmap_write(i2s->regmap, reg, val); +} + +static inline u32 tegra30_i2s_read(struct tegra30_i2s *i2s, u32 reg) +{ + u32 val; + regmap_read(i2s->regmap, reg, &val); + return val; +} + +static int tegra30_i2s_runtime_suspend(struct device *dev) +{ + struct tegra30_i2s *i2s = dev_get_drvdata(dev); + + regcache_cache_only(i2s->regmap, true); + + clk_disable(i2s->clk_i2s); + + return 0; +} + +static int tegra30_i2s_runtime_resume(struct device *dev) +{ + struct tegra30_i2s *i2s = dev_get_drvdata(dev); + int ret; + + ret = clk_enable(i2s->clk_i2s); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + + regcache_cache_only(i2s->regmap, false); + + return 0; +} + +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; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = tegra30_ahub_allocate_tx_fifo(&i2s->playback_fifo_cif, + &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(i2s->playback_i2s_cif, + i2s->playback_fifo_cif); + } else { + ret = tegra30_ahub_allocate_rx_fifo(&i2s->capture_fifo_cif, + &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->capture_fifo_cif, + i2s->capture_i2s_cif); + } + + 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); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + tegra30_ahub_unset_rx_cif_source(i2s->playback_i2s_cif); + tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif); + } else { + tegra30_ahub_unset_rx_cif_source(i2s->capture_fifo_cif); + tegra30_ahub_free_rx_fifo(i2s->capture_fifo_cif); + } +} + +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; + } + + 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); + + return 0; +} + +static void tegra30_i2s_start_playback(struct tegra30_i2s *i2s) +{ + tegra30_ahub_enable_tx_fifo(i2s->playback_fifo_cif); + 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->playback_fifo_cif); + 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->capture_fifo_cif); + 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->capture_fifo_cif); + 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: + 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); + 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 bool tegra30_i2s_wr_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA30_I2S_CTRL: + case TEGRA30_I2S_TIMING: + case TEGRA30_I2S_OFFSET: + case TEGRA30_I2S_CH_CTRL: + case TEGRA30_I2S_SLOT_CTRL: + case TEGRA30_I2S_CIF_RX_CTRL: + case TEGRA30_I2S_CIF_TX_CTRL: + case TEGRA30_I2S_FLOWCTL: + case TEGRA30_I2S_TX_STEP: + case TEGRA30_I2S_FLOW_STATUS: + case TEGRA30_I2S_FLOW_TOTAL: + case TEGRA30_I2S_FLOW_OVER: + case TEGRA30_I2S_FLOW_UNDER: + case TEGRA30_I2S_LCOEF_1_4_0: + case TEGRA30_I2S_LCOEF_1_4_1: + case TEGRA30_I2S_LCOEF_1_4_2: + case TEGRA30_I2S_LCOEF_1_4_3: + case TEGRA30_I2S_LCOEF_1_4_4: + case TEGRA30_I2S_LCOEF_1_4_5: + case TEGRA30_I2S_LCOEF_2_4_0: + case TEGRA30_I2S_LCOEF_2_4_1: + case TEGRA30_I2S_LCOEF_2_4_2: + return true; + default: + return false; + }; +} + +static bool tegra30_i2s_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA30_I2S_FLOW_STATUS: + case TEGRA30_I2S_FLOW_TOTAL: + case TEGRA30_I2S_FLOW_OVER: + case TEGRA30_I2S_FLOW_UNDER: + return true; + default: + return false; + }; +} + +static const struct regmap_config tegra30_i2s_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA30_I2S_LCOEF_2_4_2, + .writeable_reg = tegra30_i2s_wr_rd_reg, + .readable_reg = tegra30_i2s_wr_rd_reg, + .volatile_reg = tegra30_i2s_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static __devinit int tegra30_i2s_platform_probe(struct platform_device *pdev) +{ + struct tegra30_i2s *i2s; + u32 cif_ids[2]; + struct resource *mem, *memregion; + void __iomem *regs; + int 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); + + ret = of_property_read_u32_array(pdev->dev.of_node, + "nvidia,ahub-cif-ids", cif_ids, + ARRAY_SIZE(cif_ids)); + if (ret < 0) + goto err; + + i2s->playback_i2s_cif = cif_ids[0]; + i2s->capture_i2s_cif = cif_ids[1]; + + 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; + } + + 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; + } + + regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); + if (!regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_clk_put; + } + + i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &tegra30_i2s_regmap_config); + if (IS_ERR(i2s->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + ret = PTR_ERR(i2s->regmap); + goto err_clk_put; + } + regcache_cache_only(i2s->regmap, true); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = tegra30_i2s_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + 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_suspend; + } + + ret = tegra_pcm_platform_register(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); + goto err_unregister_dai; + } + + return 0; + +err_unregister_dai: + snd_soc_unregister_dai(&pdev->dev); +err_suspend: + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra30_i2s_runtime_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&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); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra30_i2s_runtime_suspend(&pdev->dev); + + tegra_pcm_platform_unregister(&pdev->dev); + snd_soc_unregister_dai(&pdev->dev); + + clk_put(i2s->clk_i2s); + + return 0; +} + +static const struct of_device_id tegra30_i2s_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra30-i2s", }, + {}, +}; + +static const struct dev_pm_ops tegra30_i2s_pm_ops __devinitconst = { + SET_RUNTIME_PM_OPS(tegra30_i2s_runtime_suspend, + tegra30_i2s_runtime_resume, NULL) +}; + +static struct platform_driver tegra30_i2s_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = tegra30_i2s_of_match, + .pm = &tegra30_i2s_pm_ops, + }, + .probe = tegra30_i2s_platform_probe, + .remove = __devexit_p(tegra30_i2s_platform_remove), +}; +module_platform_driver(tegra30_i2s_driver); + +MODULE_AUTHOR("Stephen Warren "); +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 000000000000..91adf29c7a87 --- /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 . + */ + +#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; + enum tegra30_ahub_txcif capture_i2s_cif; + enum tegra30_ahub_rxcif capture_fifo_cif; + struct tegra_pcm_dma_params capture_dma_data; + enum tegra30_ahub_rxcif playback_i2s_cif; + enum tegra30_ahub_txcif playback_fifo_cif; + struct tegra_pcm_dma_params playback_dma_data; + struct regmap *regmap; + u32 reg_ctrl; +}; + +#endif -- cgit v1.2.3-59-g8ed1b From 6264f668d51647be68bcfde8a56042bd646a7855 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 27 Apr 2012 13:34:19 -0600 Subject: ASoC: tegra: add device tree support for TrimSlice This binding doesn't include the nvidia,model or nvidia,audio-routing properties the other Tegra audio DT bindings have, because this binding is targetted at a single machine, rather than for any machine using the tlv320aic23 codec. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- .../bindings/sound/tegra-audio-trimslice.txt | 14 +++++++++ sound/soc/tegra/trimslice.c | 34 ++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/tegra-audio-trimslice.txt (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/sound/tegra-audio-trimslice.txt b/Documentation/devicetree/bindings/sound/tegra-audio-trimslice.txt new file mode 100644 index 000000000000..04b14cfb1f16 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tegra-audio-trimslice.txt @@ -0,0 +1,14 @@ +NVIDIA Tegra audio complex for TrimSlice + +Required properties: +- compatible : "nvidia,tegra-audio-trimslice" +- nvidia,i2s-controller : The phandle of the Tegra I2S1 controller +- nvidia,audio-codec : The phandle of the WM8903 audio codec + +Example: + +sound { + compatible = "nvidia,tegra-audio-trimslice"; + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&codec>; +}; diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c index 0fd115e69a8a..4a8d5b672c9f 100644 --- a/sound/soc/tegra/trimslice.c +++ b/sound/soc/tegra/trimslice.c @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -149,6 +150,32 @@ static __devinit int tegra_snd_trimslice_probe(struct platform_device *pdev) goto err; } + if (pdev->dev.of_node) { + trimslice_tlv320aic23_dai.codec_name = NULL; + trimslice_tlv320aic23_dai.codec_of_node = of_parse_phandle( + pdev->dev.of_node, "nvidia,audio-codec", 0); + if (!trimslice_tlv320aic23_dai.codec_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,audio-codec' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + trimslice_tlv320aic23_dai.cpu_dai_name = NULL; + trimslice_tlv320aic23_dai.cpu_dai_of_node = of_parse_phandle( + pdev->dev.of_node, "nvidia,i2s-controller", 0); + if (!trimslice_tlv320aic23_dai.cpu_dai_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,i2s-controller' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + trimslice_tlv320aic23_dai.platform_name = NULL; + trimslice_tlv320aic23_dai.platform_of_node = + trimslice_tlv320aic23_dai.cpu_dai_of_node; + } + ret = tegra_asoc_utils_init(&trimslice->util_data, &pdev->dev); if (ret) goto err; @@ -184,10 +211,17 @@ static int __devexit tegra_snd_trimslice_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id trimslice_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra-audio-trimslice", }, + {}, +}; +MODULE_DEVICE_TABLE(of, trimslice_of_match); + static struct platform_driver tegra_snd_trimslice_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = trimslice_of_match, }, .probe = tegra_snd_trimslice_probe, .remove = __devexit_p(tegra_snd_trimslice_remove), -- cgit v1.2.3-59-g8ed1b From 065899c9974c4adf45ddc308d84a2a7df519f70b Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 11 May 2012 22:44:23 +0800 Subject: ASoC: fsl: fix the binding of imx-sgtl5000 The sgtl5000 data sheet does not particularly mention that the Mic Bias will internally supplies to MIC_IN, so fix the example in imx-sgtl5000 binding document to have a better reflection of the actual circuit. Signed-off-by: Shawn Guo Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt index d09b4e31461c..e4acdd891e49 100644 --- a/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt +++ b/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt @@ -41,8 +41,8 @@ sound { ssi-controller = <&ssi1>; audio-codec = <&sgtl5000>; audio-routing = + "MIC_IN", "Mic Jack", "Mic Jack", "Mic Bias", - "MIC_IN", "Mic Bias", "Headphone Jack", "HP_OUT"; mux-int-port = <1>; mux-ext-port = <3>; -- cgit v1.2.3-59-g8ed1b From 08641c7c74dddfcd726512edfaa3b4cbe42e523e Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 11 May 2012 22:24:17 +0800 Subject: ASoC: mxs: add device tree support for mxs-saif Add device tree probe for mxs-saif driver. Signed-off-by: Shawn Guo Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/mxs-saif.txt | 36 +++++++++++ sound/soc/mxs/mxs-saif.c | 72 ++++++++++++++++------ 2 files changed, 90 insertions(+), 18 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/mxs-saif.txt (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/sound/mxs-saif.txt b/Documentation/devicetree/bindings/sound/mxs-saif.txt new file mode 100644 index 000000000000..c37ba6143d9b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mxs-saif.txt @@ -0,0 +1,36 @@ +* Freescale MXS Serial Audio Interface (SAIF) + +Required properties: +- compatible: Should be "fsl,-saif" +- reg: Should contain registers location and length +- interrupts: Should contain ERROR and DMA interrupts +- fsl,saif-dma-channel: APBX DMA channel for the SAIF + +Optional properties: +- fsl,saif-master: phandle to the master SAIF. It's only required for + the slave SAIF. + +Note: Each SAIF controller should have an alias correctly numbered +in "aliases" node. + +Example: + +aliases { + saif0 = &saif0; + saif1 = &saif1; +}; + +saif0: saif@80042000 { + compatible = "fsl,imx28-saif"; + reg = <0x80042000 2000>; + interrupts = <59 80>; + fsl,saif-dma-channel = <4>; +}; + +saif1: saif@80046000 { + compatible = "fsl,imx28-saif"; + reg = <0x80046000 2000>; + interrupts = <58 81>; + fsl,saif-dma-channel = <5>; + fsl,saif-master = <&saif0>; +}; diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index 356aad83c1fe..064274880673 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -18,6 +18,8 @@ #include #include +#include +#include #include #include #include @@ -622,34 +624,54 @@ static irqreturn_t mxs_saif_irq(int irq, void *dev_id) static int __devinit mxs_saif_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; struct resource *iores, *dmares; struct mxs_saif *saif; struct mxs_saif_platform_data *pdata; int ret = 0; - if (pdev->id >= ARRAY_SIZE(mxs_saif)) + + if (!np && pdev->id >= ARRAY_SIZE(mxs_saif)) return -EINVAL; saif = devm_kzalloc(&pdev->dev, sizeof(*saif), GFP_KERNEL); if (!saif) return -ENOMEM; - mxs_saif[pdev->id] = saif; - saif->id = pdev->id; - - pdata = pdev->dev.platform_data; - if (pdata && !pdata->master_mode) { - saif->master_id = pdata->master_id; - if (saif->master_id < 0 || - saif->master_id >= ARRAY_SIZE(mxs_saif) || - saif->master_id == saif->id) { - dev_err(&pdev->dev, "get wrong master id\n"); - return -EINVAL; + if (np) { + struct device_node *master; + saif->id = of_alias_get_id(np, "saif"); + if (saif->id < 0) + return saif->id; + /* + * If there is no "fsl,saif-master" phandle, it's a saif + * master. Otherwise, it's a slave and its phandle points + * to the master. + */ + master = of_parse_phandle(np, "fsl,saif-master", 0); + if (!master) { + saif->master_id = saif->id; + } else { + saif->master_id = of_alias_get_id(master, "saif"); + if (saif->master_id < 0) + return saif->master_id; } } else { - saif->master_id = saif->id; + saif->id = pdev->id; + pdata = pdev->dev.platform_data; + if (pdata && !pdata->master_mode) + saif->master_id = pdata->master_id; + else + saif->master_id = saif->id; + } + + if (saif->master_id < 0 || saif->master_id >= ARRAY_SIZE(mxs_saif)) { + dev_err(&pdev->dev, "get wrong master id\n"); + return -EINVAL; } + mxs_saif[saif->id] = saif; + saif->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(saif->clk)) { ret = PTR_ERR(saif->clk); @@ -669,12 +691,19 @@ static int __devinit mxs_saif_probe(struct platform_device *pdev) dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!dmares) { - ret = -ENODEV; - dev_err(&pdev->dev, "failed to get dma resource: %d\n", - ret); - goto failed_get_resource; + /* + * TODO: This is a temporary solution and should be changed + * to use generic DMA binding later when the helplers get in. + */ + ret = of_property_read_u32(np, "fsl,saif-dma-channel", + &saif->dma_param.chan_num); + if (ret) { + dev_err(&pdev->dev, "failed to get dma channel\n"); + goto failed_get_resource; + } + } else { + saif->dma_param.chan_num = dmares->start; } - saif->dma_param.chan_num = dmares->start; saif->irq = platform_get_irq(pdev, 0); if (saif->irq < 0) { @@ -735,6 +764,12 @@ static int __devexit mxs_saif_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id mxs_saif_dt_ids[] = { + { .compatible = "fsl,imx28-saif", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_saif_dt_ids); + static struct platform_driver mxs_saif_driver = { .probe = mxs_saif_probe, .remove = __devexit_p(mxs_saif_remove), @@ -742,6 +777,7 @@ static struct platform_driver mxs_saif_driver = { .driver = { .name = "mxs-saif", .owner = THIS_MODULE, + .of_match_table = mxs_saif_dt_ids, }, }; -- cgit v1.2.3-59-g8ed1b From e968194b45c4e8077dada75c5bae5660b37628fe Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 11 May 2012 22:24:18 +0800 Subject: ASoC: mxs: add device tree support for mxs-sgtl5000 Add device tree probe for mxs-sgtl5000 machine driver. Signed-off-by: Shawn Guo Signed-off-by: Mark Brown --- .../bindings/sound/mxs-audio-sgtl5000.txt | 17 ++++++++ sound/soc/mxs/mxs-sgtl5000.c | 46 ++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/mxs-audio-sgtl5000.txt (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/sound/mxs-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/mxs-audio-sgtl5000.txt new file mode 100644 index 000000000000..601c518eddaa --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mxs-audio-sgtl5000.txt @@ -0,0 +1,17 @@ +* Freescale MXS audio complex with SGTL5000 codec + +Required properties: +- compatible: "fsl,mxs-audio-sgtl5000" +- model: The user-visible name of this sound complex +- saif-controllers: The phandle list of the MXS SAIF controller +- audio-codec: The phandle of the SGTL5000 audio codec + +Example: + +sound { + compatible = "fsl,imx28-evk-sgtl5000", + "fsl,mxs-audio-sgtl5000"; + model = "imx28-evk-sgtl5000"; + saif-controllers = <&saif0 &saif1>; + audio-codec = <&sgtl5000>; +}; diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index e9e6112ff196..3e6e8764b2e6 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -18,6 +18,8 @@ #include #include +#include +#include #include #include #include @@ -110,11 +112,48 @@ static struct snd_soc_card mxs_sgtl5000 = { .num_links = ARRAY_SIZE(mxs_sgtl5000_dai), }; +static int __devinit mxs_sgtl5000_probe_dt(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *saif_np[2], *codec_np; + int i, ret = 0; + + if (!np) + return 1; /* no device tree */ + + saif_np[0] = of_parse_phandle(np, "saif-controllers", 0); + saif_np[1] = of_parse_phandle(np, "saif-controllers", 1); + codec_np = of_parse_phandle(np, "audio-codec", 0); + if (!saif_np[0] || !saif_np[1] || !codec_np) { + dev_err(&pdev->dev, "phandle missing or invalid\n"); + return -EINVAL; + } + + for (i = 0; i < 2; i++) { + mxs_sgtl5000_dai[i].codec_name = NULL; + mxs_sgtl5000_dai[i].codec_of_node = codec_np; + mxs_sgtl5000_dai[i].cpu_dai_name = NULL; + mxs_sgtl5000_dai[i].cpu_dai_of_node = saif_np[i]; + mxs_sgtl5000_dai[i].platform_name = NULL; + mxs_sgtl5000_dai[i].platform_of_node = saif_np[i]; + } + + of_node_put(codec_np); + of_node_put(saif_np[0]); + of_node_put(saif_np[1]); + + return ret; +} + static int __devinit mxs_sgtl5000_probe(struct platform_device *pdev) { struct snd_soc_card *card = &mxs_sgtl5000; int ret; + ret = mxs_sgtl5000_probe_dt(pdev); + if (ret < 0) + return ret; + /* * Set an init clock(11.28Mhz) for sgtl5000 initialization(i2c r/w). * The Sgtl5000 sysclk is derived from saif0 mclk and it's range @@ -148,10 +187,17 @@ static int __devexit mxs_sgtl5000_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id mxs_sgtl5000_dt_ids[] = { + { .compatible = "fsl,mxs-audio-sgtl5000", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_sgtl5000_dt_ids); + static struct platform_driver mxs_sgtl5000_audio_driver = { .driver = { .name = "mxs-sgtl5000", .owner = THIS_MODULE, + .of_match_table = mxs_sgtl5000_dt_ids, }, .probe = mxs_sgtl5000_probe, .remove = __devexit_p(mxs_sgtl5000_remove), -- cgit v1.2.3-59-g8ed1b From d8259ca50b84c0247f10d0167fb85a3a005aeca7 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 17 May 2012 16:59:51 -0600 Subject: ASoC: tegra: Add machine driver for WM8753 codec One such machine is Whistler. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- .../bindings/sound/tegra-audio-wm8753.txt | 54 +++++ sound/soc/tegra/Kconfig | 10 + sound/soc/tegra/Makefile | 2 + sound/soc/tegra/tegra_wm8753.c | 224 +++++++++++++++++++++ 4 files changed, 290 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/tegra-audio-wm8753.txt create mode 100644 sound/soc/tegra/tegra_wm8753.c (limited to 'Documentation/devicetree/bindings') diff --git a/Documentation/devicetree/bindings/sound/tegra-audio-wm8753.txt b/Documentation/devicetree/bindings/sound/tegra-audio-wm8753.txt new file mode 100644 index 000000000000..c4dd39ce6165 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tegra-audio-wm8753.txt @@ -0,0 +1,54 @@ +NVIDIA Tegra audio complex + +Required properties: +- compatible : "nvidia,tegra-audio-wm8753" +- 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, + the second being the connection's source. Valid names for sources and + sinks are the WM8753's pins, and the jacks on the board: + + WM8753 pins: + + * LOUT1 + * LOUT2 + * ROUT1 + * ROUT2 + * MONO1 + * MONO2 + * OUT3 + * OUT4 + * LINE1 + * LINE2 + * RXP + * RXN + * ACIN + * ACOP + * MIC1N + * MIC1 + * MIC2N + * MIC2 + * Mic Bias + + Board connectors: + + * Headphone Jack + * Mic Jack + +- nvidia,i2s-controller : The phandle of the Tegra I2S1 controller +- nvidia,audio-codec : The phandle of the WM8753 audio codec +Example: + +sound { + compatible = "nvidia,tegra-audio-wm8753-whistler", + "nvidia,tegra-audio-wm8753" + nvidia,model = "tegra-wm8753-harmony"; + + nvidia,audio-routing = + "Headphone Jack", "LOUT1", + "Headphone Jack", "ROUT1"; + + nvidia,i2s-controller = <&i2s1>; + nvidia,audio-codec = <&wm8753>; +}; + diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 5331c61faf01..c1c8e955f4d3 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -48,6 +48,16 @@ config SND_SOC_TEGRA30_I2S Tegra30 I2S interface. You will also need to select the individual machine drivers to support below. +config SND_SOC_TEGRA_WM8753 + tristate "SoC Audio support for Tegra boards using a WM8753 codec" + depends on SND_SOC_TEGRA && I2C + select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC + select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC + select SND_SOC_WM8753 + help + Say Y or M here if you want to add support for SoC audio on Tegra + boards using the WM8753 codec, such as Whistler. + config MACH_HAS_SND_SOC_TEGRA_WM8903 bool help diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 98704b48b62a..391e78a34c06 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -16,10 +16,12 @@ 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-wm8753-objs := tegra_wm8753.o snd-soc-tegra-wm8903-objs := tegra_wm8903.o snd-soc-tegra-trimslice-objs := trimslice.o snd-soc-tegra-alc5632-objs := tegra_alc5632.o +obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c new file mode 100644 index 000000000000..4e77026807a2 --- /dev/null +++ b/sound/soc/tegra/tegra_wm8753.c @@ -0,0 +1,224 @@ +/* + * tegra_wm8753.c - Tegra machine ASoC driver for boards using WM8753 codec. + * + * Author: Stephen Warren + * Copyright (C) 2010-2012 - NVIDIA, Inc. + * + * Based on code copyright/by: + * + * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd. + * + * Copyright 2007 Wolfson Microelectronics PLC. + * Author: Graeme Gregory + * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../codecs/wm8753.h" + +#include "tegra_asoc_utils.h" + +#define DRV_NAME "tegra-snd-wm8753" + +struct tegra_wm8753 { + struct tegra_asoc_utils_data util_data; +}; + +static int tegra_wm8753_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_card *card = codec->card; + struct tegra_wm8753 *machine = snd_soc_card_get_drvdata(card); + int srate, mclk; + int err; + + srate = params_rate(params); + switch (srate) { + case 11025: + case 22050: + case 44100: + case 88200: + mclk = 11289600; + break; + default: + mclk = 12288000; + break; + } + + err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); + if (err < 0) { + dev_err(card->dev, "Can't configure clocks\n"); + return err; + } + + err = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, mclk, + SND_SOC_CLOCK_IN); + if (err < 0) { + dev_err(card->dev, "codec_dai clock not set\n"); + return err; + } + + return 0; +} + +static struct snd_soc_ops tegra_wm8753_ops = { + .hw_params = tegra_wm8753_hw_params, +}; + +static const struct snd_soc_dapm_widget tegra_wm8753_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), +}; + +static struct snd_soc_dai_link tegra_wm8753_dai = { + .name = "WM8753", + .stream_name = "WM8753 PCM", + .codec_dai_name = "wm8753-hifi", + .ops = &tegra_wm8753_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, +}; + +static struct snd_soc_card snd_soc_tegra_wm8753 = { + .name = "tegra-wm8753", + .owner = THIS_MODULE, + .dai_link = &tegra_wm8753_dai, + .num_links = 1, + + .dapm_widgets = tegra_wm8753_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra_wm8753_dapm_widgets), + .fully_routed = true, +}; + +static __devinit int tegra_wm8753_driver_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &snd_soc_tegra_wm8753; + struct tegra_wm8753 *machine; + int ret; + + machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm8753), + GFP_KERNEL); + if (!machine) { + dev_err(&pdev->dev, "Can't allocate tegra_wm8753 struct\n"); + ret = -ENOMEM; + goto err; + } + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, machine); + + ret = snd_soc_of_parse_card_name(card, "nvidia,model"); + if (ret) + goto err; + + ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); + if (ret) + goto err; + + tegra_wm8753_dai.codec_of_node = of_parse_phandle( + pdev->dev.of_node, "nvidia,audio-codec", 0); + if (!tegra_wm8753_dai.codec_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,audio-codec' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + tegra_wm8753_dai.cpu_dai_of_node = of_parse_phandle( + pdev->dev.of_node, "nvidia,i2s-controller", 0); + if (!tegra_wm8753_dai.cpu_dai_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,i2s-controller' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + tegra_wm8753_dai.platform_of_node = + tegra_wm8753_dai.cpu_dai_of_node; + + ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); + if (ret) + goto err; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err_fini_utils; + } + + return 0; + +err_fini_utils: + tegra_asoc_utils_fini(&machine->util_data); +err: + return ret; +} + +static int __devexit tegra_wm8753_driver_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct tegra_wm8753 *machine = snd_soc_card_get_drvdata(card); + + snd_soc_unregister_card(card); + + tegra_asoc_utils_fini(&machine->util_data); + + return 0; +} + +static const struct of_device_id tegra_wm8753_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra-audio-wm8753", }, + {}, +}; + +static struct platform_driver tegra_wm8753_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = tegra_wm8753_of_match, + }, + .probe = tegra_wm8753_driver_probe, + .remove = __devexit_p(tegra_wm8753_driver_remove), +}; +module_platform_driver(tegra_wm8753_driver); + +MODULE_AUTHOR("Stephen Warren "); +MODULE_DESCRIPTION("Tegra+WM8753 machine ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra_wm8753_of_match); -- cgit v1.2.3-59-g8ed1b