mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 17:18:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
130
sound/soc/tegra/Kconfig
Normal file
130
sound/soc/tegra/Kconfig
Normal file
|
@ -0,0 +1,130 @@
|
|||
config SND_SOC_TEGRA
|
||||
tristate "SoC Audio for the Tegra System-on-Chip"
|
||||
depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST
|
||||
depends on COMMON_CLK
|
||||
depends on RESET_CONTROLLER
|
||||
select REGMAP_MMIO
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
help
|
||||
Say Y or M here if you want support for SoC audio on Tegra.
|
||||
|
||||
config SND_SOC_TEGRA20_AC97
|
||||
tristate
|
||||
depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
|
||||
select SND_SOC_AC97_BUS
|
||||
select SND_SOC_TEGRA20_DAS
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to the
|
||||
Tegra20 AC97 interface. You will also need to select the individual
|
||||
machine drivers to support below.
|
||||
|
||||
config SND_SOC_TEGRA20_DAS
|
||||
tristate
|
||||
depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
|
||||
help
|
||||
Say Y or M if you want to add support for the Tegra20 DAS module.
|
||||
You will also need to select the individual machine drivers to
|
||||
support below.
|
||||
|
||||
config SND_SOC_TEGRA20_I2S
|
||||
tristate
|
||||
depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
|
||||
select SND_SOC_TEGRA20_DAS
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to the
|
||||
Tegra20 I2S interface. You will also need to select the individual
|
||||
machine drivers to support below.
|
||||
|
||||
config SND_SOC_TEGRA20_SPDIF
|
||||
tristate
|
||||
depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
|
||||
default m
|
||||
help
|
||||
Say Y or M if you want to add support for the Tegra20 SPDIF interface.
|
||||
You will also need to select the individual machine drivers to support
|
||||
below.
|
||||
|
||||
config SND_SOC_TEGRA30_AHUB
|
||||
tristate
|
||||
depends on SND_SOC_TEGRA && ARCH_TEGRA_3x_SOC
|
||||
help
|
||||
Say Y or M if you want to add support for the Tegra20 AHUB module.
|
||||
You will also need to select the individual machine drivers to
|
||||
support below.
|
||||
|
||||
config SND_SOC_TEGRA30_I2S
|
||||
tristate
|
||||
depends on SND_SOC_TEGRA && ARCH_TEGRA_3x_SOC
|
||||
select SND_SOC_TEGRA30_AHUB
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to the
|
||||
Tegra30 I2S interface. You will also need to select the individual
|
||||
machine drivers to support below.
|
||||
|
||||
config SND_SOC_TEGRA_RT5640
|
||||
tristate "SoC Audio support for Tegra boards using an RT5640 codec"
|
||||
depends on SND_SOC_TEGRA && I2C && GPIOLIB
|
||||
select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
|
||||
select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
|
||||
select SND_SOC_RT5640
|
||||
help
|
||||
Say Y or M here if you want to add support for SoC audio on Tegra
|
||||
boards using the RT5640 codec, such as Dalmore.
|
||||
|
||||
config SND_SOC_TEGRA_WM8753
|
||||
tristate "SoC Audio support for Tegra boards using a WM8753 codec"
|
||||
depends on SND_SOC_TEGRA && I2C && GPIOLIB
|
||||
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 SND_SOC_TEGRA_WM8903
|
||||
tristate "SoC Audio support for Tegra boards using a WM8903 codec"
|
||||
depends on SND_SOC_TEGRA && I2C && GPIOLIB
|
||||
select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
|
||||
select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
|
||||
select SND_SOC_WM8903
|
||||
help
|
||||
Say Y or M here if you want to add support for SoC audio on Tegra
|
||||
boards using the WM8093 codec. Currently, the supported boards are
|
||||
Harmony, Ventana, Seaboard, Kaen, and Aebl.
|
||||
|
||||
config SND_SOC_TEGRA_WM9712
|
||||
tristate "SoC Audio support for Tegra boards using a WM9712 codec"
|
||||
depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC && GPIOLIB
|
||||
select SND_SOC_TEGRA20_AC97
|
||||
select SND_SOC_WM9712
|
||||
help
|
||||
Say Y or M here if you want to add support for SoC audio on Tegra
|
||||
boards using the WM9712 (or compatible) codec.
|
||||
|
||||
config SND_SOC_TEGRA_TRIMSLICE
|
||||
tristate "SoC Audio support for TrimSlice board"
|
||||
depends on SND_SOC_TEGRA && I2C
|
||||
select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
|
||||
select SND_SOC_TLV320AIC23_I2C
|
||||
help
|
||||
Say Y or M here if you want to add support for SoC audio on the
|
||||
TrimSlice platform.
|
||||
|
||||
config SND_SOC_TEGRA_ALC5632
|
||||
tristate "SoC Audio support for Tegra boards using an ALC5632 codec"
|
||||
depends on SND_SOC_TEGRA && I2C && GPIOLIB
|
||||
select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
|
||||
select SND_SOC_ALC5632
|
||||
help
|
||||
Say Y or M here if you want to add support for SoC audio on the
|
||||
Toshiba AC100 netbook.
|
||||
|
||||
config SND_SOC_TEGRA_MAX98090
|
||||
tristate "SoC Audio support for Tegra boards using a MAX98090 codec"
|
||||
depends on SND_SOC_TEGRA && I2C && GPIOLIB
|
||||
select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
|
||||
select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
|
||||
select SND_SOC_MAX98090
|
||||
help
|
||||
Say Y or M here if you want to add support for SoC audio on Tegra
|
||||
boards using the MAX98090 codec, such as Venice2.
|
35
sound/soc/tegra/Makefile
Normal file
35
sound/soc/tegra/Makefile
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Tegra platform Support
|
||||
snd-soc-tegra-pcm-objs := tegra_pcm.o
|
||||
snd-soc-tegra-utils-objs += tegra_asoc_utils.o
|
||||
snd-soc-tegra20-ac97-objs := tegra20_ac97.o
|
||||
snd-soc-tegra20-das-objs := tegra20_das.o
|
||||
snd-soc-tegra20-i2s-objs := tegra20_i2s.o
|
||||
snd-soc-tegra20-spdif-objs := tegra20_spdif.o
|
||||
snd-soc-tegra30-ahub-objs := tegra30_ahub.o
|
||||
snd-soc-tegra30-i2s-objs := tegra30_i2s.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
|
||||
obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
|
||||
obj-$(CONFIG_SND_SOC_TEGRA20_AC97) += snd-soc-tegra20-ac97.o
|
||||
obj-$(CONFIG_SND_SOC_TEGRA20_DAS) += snd-soc-tegra20-das.o
|
||||
obj-$(CONFIG_SND_SOC_TEGRA20_I2S) += snd-soc-tegra20-i2s.o
|
||||
obj-$(CONFIG_SND_SOC_TEGRA20_SPDIF) += snd-soc-tegra20-spdif.o
|
||||
obj-$(CONFIG_SND_SOC_TEGRA30_AHUB) += snd-soc-tegra30-ahub.o
|
||||
obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o
|
||||
|
||||
# Tegra machine Support
|
||||
snd-soc-tegra-rt5640-objs := tegra_rt5640.o
|
||||
snd-soc-tegra-wm8753-objs := tegra_wm8753.o
|
||||
snd-soc-tegra-wm8903-objs := tegra_wm8903.o
|
||||
snd-soc-tegra-wm9712-objs := tegra_wm9712.o
|
||||
snd-soc-tegra-trimslice-objs := trimslice.o
|
||||
snd-soc-tegra-alc5632-objs := tegra_alc5632.o
|
||||
snd-soc-tegra-max98090-objs := tegra_max98090.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.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_WM9712) += snd-soc-tegra-wm9712.o
|
||||
obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o
|
||||
obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o
|
||||
obj-$(CONFIG_SND_SOC_TEGRA_MAX98090) += snd-soc-tegra-max98090.o
|
453
sound/soc/tegra/tegra20_ac97.c
Normal file
453
sound/soc/tegra/tegra20_ac97.c
Normal file
|
@ -0,0 +1,453 @@
|
|||
/*
|
||||
* tegra20_ac97.c - Tegra20 AC97 platform driver
|
||||
*
|
||||
* Copyright (c) 2012 Lucas Stach <dev@lynxeye.de>
|
||||
*
|
||||
* Partly based on code copyright/by:
|
||||
*
|
||||
* Copyright (c) 2011,2012 Toradex Inc.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
#include "tegra20_ac97.h"
|
||||
|
||||
#define DRV_NAME "tegra20-ac97"
|
||||
|
||||
static struct tegra20_ac97 *workdata;
|
||||
|
||||
static void tegra20_ac97_codec_reset(struct snd_ac97 *ac97)
|
||||
{
|
||||
u32 readback;
|
||||
unsigned long timeout;
|
||||
|
||||
/* reset line is not driven by DAC pad group, have to toggle GPIO */
|
||||
gpio_set_value(workdata->reset_gpio, 0);
|
||||
udelay(2);
|
||||
|
||||
gpio_set_value(workdata->reset_gpio, 1);
|
||||
udelay(2);
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(100);
|
||||
|
||||
do {
|
||||
regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback);
|
||||
if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY)
|
||||
break;
|
||||
usleep_range(1000, 2000);
|
||||
} while (!time_after(jiffies, timeout));
|
||||
}
|
||||
|
||||
static void tegra20_ac97_codec_warm_reset(struct snd_ac97 *ac97)
|
||||
{
|
||||
u32 readback;
|
||||
unsigned long timeout;
|
||||
|
||||
/*
|
||||
* although sync line is driven by the DAC pad group warm reset using
|
||||
* the controller cmd is not working, have to toggle sync line
|
||||
* manually.
|
||||
*/
|
||||
gpio_request(workdata->sync_gpio, "codec-sync");
|
||||
|
||||
gpio_direction_output(workdata->sync_gpio, 1);
|
||||
|
||||
udelay(2);
|
||||
gpio_set_value(workdata->sync_gpio, 0);
|
||||
udelay(2);
|
||||
gpio_free(workdata->sync_gpio);
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(100);
|
||||
|
||||
do {
|
||||
regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback);
|
||||
if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY)
|
||||
break;
|
||||
usleep_range(1000, 2000);
|
||||
} while (!time_after(jiffies, timeout));
|
||||
}
|
||||
|
||||
static unsigned short tegra20_ac97_codec_read(struct snd_ac97 *ac97_snd,
|
||||
unsigned short reg)
|
||||
{
|
||||
u32 readback;
|
||||
unsigned long timeout;
|
||||
|
||||
regmap_write(workdata->regmap, TEGRA20_AC97_CMD,
|
||||
(((reg | 0x80) << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) &
|
||||
TEGRA20_AC97_CMD_CMD_ADDR_MASK) |
|
||||
TEGRA20_AC97_CMD_BUSY);
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(100);
|
||||
|
||||
do {
|
||||
regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback);
|
||||
if (readback & TEGRA20_AC97_STATUS1_STA_VALID1)
|
||||
break;
|
||||
usleep_range(1000, 2000);
|
||||
} while (!time_after(jiffies, timeout));
|
||||
|
||||
return ((readback & TEGRA20_AC97_STATUS1_STA_DATA1_MASK) >>
|
||||
TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT);
|
||||
}
|
||||
|
||||
static void tegra20_ac97_codec_write(struct snd_ac97 *ac97_snd,
|
||||
unsigned short reg, unsigned short val)
|
||||
{
|
||||
u32 readback;
|
||||
unsigned long timeout;
|
||||
|
||||
regmap_write(workdata->regmap, TEGRA20_AC97_CMD,
|
||||
((reg << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) &
|
||||
TEGRA20_AC97_CMD_CMD_ADDR_MASK) |
|
||||
((val << TEGRA20_AC97_CMD_CMD_DATA_SHIFT) &
|
||||
TEGRA20_AC97_CMD_CMD_DATA_MASK) |
|
||||
TEGRA20_AC97_CMD_BUSY);
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(100);
|
||||
|
||||
do {
|
||||
regmap_read(workdata->regmap, TEGRA20_AC97_CMD, &readback);
|
||||
if (!(readback & TEGRA20_AC97_CMD_BUSY))
|
||||
break;
|
||||
usleep_range(1000, 2000);
|
||||
} while (!time_after(jiffies, timeout));
|
||||
}
|
||||
|
||||
static struct snd_ac97_bus_ops tegra20_ac97_ops = {
|
||||
.read = tegra20_ac97_codec_read,
|
||||
.write = tegra20_ac97_codec_write,
|
||||
.reset = tegra20_ac97_codec_reset,
|
||||
.warm_reset = tegra20_ac97_codec_warm_reset,
|
||||
};
|
||||
|
||||
static inline void tegra20_ac97_start_playback(struct tegra20_ac97 *ac97)
|
||||
{
|
||||
regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
|
||||
TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN,
|
||||
TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN);
|
||||
|
||||
regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL,
|
||||
TEGRA20_AC97_CTRL_PCM_DAC_EN |
|
||||
TEGRA20_AC97_CTRL_STM_EN,
|
||||
TEGRA20_AC97_CTRL_PCM_DAC_EN |
|
||||
TEGRA20_AC97_CTRL_STM_EN);
|
||||
}
|
||||
|
||||
static inline void tegra20_ac97_stop_playback(struct tegra20_ac97 *ac97)
|
||||
{
|
||||
regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
|
||||
TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN, 0);
|
||||
|
||||
regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL,
|
||||
TEGRA20_AC97_CTRL_PCM_DAC_EN, 0);
|
||||
}
|
||||
|
||||
static inline void tegra20_ac97_start_capture(struct tegra20_ac97 *ac97)
|
||||
{
|
||||
regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
|
||||
TEGRA20_AC97_FIFO_SCR_REC_FULL_EN,
|
||||
TEGRA20_AC97_FIFO_SCR_REC_FULL_EN);
|
||||
}
|
||||
|
||||
static inline void tegra20_ac97_stop_capture(struct tegra20_ac97 *ac97)
|
||||
{
|
||||
regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
|
||||
TEGRA20_AC97_FIFO_SCR_REC_FULL_EN, 0);
|
||||
}
|
||||
|
||||
static int tegra20_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct tegra20_ac97 *ac97 = 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)
|
||||
tegra20_ac97_start_playback(ac97);
|
||||
else
|
||||
tegra20_ac97_start_capture(ac97);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
tegra20_ac97_stop_playback(ac97);
|
||||
else
|
||||
tegra20_ac97_stop_capture(ac97);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops tegra20_ac97_dai_ops = {
|
||||
.trigger = tegra20_ac97_trigger,
|
||||
};
|
||||
|
||||
static int tegra20_ac97_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
dai->capture_dma_data = &ac97->capture_dma_data;
|
||||
dai->playback_dma_data = &ac97->playback_dma_data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_driver tegra20_ac97_dai = {
|
||||
.name = "tegra-ac97-pcm",
|
||||
.ac97_control = 1,
|
||||
.probe = tegra20_ac97_probe,
|
||||
.playback = {
|
||||
.stream_name = "PCM Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "PCM Capture",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.ops = &tegra20_ac97_dai_ops,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver tegra20_ac97_component = {
|
||||
.name = DRV_NAME,
|
||||
};
|
||||
|
||||
static bool tegra20_ac97_wr_rd_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case TEGRA20_AC97_CTRL:
|
||||
case TEGRA20_AC97_CMD:
|
||||
case TEGRA20_AC97_STATUS1:
|
||||
case TEGRA20_AC97_FIFO1_SCR:
|
||||
case TEGRA20_AC97_FIFO_TX1:
|
||||
case TEGRA20_AC97_FIFO_RX1:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool tegra20_ac97_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case TEGRA20_AC97_STATUS1:
|
||||
case TEGRA20_AC97_FIFO1_SCR:
|
||||
case TEGRA20_AC97_FIFO_TX1:
|
||||
case TEGRA20_AC97_FIFO_RX1:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool tegra20_ac97_precious_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case TEGRA20_AC97_FIFO_TX1:
|
||||
case TEGRA20_AC97_FIFO_RX1:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct regmap_config tegra20_ac97_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = TEGRA20_AC97_FIFO_RX1,
|
||||
.writeable_reg = tegra20_ac97_wr_rd_reg,
|
||||
.readable_reg = tegra20_ac97_wr_rd_reg,
|
||||
.volatile_reg = tegra20_ac97_volatile_reg,
|
||||
.precious_reg = tegra20_ac97_precious_reg,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static int tegra20_ac97_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra20_ac97 *ac97;
|
||||
struct resource *mem;
|
||||
void __iomem *regs;
|
||||
int ret = 0;
|
||||
|
||||
ac97 = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_ac97),
|
||||
GFP_KERNEL);
|
||||
if (!ac97) {
|
||||
dev_err(&pdev->dev, "Can't allocate tegra20_ac97\n");
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
dev_set_drvdata(&pdev->dev, ac97);
|
||||
|
||||
ac97->clk_ac97 = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(ac97->clk_ac97)) {
|
||||
dev_err(&pdev->dev, "Can't retrieve ac97 clock\n");
|
||||
ret = PTR_ERR(ac97->clk_ac97);
|
||||
goto err;
|
||||
}
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(regs)) {
|
||||
ret = PTR_ERR(regs);
|
||||
goto err_clk_put;
|
||||
}
|
||||
|
||||
ac97->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
|
||||
&tegra20_ac97_regmap_config);
|
||||
if (IS_ERR(ac97->regmap)) {
|
||||
dev_err(&pdev->dev, "regmap init failed\n");
|
||||
ret = PTR_ERR(ac97->regmap);
|
||||
goto err_clk_put;
|
||||
}
|
||||
|
||||
ac97->reset_gpio = of_get_named_gpio(pdev->dev.of_node,
|
||||
"nvidia,codec-reset-gpio", 0);
|
||||
if (gpio_is_valid(ac97->reset_gpio)) {
|
||||
ret = devm_gpio_request_one(&pdev->dev, ac97->reset_gpio,
|
||||
GPIOF_OUT_INIT_HIGH, "codec-reset");
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not get codec-reset GPIO\n");
|
||||
goto err_clk_put;
|
||||
}
|
||||
} else {
|
||||
dev_err(&pdev->dev, "no codec-reset GPIO supplied\n");
|
||||
goto err_clk_put;
|
||||
}
|
||||
|
||||
ac97->sync_gpio = of_get_named_gpio(pdev->dev.of_node,
|
||||
"nvidia,codec-sync-gpio", 0);
|
||||
if (!gpio_is_valid(ac97->sync_gpio)) {
|
||||
dev_err(&pdev->dev, "no codec-sync GPIO supplied\n");
|
||||
goto err_clk_put;
|
||||
}
|
||||
|
||||
ac97->capture_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_RX1;
|
||||
ac97->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
ac97->capture_dma_data.maxburst = 4;
|
||||
|
||||
ac97->playback_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_TX1;
|
||||
ac97->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
ac97->playback_dma_data.maxburst = 4;
|
||||
|
||||
ret = clk_prepare_enable(ac97->clk_ac97);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "clk_enable failed: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = snd_soc_set_ac97_ops(&tegra20_ac97_ops);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
|
||||
goto err_clk_disable_unprepare;
|
||||
}
|
||||
|
||||
ret = snd_soc_register_component(&pdev->dev, &tegra20_ac97_component,
|
||||
&tegra20_ac97_dai, 1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
|
||||
ret = -ENOMEM;
|
||||
goto err_clk_disable_unprepare;
|
||||
}
|
||||
|
||||
ret = tegra_pcm_platform_register(&pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
|
||||
goto err_unregister_component;
|
||||
}
|
||||
|
||||
/* XXX: crufty ASoC AC97 API - only one AC97 codec allowed */
|
||||
workdata = ac97;
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_component:
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
err_clk_disable_unprepare:
|
||||
clk_disable_unprepare(ac97->clk_ac97);
|
||||
err_clk_put:
|
||||
err:
|
||||
snd_soc_set_ac97_ops(NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra20_ac97_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra20_ac97 *ac97 = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
tegra_pcm_platform_unregister(&pdev->dev);
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(ac97->clk_ac97);
|
||||
|
||||
snd_soc_set_ac97_ops(NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tegra20_ac97_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra20-ac97", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver tegra20_ac97_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = tegra20_ac97_of_match,
|
||||
},
|
||||
.probe = tegra20_ac97_platform_probe,
|
||||
.remove = tegra20_ac97_platform_remove,
|
||||
};
|
||||
module_platform_driver(tegra20_ac97_driver);
|
||||
|
||||
MODULE_AUTHOR("Lucas Stach");
|
||||
MODULE_DESCRIPTION("Tegra20 AC97 ASoC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra20_ac97_of_match);
|
94
sound/soc/tegra/tegra20_ac97.h
Normal file
94
sound/soc/tegra/tegra20_ac97.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* tegra20_ac97.h - Definitions for the Tegra20 AC97 controller driver
|
||||
*
|
||||
* Copyright (c) 2012 Lucas Stach <dev@lynxeye.de>
|
||||
*
|
||||
* Partly based on code copyright/by:
|
||||
*
|
||||
* Copyright (c) 2011,2012 Toradex Inc.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TEGRA20_AC97_H__
|
||||
#define __TEGRA20_AC97_H__
|
||||
|
||||
#include "tegra_pcm.h"
|
||||
|
||||
#define TEGRA20_AC97_CTRL 0x00
|
||||
#define TEGRA20_AC97_CMD 0x04
|
||||
#define TEGRA20_AC97_STATUS1 0x08
|
||||
/* ... */
|
||||
#define TEGRA20_AC97_FIFO1_SCR 0x1c
|
||||
/* ... */
|
||||
#define TEGRA20_AC97_FIFO_TX1 0x40
|
||||
#define TEGRA20_AC97_FIFO_RX1 0x80
|
||||
|
||||
/* TEGRA20_AC97_CTRL */
|
||||
#define TEGRA20_AC97_CTRL_STM2_EN (1 << 16)
|
||||
#define TEGRA20_AC97_CTRL_DOUBLE_SAMPLING_EN (1 << 11)
|
||||
#define TEGRA20_AC97_CTRL_IO_CNTRL_EN (1 << 10)
|
||||
#define TEGRA20_AC97_CTRL_HSET_DAC_EN (1 << 9)
|
||||
#define TEGRA20_AC97_CTRL_LINE2_DAC_EN (1 << 8)
|
||||
#define TEGRA20_AC97_CTRL_PCM_LFE_EN (1 << 7)
|
||||
#define TEGRA20_AC97_CTRL_PCM_SUR_EN (1 << 6)
|
||||
#define TEGRA20_AC97_CTRL_PCM_CEN_DAC_EN (1 << 5)
|
||||
#define TEGRA20_AC97_CTRL_LINE1_DAC_EN (1 << 4)
|
||||
#define TEGRA20_AC97_CTRL_PCM_DAC_EN (1 << 3)
|
||||
#define TEGRA20_AC97_CTRL_COLD_RESET (1 << 2)
|
||||
#define TEGRA20_AC97_CTRL_WARM_RESET (1 << 1)
|
||||
#define TEGRA20_AC97_CTRL_STM_EN (1 << 0)
|
||||
|
||||
/* TEGRA20_AC97_CMD */
|
||||
#define TEGRA20_AC97_CMD_CMD_ADDR_SHIFT 24
|
||||
#define TEGRA20_AC97_CMD_CMD_ADDR_MASK (0xff << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT)
|
||||
#define TEGRA20_AC97_CMD_CMD_DATA_SHIFT 8
|
||||
#define TEGRA20_AC97_CMD_CMD_DATA_MASK (0xffff << TEGRA20_AC97_CMD_CMD_DATA_SHIFT)
|
||||
#define TEGRA20_AC97_CMD_CMD_ID_SHIFT 2
|
||||
#define TEGRA20_AC97_CMD_CMD_ID_MASK (0x3 << TEGRA20_AC97_CMD_CMD_ID_SHIFT)
|
||||
#define TEGRA20_AC97_CMD_BUSY (1 << 0)
|
||||
|
||||
/* TEGRA20_AC97_STATUS1 */
|
||||
#define TEGRA20_AC97_STATUS1_STA_ADDR1_SHIFT 24
|
||||
#define TEGRA20_AC97_STATUS1_STA_ADDR1_MASK (0xff << TEGRA20_AC97_STATUS1_STA_ADDR1_SHIFT)
|
||||
#define TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT 8
|
||||
#define TEGRA20_AC97_STATUS1_STA_DATA1_MASK (0xffff << TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT)
|
||||
#define TEGRA20_AC97_STATUS1_STA_VALID1 (1 << 2)
|
||||
#define TEGRA20_AC97_STATUS1_STANDBY1 (1 << 1)
|
||||
#define TEGRA20_AC97_STATUS1_CODEC1_RDY (1 << 0)
|
||||
|
||||
/* TEGRA20_AC97_FIFO1_SCR */
|
||||
#define TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_SHIFT 27
|
||||
#define TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_MASK (0x1f << TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_SHIFT)
|
||||
#define TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_SHIFT 22
|
||||
#define TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_MASK (0x1f << TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_SHIFT)
|
||||
#define TEGRA20_AC97_FIFO_SCR_REC_OVERRUN_INT_STA (1 << 19)
|
||||
#define TEGRA20_AC97_FIFO_SCR_PB_UNDERRUN_INT_STA (1 << 18)
|
||||
#define TEGRA20_AC97_FIFO_SCR_REC_FORCE_MT (1 << 17)
|
||||
#define TEGRA20_AC97_FIFO_SCR_PB_FORCE_MT (1 << 16)
|
||||
#define TEGRA20_AC97_FIFO_SCR_REC_FULL_EN (1 << 15)
|
||||
#define TEGRA20_AC97_FIFO_SCR_REC_3QRT_FULL_EN (1 << 14)
|
||||
#define TEGRA20_AC97_FIFO_SCR_REC_QRT_FULL_EN (1 << 13)
|
||||
#define TEGRA20_AC97_FIFO_SCR_REC_EMPTY_EN (1 << 12)
|
||||
#define TEGRA20_AC97_FIFO_SCR_PB_NOT_FULL_EN (1 << 11)
|
||||
#define TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN (1 << 10)
|
||||
#define TEGRA20_AC97_FIFO_SCR_PB_3QRT_MT_EN (1 << 9)
|
||||
#define TEGRA20_AC97_FIFO_SCR_PB_EMPTY_MT_EN (1 << 8)
|
||||
|
||||
struct tegra20_ac97 {
|
||||
struct clk *clk_ac97;
|
||||
struct snd_dmaengine_dai_dma_data capture_dma_data;
|
||||
struct snd_dmaengine_dai_dma_data playback_dma_data;
|
||||
struct regmap *regmap;
|
||||
int reset_gpio;
|
||||
int sync_gpio;
|
||||
};
|
||||
#endif /* __TEGRA20_AC97_H__ */
|
246
sound/soc/tegra/tegra20_das.c
Normal file
246
sound/soc/tegra/tegra20_das.c
Normal file
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* tegra20_das.c - Tegra20 DAS driver
|
||||
*
|
||||
* Author: Stephen Warren <swarren@nvidia.com>
|
||||
* Copyright (C) 2010 - NVIDIA, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/soc.h>
|
||||
#include "tegra20_das.h"
|
||||
|
||||
#define DRV_NAME "tegra20-das"
|
||||
|
||||
static struct tegra20_das *das;
|
||||
|
||||
static inline void tegra20_das_write(u32 reg, u32 val)
|
||||
{
|
||||
regmap_write(das->regmap, reg, val);
|
||||
}
|
||||
|
||||
static inline u32 tegra20_das_read(u32 reg)
|
||||
{
|
||||
u32 val;
|
||||
regmap_read(das->regmap, reg, &val);
|
||||
return val;
|
||||
}
|
||||
|
||||
int tegra20_das_connect_dap_to_dac(int dap, int dac)
|
||||
{
|
||||
u32 addr;
|
||||
u32 reg;
|
||||
|
||||
if (!das)
|
||||
return -ENODEV;
|
||||
|
||||
addr = TEGRA20_DAS_DAP_CTRL_SEL +
|
||||
(dap * TEGRA20_DAS_DAP_CTRL_SEL_STRIDE);
|
||||
reg = dac << TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P;
|
||||
|
||||
tegra20_das_write(addr, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra20_das_connect_dap_to_dac);
|
||||
|
||||
int tegra20_das_connect_dap_to_dap(int dap, int otherdap, int master,
|
||||
int sdata1rx, int sdata2rx)
|
||||
{
|
||||
u32 addr;
|
||||
u32 reg;
|
||||
|
||||
if (!das)
|
||||
return -ENODEV;
|
||||
|
||||
addr = TEGRA20_DAS_DAP_CTRL_SEL +
|
||||
(dap * TEGRA20_DAS_DAP_CTRL_SEL_STRIDE);
|
||||
reg = otherdap << TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P |
|
||||
!!sdata2rx << TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P |
|
||||
!!sdata1rx << TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P |
|
||||
!!master << TEGRA20_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P;
|
||||
|
||||
tegra20_das_write(addr, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra20_das_connect_dap_to_dap);
|
||||
|
||||
int tegra20_das_connect_dac_to_dap(int dac, int dap)
|
||||
{
|
||||
u32 addr;
|
||||
u32 reg;
|
||||
|
||||
if (!das)
|
||||
return -ENODEV;
|
||||
|
||||
addr = TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL +
|
||||
(dac * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
|
||||
reg = dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P |
|
||||
dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P |
|
||||
dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P;
|
||||
|
||||
tegra20_das_write(addr, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra20_das_connect_dac_to_dap);
|
||||
|
||||
#define LAST_REG(name) \
|
||||
(TEGRA20_DAS_##name + \
|
||||
(TEGRA20_DAS_##name##_STRIDE * (TEGRA20_DAS_##name##_COUNT - 1)))
|
||||
|
||||
static bool tegra20_das_wr_rd_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
if ((reg >= TEGRA20_DAS_DAP_CTRL_SEL) &&
|
||||
(reg <= LAST_REG(DAP_CTRL_SEL)))
|
||||
return true;
|
||||
if ((reg >= TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL) &&
|
||||
(reg <= LAST_REG(DAC_INPUT_DATA_CLK_SEL)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct regmap_config tegra20_das_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = LAST_REG(DAC_INPUT_DATA_CLK_SEL),
|
||||
.writeable_reg = tegra20_das_wr_rd_reg,
|
||||
.readable_reg = tegra20_das_wr_rd_reg,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static int tegra20_das_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res, *region;
|
||||
void __iomem *regs;
|
||||
int ret = 0;
|
||||
|
||||
if (das)
|
||||
return -ENODEV;
|
||||
|
||||
das = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_das), GFP_KERNEL);
|
||||
if (!das) {
|
||||
dev_err(&pdev->dev, "Can't allocate tegra20_das\n");
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
das->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "No memory resource\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
region = devm_request_mem_region(&pdev->dev, res->start,
|
||||
resource_size(res), pdev->name);
|
||||
if (!region) {
|
||||
dev_err(&pdev->dev, "Memory region already claimed\n");
|
||||
ret = -EBUSY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||
if (!regs) {
|
||||
dev_err(&pdev->dev, "ioremap failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
das->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
|
||||
&tegra20_das_regmap_config);
|
||||
if (IS_ERR(das->regmap)) {
|
||||
dev_err(&pdev->dev, "regmap init failed\n");
|
||||
ret = PTR_ERR(das->regmap);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = tegra20_das_connect_dap_to_dac(TEGRA20_DAS_DAP_ID_1,
|
||||
TEGRA20_DAS_DAP_SEL_DAC1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Can't set up DAS DAP connection\n");
|
||||
goto err;
|
||||
}
|
||||
ret = tegra20_das_connect_dac_to_dap(TEGRA20_DAS_DAC_ID_1,
|
||||
TEGRA20_DAS_DAC_SEL_DAP1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Can't set up DAS DAC connection\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = tegra20_das_connect_dap_to_dac(TEGRA20_DAS_DAP_ID_3,
|
||||
TEGRA20_DAS_DAP_SEL_DAC3);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Can't set up DAS DAP connection\n");
|
||||
goto err;
|
||||
}
|
||||
ret = tegra20_das_connect_dac_to_dap(TEGRA20_DAS_DAC_ID_3,
|
||||
TEGRA20_DAS_DAC_SEL_DAP3);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Can't set up DAS DAC connection\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, das);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
das = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra20_das_remove(struct platform_device *pdev)
|
||||
{
|
||||
if (!das)
|
||||
return -ENODEV;
|
||||
|
||||
das = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tegra20_das_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra20-das", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver tegra20_das_driver = {
|
||||
.probe = tegra20_das_probe,
|
||||
.remove = tegra20_das_remove,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = tegra20_das_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(tegra20_das_driver);
|
||||
|
||||
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra20 DAS driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra20_das_of_match);
|
134
sound/soc/tegra/tegra20_das.h
Normal file
134
sound/soc/tegra/tegra20_das.h
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* tegra20_das.h - Definitions for Tegra20 DAS driver
|
||||
*
|
||||
* Author: Stephen Warren <swarren@nvidia.com>
|
||||
* Copyright (C) 2010,2012 - NVIDIA, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TEGRA20_DAS_H__
|
||||
#define __TEGRA20_DAS_H__
|
||||
|
||||
/* Register TEGRA20_DAS_DAP_CTRL_SEL */
|
||||
#define TEGRA20_DAS_DAP_CTRL_SEL 0x00
|
||||
#define TEGRA20_DAS_DAP_CTRL_SEL_COUNT 5
|
||||
#define TEGRA20_DAS_DAP_CTRL_SEL_STRIDE 4
|
||||
#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P 31
|
||||
#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_MS_SEL_S 1
|
||||
#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P 30
|
||||
#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_S 1
|
||||
#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P 29
|
||||
#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_S 1
|
||||
#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P 0
|
||||
#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_S 5
|
||||
|
||||
/* Values for field TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL */
|
||||
#define TEGRA20_DAS_DAP_SEL_DAC1 0
|
||||
#define TEGRA20_DAS_DAP_SEL_DAC2 1
|
||||
#define TEGRA20_DAS_DAP_SEL_DAC3 2
|
||||
#define TEGRA20_DAS_DAP_SEL_DAP1 16
|
||||
#define TEGRA20_DAS_DAP_SEL_DAP2 17
|
||||
#define TEGRA20_DAS_DAP_SEL_DAP3 18
|
||||
#define TEGRA20_DAS_DAP_SEL_DAP4 19
|
||||
#define TEGRA20_DAS_DAP_SEL_DAP5 20
|
||||
|
||||
/* Register TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL */
|
||||
#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL 0x40
|
||||
#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT 3
|
||||
#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE 4
|
||||
#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P 28
|
||||
#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_S 4
|
||||
#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P 24
|
||||
#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_S 4
|
||||
#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P 0
|
||||
#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_S 4
|
||||
|
||||
/*
|
||||
* Values for:
|
||||
* TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL
|
||||
* TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL
|
||||
* TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL
|
||||
*/
|
||||
#define TEGRA20_DAS_DAC_SEL_DAP1 0
|
||||
#define TEGRA20_DAS_DAC_SEL_DAP2 1
|
||||
#define TEGRA20_DAS_DAC_SEL_DAP3 2
|
||||
#define TEGRA20_DAS_DAC_SEL_DAP4 3
|
||||
#define TEGRA20_DAS_DAC_SEL_DAP5 4
|
||||
|
||||
/*
|
||||
* Names/IDs of the DACs/DAPs.
|
||||
*/
|
||||
|
||||
#define TEGRA20_DAS_DAP_ID_1 0
|
||||
#define TEGRA20_DAS_DAP_ID_2 1
|
||||
#define TEGRA20_DAS_DAP_ID_3 2
|
||||
#define TEGRA20_DAS_DAP_ID_4 3
|
||||
#define TEGRA20_DAS_DAP_ID_5 4
|
||||
|
||||
#define TEGRA20_DAS_DAC_ID_1 0
|
||||
#define TEGRA20_DAS_DAC_ID_2 1
|
||||
#define TEGRA20_DAS_DAC_ID_3 2
|
||||
|
||||
struct tegra20_das {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
/*
|
||||
* Terminology:
|
||||
* DAS: Digital audio switch (HW module controlled by this driver)
|
||||
* DAP: Digital audio port (port/pins on Tegra device)
|
||||
* DAC: Digital audio controller (e.g. I2S or AC97 controller elsewhere)
|
||||
*
|
||||
* The Tegra DAS is a mux/cross-bar which can connect each DAP to a specific
|
||||
* DAC, or another DAP. When DAPs are connected, one must be the master and
|
||||
* one the slave. Each DAC allows selection of a specific DAP for input, to
|
||||
* cater for the case where N DAPs are connected to 1 DAC for broadcast
|
||||
* output.
|
||||
*
|
||||
* This driver is dumb; no attempt is made to ensure that a valid routing
|
||||
* configuration is programmed.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Connect a DAP to to a DAC
|
||||
* dap_id: DAP to connect: TEGRA20_DAS_DAP_ID_*
|
||||
* dac_sel: DAC to connect to: TEGRA20_DAS_DAP_SEL_DAC*
|
||||
*/
|
||||
extern int tegra20_das_connect_dap_to_dac(int dap_id, int dac_sel);
|
||||
|
||||
/*
|
||||
* Connect a DAP to to another DAP
|
||||
* dap_id: DAP to connect: TEGRA20_DAS_DAP_ID_*
|
||||
* other_dap_sel: DAP to connect to: TEGRA20_DAS_DAP_SEL_DAP*
|
||||
* master: Is this DAP the master (1) or slave (0)
|
||||
* sdata1rx: Is this DAP's SDATA1 pin RX (1) or TX (0)
|
||||
* sdata2rx: Is this DAP's SDATA2 pin RX (1) or TX (0)
|
||||
*/
|
||||
extern int tegra20_das_connect_dap_to_dap(int dap_id, int other_dap_sel,
|
||||
int master, int sdata1rx,
|
||||
int sdata2rx);
|
||||
|
||||
/*
|
||||
* Connect a DAC's input to a DAP
|
||||
* (DAC outputs are selected by the DAP)
|
||||
* dac_id: DAC ID to connect: TEGRA20_DAS_DAC_ID_*
|
||||
* dap_sel: DAP to receive input from: TEGRA20_DAS_DAC_SEL_DAP*
|
||||
*/
|
||||
extern int tegra20_das_connect_dac_to_dap(int dac_id, int dap_sel);
|
||||
|
||||
#endif
|
480
sound/soc/tegra/tegra20_i2s.c
Normal file
480
sound/soc/tegra/tegra20_i2s.c
Normal file
|
@ -0,0 +1,480 @@
|
|||
/*
|
||||
* tegra20_i2s.c - Tegra20 I2S driver
|
||||
*
|
||||
* Author: Stephen Warren <swarren@nvidia.com>
|
||||
* Copyright (C) 2010,2012 - NVIDIA, Inc.
|
||||
*
|
||||
* Based on code copyright/by:
|
||||
*
|
||||
* Copyright (c) 2009-2010, NVIDIA Corporation.
|
||||
* Scott Peterson <speterson@nvidia.com>
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Iliyan Malchev <malchev@google.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
#include "tegra20_i2s.h"
|
||||
|
||||
#define DRV_NAME "tegra20-i2s"
|
||||
|
||||
static int tegra20_i2s_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct tegra20_i2s *i2s = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(i2s->clk_i2s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra20_i2s_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct tegra20_i2s *i2s = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(i2s->clk_i2s);
|
||||
if (ret) {
|
||||
dev_err(dev, "clk_enable failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra20_i2s_set_fmt(struct snd_soc_dai *dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int mask = 0, val = 0;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mask |= TEGRA20_I2S_CTRL_MASTER_ENABLE;
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
val |= TEGRA20_I2S_CTRL_MASTER_ENABLE;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mask |= TEGRA20_I2S_CTRL_BIT_FORMAT_MASK |
|
||||
TEGRA20_I2S_CTRL_LRCK_MASK;
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
val |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP;
|
||||
val |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
val |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP;
|
||||
val |= TEGRA20_I2S_CTRL_LRCK_R_LOW;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
val |= TEGRA20_I2S_CTRL_BIT_FORMAT_I2S;
|
||||
val |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
val |= TEGRA20_I2S_CTRL_BIT_FORMAT_RJM;
|
||||
val |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
val |= TEGRA20_I2S_CTRL_BIT_FORMAT_LJM;
|
||||
val |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL, mask, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra20_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct device *dev = dai->dev;
|
||||
struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int mask, val;
|
||||
int ret, sample_size, srate, i2sclock, bitcnt;
|
||||
|
||||
mask = TEGRA20_I2S_CTRL_BIT_SIZE_MASK;
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
val = TEGRA20_I2S_CTRL_BIT_SIZE_16;
|
||||
sample_size = 16;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
val = TEGRA20_I2S_CTRL_BIT_SIZE_24;
|
||||
sample_size = 24;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
val = TEGRA20_I2S_CTRL_BIT_SIZE_32;
|
||||
sample_size = 32;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mask |= TEGRA20_I2S_CTRL_FIFO_FORMAT_MASK;
|
||||
val |= TEGRA20_I2S_CTRL_FIFO_FORMAT_PACKED;
|
||||
|
||||
regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL, mask, val);
|
||||
|
||||
srate = params_rate(params);
|
||||
|
||||
/* Final "* 2" required by Tegra hardware */
|
||||
i2sclock = srate * params_channels(params) * sample_size * 2;
|
||||
|
||||
ret = clk_set_rate(i2s->clk_i2s, i2sclock);
|
||||
if (ret) {
|
||||
dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bitcnt = (i2sclock / (2 * srate)) - 1;
|
||||
if (bitcnt < 0 || bitcnt > TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US)
|
||||
return -EINVAL;
|
||||
val = bitcnt << TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
|
||||
|
||||
if (i2sclock % (2 * srate))
|
||||
val |= TEGRA20_I2S_TIMING_NON_SYM_ENABLE;
|
||||
|
||||
regmap_write(i2s->regmap, TEGRA20_I2S_TIMING, val);
|
||||
|
||||
regmap_write(i2s->regmap, TEGRA20_I2S_FIFO_SCR,
|
||||
TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS |
|
||||
TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra20_i2s_start_playback(struct tegra20_i2s *i2s)
|
||||
{
|
||||
regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL,
|
||||
TEGRA20_I2S_CTRL_FIFO1_ENABLE,
|
||||
TEGRA20_I2S_CTRL_FIFO1_ENABLE);
|
||||
}
|
||||
|
||||
static void tegra20_i2s_stop_playback(struct tegra20_i2s *i2s)
|
||||
{
|
||||
regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL,
|
||||
TEGRA20_I2S_CTRL_FIFO1_ENABLE, 0);
|
||||
}
|
||||
|
||||
static void tegra20_i2s_start_capture(struct tegra20_i2s *i2s)
|
||||
{
|
||||
regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL,
|
||||
TEGRA20_I2S_CTRL_FIFO2_ENABLE,
|
||||
TEGRA20_I2S_CTRL_FIFO2_ENABLE);
|
||||
}
|
||||
|
||||
static void tegra20_i2s_stop_capture(struct tegra20_i2s *i2s)
|
||||
{
|
||||
regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL,
|
||||
TEGRA20_I2S_CTRL_FIFO2_ENABLE, 0);
|
||||
}
|
||||
|
||||
static int tegra20_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct tegra20_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)
|
||||
tegra20_i2s_start_playback(i2s);
|
||||
else
|
||||
tegra20_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)
|
||||
tegra20_i2s_stop_playback(i2s);
|
||||
else
|
||||
tegra20_i2s_stop_capture(i2s);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra20_i2s_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct tegra20_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 const struct snd_soc_dai_ops tegra20_i2s_dai_ops = {
|
||||
.set_fmt = tegra20_i2s_set_fmt,
|
||||
.hw_params = tegra20_i2s_hw_params,
|
||||
.trigger = tegra20_i2s_trigger,
|
||||
};
|
||||
|
||||
static const struct snd_soc_dai_driver tegra20_i2s_dai_template = {
|
||||
.probe = tegra20_i2s_probe,
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.ops = &tegra20_i2s_dai_ops,
|
||||
.symmetric_rates = 1,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver tegra20_i2s_component = {
|
||||
.name = DRV_NAME,
|
||||
};
|
||||
|
||||
static bool tegra20_i2s_wr_rd_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case TEGRA20_I2S_CTRL:
|
||||
case TEGRA20_I2S_STATUS:
|
||||
case TEGRA20_I2S_TIMING:
|
||||
case TEGRA20_I2S_FIFO_SCR:
|
||||
case TEGRA20_I2S_PCM_CTRL:
|
||||
case TEGRA20_I2S_NW_CTRL:
|
||||
case TEGRA20_I2S_TDM_CTRL:
|
||||
case TEGRA20_I2S_TDM_TX_RX_CTRL:
|
||||
case TEGRA20_I2S_FIFO1:
|
||||
case TEGRA20_I2S_FIFO2:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool tegra20_i2s_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case TEGRA20_I2S_STATUS:
|
||||
case TEGRA20_I2S_FIFO_SCR:
|
||||
case TEGRA20_I2S_FIFO1:
|
||||
case TEGRA20_I2S_FIFO2:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool tegra20_i2s_precious_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case TEGRA20_I2S_FIFO1:
|
||||
case TEGRA20_I2S_FIFO2:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct regmap_config tegra20_i2s_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = TEGRA20_I2S_FIFO2,
|
||||
.writeable_reg = tegra20_i2s_wr_rd_reg,
|
||||
.readable_reg = tegra20_i2s_wr_rd_reg,
|
||||
.volatile_reg = tegra20_i2s_volatile_reg,
|
||||
.precious_reg = tegra20_i2s_precious_reg,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static int tegra20_i2s_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra20_i2s *i2s;
|
||||
struct resource *mem, *memregion;
|
||||
void __iomem *regs;
|
||||
int ret;
|
||||
|
||||
i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_i2s), GFP_KERNEL);
|
||||
if (!i2s) {
|
||||
dev_err(&pdev->dev, "Can't allocate tegra20_i2s\n");
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
dev_set_drvdata(&pdev->dev, i2s);
|
||||
|
||||
i2s->dai = tegra20_i2s_dai_template;
|
||||
i2s->dai.name = dev_name(&pdev->dev);
|
||||
|
||||
i2s->clk_i2s = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(i2s->clk_i2s)) {
|
||||
dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
|
||||
ret = PTR_ERR(i2s->clk_i2s);
|
||||
goto err;
|
||||
}
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!mem) {
|
||||
dev_err(&pdev->dev, "No memory resource\n");
|
||||
ret = -ENODEV;
|
||||
goto err_clk_put;
|
||||
}
|
||||
|
||||
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,
|
||||
&tegra20_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;
|
||||
}
|
||||
|
||||
i2s->capture_dma_data.addr = mem->start + TEGRA20_I2S_FIFO2;
|
||||
i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
i2s->capture_dma_data.maxburst = 4;
|
||||
|
||||
i2s->playback_dma_data.addr = mem->start + TEGRA20_I2S_FIFO1;
|
||||
i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
i2s->playback_dma_data.maxburst = 4;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
if (!pm_runtime_enabled(&pdev->dev)) {
|
||||
ret = tegra20_i2s_runtime_resume(&pdev->dev);
|
||||
if (ret)
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
ret = snd_soc_register_component(&pdev->dev, &tegra20_i2s_component,
|
||||
&i2s->dai, 1);
|
||||
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_component;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_component:
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
err_suspend:
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
tegra20_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 tegra20_i2s_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra20_i2s *i2s = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
tegra20_i2s_runtime_suspend(&pdev->dev);
|
||||
|
||||
tegra_pcm_platform_unregister(&pdev->dev);
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
|
||||
clk_put(i2s->clk_i2s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tegra20_i2s_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra20-i2s", },
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct dev_pm_ops tegra20_i2s_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(tegra20_i2s_runtime_suspend,
|
||||
tegra20_i2s_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver tegra20_i2s_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = tegra20_i2s_of_match,
|
||||
.pm = &tegra20_i2s_pm_ops,
|
||||
},
|
||||
.probe = tegra20_i2s_platform_probe,
|
||||
.remove = tegra20_i2s_platform_remove,
|
||||
};
|
||||
module_platform_driver(tegra20_i2s_driver);
|
||||
|
||||
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra20 I2S ASoC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra20_i2s_of_match);
|
163
sound/soc/tegra/tegra20_i2s.h
Normal file
163
sound/soc/tegra/tegra20_i2s.h
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* tegra20_i2s.h - Definitions for Tegra20 I2S driver
|
||||
*
|
||||
* Author: Stephen Warren <swarren@nvidia.com>
|
||||
* Copyright (C) 2010,2012 - NVIDIA, Inc.
|
||||
*
|
||||
* Based on code copyright/by:
|
||||
*
|
||||
* Copyright (c) 2009-2010, NVIDIA Corporation.
|
||||
* Scott Peterson <speterson@nvidia.com>
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Iliyan Malchev <malchev@google.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TEGRA20_I2S_H__
|
||||
#define __TEGRA20_I2S_H__
|
||||
|
||||
#include "tegra_pcm.h"
|
||||
|
||||
/* Register offsets from TEGRA20_I2S1_BASE and TEGRA20_I2S2_BASE */
|
||||
|
||||
#define TEGRA20_I2S_CTRL 0x00
|
||||
#define TEGRA20_I2S_STATUS 0x04
|
||||
#define TEGRA20_I2S_TIMING 0x08
|
||||
#define TEGRA20_I2S_FIFO_SCR 0x0c
|
||||
#define TEGRA20_I2S_PCM_CTRL 0x10
|
||||
#define TEGRA20_I2S_NW_CTRL 0x14
|
||||
#define TEGRA20_I2S_TDM_CTRL 0x20
|
||||
#define TEGRA20_I2S_TDM_TX_RX_CTRL 0x24
|
||||
#define TEGRA20_I2S_FIFO1 0x40
|
||||
#define TEGRA20_I2S_FIFO2 0x80
|
||||
|
||||
/* Fields in TEGRA20_I2S_CTRL */
|
||||
|
||||
#define TEGRA20_I2S_CTRL_FIFO2_TX_ENABLE (1 << 30)
|
||||
#define TEGRA20_I2S_CTRL_FIFO1_ENABLE (1 << 29)
|
||||
#define TEGRA20_I2S_CTRL_FIFO2_ENABLE (1 << 28)
|
||||
#define TEGRA20_I2S_CTRL_FIFO1_RX_ENABLE (1 << 27)
|
||||
#define TEGRA20_I2S_CTRL_FIFO_LPBK_ENABLE (1 << 26)
|
||||
#define TEGRA20_I2S_CTRL_MASTER_ENABLE (1 << 25)
|
||||
|
||||
#define TEGRA20_I2S_LRCK_LEFT_LOW 0
|
||||
#define TEGRA20_I2S_LRCK_RIGHT_LOW 1
|
||||
|
||||
#define TEGRA20_I2S_CTRL_LRCK_SHIFT 24
|
||||
#define TEGRA20_I2S_CTRL_LRCK_MASK (1 << TEGRA20_I2S_CTRL_LRCK_SHIFT)
|
||||
#define TEGRA20_I2S_CTRL_LRCK_L_LOW (TEGRA20_I2S_LRCK_LEFT_LOW << TEGRA20_I2S_CTRL_LRCK_SHIFT)
|
||||
#define TEGRA20_I2S_CTRL_LRCK_R_LOW (TEGRA20_I2S_LRCK_RIGHT_LOW << TEGRA20_I2S_CTRL_LRCK_SHIFT)
|
||||
|
||||
#define TEGRA20_I2S_BIT_FORMAT_I2S 0
|
||||
#define TEGRA20_I2S_BIT_FORMAT_RJM 1
|
||||
#define TEGRA20_I2S_BIT_FORMAT_LJM 2
|
||||
#define TEGRA20_I2S_BIT_FORMAT_DSP 3
|
||||
|
||||
#define TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT 10
|
||||
#define TEGRA20_I2S_CTRL_BIT_FORMAT_MASK (3 << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT)
|
||||
#define TEGRA20_I2S_CTRL_BIT_FORMAT_I2S (TEGRA20_I2S_BIT_FORMAT_I2S << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT)
|
||||
#define TEGRA20_I2S_CTRL_BIT_FORMAT_RJM (TEGRA20_I2S_BIT_FORMAT_RJM << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT)
|
||||
#define TEGRA20_I2S_CTRL_BIT_FORMAT_LJM (TEGRA20_I2S_BIT_FORMAT_LJM << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT)
|
||||
#define TEGRA20_I2S_CTRL_BIT_FORMAT_DSP (TEGRA20_I2S_BIT_FORMAT_DSP << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT)
|
||||
|
||||
#define TEGRA20_I2S_BIT_SIZE_16 0
|
||||
#define TEGRA20_I2S_BIT_SIZE_20 1
|
||||
#define TEGRA20_I2S_BIT_SIZE_24 2
|
||||
#define TEGRA20_I2S_BIT_SIZE_32 3
|
||||
|
||||
#define TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT 8
|
||||
#define TEGRA20_I2S_CTRL_BIT_SIZE_MASK (3 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT)
|
||||
#define TEGRA20_I2S_CTRL_BIT_SIZE_16 (TEGRA20_I2S_BIT_SIZE_16 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT)
|
||||
#define TEGRA20_I2S_CTRL_BIT_SIZE_20 (TEGRA20_I2S_BIT_SIZE_20 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT)
|
||||
#define TEGRA20_I2S_CTRL_BIT_SIZE_24 (TEGRA20_I2S_BIT_SIZE_24 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT)
|
||||
#define TEGRA20_I2S_CTRL_BIT_SIZE_32 (TEGRA20_I2S_BIT_SIZE_32 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT)
|
||||
|
||||
#define TEGRA20_I2S_FIFO_16_LSB 0
|
||||
#define TEGRA20_I2S_FIFO_20_LSB 1
|
||||
#define TEGRA20_I2S_FIFO_24_LSB 2
|
||||
#define TEGRA20_I2S_FIFO_32 3
|
||||
#define TEGRA20_I2S_FIFO_PACKED 7
|
||||
|
||||
#define TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT 4
|
||||
#define TEGRA20_I2S_CTRL_FIFO_FORMAT_MASK (7 << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
|
||||
#define TEGRA20_I2S_CTRL_FIFO_FORMAT_16_LSB (TEGRA20_I2S_FIFO_16_LSB << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
|
||||
#define TEGRA20_I2S_CTRL_FIFO_FORMAT_20_LSB (TEGRA20_I2S_FIFO_20_LSB << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
|
||||
#define TEGRA20_I2S_CTRL_FIFO_FORMAT_24_LSB (TEGRA20_I2S_FIFO_24_LSB << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
|
||||
#define TEGRA20_I2S_CTRL_FIFO_FORMAT_32 (TEGRA20_I2S_FIFO_32 << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
|
||||
#define TEGRA20_I2S_CTRL_FIFO_FORMAT_PACKED (TEGRA20_I2S_FIFO_PACKED << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
|
||||
|
||||
#define TEGRA20_I2S_CTRL_IE_FIFO1_ERR (1 << 3)
|
||||
#define TEGRA20_I2S_CTRL_IE_FIFO2_ERR (1 << 2)
|
||||
#define TEGRA20_I2S_CTRL_QE_FIFO1 (1 << 1)
|
||||
#define TEGRA20_I2S_CTRL_QE_FIFO2 (1 << 0)
|
||||
|
||||
/* Fields in TEGRA20_I2S_STATUS */
|
||||
|
||||
#define TEGRA20_I2S_STATUS_FIFO1_RDY (1 << 31)
|
||||
#define TEGRA20_I2S_STATUS_FIFO2_RDY (1 << 30)
|
||||
#define TEGRA20_I2S_STATUS_FIFO1_BSY (1 << 29)
|
||||
#define TEGRA20_I2S_STATUS_FIFO2_BSY (1 << 28)
|
||||
#define TEGRA20_I2S_STATUS_FIFO1_ERR (1 << 3)
|
||||
#define TEGRA20_I2S_STATUS_FIFO2_ERR (1 << 2)
|
||||
#define TEGRA20_I2S_STATUS_QS_FIFO1 (1 << 1)
|
||||
#define TEGRA20_I2S_STATUS_QS_FIFO2 (1 << 0)
|
||||
|
||||
/* Fields in TEGRA20_I2S_TIMING */
|
||||
|
||||
#define TEGRA20_I2S_TIMING_NON_SYM_ENABLE (1 << 12)
|
||||
#define TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT 0
|
||||
#define TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US 0x7ff
|
||||
#define TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK (TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT)
|
||||
|
||||
/* Fields in TEGRA20_I2S_FIFO_SCR */
|
||||
|
||||
#define TEGRA20_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT 24
|
||||
#define TEGRA20_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT 16
|
||||
#define TEGRA20_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK 0x3f
|
||||
|
||||
#define TEGRA20_I2S_FIFO_SCR_FIFO2_CLR (1 << 12)
|
||||
#define TEGRA20_I2S_FIFO_SCR_FIFO1_CLR (1 << 8)
|
||||
|
||||
#define TEGRA20_I2S_FIFO_ATN_LVL_ONE_SLOT 0
|
||||
#define TEGRA20_I2S_FIFO_ATN_LVL_FOUR_SLOTS 1
|
||||
#define TEGRA20_I2S_FIFO_ATN_LVL_EIGHT_SLOTS 2
|
||||
#define TEGRA20_I2S_FIFO_ATN_LVL_TWELVE_SLOTS 3
|
||||
|
||||
#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT 4
|
||||
#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK (3 << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_ONE_SLOT (TEGRA20_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_EIGHT_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_TWELVE_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
|
||||
|
||||
#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT 0
|
||||
#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK (3 << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_ONE_SLOT (TEGRA20_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_EIGHT_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_TWELVE_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
|
||||
|
||||
struct tegra20_i2s {
|
||||
struct snd_soc_dai_driver dai;
|
||||
struct clk *clk_i2s;
|
||||
struct snd_dmaengine_dai_dma_data capture_dma_data;
|
||||
struct snd_dmaengine_dai_dma_data playback_dma_data;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
#endif
|
402
sound/soc/tegra/tegra20_spdif.c
Normal file
402
sound/soc/tegra/tegra20_spdif.c
Normal file
|
@ -0,0 +1,402 @@
|
|||
/*
|
||||
* tegra20_spdif.c - Tegra20 SPDIF driver
|
||||
*
|
||||
* Author: Stephen Warren <swarren@nvidia.com>
|
||||
* Copyright (C) 2011-2012 - NVIDIA, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
#include "tegra20_spdif.h"
|
||||
|
||||
#define DRV_NAME "tegra20-spdif"
|
||||
|
||||
static int tegra20_spdif_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct tegra20_spdif *spdif = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(spdif->clk_spdif_out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra20_spdif_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct tegra20_spdif *spdif = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(spdif->clk_spdif_out);
|
||||
if (ret) {
|
||||
dev_err(dev, "clk_enable failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct device *dev = dai->dev;
|
||||
struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int mask = 0, val = 0;
|
||||
int ret, spdifclock;
|
||||
|
||||
mask |= TEGRA20_SPDIF_CTRL_PACK |
|
||||
TEGRA20_SPDIF_CTRL_BIT_MODE_MASK;
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
val |= TEGRA20_SPDIF_CTRL_PACK |
|
||||
TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, mask, val);
|
||||
|
||||
switch (params_rate(params)) {
|
||||
case 32000:
|
||||
spdifclock = 4096000;
|
||||
break;
|
||||
case 44100:
|
||||
spdifclock = 5644800;
|
||||
break;
|
||||
case 48000:
|
||||
spdifclock = 6144000;
|
||||
break;
|
||||
case 88200:
|
||||
spdifclock = 11289600;
|
||||
break;
|
||||
case 96000:
|
||||
spdifclock = 12288000;
|
||||
break;
|
||||
case 176400:
|
||||
spdifclock = 22579200;
|
||||
break;
|
||||
case 192000:
|
||||
spdifclock = 24576000;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);
|
||||
if (ret) {
|
||||
dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra20_spdif_start_playback(struct tegra20_spdif *spdif)
|
||||
{
|
||||
regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL,
|
||||
TEGRA20_SPDIF_CTRL_TX_EN,
|
||||
TEGRA20_SPDIF_CTRL_TX_EN);
|
||||
}
|
||||
|
||||
static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif)
|
||||
{
|
||||
regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL,
|
||||
TEGRA20_SPDIF_CTRL_TX_EN, 0);
|
||||
}
|
||||
|
||||
static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
tegra20_spdif_start_playback(spdif);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
tegra20_spdif_stop_playback(spdif);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra20_spdif_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
dai->capture_dma_data = NULL;
|
||||
dai->playback_dma_data = &spdif->playback_dma_data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = {
|
||||
.hw_params = tegra20_spdif_hw_params,
|
||||
.trigger = tegra20_spdif_trigger,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver tegra20_spdif_dai = {
|
||||
.name = DRV_NAME,
|
||||
.probe = tegra20_spdif_probe,
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
|
||||
SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.ops = &tegra20_spdif_dai_ops,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver tegra20_spdif_component = {
|
||||
.name = DRV_NAME,
|
||||
};
|
||||
|
||||
static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case TEGRA20_SPDIF_CTRL:
|
||||
case TEGRA20_SPDIF_STATUS:
|
||||
case TEGRA20_SPDIF_STROBE_CTRL:
|
||||
case TEGRA20_SPDIF_DATA_FIFO_CSR:
|
||||
case TEGRA20_SPDIF_DATA_OUT:
|
||||
case TEGRA20_SPDIF_DATA_IN:
|
||||
case TEGRA20_SPDIF_CH_STA_RX_A:
|
||||
case TEGRA20_SPDIF_CH_STA_RX_B:
|
||||
case TEGRA20_SPDIF_CH_STA_RX_C:
|
||||
case TEGRA20_SPDIF_CH_STA_RX_D:
|
||||
case TEGRA20_SPDIF_CH_STA_RX_E:
|
||||
case TEGRA20_SPDIF_CH_STA_RX_F:
|
||||
case TEGRA20_SPDIF_CH_STA_TX_A:
|
||||
case TEGRA20_SPDIF_CH_STA_TX_B:
|
||||
case TEGRA20_SPDIF_CH_STA_TX_C:
|
||||
case TEGRA20_SPDIF_CH_STA_TX_D:
|
||||
case TEGRA20_SPDIF_CH_STA_TX_E:
|
||||
case TEGRA20_SPDIF_CH_STA_TX_F:
|
||||
case TEGRA20_SPDIF_USR_STA_RX_A:
|
||||
case TEGRA20_SPDIF_USR_DAT_TX_A:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case TEGRA20_SPDIF_STATUS:
|
||||
case TEGRA20_SPDIF_DATA_FIFO_CSR:
|
||||
case TEGRA20_SPDIF_DATA_OUT:
|
||||
case TEGRA20_SPDIF_DATA_IN:
|
||||
case TEGRA20_SPDIF_CH_STA_RX_A:
|
||||
case TEGRA20_SPDIF_CH_STA_RX_B:
|
||||
case TEGRA20_SPDIF_CH_STA_RX_C:
|
||||
case TEGRA20_SPDIF_CH_STA_RX_D:
|
||||
case TEGRA20_SPDIF_CH_STA_RX_E:
|
||||
case TEGRA20_SPDIF_CH_STA_RX_F:
|
||||
case TEGRA20_SPDIF_USR_STA_RX_A:
|
||||
case TEGRA20_SPDIF_USR_DAT_TX_A:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case TEGRA20_SPDIF_DATA_OUT:
|
||||
case TEGRA20_SPDIF_DATA_IN:
|
||||
case TEGRA20_SPDIF_USR_STA_RX_A:
|
||||
case TEGRA20_SPDIF_USR_DAT_TX_A:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct regmap_config tegra20_spdif_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = TEGRA20_SPDIF_USR_DAT_TX_A,
|
||||
.writeable_reg = tegra20_spdif_wr_rd_reg,
|
||||
.readable_reg = tegra20_spdif_wr_rd_reg,
|
||||
.volatile_reg = tegra20_spdif_volatile_reg,
|
||||
.precious_reg = tegra20_spdif_precious_reg,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static int tegra20_spdif_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra20_spdif *spdif;
|
||||
struct resource *mem, *memregion, *dmareq;
|
||||
void __iomem *regs;
|
||||
int ret;
|
||||
|
||||
spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_spdif),
|
||||
GFP_KERNEL);
|
||||
if (!spdif) {
|
||||
dev_err(&pdev->dev, "Can't allocate tegra20_spdif\n");
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
dev_set_drvdata(&pdev->dev, spdif);
|
||||
|
||||
spdif->clk_spdif_out = clk_get(&pdev->dev, "spdif_out");
|
||||
if (IS_ERR(spdif->clk_spdif_out)) {
|
||||
pr_err("Can't retrieve spdif clock\n");
|
||||
ret = PTR_ERR(spdif->clk_spdif_out);
|
||||
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;
|
||||
}
|
||||
|
||||
dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!dmareq) {
|
||||
dev_err(&pdev->dev, "No DMA 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;
|
||||
}
|
||||
|
||||
spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
|
||||
&tegra20_spdif_regmap_config);
|
||||
if (IS_ERR(spdif->regmap)) {
|
||||
dev_err(&pdev->dev, "regmap init failed\n");
|
||||
ret = PTR_ERR(spdif->regmap);
|
||||
goto err_clk_put;
|
||||
}
|
||||
|
||||
spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT;
|
||||
spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
spdif->playback_dma_data.maxburst = 4;
|
||||
spdif->playback_dma_data.slave_id = dmareq->start;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
if (!pm_runtime_enabled(&pdev->dev)) {
|
||||
ret = tegra20_spdif_runtime_resume(&pdev->dev);
|
||||
if (ret)
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
ret = snd_soc_register_component(&pdev->dev, &tegra20_spdif_component,
|
||||
&tegra20_spdif_dai, 1);
|
||||
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_component;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_component:
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
err_suspend:
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
tegra20_spdif_runtime_suspend(&pdev->dev);
|
||||
err_pm_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
err_clk_put:
|
||||
clk_put(spdif->clk_spdif_out);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra20_spdif_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra20_spdif *spdif = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
tegra20_spdif_runtime_suspend(&pdev->dev);
|
||||
|
||||
tegra_pcm_platform_unregister(&pdev->dev);
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
|
||||
clk_put(spdif->clk_spdif_out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops tegra20_spdif_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend,
|
||||
tegra20_spdif_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver tegra20_spdif_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &tegra20_spdif_pm_ops,
|
||||
},
|
||||
.probe = tegra20_spdif_platform_probe,
|
||||
.remove = tegra20_spdif_platform_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(tegra20_spdif_driver);
|
||||
|
||||
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
470
sound/soc/tegra/tegra20_spdif.h
Normal file
470
sound/soc/tegra/tegra20_spdif.h
Normal file
|
@ -0,0 +1,470 @@
|
|||
/*
|
||||
* tegra20_spdif.h - Definitions for Tegra20 SPDIF driver
|
||||
*
|
||||
* Author: Stephen Warren <swarren@nvidia.com>
|
||||
* Copyright (C) 2011 - NVIDIA, Inc.
|
||||
*
|
||||
* Based on code copyright/by:
|
||||
* Copyright (c) 2008-2009, NVIDIA Corporation
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TEGRA20_SPDIF_H__
|
||||
#define __TEGRA20_SPDIF_H__
|
||||
|
||||
#include "tegra_pcm.h"
|
||||
|
||||
/* Offsets from TEGRA20_SPDIF_BASE */
|
||||
|
||||
#define TEGRA20_SPDIF_CTRL 0x0
|
||||
#define TEGRA20_SPDIF_STATUS 0x4
|
||||
#define TEGRA20_SPDIF_STROBE_CTRL 0x8
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR 0x0C
|
||||
#define TEGRA20_SPDIF_DATA_OUT 0x40
|
||||
#define TEGRA20_SPDIF_DATA_IN 0x80
|
||||
#define TEGRA20_SPDIF_CH_STA_RX_A 0x100
|
||||
#define TEGRA20_SPDIF_CH_STA_RX_B 0x104
|
||||
#define TEGRA20_SPDIF_CH_STA_RX_C 0x108
|
||||
#define TEGRA20_SPDIF_CH_STA_RX_D 0x10C
|
||||
#define TEGRA20_SPDIF_CH_STA_RX_E 0x110
|
||||
#define TEGRA20_SPDIF_CH_STA_RX_F 0x114
|
||||
#define TEGRA20_SPDIF_CH_STA_TX_A 0x140
|
||||
#define TEGRA20_SPDIF_CH_STA_TX_B 0x144
|
||||
#define TEGRA20_SPDIF_CH_STA_TX_C 0x148
|
||||
#define TEGRA20_SPDIF_CH_STA_TX_D 0x14C
|
||||
#define TEGRA20_SPDIF_CH_STA_TX_E 0x150
|
||||
#define TEGRA20_SPDIF_CH_STA_TX_F 0x154
|
||||
#define TEGRA20_SPDIF_USR_STA_RX_A 0x180
|
||||
#define TEGRA20_SPDIF_USR_DAT_TX_A 0x1C0
|
||||
|
||||
/* Fields in TEGRA20_SPDIF_CTRL */
|
||||
|
||||
/* Start capturing from 0=right, 1=left channel */
|
||||
#define TEGRA20_SPDIF_CTRL_CAP_LC (1 << 30)
|
||||
|
||||
/* SPDIF receiver(RX) enable */
|
||||
#define TEGRA20_SPDIF_CTRL_RX_EN (1 << 29)
|
||||
|
||||
/* SPDIF Transmitter(TX) enable */
|
||||
#define TEGRA20_SPDIF_CTRL_TX_EN (1 << 28)
|
||||
|
||||
/* Transmit Channel status */
|
||||
#define TEGRA20_SPDIF_CTRL_TC_EN (1 << 27)
|
||||
|
||||
/* Transmit user Data */
|
||||
#define TEGRA20_SPDIF_CTRL_TU_EN (1 << 26)
|
||||
|
||||
/* Interrupt on transmit error */
|
||||
#define TEGRA20_SPDIF_CTRL_IE_TXE (1 << 25)
|
||||
|
||||
/* Interrupt on receive error */
|
||||
#define TEGRA20_SPDIF_CTRL_IE_RXE (1 << 24)
|
||||
|
||||
/* Interrupt on invalid preamble */
|
||||
#define TEGRA20_SPDIF_CTRL_IE_P (1 << 23)
|
||||
|
||||
/* Interrupt on "B" preamble */
|
||||
#define TEGRA20_SPDIF_CTRL_IE_B (1 << 22)
|
||||
|
||||
/* Interrupt when block of channel status received */
|
||||
#define TEGRA20_SPDIF_CTRL_IE_C (1 << 21)
|
||||
|
||||
/* Interrupt when a valid information unit (IU) is received */
|
||||
#define TEGRA20_SPDIF_CTRL_IE_U (1 << 20)
|
||||
|
||||
/* Interrupt when RX user FIFO attention level is reached */
|
||||
#define TEGRA20_SPDIF_CTRL_QE_RU (1 << 19)
|
||||
|
||||
/* Interrupt when TX user FIFO attention level is reached */
|
||||
#define TEGRA20_SPDIF_CTRL_QE_TU (1 << 18)
|
||||
|
||||
/* Interrupt when RX data FIFO attention level is reached */
|
||||
#define TEGRA20_SPDIF_CTRL_QE_RX (1 << 17)
|
||||
|
||||
/* Interrupt when TX data FIFO attention level is reached */
|
||||
#define TEGRA20_SPDIF_CTRL_QE_TX (1 << 16)
|
||||
|
||||
/* Loopback test mode enable */
|
||||
#define TEGRA20_SPDIF_CTRL_LBK_EN (1 << 15)
|
||||
|
||||
/*
|
||||
* Pack data mode:
|
||||
* 0 = Single data (16 bit needs to be padded to match the
|
||||
* interface data bit size).
|
||||
* 1 = Packeted left/right channel data into a single word.
|
||||
*/
|
||||
#define TEGRA20_SPDIF_CTRL_PACK (1 << 14)
|
||||
|
||||
/*
|
||||
* 00 = 16bit data
|
||||
* 01 = 20bit data
|
||||
* 10 = 24bit data
|
||||
* 11 = raw data
|
||||
*/
|
||||
#define TEGRA20_SPDIF_BIT_MODE_16BIT 0
|
||||
#define TEGRA20_SPDIF_BIT_MODE_20BIT 1
|
||||
#define TEGRA20_SPDIF_BIT_MODE_24BIT 2
|
||||
#define TEGRA20_SPDIF_BIT_MODE_RAW 3
|
||||
|
||||
#define TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT 12
|
||||
#define TEGRA20_SPDIF_CTRL_BIT_MODE_MASK (3 << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT)
|
||||
#define TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT (TEGRA20_SPDIF_BIT_MODE_16BIT << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT)
|
||||
#define TEGRA20_SPDIF_CTRL_BIT_MODE_20BIT (TEGRA20_SPDIF_BIT_MODE_20BIT << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT)
|
||||
#define TEGRA20_SPDIF_CTRL_BIT_MODE_24BIT (TEGRA20_SPDIF_BIT_MODE_24BIT << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT)
|
||||
#define TEGRA20_SPDIF_CTRL_BIT_MODE_RAW (TEGRA20_SPDIF_BIT_MODE_RAW << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT)
|
||||
|
||||
/* Fields in TEGRA20_SPDIF_STATUS */
|
||||
|
||||
/*
|
||||
* Note: IS_P, IS_B, IS_C, and IS_U are sticky bits. Software must
|
||||
* write a 1 to the corresponding bit location to clear the status.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Receiver(RX) shifter is busy receiving data.
|
||||
* This bit is asserted when the receiver first locked onto the
|
||||
* preamble of the data stream after RX_EN is asserted. This bit is
|
||||
* deasserted when either,
|
||||
* (a) the end of a frame is reached after RX_EN is deeasserted, or
|
||||
* (b) the SPDIF data stream becomes inactive.
|
||||
*/
|
||||
#define TEGRA20_SPDIF_STATUS_RX_BSY (1 << 29)
|
||||
|
||||
/*
|
||||
* Transmitter(TX) shifter is busy transmitting data.
|
||||
* This bit is asserted when TX_EN is asserted.
|
||||
* This bit is deasserted when the end of a frame is reached after
|
||||
* TX_EN is deasserted.
|
||||
*/
|
||||
#define TEGRA20_SPDIF_STATUS_TX_BSY (1 << 28)
|
||||
|
||||
/*
|
||||
* TX is busy shifting out channel status.
|
||||
* This bit is asserted when both TX_EN and TC_EN are asserted and
|
||||
* data from CH_STA_TX_A register is loaded into the internal shifter.
|
||||
* This bit is deasserted when either,
|
||||
* (a) the end of a frame is reached after TX_EN is deasserted, or
|
||||
* (b) CH_STA_TX_F register is loaded into the internal shifter.
|
||||
*/
|
||||
#define TEGRA20_SPDIF_STATUS_TC_BSY (1 << 27)
|
||||
|
||||
/*
|
||||
* TX User data FIFO busy.
|
||||
* This bit is asserted when TX_EN and TXU_EN are asserted and
|
||||
* there's data in the TX user FIFO. This bit is deassert when either,
|
||||
* (a) the end of a frame is reached after TX_EN is deasserted, or
|
||||
* (b) there's no data left in the TX user FIFO.
|
||||
*/
|
||||
#define TEGRA20_SPDIF_STATUS_TU_BSY (1 << 26)
|
||||
|
||||
/* TX FIFO Underrun error status */
|
||||
#define TEGRA20_SPDIF_STATUS_TX_ERR (1 << 25)
|
||||
|
||||
/* RX FIFO Overrun error status */
|
||||
#define TEGRA20_SPDIF_STATUS_RX_ERR (1 << 24)
|
||||
|
||||
/* Preamble status: 0=Preamble OK, 1=bad/missing preamble */
|
||||
#define TEGRA20_SPDIF_STATUS_IS_P (1 << 23)
|
||||
|
||||
/* B-preamble detection status: 0=not detected, 1=B-preamble detected */
|
||||
#define TEGRA20_SPDIF_STATUS_IS_B (1 << 22)
|
||||
|
||||
/*
|
||||
* RX channel block data receive status:
|
||||
* 0=entire block not recieved yet.
|
||||
* 1=received entire block of channel status,
|
||||
*/
|
||||
#define TEGRA20_SPDIF_STATUS_IS_C (1 << 21)
|
||||
|
||||
/* RX User Data Valid flag: 1=valid IU detected, 0 = no IU detected. */
|
||||
#define TEGRA20_SPDIF_STATUS_IS_U (1 << 20)
|
||||
|
||||
/*
|
||||
* RX User FIFO Status:
|
||||
* 1=attention level reached, 0=attention level not reached.
|
||||
*/
|
||||
#define TEGRA20_SPDIF_STATUS_QS_RU (1 << 19)
|
||||
|
||||
/*
|
||||
* TX User FIFO Status:
|
||||
* 1=attention level reached, 0=attention level not reached.
|
||||
*/
|
||||
#define TEGRA20_SPDIF_STATUS_QS_TU (1 << 18)
|
||||
|
||||
/*
|
||||
* RX Data FIFO Status:
|
||||
* 1=attention level reached, 0=attention level not reached.
|
||||
*/
|
||||
#define TEGRA20_SPDIF_STATUS_QS_RX (1 << 17)
|
||||
|
||||
/*
|
||||
* TX Data FIFO Status:
|
||||
* 1=attention level reached, 0=attention level not reached.
|
||||
*/
|
||||
#define TEGRA20_SPDIF_STATUS_QS_TX (1 << 16)
|
||||
|
||||
/* Fields in TEGRA20_SPDIF_STROBE_CTRL */
|
||||
|
||||
/*
|
||||
* Indicates the approximate number of detected SPDIFIN clocks within a
|
||||
* bi-phase period.
|
||||
*/
|
||||
#define TEGRA20_SPDIF_STROBE_CTRL_PERIOD_SHIFT 16
|
||||
#define TEGRA20_SPDIF_STROBE_CTRL_PERIOD_MASK (0xff << TEGRA20_SPDIF_STROBE_CTRL_PERIOD_SHIFT)
|
||||
|
||||
/* Data strobe mode: 0=Auto-locked 1=Manual locked */
|
||||
#define TEGRA20_SPDIF_STROBE_CTRL_STROBE (1 << 15)
|
||||
|
||||
/*
|
||||
* Manual data strobe time within the bi-phase clock period (in terms of
|
||||
* the number of over-sampling clocks).
|
||||
*/
|
||||
#define TEGRA20_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT 8
|
||||
#define TEGRA20_SPDIF_STROBE_CTRL_DATA_STROBES_MASK (0x1f << TEGRA20_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT)
|
||||
|
||||
/*
|
||||
* Manual SPDIFIN bi-phase clock period (in terms of the number of
|
||||
* over-sampling clocks).
|
||||
*/
|
||||
#define TEGRA20_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT 0
|
||||
#define TEGRA20_SPDIF_STROBE_CTRL_CLOCK_PERIOD_MASK (0x3f << TEGRA20_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT)
|
||||
|
||||
/* Fields in SPDIF_DATA_FIFO_CSR */
|
||||
|
||||
/* Clear Receiver User FIFO (RX USR.FIFO) */
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_CLR (1 << 31)
|
||||
|
||||
#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT 0
|
||||
#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS 1
|
||||
#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS 2
|
||||
#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS 3
|
||||
|
||||
/* RU FIFO attention level */
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT 29
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_MASK \
|
||||
(0x3 << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU1_WORD_FULL \
|
||||
(TEGRA20_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU2_WORD_FULL \
|
||||
(TEGRA20_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU3_WORD_FULL \
|
||||
(TEGRA20_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU4_WORD_FULL \
|
||||
(TEGRA20_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
|
||||
|
||||
/* Number of RX USR.FIFO levels with valid data. */
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT 24
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_MASK (0x1f << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT)
|
||||
|
||||
/* Clear Transmitter User FIFO (TX USR.FIFO) */
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_CLR (1 << 23)
|
||||
|
||||
/* TU FIFO attention level */
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT 21
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_MASK \
|
||||
(0x3 << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU1_WORD_FULL \
|
||||
(TEGRA20_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU2_WORD_FULL \
|
||||
(TEGRA20_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU3_WORD_FULL \
|
||||
(TEGRA20_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU4_WORD_FULL \
|
||||
(TEGRA20_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
|
||||
|
||||
/* Number of TX USR.FIFO levels that could be filled. */
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT 16
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT)
|
||||
|
||||
/* Clear Receiver Data FIFO (RX DATA.FIFO) */
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_CLR (1 << 15)
|
||||
|
||||
#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT 0
|
||||
#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS 1
|
||||
#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS 2
|
||||
#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS 3
|
||||
|
||||
/* RU FIFO attention level */
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT 13
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_MASK \
|
||||
(0x3 << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU1_WORD_FULL \
|
||||
(TEGRA20_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU4_WORD_FULL \
|
||||
(TEGRA20_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU8_WORD_FULL \
|
||||
(TEGRA20_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU12_WORD_FULL \
|
||||
(TEGRA20_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
|
||||
|
||||
/* Number of RX DATA.FIFO levels with valid data. */
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT 8
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_MASK (0x1f << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT)
|
||||
|
||||
/* Clear Transmitter Data FIFO (TX DATA.FIFO) */
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_CLR (1 << 7)
|
||||
|
||||
/* TU FIFO attention level */
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT 5
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK \
|
||||
(0x3 << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU1_WORD_FULL \
|
||||
(TEGRA20_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL \
|
||||
(TEGRA20_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU8_WORD_FULL \
|
||||
(TEGRA20_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU12_WORD_FULL \
|
||||
(TEGRA20_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
|
||||
|
||||
/* Number of TX DATA.FIFO levels that could be filled. */
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT 0
|
||||
#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT)
|
||||
|
||||
/* Fields in TEGRA20_SPDIF_DATA_OUT */
|
||||
|
||||
/*
|
||||
* This register has 5 different formats:
|
||||
* 16-bit (BIT_MODE=00, PACK=0)
|
||||
* 20-bit (BIT_MODE=01, PACK=0)
|
||||
* 24-bit (BIT_MODE=10, PACK=0)
|
||||
* raw (BIT_MODE=11, PACK=0)
|
||||
* 16-bit packed (BIT_MODE=00, PACK=1)
|
||||
*/
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_16_SHIFT 0
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_16_MASK (0xffff << TEGRA20_SPDIF_DATA_OUT_DATA_16_SHIFT)
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_20_SHIFT 0
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_20_MASK (0xfffff << TEGRA20_SPDIF_DATA_OUT_DATA_20_SHIFT)
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_24_SHIFT 0
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_24_MASK (0xffffff << TEGRA20_SPDIF_DATA_OUT_DATA_24_SHIFT)
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_P (1 << 31)
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_C (1 << 30)
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_U (1 << 29)
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_V (1 << 28)
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT 8
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_DATA_MASK (0xfffff << TEGRA20_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT)
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT 4
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_AUX_MASK (0xf << TEGRA20_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT)
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT 0
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA20_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT)
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT 16
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT)
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT 0
|
||||
#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT)
|
||||
|
||||
/* Fields in TEGRA20_SPDIF_DATA_IN */
|
||||
|
||||
/*
|
||||
* This register has 5 different formats:
|
||||
* 16-bit (BIT_MODE=00, PACK=0)
|
||||
* 20-bit (BIT_MODE=01, PACK=0)
|
||||
* 24-bit (BIT_MODE=10, PACK=0)
|
||||
* raw (BIT_MODE=11, PACK=0)
|
||||
* 16-bit packed (BIT_MODE=00, PACK=1)
|
||||
*
|
||||
* Bits 31:24 are common to all modes except 16-bit packed
|
||||
*/
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_P (1 << 31)
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_C (1 << 30)
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_U (1 << 29)
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_V (1 << 28)
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT 24
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_PREAMBLE_MASK (0xf << TEGRA20_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT)
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_16_SHIFT 0
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_16_MASK (0xffff << TEGRA20_SPDIF_DATA_IN_DATA_16_SHIFT)
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_20_SHIFT 0
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_20_MASK (0xfffff << TEGRA20_SPDIF_DATA_IN_DATA_20_SHIFT)
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_24_SHIFT 0
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_24_MASK (0xffffff << TEGRA20_SPDIF_DATA_IN_DATA_24_SHIFT)
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT 8
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_DATA_MASK (0xfffff << TEGRA20_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT)
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT 4
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_AUX_MASK (0xf << TEGRA20_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT)
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT 0
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA20_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT)
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT 16
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT)
|
||||
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT 0
|
||||
#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT)
|
||||
|
||||
/* Fields in TEGRA20_SPDIF_CH_STA_RX_A */
|
||||
/* Fields in TEGRA20_SPDIF_CH_STA_RX_B */
|
||||
/* Fields in TEGRA20_SPDIF_CH_STA_RX_C */
|
||||
/* Fields in TEGRA20_SPDIF_CH_STA_RX_D */
|
||||
/* Fields in TEGRA20_SPDIF_CH_STA_RX_E */
|
||||
/* Fields in TEGRA20_SPDIF_CH_STA_RX_F */
|
||||
|
||||
/*
|
||||
* The 6-word receive channel data page buffer holds a block (192 frames) of
|
||||
* channel status information. The order of receive is from LSB to MSB
|
||||
* bit, and from CH_STA_RX_A to CH_STA_RX_F then back to CH_STA_RX_A.
|
||||
*/
|
||||
|
||||
/* Fields in TEGRA20_SPDIF_CH_STA_TX_A */
|
||||
/* Fields in TEGRA20_SPDIF_CH_STA_TX_B */
|
||||
/* Fields in TEGRA20_SPDIF_CH_STA_TX_C */
|
||||
/* Fields in TEGRA20_SPDIF_CH_STA_TX_D */
|
||||
/* Fields in TEGRA20_SPDIF_CH_STA_TX_E */
|
||||
/* Fields in TEGRA20_SPDIF_CH_STA_TX_F */
|
||||
|
||||
/*
|
||||
* The 6-word transmit channel data page buffer holds a block (192 frames) of
|
||||
* channel status information. The order of transmission is from LSB to MSB
|
||||
* bit, and from CH_STA_TX_A to CH_STA_TX_F then back to CH_STA_TX_A.
|
||||
*/
|
||||
|
||||
/* Fields in TEGRA20_SPDIF_USR_STA_RX_A */
|
||||
|
||||
/*
|
||||
* This 4-word deep FIFO receives user FIFO field information. The order of
|
||||
* receive is from LSB to MSB bit.
|
||||
*/
|
||||
|
||||
/* Fields in TEGRA20_SPDIF_USR_DAT_TX_A */
|
||||
|
||||
/*
|
||||
* This 4-word deep FIFO transmits user FIFO field information. The order of
|
||||
* transmission is from LSB to MSB bit.
|
||||
*/
|
||||
|
||||
struct tegra20_spdif {
|
||||
struct clk *clk_spdif_out;
|
||||
struct snd_dmaengine_dai_dma_data capture_dma_data;
|
||||
struct snd_dmaengine_dai_dma_data playback_dma_data;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
#endif
|
801
sound/soc/tegra/tegra30_ahub.c
Normal file
801
sound/soc/tegra/tegra30_ahub.c
Normal file
|
@ -0,0 +1,801 @@
|
|||
/*
|
||||
* tegra30_ahub.c - Tegra30 AHUB driver
|
||||
*
|
||||
* Copyright (c) 2011,2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/soc.h>
|
||||
#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_unprepare(ahub->clk_apbif);
|
||||
clk_disable_unprepare(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_prepare_enable(ahub->clk_d_audio);
|
||||
if (ret) {
|
||||
dev_err(dev, "clk_enable d_audio failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = clk_prepare_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,
|
||||
char *dmachan, int dmachan_len,
|
||||
dma_addr_t *fiforeg)
|
||||
{
|
||||
int channel;
|
||||
u32 reg, val;
|
||||
struct tegra30_ahub_cif_conf cif_conf;
|
||||
|
||||
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;
|
||||
snprintf(dmachan, dmachan_len, "rx%d", channel);
|
||||
*fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_RXFIFO +
|
||||
(channel * TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE);
|
||||
|
||||
pm_runtime_get_sync(ahub->dev);
|
||||
|
||||
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);
|
||||
|
||||
cif_conf.threshold = 0;
|
||||
cif_conf.audio_channels = 2;
|
||||
cif_conf.client_channels = 2;
|
||||
cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16;
|
||||
cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16;
|
||||
cif_conf.expand = 0;
|
||||
cif_conf.stereo_conv = 0;
|
||||
cif_conf.replicate = 0;
|
||||
cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_RX;
|
||||
cif_conf.truncate = 0;
|
||||
cif_conf.mono_conv = 0;
|
||||
|
||||
reg = TEGRA30_AHUB_CIF_RX_CTRL +
|
||||
(channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE);
|
||||
ahub->soc_data->set_audio_cif(ahub->regmap_apbif, reg, &cif_conf);
|
||||
|
||||
pm_runtime_put(ahub->dev);
|
||||
|
||||
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;
|
||||
|
||||
pm_runtime_get_sync(ahub->dev);
|
||||
|
||||
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);
|
||||
|
||||
pm_runtime_put(ahub->dev);
|
||||
|
||||
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;
|
||||
|
||||
pm_runtime_get_sync(ahub->dev);
|
||||
|
||||
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);
|
||||
|
||||
pm_runtime_put(ahub->dev);
|
||||
|
||||
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,
|
||||
char *dmachan, int dmachan_len,
|
||||
dma_addr_t *fiforeg)
|
||||
{
|
||||
int channel;
|
||||
u32 reg, val;
|
||||
struct tegra30_ahub_cif_conf cif_conf;
|
||||
|
||||
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;
|
||||
snprintf(dmachan, dmachan_len, "tx%d", channel);
|
||||
*fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_TXFIFO +
|
||||
(channel * TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE);
|
||||
|
||||
pm_runtime_get_sync(ahub->dev);
|
||||
|
||||
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);
|
||||
|
||||
cif_conf.threshold = 0;
|
||||
cif_conf.audio_channels = 2;
|
||||
cif_conf.client_channels = 2;
|
||||
cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16;
|
||||
cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16;
|
||||
cif_conf.expand = 0;
|
||||
cif_conf.stereo_conv = 0;
|
||||
cif_conf.replicate = 0;
|
||||
cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_TX;
|
||||
cif_conf.truncate = 0;
|
||||
cif_conf.mono_conv = 0;
|
||||
|
||||
reg = TEGRA30_AHUB_CIF_TX_CTRL +
|
||||
(channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE);
|
||||
ahub->soc_data->set_audio_cif(ahub->regmap_apbif, reg, &cif_conf);
|
||||
|
||||
pm_runtime_put(ahub->dev);
|
||||
|
||||
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;
|
||||
|
||||
pm_runtime_get_sync(ahub->dev);
|
||||
|
||||
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);
|
||||
|
||||
pm_runtime_put(ahub->dev);
|
||||
|
||||
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;
|
||||
|
||||
pm_runtime_get_sync(ahub->dev);
|
||||
|
||||
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);
|
||||
|
||||
pm_runtime_put(ahub->dev);
|
||||
|
||||
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;
|
||||
|
||||
pm_runtime_get_sync(ahub->dev);
|
||||
|
||||
reg = TEGRA30_AHUB_AUDIO_RX +
|
||||
(channel * TEGRA30_AHUB_AUDIO_RX_STRIDE);
|
||||
tegra30_audio_write(reg, 1 << txcif);
|
||||
|
||||
pm_runtime_put(ahub->dev);
|
||||
|
||||
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;
|
||||
|
||||
pm_runtime_get_sync(ahub->dev);
|
||||
|
||||
reg = TEGRA30_AHUB_AUDIO_RX +
|
||||
(channel * TEGRA30_AHUB_AUDIO_RX_STRIDE);
|
||||
tegra30_audio_write(reg, 0);
|
||||
|
||||
pm_runtime_put(ahub->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra30_ahub_unset_rx_cif_source);
|
||||
|
||||
#define MOD_LIST_MASK_TEGRA30 BIT(0)
|
||||
#define MOD_LIST_MASK_TEGRA114 BIT(1)
|
||||
#define MOD_LIST_MASK_TEGRA124 BIT(2)
|
||||
|
||||
#define MOD_LIST_MASK_TEGRA30_OR_LATER \
|
||||
(MOD_LIST_MASK_TEGRA30 | MOD_LIST_MASK_TEGRA114 | \
|
||||
MOD_LIST_MASK_TEGRA124)
|
||||
#define MOD_LIST_MASK_TEGRA114_OR_LATER \
|
||||
(MOD_LIST_MASK_TEGRA114 | MOD_LIST_MASK_TEGRA124)
|
||||
|
||||
static const struct {
|
||||
const char *rst_name;
|
||||
u32 mod_list_mask;
|
||||
} configlink_mods[] = {
|
||||
{ "i2s0", MOD_LIST_MASK_TEGRA30_OR_LATER },
|
||||
{ "i2s1", MOD_LIST_MASK_TEGRA30_OR_LATER },
|
||||
{ "i2s2", MOD_LIST_MASK_TEGRA30_OR_LATER },
|
||||
{ "i2s3", MOD_LIST_MASK_TEGRA30_OR_LATER },
|
||||
{ "i2s4", MOD_LIST_MASK_TEGRA30_OR_LATER },
|
||||
{ "dam0", MOD_LIST_MASK_TEGRA30_OR_LATER },
|
||||
{ "dam1", MOD_LIST_MASK_TEGRA30_OR_LATER },
|
||||
{ "dam2", MOD_LIST_MASK_TEGRA30_OR_LATER },
|
||||
{ "spdif", MOD_LIST_MASK_TEGRA30_OR_LATER },
|
||||
{ "amx", MOD_LIST_MASK_TEGRA114_OR_LATER },
|
||||
{ "adx", MOD_LIST_MASK_TEGRA114_OR_LATER },
|
||||
{ "amx1", MOD_LIST_MASK_TEGRA124 },
|
||||
{ "adx1", MOD_LIST_MASK_TEGRA124 },
|
||||
{ "afc0", MOD_LIST_MASK_TEGRA124 },
|
||||
{ "afc1", MOD_LIST_MASK_TEGRA124 },
|
||||
{ "afc2", MOD_LIST_MASK_TEGRA124 },
|
||||
{ "afc3", MOD_LIST_MASK_TEGRA124 },
|
||||
{ "afc4", MOD_LIST_MASK_TEGRA124 },
|
||||
{ "afc5", MOD_LIST_MASK_TEGRA124 },
|
||||
};
|
||||
|
||||
#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_FLAT,
|
||||
};
|
||||
|
||||
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_FLAT,
|
||||
};
|
||||
|
||||
static struct tegra30_ahub_soc_data soc_data_tegra30 = {
|
||||
.mod_list_mask = MOD_LIST_MASK_TEGRA30,
|
||||
.set_audio_cif = tegra30_ahub_set_cif,
|
||||
};
|
||||
|
||||
static struct tegra30_ahub_soc_data soc_data_tegra114 = {
|
||||
.mod_list_mask = MOD_LIST_MASK_TEGRA114,
|
||||
.set_audio_cif = tegra30_ahub_set_cif,
|
||||
};
|
||||
|
||||
static struct tegra30_ahub_soc_data soc_data_tegra124 = {
|
||||
.mod_list_mask = MOD_LIST_MASK_TEGRA124,
|
||||
.set_audio_cif = tegra124_ahub_set_cif,
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra30_ahub_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra124-ahub", .data = &soc_data_tegra124 },
|
||||
{ .compatible = "nvidia,tegra114-ahub", .data = &soc_data_tegra114 },
|
||||
{ .compatible = "nvidia,tegra30-ahub", .data = &soc_data_tegra30 },
|
||||
{},
|
||||
};
|
||||
|
||||
static int tegra30_ahub_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
const struct tegra30_ahub_soc_data *soc_data;
|
||||
struct reset_control *rst;
|
||||
int i;
|
||||
struct resource *res0, *res1, *region;
|
||||
void __iomem *regs_apbif, *regs_ahub;
|
||||
int ret = 0;
|
||||
|
||||
if (ahub)
|
||||
return -ENODEV;
|
||||
|
||||
match = of_match_device(tegra30_ahub_of_match, &pdev->dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
soc_data = match->data;
|
||||
|
||||
/*
|
||||
* 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_mods); i++) {
|
||||
if (!(configlink_mods[i].mod_list_mask &
|
||||
soc_data->mod_list_mask))
|
||||
continue;
|
||||
|
||||
rst = reset_control_get(&pdev->dev,
|
||||
configlink_mods[i].rst_name);
|
||||
if (IS_ERR(rst)) {
|
||||
dev_err(&pdev->dev, "Can't get reset %s\n",
|
||||
configlink_mods[i].rst_name);
|
||||
ret = PTR_ERR(rst);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = reset_control_deassert(rst);
|
||||
reset_control_put(rst);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
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->soc_data = soc_data;
|
||||
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;
|
||||
}
|
||||
|
||||
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, NULL, &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 = NULL;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int 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 = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tegra30_ahub_suspend(struct device *dev)
|
||||
{
|
||||
regcache_mark_dirty(ahub->regmap_ahub);
|
||||
regcache_mark_dirty(ahub->regmap_apbif);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra30_ahub_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regcache_sync(ahub->regmap_ahub);
|
||||
ret |= regcache_sync(ahub->regmap_apbif);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops tegra30_ahub_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(tegra30_ahub_runtime_suspend,
|
||||
tegra30_ahub_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tegra30_ahub_suspend, tegra30_ahub_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver tegra30_ahub_driver = {
|
||||
.probe = tegra30_ahub_probe,
|
||||
.remove = 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);
|
||||
|
||||
void tegra30_ahub_set_cif(struct regmap *regmap, unsigned int reg,
|
||||
struct tegra30_ahub_cif_conf *conf)
|
||||
{
|
||||
unsigned int value;
|
||||
|
||||
value = (conf->threshold <<
|
||||
TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
|
||||
((conf->audio_channels - 1) <<
|
||||
TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
|
||||
((conf->client_channels - 1) <<
|
||||
TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
|
||||
(conf->audio_bits <<
|
||||
TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) |
|
||||
(conf->client_bits <<
|
||||
TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) |
|
||||
(conf->expand <<
|
||||
TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) |
|
||||
(conf->stereo_conv <<
|
||||
TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) |
|
||||
(conf->replicate <<
|
||||
TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT) |
|
||||
(conf->direction <<
|
||||
TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) |
|
||||
(conf->truncate <<
|
||||
TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) |
|
||||
(conf->mono_conv <<
|
||||
TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT);
|
||||
|
||||
regmap_write(regmap, reg, value);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra30_ahub_set_cif);
|
||||
|
||||
void tegra124_ahub_set_cif(struct regmap *regmap, unsigned int reg,
|
||||
struct tegra30_ahub_cif_conf *conf)
|
||||
{
|
||||
unsigned int value;
|
||||
|
||||
value = (conf->threshold <<
|
||||
TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
|
||||
((conf->audio_channels - 1) <<
|
||||
TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
|
||||
((conf->client_channels - 1) <<
|
||||
TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
|
||||
(conf->audio_bits <<
|
||||
TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) |
|
||||
(conf->client_bits <<
|
||||
TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) |
|
||||
(conf->expand <<
|
||||
TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) |
|
||||
(conf->stereo_conv <<
|
||||
TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) |
|
||||
(conf->replicate <<
|
||||
TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT) |
|
||||
(conf->direction <<
|
||||
TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) |
|
||||
(conf->truncate <<
|
||||
TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) |
|
||||
(conf->mono_conv <<
|
||||
TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT);
|
||||
|
||||
regmap_write(regmap, reg, value);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra124_ahub_set_cif);
|
||||
|
||||
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra30 AHUB driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra30_ahub_of_match);
|
534
sound/soc/tegra/tegra30_ahub.h
Normal file
534
sound/soc/tegra/tegra30_ahub.h
Normal file
|
@ -0,0 +1,534 @@
|
|||
/*
|
||||
* tegra30_ahub.h - Definitions for Tegra30 AHUB driver
|
||||
*
|
||||
* Copyright (c) 2011,2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __TEGRA30_AHUB_H__
|
||||
#define __TEGRA30_AHUB_H__
|
||||
|
||||
/* Fields in *_CIF_RX/TX_CTRL; used by AHUB FIFOs, and all other audio modules */
|
||||
|
||||
#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT 28
|
||||
#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US 0xf
|
||||
#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK (TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT)
|
||||
|
||||
#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT 24
|
||||
#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US 0x3f
|
||||
#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK (TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US << TEGRA124_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 TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT 20
|
||||
#define TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US 0xf
|
||||
#define TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK (TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US << TEGRA124_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)
|
||||
|
||||
/* Channel count minus 1 */
|
||||
#define TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT 16
|
||||
#define TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US 0xf
|
||||
#define TEGRA124_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_SHIFT 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,
|
||||
char *dmachan, int dmachan_len,
|
||||
dma_addr_t *fiforeg);
|
||||
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,
|
||||
char *dmachan, int dmachan_len,
|
||||
dma_addr_t *fiforeg);
|
||||
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_cif_conf {
|
||||
unsigned int threshold;
|
||||
unsigned int audio_channels;
|
||||
unsigned int client_channels;
|
||||
unsigned int audio_bits;
|
||||
unsigned int client_bits;
|
||||
unsigned int expand;
|
||||
unsigned int stereo_conv;
|
||||
unsigned int replicate;
|
||||
unsigned int direction;
|
||||
unsigned int truncate;
|
||||
unsigned int mono_conv;
|
||||
};
|
||||
|
||||
void tegra30_ahub_set_cif(struct regmap *regmap, unsigned int reg,
|
||||
struct tegra30_ahub_cif_conf *conf);
|
||||
void tegra124_ahub_set_cif(struct regmap *regmap, unsigned int reg,
|
||||
struct tegra30_ahub_cif_conf *conf);
|
||||
|
||||
struct tegra30_ahub_soc_data {
|
||||
u32 mod_list_mask;
|
||||
void (*set_audio_cif)(struct regmap *regmap,
|
||||
unsigned int reg,
|
||||
struct tegra30_ahub_cif_conf *conf);
|
||||
/*
|
||||
* FIXME: There are many more differences in HW, such as:
|
||||
* - More APBIF channels.
|
||||
* - Extra separate chunks of register address space to represent
|
||||
* the extra APBIF channels.
|
||||
* - More units connected to the AHUB, so that tegra30_ahub_[rt]xcif
|
||||
* need expansion, coupled with there being more defined bits in
|
||||
* the AHUB routing registers.
|
||||
* However, the driver doesn't support those new features yet, so we
|
||||
* don't represent them here yet.
|
||||
*/
|
||||
};
|
||||
|
||||
struct tegra30_ahub {
|
||||
const struct tegra30_ahub_soc_data *soc_data;
|
||||
struct device *dev;
|
||||
struct clk *clk_d_audio;
|
||||
struct clk *clk_apbif;
|
||||
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
|
601
sound/soc/tegra/tegra30_i2s.c
Normal file
601
sound/soc/tegra/tegra30_i2s.c
Normal file
|
@ -0,0 +1,601 @@
|
|||
/*
|
||||
* tegra30_i2s.c - Tegra30 I2S driver
|
||||
*
|
||||
* Author: Stephen Warren <swarren@nvidia.com>
|
||||
* Copyright (c) 2010-2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Based on code copyright/by:
|
||||
*
|
||||
* Copyright (c) 2009-2010, NVIDIA Corporation.
|
||||
* Scott Peterson <speterson@nvidia.com>
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Iliyan Malchev <malchev@google.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
#include "tegra30_ahub.h"
|
||||
#include "tegra30_i2s.h"
|
||||
|
||||
#define DRV_NAME "tegra30-i2s"
|
||||
|
||||
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_unprepare(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_prepare_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;
|
||||
}
|
||||
|
||||
static int tegra30_i2s_set_fmt(struct snd_soc_dai *dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int mask = 0, val = 0;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mask |= TEGRA30_I2S_CTRL_MASTER_ENABLE;
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
val |= TEGRA30_I2S_CTRL_MASTER_ENABLE;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mask |= TEGRA30_I2S_CTRL_FRAME_FORMAT_MASK |
|
||||
TEGRA30_I2S_CTRL_LRCK_MASK;
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
val |= TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC;
|
||||
val |= TEGRA30_I2S_CTRL_LRCK_L_LOW;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
val |= TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC;
|
||||
val |= TEGRA30_I2S_CTRL_LRCK_R_LOW;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
val |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK;
|
||||
val |= TEGRA30_I2S_CTRL_LRCK_L_LOW;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
val |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK;
|
||||
val |= TEGRA30_I2S_CTRL_LRCK_L_LOW;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
val |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK;
|
||||
val |= TEGRA30_I2S_CTRL_LRCK_L_LOW;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(dai->dev);
|
||||
regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL, mask, val);
|
||||
pm_runtime_put(dai->dev);
|
||||
|
||||
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 = dai->dev;
|
||||
struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int mask, val, reg;
|
||||
int ret, sample_size, srate, i2sclock, bitcnt;
|
||||
struct tegra30_ahub_cif_conf cif_conf;
|
||||
|
||||
if (params_channels(params) != 2)
|
||||
return -EINVAL;
|
||||
|
||||
mask = TEGRA30_I2S_CTRL_BIT_SIZE_MASK;
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
val = TEGRA30_I2S_CTRL_BIT_SIZE_16;
|
||||
sample_size = 16;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL, mask, val);
|
||||
|
||||
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;
|
||||
|
||||
regmap_write(i2s->regmap, TEGRA30_I2S_TIMING, val);
|
||||
|
||||
cif_conf.threshold = 0;
|
||||
cif_conf.audio_channels = 2;
|
||||
cif_conf.client_channels = 2;
|
||||
cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16;
|
||||
cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16;
|
||||
cif_conf.expand = 0;
|
||||
cif_conf.stereo_conv = 0;
|
||||
cif_conf.replicate = 0;
|
||||
cif_conf.truncate = 0;
|
||||
cif_conf.mono_conv = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_RX;
|
||||
reg = TEGRA30_I2S_CIF_RX_CTRL;
|
||||
} else {
|
||||
cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_TX;
|
||||
reg = TEGRA30_I2S_CIF_TX_CTRL;
|
||||
}
|
||||
|
||||
i2s->soc_data->set_audio_cif(i2s->regmap, reg, &cif_conf);
|
||||
|
||||
val = (1 << TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT) |
|
||||
(1 << TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT);
|
||||
regmap_write(i2s->regmap, 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);
|
||||
regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL,
|
||||
TEGRA30_I2S_CTRL_XFER_EN_TX,
|
||||
TEGRA30_I2S_CTRL_XFER_EN_TX);
|
||||
}
|
||||
|
||||
static void tegra30_i2s_stop_playback(struct tegra30_i2s *i2s)
|
||||
{
|
||||
tegra30_ahub_disable_tx_fifo(i2s->playback_fifo_cif);
|
||||
regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL,
|
||||
TEGRA30_I2S_CTRL_XFER_EN_TX, 0);
|
||||
}
|
||||
|
||||
static void tegra30_i2s_start_capture(struct tegra30_i2s *i2s)
|
||||
{
|
||||
tegra30_ahub_enable_rx_fifo(i2s->capture_fifo_cif);
|
||||
regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL,
|
||||
TEGRA30_I2S_CTRL_XFER_EN_RX,
|
||||
TEGRA30_I2S_CTRL_XFER_EN_RX);
|
||||
}
|
||||
|
||||
static void tegra30_i2s_stop_capture(struct tegra30_i2s *i2s)
|
||||
{
|
||||
tegra30_ahub_disable_rx_fifo(i2s->capture_fifo_cif);
|
||||
regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL,
|
||||
TEGRA30_I2S_CTRL_XFER_EN_RX, 0);
|
||||
}
|
||||
|
||||
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 = {
|
||||
.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 = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "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 const struct snd_soc_component_driver tegra30_i2s_component = {
|
||||
.name = DRV_NAME,
|
||||
};
|
||||
|
||||
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_FLAT,
|
||||
};
|
||||
|
||||
static const struct tegra30_i2s_soc_data tegra30_i2s_config = {
|
||||
.set_audio_cif = tegra30_ahub_set_cif,
|
||||
};
|
||||
|
||||
static const struct tegra30_i2s_soc_data tegra124_i2s_config = {
|
||||
.set_audio_cif = tegra124_ahub_set_cif,
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra30_i2s_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra124-i2s", .data = &tegra124_i2s_config },
|
||||
{ .compatible = "nvidia,tegra30-i2s", .data = &tegra30_i2s_config },
|
||||
{},
|
||||
};
|
||||
|
||||
static int tegra30_i2s_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra30_i2s *i2s;
|
||||
const struct of_device_id *match;
|
||||
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);
|
||||
|
||||
match = of_match_device(tegra30_i2s_of_match, &pdev->dev);
|
||||
if (!match) {
|
||||
dev_err(&pdev->dev, "Error: No device match found\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
i2s->soc_data = (struct tegra30_i2s_soc_data *)match->data;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
i2s->playback_dma_data.maxburst = 4;
|
||||
ret = tegra30_ahub_allocate_tx_fifo(&i2s->playback_fifo_cif,
|
||||
i2s->playback_dma_chan,
|
||||
sizeof(i2s->playback_dma_chan),
|
||||
&i2s->playback_dma_data.addr);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Could not alloc TX FIFO: %d\n", ret);
|
||||
goto err_suspend;
|
||||
}
|
||||
ret = tegra30_ahub_set_rx_cif_source(i2s->playback_i2s_cif,
|
||||
i2s->playback_fifo_cif);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Could not route TX FIFO: %d\n", ret);
|
||||
goto err_free_tx_fifo;
|
||||
}
|
||||
|
||||
i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
i2s->capture_dma_data.maxburst = 4;
|
||||
ret = tegra30_ahub_allocate_rx_fifo(&i2s->capture_fifo_cif,
|
||||
i2s->capture_dma_chan,
|
||||
sizeof(i2s->capture_dma_chan),
|
||||
&i2s->capture_dma_data.addr);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Could not alloc RX FIFO: %d\n", ret);
|
||||
goto err_unroute_tx_fifo;
|
||||
}
|
||||
ret = tegra30_ahub_set_rx_cif_source(i2s->capture_fifo_cif,
|
||||
i2s->capture_i2s_cif);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Could not route TX FIFO: %d\n", ret);
|
||||
goto err_free_rx_fifo;
|
||||
}
|
||||
|
||||
ret = snd_soc_register_component(&pdev->dev, &tegra30_i2s_component,
|
||||
&i2s->dai, 1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
|
||||
ret = -ENOMEM;
|
||||
goto err_unroute_rx_fifo;
|
||||
}
|
||||
|
||||
ret = tegra_pcm_platform_register_with_chan_names(&pdev->dev,
|
||||
&i2s->dma_config, i2s->playback_dma_chan,
|
||||
i2s->capture_dma_chan);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
|
||||
goto err_unregister_component;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_component:
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
err_unroute_rx_fifo:
|
||||
tegra30_ahub_unset_rx_cif_source(i2s->capture_fifo_cif);
|
||||
err_free_rx_fifo:
|
||||
tegra30_ahub_free_rx_fifo(i2s->capture_fifo_cif);
|
||||
err_unroute_tx_fifo:
|
||||
tegra30_ahub_unset_rx_cif_source(i2s->playback_i2s_cif);
|
||||
err_free_tx_fifo:
|
||||
tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif);
|
||||
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 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_component(&pdev->dev);
|
||||
|
||||
tegra30_ahub_unset_rx_cif_source(i2s->capture_fifo_cif);
|
||||
tegra30_ahub_free_rx_fifo(i2s->capture_fifo_cif);
|
||||
|
||||
tegra30_ahub_unset_rx_cif_source(i2s->playback_i2s_cif);
|
||||
tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif);
|
||||
|
||||
clk_put(i2s->clk_i2s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tegra30_i2s_suspend(struct device *dev)
|
||||
{
|
||||
struct tegra30_i2s *i2s = dev_get_drvdata(dev);
|
||||
|
||||
regcache_mark_dirty(i2s->regmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra30_i2s_resume(struct device *dev)
|
||||
{
|
||||
struct tegra30_i2s *i2s = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regcache_sync(i2s->regmap);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops tegra30_i2s_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(tegra30_i2s_runtime_suspend,
|
||||
tegra30_i2s_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tegra30_i2s_suspend, tegra30_i2s_resume)
|
||||
};
|
||||
|
||||
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 = tegra30_i2s_platform_remove,
|
||||
};
|
||||
module_platform_driver(tegra30_i2s_driver);
|
||||
|
||||
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra30 I2S ASoC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra30_i2s_of_match);
|
251
sound/soc/tegra/tegra30_i2s.h
Normal file
251
sound/soc/tegra/tegra30_i2s.h
Normal file
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* tegra30_i2s.h - Definitions for Tegra30 I2S driver
|
||||
*
|
||||
* Copyright (c) 2011,2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __TEGRA30_I2S_H__
|
||||
#define __TEGRA30_I2S_H__
|
||||
|
||||
#include "tegra_pcm.h"
|
||||
|
||||
/* Register offsets from TEGRA30_I2S*_BASE */
|
||||
|
||||
#define TEGRA30_I2S_CTRL 0x0
|
||||
#define TEGRA30_I2S_TIMING 0x4
|
||||
#define TEGRA30_I2S_OFFSET 0x08
|
||||
#define TEGRA30_I2S_CH_CTRL 0x0c
|
||||
#define TEGRA30_I2S_SLOT_CTRL 0x10
|
||||
#define TEGRA30_I2S_CIF_RX_CTRL 0x14
|
||||
#define TEGRA30_I2S_CIF_TX_CTRL 0x18
|
||||
#define TEGRA30_I2S_FLOWCTL 0x1c
|
||||
#define TEGRA30_I2S_TX_STEP 0x20
|
||||
#define TEGRA30_I2S_FLOW_STATUS 0x24
|
||||
#define TEGRA30_I2S_FLOW_TOTAL 0x28
|
||||
#define TEGRA30_I2S_FLOW_OVER 0x2c
|
||||
#define TEGRA30_I2S_FLOW_UNDER 0x30
|
||||
#define TEGRA30_I2S_LCOEF_1_4_0 0x34
|
||||
#define TEGRA30_I2S_LCOEF_1_4_1 0x38
|
||||
#define TEGRA30_I2S_LCOEF_1_4_2 0x3c
|
||||
#define TEGRA30_I2S_LCOEF_1_4_3 0x40
|
||||
#define TEGRA30_I2S_LCOEF_1_4_4 0x44
|
||||
#define TEGRA30_I2S_LCOEF_1_4_5 0x48
|
||||
#define TEGRA30_I2S_LCOEF_2_4_0 0x4c
|
||||
#define TEGRA30_I2S_LCOEF_2_4_1 0x50
|
||||
#define TEGRA30_I2S_LCOEF_2_4_2 0x54
|
||||
|
||||
/* Fields in TEGRA30_I2S_CTRL */
|
||||
|
||||
#define TEGRA30_I2S_CTRL_XFER_EN_TX (1 << 31)
|
||||
#define TEGRA30_I2S_CTRL_XFER_EN_RX (1 << 30)
|
||||
#define TEGRA30_I2S_CTRL_CG_EN (1 << 29)
|
||||
#define TEGRA30_I2S_CTRL_SOFT_RESET (1 << 28)
|
||||
#define TEGRA30_I2S_CTRL_TX_FLOWCTL_EN (1 << 27)
|
||||
|
||||
#define TEGRA30_I2S_CTRL_OBS_SEL_SHIFT 24
|
||||
#define TEGRA30_I2S_CTRL_OBS_SEL_MASK (7 << TEGRA30_I2S_CTRL_OBS_SEL_SHIFT)
|
||||
|
||||
#define TEGRA30_I2S_FRAME_FORMAT_LRCK 0
|
||||
#define TEGRA30_I2S_FRAME_FORMAT_FSYNC 1
|
||||
|
||||
#define TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT 12
|
||||
#define TEGRA30_I2S_CTRL_FRAME_FORMAT_MASK (7 << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT)
|
||||
#define TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK (TEGRA30_I2S_FRAME_FORMAT_LRCK << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT)
|
||||
#define TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC (TEGRA30_I2S_FRAME_FORMAT_FSYNC << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT)
|
||||
|
||||
#define TEGRA30_I2S_CTRL_MASTER_ENABLE (1 << 10)
|
||||
|
||||
#define TEGRA30_I2S_LRCK_LEFT_LOW 0
|
||||
#define TEGRA30_I2S_LRCK_RIGHT_LOW 1
|
||||
|
||||
#define TEGRA30_I2S_CTRL_LRCK_SHIFT 9
|
||||
#define TEGRA30_I2S_CTRL_LRCK_MASK (1 << TEGRA30_I2S_CTRL_LRCK_SHIFT)
|
||||
#define TEGRA30_I2S_CTRL_LRCK_L_LOW (TEGRA30_I2S_LRCK_LEFT_LOW << TEGRA30_I2S_CTRL_LRCK_SHIFT)
|
||||
#define TEGRA30_I2S_CTRL_LRCK_R_LOW (TEGRA30_I2S_LRCK_RIGHT_LOW << TEGRA30_I2S_CTRL_LRCK_SHIFT)
|
||||
|
||||
#define TEGRA30_I2S_CTRL_LPBK_ENABLE (1 << 8)
|
||||
|
||||
#define TEGRA30_I2S_BIT_CODE_LINEAR 0
|
||||
#define TEGRA30_I2S_BIT_CODE_ULAW 1
|
||||
#define TEGRA30_I2S_BIT_CODE_ALAW 2
|
||||
|
||||
#define TEGRA30_I2S_CTRL_BIT_CODE_SHIFT 4
|
||||
#define TEGRA30_I2S_CTRL_BIT_CODE_MASK (3 << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT)
|
||||
#define TEGRA30_I2S_CTRL_BIT_CODE_LINEAR (TEGRA30_I2S_BIT_CODE_LINEAR << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT)
|
||||
#define TEGRA30_I2S_CTRL_BIT_CODE_ULAW (TEGRA30_I2S_BIT_CODE_ULAW << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT)
|
||||
#define TEGRA30_I2S_CTRL_BIT_CODE_ALAW (TEGRA30_I2S_BIT_CODE_ALAW << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT)
|
||||
|
||||
#define TEGRA30_I2S_BITS_8 1
|
||||
#define TEGRA30_I2S_BITS_12 2
|
||||
#define TEGRA30_I2S_BITS_16 3
|
||||
#define TEGRA30_I2S_BITS_20 4
|
||||
#define TEGRA30_I2S_BITS_24 5
|
||||
#define TEGRA30_I2S_BITS_28 6
|
||||
#define TEGRA30_I2S_BITS_32 7
|
||||
|
||||
/* Sample container size; see {RX,TX}_MASK field in CH_CTRL below */
|
||||
#define TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT 0
|
||||
#define TEGRA30_I2S_CTRL_BIT_SIZE_MASK (7 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
|
||||
#define TEGRA30_I2S_CTRL_BIT_SIZE_8 (TEGRA30_I2S_BITS_8 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
|
||||
#define TEGRA30_I2S_CTRL_BIT_SIZE_12 (TEGRA30_I2S_BITS_12 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
|
||||
#define TEGRA30_I2S_CTRL_BIT_SIZE_16 (TEGRA30_I2S_BITS_16 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
|
||||
#define TEGRA30_I2S_CTRL_BIT_SIZE_20 (TEGRA30_I2S_BITS_20 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
|
||||
#define TEGRA30_I2S_CTRL_BIT_SIZE_24 (TEGRA30_I2S_BITS_24 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
|
||||
#define TEGRA30_I2S_CTRL_BIT_SIZE_28 (TEGRA30_I2S_BITS_28 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
|
||||
#define TEGRA30_I2S_CTRL_BIT_SIZE_32 (TEGRA30_I2S_BITS_32 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
|
||||
|
||||
/* Fields in TEGRA30_I2S_TIMING */
|
||||
|
||||
#define TEGRA30_I2S_TIMING_NON_SYM_ENABLE (1 << 12)
|
||||
#define TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT 0
|
||||
#define TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US 0x7ff
|
||||
#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_soc_data {
|
||||
void (*set_audio_cif)(struct regmap *regmap,
|
||||
unsigned int reg,
|
||||
struct tegra30_ahub_cif_conf *conf);
|
||||
};
|
||||
|
||||
struct tegra30_i2s {
|
||||
const struct tegra30_i2s_soc_data *soc_data;
|
||||
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;
|
||||
char capture_dma_chan[8];
|
||||
struct snd_dmaengine_dai_dma_data capture_dma_data;
|
||||
enum tegra30_ahub_rxcif playback_i2s_cif;
|
||||
enum tegra30_ahub_txcif playback_fifo_cif;
|
||||
char playback_dma_chan[8];
|
||||
struct snd_dmaengine_dai_dma_data playback_dma_data;
|
||||
struct regmap *regmap;
|
||||
struct snd_dmaengine_pcm_config dma_config;
|
||||
};
|
||||
|
||||
#endif
|
266
sound/soc/tegra/tegra_alc5632.c
Normal file
266
sound/soc/tegra/tegra_alc5632.c
Normal file
|
@ -0,0 +1,266 @@
|
|||
/*
|
||||
* tegra_alc5632.c -- Toshiba AC100(PAZ00) machine ASoC driver
|
||||
*
|
||||
* Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
|
||||
* Copyright (C) 2012 - NVIDIA, Inc.
|
||||
*
|
||||
* Authors: Leon Romanovsky <leon@leon.nu>
|
||||
* Andrey Danin <danindrey@mail.ru>
|
||||
* Marc Dietrich <marvin24@gmx.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "../codecs/alc5632.h"
|
||||
|
||||
#include "tegra_asoc_utils.h"
|
||||
|
||||
#define DRV_NAME "tegra-alc5632"
|
||||
|
||||
struct tegra_alc5632 {
|
||||
struct tegra_asoc_utils_data util_data;
|
||||
int gpio_hp_det;
|
||||
};
|
||||
|
||||
static int tegra_alc5632_asoc_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
struct tegra_alc5632 *alc5632 = snd_soc_card_get_drvdata(card);
|
||||
int srate, mclk;
|
||||
int err;
|
||||
|
||||
srate = params_rate(params);
|
||||
mclk = 512 * srate;
|
||||
|
||||
err = tegra_asoc_utils_set_rate(&alc5632->util_data, srate, mclk);
|
||||
if (err < 0) {
|
||||
dev_err(card->dev, "Can't configure clocks\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (err < 0) {
|
||||
dev_err(card->dev, "codec_dai clock not set\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops tegra_alc5632_asoc_ops = {
|
||||
.hw_params = tegra_alc5632_asoc_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_jack tegra_alc5632_hs_jack;
|
||||
|
||||
static struct snd_soc_jack_pin tegra_alc5632_hs_jack_pins[] = {
|
||||
{
|
||||
.pin = "Headset Mic",
|
||||
.mask = SND_JACK_MICROPHONE,
|
||||
},
|
||||
{
|
||||
.pin = "Headset Stereophone",
|
||||
.mask = SND_JACK_HEADPHONE,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_jack_gpio tegra_alc5632_hp_jack_gpio = {
|
||||
.name = "Headset detection",
|
||||
.report = SND_JACK_HEADSET,
|
||||
.debounce_time = 150,
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget tegra_alc5632_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SPK("Int Spk", NULL),
|
||||
SND_SOC_DAPM_HP("Headset Stereophone", NULL),
|
||||
SND_SOC_DAPM_MIC("Headset Mic", NULL),
|
||||
SND_SOC_DAPM_MIC("Digital Mic", NULL),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new tegra_alc5632_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Int Spk"),
|
||||
};
|
||||
|
||||
static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(rtd->card);
|
||||
|
||||
snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET,
|
||||
&tegra_alc5632_hs_jack);
|
||||
snd_soc_jack_add_pins(&tegra_alc5632_hs_jack,
|
||||
ARRAY_SIZE(tegra_alc5632_hs_jack_pins),
|
||||
tegra_alc5632_hs_jack_pins);
|
||||
|
||||
if (gpio_is_valid(machine->gpio_hp_det)) {
|
||||
tegra_alc5632_hp_jack_gpio.gpio = machine->gpio_hp_det;
|
||||
snd_soc_jack_add_gpios(&tegra_alc5632_hs_jack,
|
||||
1,
|
||||
&tegra_alc5632_hp_jack_gpio);
|
||||
}
|
||||
|
||||
snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_alc5632_card_remove(struct snd_soc_card *card)
|
||||
{
|
||||
struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(card);
|
||||
|
||||
if (gpio_is_valid(machine->gpio_hp_det)) {
|
||||
snd_soc_jack_free_gpios(&tegra_alc5632_hs_jack, 1,
|
||||
&tegra_alc5632_hp_jack_gpio);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_link tegra_alc5632_dai = {
|
||||
.name = "ALC5632",
|
||||
.stream_name = "ALC5632 PCM",
|
||||
.codec_dai_name = "alc5632-hifi",
|
||||
.init = tegra_alc5632_asoc_init,
|
||||
.ops = &tegra_alc5632_asoc_ops,
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S
|
||||
| SND_SOC_DAIFMT_NB_NF
|
||||
| SND_SOC_DAIFMT_CBS_CFS,
|
||||
};
|
||||
|
||||
static struct snd_soc_card snd_soc_tegra_alc5632 = {
|
||||
.name = "tegra-alc5632",
|
||||
.owner = THIS_MODULE,
|
||||
.remove = tegra_alc5632_card_remove,
|
||||
.dai_link = &tegra_alc5632_dai,
|
||||
.num_links = 1,
|
||||
.controls = tegra_alc5632_controls,
|
||||
.num_controls = ARRAY_SIZE(tegra_alc5632_controls),
|
||||
.dapm_widgets = tegra_alc5632_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(tegra_alc5632_dapm_widgets),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int tegra_alc5632_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct snd_soc_card *card = &snd_soc_tegra_alc5632;
|
||||
struct tegra_alc5632 *alc5632;
|
||||
int ret;
|
||||
|
||||
alc5632 = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct tegra_alc5632), GFP_KERNEL);
|
||||
if (!alc5632) {
|
||||
dev_err(&pdev->dev, "Can't allocate tegra_alc5632\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, card);
|
||||
snd_soc_card_set_drvdata(card, alc5632);
|
||||
|
||||
alc5632->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
|
||||
if (alc5632->gpio_hp_det == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
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_alc5632_dai.codec_of_node = of_parse_phandle(
|
||||
pdev->dev.of_node, "nvidia,audio-codec", 0);
|
||||
|
||||
if (!tegra_alc5632_dai.codec_of_node) {
|
||||
dev_err(&pdev->dev,
|
||||
"Property 'nvidia,audio-codec' missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
tegra_alc5632_dai.cpu_of_node = of_parse_phandle(np,
|
||||
"nvidia,i2s-controller", 0);
|
||||
if (!tegra_alc5632_dai.cpu_of_node) {
|
||||
dev_err(&pdev->dev,
|
||||
"Property 'nvidia,i2s-controller' missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
tegra_alc5632_dai.platform_of_node = tegra_alc5632_dai.cpu_of_node;
|
||||
|
||||
ret = tegra_asoc_utils_init(&alc5632->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(&alc5632->util_data);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra_alc5632_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
struct tegra_alc5632 *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_alc5632_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra-audio-alc5632", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver tegra_alc5632_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &snd_soc_pm_ops,
|
||||
.of_match_table = tegra_alc5632_of_match,
|
||||
},
|
||||
.probe = tegra_alc5632_probe,
|
||||
.remove = tegra_alc5632_remove,
|
||||
};
|
||||
module_platform_driver(tegra_alc5632_driver);
|
||||
|
||||
MODULE_AUTHOR("Leon Romanovsky <leon@leon.nu>");
|
||||
MODULE_DESCRIPTION("Tegra+ALC5632 machine ASoC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra_alc5632_of_match);
|
240
sound/soc/tegra/tegra_asoc_utils.c
Normal file
240
sound/soc/tegra/tegra_asoc_utils.c
Normal file
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* tegra_asoc_utils.c - Harmony machine ASoC driver
|
||||
*
|
||||
* Author: Stephen Warren <swarren@nvidia.com>
|
||||
* Copyright (C) 2010,2012 - NVIDIA, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "tegra_asoc_utils.h"
|
||||
|
||||
int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
|
||||
int mclk)
|
||||
{
|
||||
int new_baseclock;
|
||||
bool clk_change;
|
||||
int err;
|
||||
|
||||
switch (srate) {
|
||||
case 11025:
|
||||
case 22050:
|
||||
case 44100:
|
||||
case 88200:
|
||||
if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
|
||||
new_baseclock = 56448000;
|
||||
else if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA30)
|
||||
new_baseclock = 564480000;
|
||||
else
|
||||
new_baseclock = 282240000;
|
||||
break;
|
||||
case 8000:
|
||||
case 16000:
|
||||
case 32000:
|
||||
case 48000:
|
||||
case 64000:
|
||||
case 96000:
|
||||
if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
|
||||
new_baseclock = 73728000;
|
||||
else if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA30)
|
||||
new_baseclock = 552960000;
|
||||
else
|
||||
new_baseclock = 368640000;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
clk_change = ((new_baseclock != data->set_baseclock) ||
|
||||
(mclk != data->set_mclk));
|
||||
if (!clk_change)
|
||||
return 0;
|
||||
|
||||
data->set_baseclock = 0;
|
||||
data->set_mclk = 0;
|
||||
|
||||
clk_disable_unprepare(data->clk_cdev1);
|
||||
clk_disable_unprepare(data->clk_pll_a_out0);
|
||||
clk_disable_unprepare(data->clk_pll_a);
|
||||
|
||||
err = clk_set_rate(data->clk_pll_a, new_baseclock);
|
||||
if (err) {
|
||||
dev_err(data->dev, "Can't set pll_a rate: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = clk_set_rate(data->clk_pll_a_out0, mclk);
|
||||
if (err) {
|
||||
dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */
|
||||
|
||||
err = clk_prepare_enable(data->clk_pll_a);
|
||||
if (err) {
|
||||
dev_err(data->dev, "Can't enable pll_a: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(data->clk_pll_a_out0);
|
||||
if (err) {
|
||||
dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(data->clk_cdev1);
|
||||
if (err) {
|
||||
dev_err(data->dev, "Can't enable cdev1: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
data->set_baseclock = new_baseclock;
|
||||
data->set_mclk = mclk;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate);
|
||||
|
||||
int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data)
|
||||
{
|
||||
const int pll_rate = 73728000;
|
||||
const int ac97_rate = 24576000;
|
||||
int err;
|
||||
|
||||
clk_disable_unprepare(data->clk_cdev1);
|
||||
clk_disable_unprepare(data->clk_pll_a_out0);
|
||||
clk_disable_unprepare(data->clk_pll_a);
|
||||
|
||||
/*
|
||||
* AC97 rate is fixed at 24.576MHz and is used for both the host
|
||||
* controller and the external codec
|
||||
*/
|
||||
err = clk_set_rate(data->clk_pll_a, pll_rate);
|
||||
if (err) {
|
||||
dev_err(data->dev, "Can't set pll_a rate: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = clk_set_rate(data->clk_pll_a_out0, ac97_rate);
|
||||
if (err) {
|
||||
dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */
|
||||
|
||||
err = clk_prepare_enable(data->clk_pll_a);
|
||||
if (err) {
|
||||
dev_err(data->dev, "Can't enable pll_a: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(data->clk_pll_a_out0);
|
||||
if (err) {
|
||||
dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(data->clk_cdev1);
|
||||
if (err) {
|
||||
dev_err(data->dev, "Can't enable cdev1: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
data->set_baseclock = pll_rate;
|
||||
data->set_mclk = ac97_rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate);
|
||||
|
||||
int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
|
||||
struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
data->dev = dev;
|
||||
|
||||
if (of_machine_is_compatible("nvidia,tegra20"))
|
||||
data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20;
|
||||
else if (of_machine_is_compatible("nvidia,tegra30"))
|
||||
data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA30;
|
||||
else if (of_machine_is_compatible("nvidia,tegra114"))
|
||||
data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA114;
|
||||
else if (of_machine_is_compatible("nvidia,tegra124"))
|
||||
data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA124;
|
||||
else {
|
||||
dev_err(data->dev, "SoC unknown to Tegra ASoC utils\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data->clk_pll_a = clk_get(dev, "pll_a");
|
||||
if (IS_ERR(data->clk_pll_a)) {
|
||||
dev_err(data->dev, "Can't retrieve clk pll_a\n");
|
||||
ret = PTR_ERR(data->clk_pll_a);
|
||||
goto err;
|
||||
}
|
||||
|
||||
data->clk_pll_a_out0 = clk_get(dev, "pll_a_out0");
|
||||
if (IS_ERR(data->clk_pll_a_out0)) {
|
||||
dev_err(data->dev, "Can't retrieve clk pll_a_out0\n");
|
||||
ret = PTR_ERR(data->clk_pll_a_out0);
|
||||
goto err_put_pll_a;
|
||||
}
|
||||
|
||||
data->clk_cdev1 = clk_get(dev, "mclk");
|
||||
if (IS_ERR(data->clk_cdev1)) {
|
||||
dev_err(data->dev, "Can't retrieve clk cdev1\n");
|
||||
ret = PTR_ERR(data->clk_cdev1);
|
||||
goto err_put_pll_a_out0;
|
||||
}
|
||||
|
||||
ret = tegra_asoc_utils_set_rate(data, 44100, 256 * 44100);
|
||||
if (ret)
|
||||
goto err_put_cdev1;
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_cdev1:
|
||||
clk_put(data->clk_cdev1);
|
||||
err_put_pll_a_out0:
|
||||
clk_put(data->clk_pll_a_out0);
|
||||
err_put_pll_a:
|
||||
clk_put(data->clk_pll_a);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_asoc_utils_init);
|
||||
|
||||
void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data)
|
||||
{
|
||||
clk_put(data->clk_cdev1);
|
||||
clk_put(data->clk_pll_a_out0);
|
||||
clk_put(data->clk_pll_a);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_asoc_utils_fini);
|
||||
|
||||
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra ASoC utility code");
|
||||
MODULE_LICENSE("GPL");
|
53
sound/soc/tegra/tegra_asoc_utils.h
Normal file
53
sound/soc/tegra/tegra_asoc_utils.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* tegra_asoc_utils.h - Definitions for Tegra DAS driver
|
||||
*
|
||||
* Author: Stephen Warren <swarren@nvidia.com>
|
||||
* Copyright (C) 2010,2012 - NVIDIA, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TEGRA_ASOC_UTILS_H__
|
||||
#define __TEGRA_ASOC_UTILS_H__
|
||||
|
||||
struct clk;
|
||||
struct device;
|
||||
|
||||
enum tegra_asoc_utils_soc {
|
||||
TEGRA_ASOC_UTILS_SOC_TEGRA20,
|
||||
TEGRA_ASOC_UTILS_SOC_TEGRA30,
|
||||
TEGRA_ASOC_UTILS_SOC_TEGRA114,
|
||||
TEGRA_ASOC_UTILS_SOC_TEGRA124,
|
||||
};
|
||||
|
||||
struct tegra_asoc_utils_data {
|
||||
struct device *dev;
|
||||
enum tegra_asoc_utils_soc soc;
|
||||
struct clk *clk_pll_a;
|
||||
struct clk *clk_pll_a_out0;
|
||||
struct clk *clk_cdev1;
|
||||
int set_baseclock;
|
||||
int set_mclk;
|
||||
};
|
||||
|
||||
int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
|
||||
int mclk);
|
||||
int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data);
|
||||
int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
|
||||
struct device *dev);
|
||||
void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data);
|
||||
|
||||
#endif
|
324
sound/soc/tegra/tegra_max98090.c
Normal file
324
sound/soc/tegra/tegra_max98090.c
Normal file
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
* Tegra machine ASoC driver for boards using a MAX90809 CODEC.
|
||||
*
|
||||
* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Based on code copyright/by:
|
||||
*
|
||||
* Copyright (C) 2010-2012 - NVIDIA, Inc.
|
||||
* Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
|
||||
* (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
|
||||
* Copyright 2007 Wolfson Microelectronics PLC.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "tegra_asoc_utils.h"
|
||||
|
||||
#define DRV_NAME "tegra-snd-max98090"
|
||||
|
||||
struct tegra_max98090 {
|
||||
struct tegra_asoc_utils_data util_data;
|
||||
int gpio_hp_det;
|
||||
int gpio_mic_det;
|
||||
};
|
||||
|
||||
static int tegra_max98090_asoc_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
struct tegra_max98090 *machine = snd_soc_card_get_drvdata(card);
|
||||
int srate, mclk;
|
||||
int err;
|
||||
|
||||
srate = params_rate(params);
|
||||
switch (srate) {
|
||||
case 8000:
|
||||
case 16000:
|
||||
case 24000:
|
||||
case 32000:
|
||||
case 48000:
|
||||
case 64000:
|
||||
case 96000:
|
||||
mclk = 12288000;
|
||||
break;
|
||||
case 11025:
|
||||
case 22050:
|
||||
case 44100:
|
||||
case 88200:
|
||||
mclk = 11289600;
|
||||
break;
|
||||
default:
|
||||
mclk = 12000000;
|
||||
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, 0, mclk,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (err < 0) {
|
||||
dev_err(card->dev, "codec_dai clock not set\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops tegra_max98090_ops = {
|
||||
.hw_params = tegra_max98090_asoc_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_jack tegra_max98090_hp_jack;
|
||||
|
||||
static struct snd_soc_jack_pin tegra_max98090_hp_jack_pins[] = {
|
||||
{
|
||||
.pin = "Headphones",
|
||||
.mask = SND_JACK_HEADPHONE,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_jack_gpio tegra_max98090_hp_jack_gpio = {
|
||||
.name = "Headphone detection",
|
||||
.report = SND_JACK_HEADPHONE,
|
||||
.debounce_time = 150,
|
||||
.invert = 1,
|
||||
};
|
||||
|
||||
static struct snd_soc_jack tegra_max98090_mic_jack;
|
||||
|
||||
static struct snd_soc_jack_pin tegra_max98090_mic_jack_pins[] = {
|
||||
{
|
||||
.pin = "Mic Jack",
|
||||
.mask = SND_JACK_MICROPHONE,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_jack_gpio tegra_max98090_mic_jack_gpio = {
|
||||
.name = "Mic detection",
|
||||
.report = SND_JACK_MICROPHONE,
|
||||
.debounce_time = 150,
|
||||
.invert = 1,
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget tegra_max98090_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphones", NULL),
|
||||
SND_SOC_DAPM_SPK("Speakers", NULL),
|
||||
SND_SOC_DAPM_MIC("Mic Jack", NULL),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new tegra_max98090_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Speakers"),
|
||||
};
|
||||
|
||||
static int tegra_max98090_asoc_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct tegra_max98090 *machine = snd_soc_card_get_drvdata(rtd->card);
|
||||
|
||||
if (gpio_is_valid(machine->gpio_hp_det)) {
|
||||
snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE,
|
||||
&tegra_max98090_hp_jack);
|
||||
snd_soc_jack_add_pins(&tegra_max98090_hp_jack,
|
||||
ARRAY_SIZE(tegra_max98090_hp_jack_pins),
|
||||
tegra_max98090_hp_jack_pins);
|
||||
|
||||
tegra_max98090_hp_jack_gpio.gpio = machine->gpio_hp_det;
|
||||
snd_soc_jack_add_gpios(&tegra_max98090_hp_jack,
|
||||
1,
|
||||
&tegra_max98090_hp_jack_gpio);
|
||||
}
|
||||
|
||||
if (gpio_is_valid(machine->gpio_mic_det)) {
|
||||
snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
|
||||
&tegra_max98090_mic_jack);
|
||||
snd_soc_jack_add_pins(&tegra_max98090_mic_jack,
|
||||
ARRAY_SIZE(tegra_max98090_mic_jack_pins),
|
||||
tegra_max98090_mic_jack_pins);
|
||||
|
||||
tegra_max98090_mic_jack_gpio.gpio = machine->gpio_mic_det;
|
||||
snd_soc_jack_add_gpios(&tegra_max98090_mic_jack,
|
||||
1,
|
||||
&tegra_max98090_mic_jack_gpio);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_max98090_card_remove(struct snd_soc_card *card)
|
||||
{
|
||||
struct tegra_max98090 *machine = snd_soc_card_get_drvdata(card);
|
||||
|
||||
if (gpio_is_valid(machine->gpio_hp_det)) {
|
||||
snd_soc_jack_free_gpios(&tegra_max98090_hp_jack, 1,
|
||||
&tegra_max98090_hp_jack_gpio);
|
||||
}
|
||||
|
||||
if (gpio_is_valid(machine->gpio_mic_det)) {
|
||||
snd_soc_jack_free_gpios(&tegra_max98090_mic_jack, 1,
|
||||
&tegra_max98090_mic_jack_gpio);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_link tegra_max98090_dai = {
|
||||
.name = "max98090",
|
||||
.stream_name = "max98090 PCM",
|
||||
.codec_dai_name = "HiFi",
|
||||
.init = tegra_max98090_asoc_init,
|
||||
.ops = &tegra_max98090_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_max98090 = {
|
||||
.name = "tegra-max98090",
|
||||
.owner = THIS_MODULE,
|
||||
.remove = tegra_max98090_card_remove,
|
||||
.dai_link = &tegra_max98090_dai,
|
||||
.num_links = 1,
|
||||
.controls = tegra_max98090_controls,
|
||||
.num_controls = ARRAY_SIZE(tegra_max98090_controls),
|
||||
.dapm_widgets = tegra_max98090_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(tegra_max98090_dapm_widgets),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int tegra_max98090_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct snd_soc_card *card = &snd_soc_tegra_max98090;
|
||||
struct tegra_max98090 *machine;
|
||||
int ret;
|
||||
|
||||
machine = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct tegra_max98090), GFP_KERNEL);
|
||||
if (!machine) {
|
||||
dev_err(&pdev->dev, "Can't allocate tegra_max98090\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, card);
|
||||
snd_soc_card_set_drvdata(card, machine);
|
||||
|
||||
machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
|
||||
if (machine->gpio_hp_det == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
machine->gpio_mic_det =
|
||||
of_get_named_gpio(np, "nvidia,mic-det-gpios", 0);
|
||||
if (machine->gpio_mic_det == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
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_max98090_dai.codec_of_node = of_parse_phandle(np,
|
||||
"nvidia,audio-codec", 0);
|
||||
if (!tegra_max98090_dai.codec_of_node) {
|
||||
dev_err(&pdev->dev,
|
||||
"Property 'nvidia,audio-codec' missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
tegra_max98090_dai.cpu_of_node = of_parse_phandle(np,
|
||||
"nvidia,i2s-controller", 0);
|
||||
if (!tegra_max98090_dai.cpu_of_node) {
|
||||
dev_err(&pdev->dev,
|
||||
"Property 'nvidia,i2s-controller' missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
tegra_max98090_dai.platform_of_node = tegra_max98090_dai.cpu_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 tegra_max98090_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
struct tegra_max98090 *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_max98090_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra-audio-max98090", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver tegra_max98090_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &snd_soc_pm_ops,
|
||||
.of_match_table = tegra_max98090_of_match,
|
||||
},
|
||||
.probe = tegra_max98090_probe,
|
||||
.remove = tegra_max98090_remove,
|
||||
};
|
||||
module_platform_driver(tegra_max98090_driver);
|
||||
|
||||
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra max98090 machine ASoC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra_max98090_of_match);
|
86
sound/soc/tegra/tegra_pcm.c
Normal file
86
sound/soc/tegra/tegra_pcm.c
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* tegra_pcm.c - Tegra PCM driver
|
||||
*
|
||||
* Author: Stephen Warren <swarren@nvidia.com>
|
||||
* Copyright (C) 2010,2012 - NVIDIA, Inc.
|
||||
*
|
||||
* Based on code copyright/by:
|
||||
*
|
||||
* Copyright (c) 2009-2010, NVIDIA Corporation.
|
||||
* Scott Peterson <speterson@nvidia.com>
|
||||
* Vijay Mali <vmali@nvidia.com>
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Iliyan Malchev <malchev@google.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
#include "tegra_pcm.h"
|
||||
|
||||
static const struct snd_pcm_hardware tegra_pcm_hardware = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED,
|
||||
.period_bytes_min = 1024,
|
||||
.period_bytes_max = PAGE_SIZE,
|
||||
.periods_min = 2,
|
||||
.periods_max = 8,
|
||||
.buffer_bytes_max = PAGE_SIZE * 8,
|
||||
.fifo_size = 4,
|
||||
};
|
||||
|
||||
static const struct snd_dmaengine_pcm_config tegra_dmaengine_pcm_config = {
|
||||
.pcm_hardware = &tegra_pcm_hardware,
|
||||
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
|
||||
.prealloc_buffer_size = PAGE_SIZE * 8,
|
||||
};
|
||||
|
||||
int tegra_pcm_platform_register(struct device *dev)
|
||||
{
|
||||
return snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_pcm_platform_register);
|
||||
|
||||
int tegra_pcm_platform_register_with_chan_names(struct device *dev,
|
||||
struct snd_dmaengine_pcm_config *config,
|
||||
char *txdmachan, char *rxdmachan)
|
||||
{
|
||||
*config = tegra_dmaengine_pcm_config;
|
||||
config->dma_dev = dev->parent;
|
||||
config->chan_names[0] = txdmachan;
|
||||
config->chan_names[1] = rxdmachan;
|
||||
|
||||
return snd_dmaengine_pcm_register(dev, config, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_pcm_platform_register_with_chan_names);
|
||||
|
||||
void tegra_pcm_platform_unregister(struct device *dev)
|
||||
{
|
||||
return snd_dmaengine_pcm_unregister(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_pcm_platform_unregister);
|
||||
|
||||
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra PCM ASoC driver");
|
||||
MODULE_LICENSE("GPL");
|
42
sound/soc/tegra/tegra_pcm.h
Normal file
42
sound/soc/tegra/tegra_pcm.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* tegra_pcm.h - Definitions for Tegra PCM driver
|
||||
*
|
||||
* Author: Stephen Warren <swarren@nvidia.com>
|
||||
* Copyright (C) 2010,2012 - NVIDIA, Inc.
|
||||
*
|
||||
* Based on code copyright/by:
|
||||
*
|
||||
* Copyright (c) 2009-2010, NVIDIA Corporation.
|
||||
* Scott Peterson <speterson@nvidia.com>
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Iliyan Malchev <malchev@google.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TEGRA_PCM_H__
|
||||
#define __TEGRA_PCM_H__
|
||||
|
||||
struct snd_dmaengine_pcm_config;
|
||||
|
||||
int tegra_pcm_platform_register(struct device *dev);
|
||||
int tegra_pcm_platform_register_with_chan_names(struct device *dev,
|
||||
struct snd_dmaengine_pcm_config *config,
|
||||
char *txdmachan, char *rxdmachan);
|
||||
void tegra_pcm_platform_unregister(struct device *dev);
|
||||
|
||||
#endif
|
267
sound/soc/tegra/tegra_rt5640.c
Normal file
267
sound/soc/tegra/tegra_rt5640.c
Normal file
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* tegra_rt5640.c - Tegra machine ASoC driver for boards using WM8903 codec.
|
||||
*
|
||||
* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Based on code copyright/by:
|
||||
*
|
||||
* Copyright (C) 2010-2012 - NVIDIA, Inc.
|
||||
* Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
|
||||
* (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
|
||||
* Copyright 2007 Wolfson Microelectronics PLC.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "../codecs/rt5640.h"
|
||||
|
||||
#include "tegra_asoc_utils.h"
|
||||
|
||||
#define DRV_NAME "tegra-snd-rt5640"
|
||||
|
||||
struct tegra_rt5640 {
|
||||
struct tegra_asoc_utils_data util_data;
|
||||
int gpio_hp_det;
|
||||
};
|
||||
|
||||
static int tegra_rt5640_asoc_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
|
||||
int srate, mclk;
|
||||
int err;
|
||||
|
||||
srate = params_rate(params);
|
||||
mclk = 256 * srate;
|
||||
|
||||
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, RT5640_SCLK_S_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_rt5640_ops = {
|
||||
.hw_params = tegra_rt5640_asoc_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_jack tegra_rt5640_hp_jack;
|
||||
|
||||
static struct snd_soc_jack_pin tegra_rt5640_hp_jack_pins[] = {
|
||||
{
|
||||
.pin = "Headphones",
|
||||
.mask = SND_JACK_HEADPHONE,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_jack_gpio tegra_rt5640_hp_jack_gpio = {
|
||||
.name = "Headphone detection",
|
||||
.report = SND_JACK_HEADPHONE,
|
||||
.debounce_time = 150,
|
||||
.invert = 1,
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget tegra_rt5640_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphones", NULL),
|
||||
SND_SOC_DAPM_SPK("Speakers", NULL),
|
||||
SND_SOC_DAPM_MIC("Mic Jack", NULL),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new tegra_rt5640_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Speakers"),
|
||||
};
|
||||
|
||||
static int tegra_rt5640_asoc_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(rtd->card);
|
||||
|
||||
snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE,
|
||||
&tegra_rt5640_hp_jack);
|
||||
snd_soc_jack_add_pins(&tegra_rt5640_hp_jack,
|
||||
ARRAY_SIZE(tegra_rt5640_hp_jack_pins),
|
||||
tegra_rt5640_hp_jack_pins);
|
||||
|
||||
if (gpio_is_valid(machine->gpio_hp_det)) {
|
||||
tegra_rt5640_hp_jack_gpio.gpio = machine->gpio_hp_det;
|
||||
snd_soc_jack_add_gpios(&tegra_rt5640_hp_jack,
|
||||
1,
|
||||
&tegra_rt5640_hp_jack_gpio);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_rt5640_card_remove(struct snd_soc_card *card)
|
||||
{
|
||||
struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
|
||||
|
||||
if (gpio_is_valid(machine->gpio_hp_det)) {
|
||||
snd_soc_jack_free_gpios(&tegra_rt5640_hp_jack, 1,
|
||||
&tegra_rt5640_hp_jack_gpio);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_link tegra_rt5640_dai = {
|
||||
.name = "RT5640",
|
||||
.stream_name = "RT5640 PCM",
|
||||
.codec_dai_name = "rt5640-aif1",
|
||||
.init = tegra_rt5640_asoc_init,
|
||||
.ops = &tegra_rt5640_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_rt5640 = {
|
||||
.name = "tegra-rt5640",
|
||||
.owner = THIS_MODULE,
|
||||
.remove = tegra_rt5640_card_remove,
|
||||
.dai_link = &tegra_rt5640_dai,
|
||||
.num_links = 1,
|
||||
.controls = tegra_rt5640_controls,
|
||||
.num_controls = ARRAY_SIZE(tegra_rt5640_controls),
|
||||
.dapm_widgets = tegra_rt5640_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(tegra_rt5640_dapm_widgets),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int tegra_rt5640_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct snd_soc_card *card = &snd_soc_tegra_rt5640;
|
||||
struct tegra_rt5640 *machine;
|
||||
int ret;
|
||||
|
||||
machine = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct tegra_rt5640), GFP_KERNEL);
|
||||
if (!machine) {
|
||||
dev_err(&pdev->dev, "Can't allocate tegra_rt5640\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, card);
|
||||
snd_soc_card_set_drvdata(card, machine);
|
||||
|
||||
machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
|
||||
if (machine->gpio_hp_det == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
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_rt5640_dai.codec_of_node = of_parse_phandle(np,
|
||||
"nvidia,audio-codec", 0);
|
||||
if (!tegra_rt5640_dai.codec_of_node) {
|
||||
dev_err(&pdev->dev,
|
||||
"Property 'nvidia,audio-codec' missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
tegra_rt5640_dai.cpu_of_node = of_parse_phandle(np,
|
||||
"nvidia,i2s-controller", 0);
|
||||
if (!tegra_rt5640_dai.cpu_of_node) {
|
||||
dev_err(&pdev->dev,
|
||||
"Property 'nvidia,i2s-controller' missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
tegra_rt5640_dai.platform_of_node = tegra_rt5640_dai.cpu_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 tegra_rt5640_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
struct tegra_rt5640 *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_rt5640_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra-audio-rt5640", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver tegra_rt5640_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &snd_soc_pm_ops,
|
||||
.of_match_table = tegra_rt5640_of_match,
|
||||
},
|
||||
.probe = tegra_rt5640_probe,
|
||||
.remove = tegra_rt5640_remove,
|
||||
};
|
||||
module_platform_driver(tegra_rt5640_driver);
|
||||
|
||||
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra+RT5640 machine ASoC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra_rt5640_of_match);
|
220
sound/soc/tegra/tegra_wm8753.c
Normal file
220
sound/soc/tegra/tegra_wm8753.c
Normal file
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* tegra_wm8753.c - Tegra machine ASoC driver for boards using WM8753 codec.
|
||||
*
|
||||
* Author: Stephen Warren <swarren@nvidia.com>
|
||||
* 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 <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "../codecs/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_card *card = rtd->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 int tegra_wm8753_driver_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
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");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
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(np,
|
||||
"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_of_node = of_parse_phandle(np,
|
||||
"nvidia,i2s-controller", 0);
|
||||
if (!tegra_wm8753_dai.cpu_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_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 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[] = {
|
||||
{ .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 = tegra_wm8753_driver_remove,
|
||||
};
|
||||
module_platform_driver(tegra_wm8753_driver);
|
||||
|
||||
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra+WM8753 machine ASoC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra_wm8753_of_match);
|
402
sound/soc/tegra/tegra_wm8903.c
Normal file
402
sound/soc/tegra/tegra_wm8903.c
Normal file
|
@ -0,0 +1,402 @@
|
|||
/*
|
||||
* tegra_wm8903.c - Tegra machine ASoC driver for boards using WM8903 codec.
|
||||
*
|
||||
* Author: Stephen Warren <swarren@nvidia.com>
|
||||
* 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 <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "../codecs/wm8903.h"
|
||||
|
||||
#include "tegra_asoc_utils.h"
|
||||
|
||||
#define DRV_NAME "tegra-snd-wm8903"
|
||||
|
||||
struct tegra_wm8903 {
|
||||
int gpio_spkr_en;
|
||||
int gpio_hp_det;
|
||||
int gpio_hp_mute;
|
||||
int gpio_int_mic_en;
|
||||
int gpio_ext_mic_en;
|
||||
struct tegra_asoc_utils_data util_data;
|
||||
};
|
||||
|
||||
static int tegra_wm8903_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_card *card = rtd->card;
|
||||
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
|
||||
int srate, mclk;
|
||||
int err;
|
||||
|
||||
srate = params_rate(params);
|
||||
switch (srate) {
|
||||
case 64000:
|
||||
case 88200:
|
||||
case 96000:
|
||||
mclk = 128 * srate;
|
||||
break;
|
||||
default:
|
||||
mclk = 256 * srate;
|
||||
break;
|
||||
}
|
||||
/* FIXME: Codec only requires >= 3MHz if OSR==0 */
|
||||
while (mclk < 6000000)
|
||||
mclk *= 2;
|
||||
|
||||
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, 0, mclk,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (err < 0) {
|
||||
dev_err(card->dev, "codec_dai clock not set\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops tegra_wm8903_ops = {
|
||||
.hw_params = tegra_wm8903_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_jack tegra_wm8903_hp_jack;
|
||||
|
||||
static struct snd_soc_jack_pin tegra_wm8903_hp_jack_pins[] = {
|
||||
{
|
||||
.pin = "Headphone Jack",
|
||||
.mask = SND_JACK_HEADPHONE,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_jack_gpio tegra_wm8903_hp_jack_gpio = {
|
||||
.name = "headphone detect",
|
||||
.report = SND_JACK_HEADPHONE,
|
||||
.debounce_time = 150,
|
||||
.invert = 1,
|
||||
};
|
||||
|
||||
static struct snd_soc_jack tegra_wm8903_mic_jack;
|
||||
|
||||
static struct snd_soc_jack_pin tegra_wm8903_mic_jack_pins[] = {
|
||||
{
|
||||
.pin = "Mic Jack",
|
||||
.mask = SND_JACK_MICROPHONE,
|
||||
},
|
||||
};
|
||||
|
||||
static int tegra_wm8903_event_int_spk(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = w->dapm;
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
|
||||
|
||||
if (!gpio_is_valid(machine->gpio_spkr_en))
|
||||
return 0;
|
||||
|
||||
gpio_set_value_cansleep(machine->gpio_spkr_en,
|
||||
SND_SOC_DAPM_EVENT_ON(event));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_wm8903_event_hp(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = w->dapm;
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
|
||||
|
||||
if (!gpio_is_valid(machine->gpio_hp_mute))
|
||||
return 0;
|
||||
|
||||
gpio_set_value_cansleep(machine->gpio_hp_mute,
|
||||
!SND_SOC_DAPM_EVENT_ON(event));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget tegra_wm8903_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SPK("Int Spk", tegra_wm8903_event_int_spk),
|
||||
SND_SOC_DAPM_HP("Headphone Jack", tegra_wm8903_event_hp),
|
||||
SND_SOC_DAPM_MIC("Mic Jack", NULL),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new tegra_wm8903_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Int Spk"),
|
||||
};
|
||||
|
||||
static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
|
||||
|
||||
if (gpio_is_valid(machine->gpio_hp_det)) {
|
||||
tegra_wm8903_hp_jack_gpio.gpio = machine->gpio_hp_det;
|
||||
snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
|
||||
&tegra_wm8903_hp_jack);
|
||||
snd_soc_jack_add_pins(&tegra_wm8903_hp_jack,
|
||||
ARRAY_SIZE(tegra_wm8903_hp_jack_pins),
|
||||
tegra_wm8903_hp_jack_pins);
|
||||
snd_soc_jack_add_gpios(&tegra_wm8903_hp_jack,
|
||||
1,
|
||||
&tegra_wm8903_hp_jack_gpio);
|
||||
}
|
||||
|
||||
snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
|
||||
&tegra_wm8903_mic_jack);
|
||||
snd_soc_jack_add_pins(&tegra_wm8903_mic_jack,
|
||||
ARRAY_SIZE(tegra_wm8903_mic_jack_pins),
|
||||
tegra_wm8903_mic_jack_pins);
|
||||
wm8903_mic_detect(codec, &tegra_wm8903_mic_jack, SND_JACK_MICROPHONE,
|
||||
0);
|
||||
|
||||
snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_wm8903_remove(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = &(card->rtd[0]);
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
|
||||
|
||||
if (gpio_is_valid(machine->gpio_hp_det)) {
|
||||
snd_soc_jack_free_gpios(&tegra_wm8903_hp_jack, 1,
|
||||
&tegra_wm8903_hp_jack_gpio);
|
||||
}
|
||||
|
||||
wm8903_mic_detect(codec, NULL, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_link tegra_wm8903_dai = {
|
||||
.name = "WM8903",
|
||||
.stream_name = "WM8903 PCM",
|
||||
.codec_dai_name = "wm8903-hifi",
|
||||
.init = tegra_wm8903_init,
|
||||
.ops = &tegra_wm8903_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_wm8903 = {
|
||||
.name = "tegra-wm8903",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = &tegra_wm8903_dai,
|
||||
.num_links = 1,
|
||||
.remove = tegra_wm8903_remove,
|
||||
.controls = tegra_wm8903_controls,
|
||||
.num_controls = ARRAY_SIZE(tegra_wm8903_controls),
|
||||
.dapm_widgets = tegra_wm8903_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(tegra_wm8903_dapm_widgets),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int tegra_wm8903_driver_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct snd_soc_card *card = &snd_soc_tegra_wm8903;
|
||||
struct tegra_wm8903 *machine;
|
||||
int ret;
|
||||
|
||||
machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm8903),
|
||||
GFP_KERNEL);
|
||||
if (!machine) {
|
||||
dev_err(&pdev->dev, "Can't allocate tegra_wm8903 struct\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, card);
|
||||
snd_soc_card_set_drvdata(card, machine);
|
||||
|
||||
machine->gpio_spkr_en = of_get_named_gpio(np, "nvidia,spkr-en-gpios",
|
||||
0);
|
||||
if (machine->gpio_spkr_en == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
if (gpio_is_valid(machine->gpio_spkr_en)) {
|
||||
ret = devm_gpio_request_one(&pdev->dev, machine->gpio_spkr_en,
|
||||
GPIOF_OUT_INIT_LOW, "spkr_en");
|
||||
if (ret) {
|
||||
dev_err(card->dev, "cannot get spkr_en gpio\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
machine->gpio_hp_mute = of_get_named_gpio(np, "nvidia,hp-mute-gpios",
|
||||
0);
|
||||
if (machine->gpio_hp_mute == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
if (gpio_is_valid(machine->gpio_hp_mute)) {
|
||||
ret = devm_gpio_request_one(&pdev->dev, machine->gpio_hp_mute,
|
||||
GPIOF_OUT_INIT_HIGH, "hp_mute");
|
||||
if (ret) {
|
||||
dev_err(card->dev, "cannot get hp_mute gpio\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
|
||||
if (machine->gpio_hp_det == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
machine->gpio_int_mic_en = of_get_named_gpio(np,
|
||||
"nvidia,int-mic-en-gpios", 0);
|
||||
if (machine->gpio_int_mic_en == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
if (gpio_is_valid(machine->gpio_int_mic_en)) {
|
||||
/* Disable int mic; enable signal is active-high */
|
||||
ret = devm_gpio_request_one(&pdev->dev,
|
||||
machine->gpio_int_mic_en,
|
||||
GPIOF_OUT_INIT_LOW, "int_mic_en");
|
||||
if (ret) {
|
||||
dev_err(card->dev, "cannot get int_mic_en gpio\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
machine->gpio_ext_mic_en = of_get_named_gpio(np,
|
||||
"nvidia,ext-mic-en-gpios", 0);
|
||||
if (machine->gpio_ext_mic_en == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
if (gpio_is_valid(machine->gpio_ext_mic_en)) {
|
||||
/* Enable ext mic; enable signal is active-low */
|
||||
ret = devm_gpio_request_one(&pdev->dev,
|
||||
machine->gpio_ext_mic_en,
|
||||
GPIOF_OUT_INIT_LOW, "ext_mic_en");
|
||||
if (ret) {
|
||||
dev_err(card->dev, "cannot get ext_mic_en gpio\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = snd_soc_of_parse_card_name(card, "nvidia,model");
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
tegra_wm8903_dai.codec_of_node = of_parse_phandle(np,
|
||||
"nvidia,audio-codec", 0);
|
||||
if (!tegra_wm8903_dai.codec_of_node) {
|
||||
dev_err(&pdev->dev,
|
||||
"Property 'nvidia,audio-codec' missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
tegra_wm8903_dai.cpu_of_node = of_parse_phandle(np,
|
||||
"nvidia,i2s-controller", 0);
|
||||
if (!tegra_wm8903_dai.cpu_of_node) {
|
||||
dev_err(&pdev->dev,
|
||||
"Property 'nvidia,i2s-controller' missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
tegra_wm8903_dai.platform_of_node = tegra_wm8903_dai.cpu_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 tegra_wm8903_driver_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
|
||||
|
||||
snd_soc_unregister_card(card);
|
||||
|
||||
tegra_asoc_utils_fini(&machine->util_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tegra_wm8903_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra-audio-wm8903", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver tegra_wm8903_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &snd_soc_pm_ops,
|
||||
.of_match_table = tegra_wm8903_of_match,
|
||||
},
|
||||
.probe = tegra_wm8903_driver_probe,
|
||||
.remove = tegra_wm8903_driver_remove,
|
||||
};
|
||||
module_platform_driver(tegra_wm8903_driver);
|
||||
|
||||
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra+WM8903 machine ASoC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra_wm8903_of_match);
|
183
sound/soc/tegra/tegra_wm9712.c
Normal file
183
sound/soc/tegra/tegra_wm9712.c
Normal file
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* tegra20_wm9712.c - Tegra machine ASoC driver for boards using WM9712 codec.
|
||||
*
|
||||
* Copyright 2012 Lucas Stach <dev@lynxeye.de>
|
||||
*
|
||||
* Partly based on code copyright/by:
|
||||
* Copyright 2011,2012 Toradex Inc.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "tegra_asoc_utils.h"
|
||||
|
||||
#define DRV_NAME "tegra-snd-wm9712"
|
||||
|
||||
struct tegra_wm9712 {
|
||||
struct platform_device *codec;
|
||||
struct tegra_asoc_utils_data util_data;
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget tegra_wm9712_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone", NULL),
|
||||
SND_SOC_DAPM_LINE("LineIn", NULL),
|
||||
SND_SOC_DAPM_MIC("Mic", NULL),
|
||||
};
|
||||
|
||||
static int tegra_wm9712_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
|
||||
return snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_link tegra_wm9712_dai = {
|
||||
.name = "AC97 HiFi",
|
||||
.stream_name = "AC97 HiFi",
|
||||
.codec_dai_name = "wm9712-hifi",
|
||||
.codec_name = "wm9712-codec",
|
||||
.init = tegra_wm9712_init,
|
||||
};
|
||||
|
||||
static struct snd_soc_card snd_soc_tegra_wm9712 = {
|
||||
.name = "tegra-wm9712",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = &tegra_wm9712_dai,
|
||||
.num_links = 1,
|
||||
|
||||
.dapm_widgets = tegra_wm9712_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(tegra_wm9712_dapm_widgets),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int tegra_wm9712_driver_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct snd_soc_card *card = &snd_soc_tegra_wm9712;
|
||||
struct tegra_wm9712 *machine;
|
||||
int ret;
|
||||
|
||||
machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm9712),
|
||||
GFP_KERNEL);
|
||||
if (!machine) {
|
||||
dev_err(&pdev->dev, "Can't allocate tegra_wm9712 struct\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, card);
|
||||
snd_soc_card_set_drvdata(card, machine);
|
||||
|
||||
machine->codec = platform_device_alloc("wm9712-codec", -1);
|
||||
if (!machine->codec) {
|
||||
dev_err(&pdev->dev, "Can't allocate wm9712 platform device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = platform_device_add(machine->codec);
|
||||
if (ret)
|
||||
goto codec_put;
|
||||
|
||||
ret = snd_soc_of_parse_card_name(card, "nvidia,model");
|
||||
if (ret)
|
||||
goto codec_unregister;
|
||||
|
||||
ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
|
||||
if (ret)
|
||||
goto codec_unregister;
|
||||
|
||||
tegra_wm9712_dai.cpu_of_node = of_parse_phandle(np,
|
||||
"nvidia,ac97-controller", 0);
|
||||
if (!tegra_wm9712_dai.cpu_of_node) {
|
||||
dev_err(&pdev->dev,
|
||||
"Property 'nvidia,ac97-controller' missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto codec_unregister;
|
||||
}
|
||||
|
||||
tegra_wm9712_dai.platform_of_node = tegra_wm9712_dai.cpu_of_node;
|
||||
|
||||
ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
|
||||
if (ret)
|
||||
goto codec_unregister;
|
||||
|
||||
ret = tegra_asoc_utils_set_ac97_rate(&machine->util_data);
|
||||
if (ret)
|
||||
goto asoc_utils_fini;
|
||||
|
||||
ret = snd_soc_register_card(card);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
|
||||
ret);
|
||||
goto asoc_utils_fini;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
asoc_utils_fini:
|
||||
tegra_asoc_utils_fini(&machine->util_data);
|
||||
codec_unregister:
|
||||
platform_device_del(machine->codec);
|
||||
codec_put:
|
||||
platform_device_put(machine->codec);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra_wm9712_driver_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
struct tegra_wm9712 *machine = snd_soc_card_get_drvdata(card);
|
||||
|
||||
snd_soc_unregister_card(card);
|
||||
|
||||
tegra_asoc_utils_fini(&machine->util_data);
|
||||
|
||||
platform_device_unregister(machine->codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tegra_wm9712_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra-audio-wm9712", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver tegra_wm9712_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &snd_soc_pm_ops,
|
||||
.of_match_table = tegra_wm9712_of_match,
|
||||
},
|
||||
.probe = tegra_wm9712_driver_probe,
|
||||
.remove = tegra_wm9712_driver_remove,
|
||||
};
|
||||
module_platform_driver(tegra_wm9712_driver);
|
||||
|
||||
MODULE_AUTHOR("Lucas Stach");
|
||||
MODULE_DESCRIPTION("Tegra+WM9712 machine ASoC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra_wm9712_of_match);
|
207
sound/soc/tegra/trimslice.c
Normal file
207
sound/soc/tegra/trimslice.c
Normal file
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* trimslice.c - TrimSlice machine ASoC driver
|
||||
*
|
||||
* Copyright (C) 2011 - CompuLab, Ltd.
|
||||
* Author: Mike Rapoport <mike@compulab.co.il>
|
||||
*
|
||||
* Based on code copyright/by:
|
||||
* Author: Stephen Warren <swarren@nvidia.com>
|
||||
* Copyright (C) 2010-2011 - NVIDIA, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "../codecs/tlv320aic23.h"
|
||||
|
||||
#include "tegra_asoc_utils.h"
|
||||
|
||||
#define DRV_NAME "tegra-snd-trimslice"
|
||||
|
||||
struct tegra_trimslice {
|
||||
struct tegra_asoc_utils_data util_data;
|
||||
};
|
||||
|
||||
static int trimslice_asoc_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
struct tegra_trimslice *trimslice = snd_soc_card_get_drvdata(card);
|
||||
int srate, mclk;
|
||||
int err;
|
||||
|
||||
srate = params_rate(params);
|
||||
mclk = 128 * srate;
|
||||
|
||||
err = tegra_asoc_utils_set_rate(&trimslice->util_data, srate, mclk);
|
||||
if (err < 0) {
|
||||
dev_err(card->dev, "Can't configure clocks\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (err < 0) {
|
||||
dev_err(card->dev, "codec_dai clock not set\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops trimslice_asoc_ops = {
|
||||
.hw_params = trimslice_asoc_hw_params,
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget trimslice_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Line Out", NULL),
|
||||
SND_SOC_DAPM_LINE("Line In", NULL),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route trimslice_audio_map[] = {
|
||||
{"Line Out", NULL, "LOUT"},
|
||||
{"Line Out", NULL, "ROUT"},
|
||||
|
||||
{"LLINEIN", NULL, "Line In"},
|
||||
{"RLINEIN", NULL, "Line In"},
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link trimslice_tlv320aic23_dai = {
|
||||
.name = "TLV320AIC23",
|
||||
.stream_name = "AIC23",
|
||||
.codec_dai_name = "tlv320aic23-hifi",
|
||||
.ops = &trimslice_asoc_ops,
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
};
|
||||
|
||||
static struct snd_soc_card snd_soc_trimslice = {
|
||||
.name = "tegra-trimslice",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = &trimslice_tlv320aic23_dai,
|
||||
.num_links = 1,
|
||||
|
||||
.dapm_widgets = trimslice_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(trimslice_dapm_widgets),
|
||||
.dapm_routes = trimslice_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(trimslice_audio_map),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int tegra_snd_trimslice_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct snd_soc_card *card = &snd_soc_trimslice;
|
||||
struct tegra_trimslice *trimslice;
|
||||
int ret;
|
||||
|
||||
trimslice = devm_kzalloc(&pdev->dev, sizeof(struct tegra_trimslice),
|
||||
GFP_KERNEL);
|
||||
if (!trimslice) {
|
||||
dev_err(&pdev->dev, "Can't allocate tegra_trimslice\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, card);
|
||||
snd_soc_card_set_drvdata(card, trimslice);
|
||||
|
||||
trimslice_tlv320aic23_dai.codec_of_node = of_parse_phandle(np,
|
||||
"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_of_node = of_parse_phandle(np,
|
||||
"nvidia,i2s-controller", 0);
|
||||
if (!trimslice_tlv320aic23_dai.cpu_of_node) {
|
||||
dev_err(&pdev->dev,
|
||||
"Property 'nvidia,i2s-controller' missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
trimslice_tlv320aic23_dai.platform_of_node =
|
||||
trimslice_tlv320aic23_dai.cpu_of_node;
|
||||
|
||||
ret = tegra_asoc_utils_init(&trimslice->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(&trimslice->util_data);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra_snd_trimslice_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
struct tegra_trimslice *trimslice = snd_soc_card_get_drvdata(card);
|
||||
|
||||
snd_soc_unregister_card(card);
|
||||
|
||||
tegra_asoc_utils_fini(&trimslice->util_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id trimslice_of_match[] = {
|
||||
{ .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 = tegra_snd_trimslice_remove,
|
||||
};
|
||||
module_platform_driver(tegra_snd_trimslice_driver);
|
||||
|
||||
MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
|
||||
MODULE_DESCRIPTION("Trimslice machine ASoC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
Loading…
Add table
Add a link
Reference in a new issue