diff options
-rw-r--r-- | sound/soc/tegra/Kconfig | 5 | ||||
-rw-r--r-- | sound/soc/tegra/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/tegra/tegra30_dam.c | 634 | ||||
-rw-r--r-- | sound/soc/tegra/tegra30_dam.h | 162 |
4 files changed, 803 insertions, 0 deletions
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 122b515efb8b..4db390432945 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -22,6 +22,11 @@ config SND_SOC_TEGRA30_AHUB tristate "Tegra 30 Audio Hub driver" depends on SND_SOC_TEGRA && ARCH_TEGRA_3x_SOC +config SND_SOC_TEGRA30_DAM + tristate "Tegra 30 Audio Dam driver" + depends on SND_SOC_TEGRA && ARCH_TEGRA_3x_SOC + select SND_SOC_TEGRA30_AHUB + config SND_SOC_TEGRA30_I2S tristate "Tegra 30 I2S driver" depends on SND_SOC_TEGRA && ARCH_TEGRA_3x_SOC diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 0c4fc1e78cf8..f5e1b36cae1d 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -7,12 +7,14 @@ snd-soc-tegra20-i2s-objs := tegra20_i2s.o snd-soc-tegra30-ahub-objs := tegra30_ahub.o snd-soc-tegra30-i2s-objs := tegra30_i2s.o snd-soc-tegra30-spdif-objs := tegra30_spdif.o +snd-soc-tegra30-dam-objs := tegra30_dam.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o obj-$(CONFIG_SND_SOC_TEGRA20_DAS) += snd-soc-tegra20-das.o obj-$(CONFIG_SND_SOC_TEGRA20_I2S) += snd-soc-tegra20-i2s.o obj-$(CONFIG_SND_SOC_TEGRA30_AHUB) += snd-soc-tegra30-ahub.o +obj-$(CONFIG_SND_SOC_TEGRA30_DAM) += snd-soc-tegra30-dam.o obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o obj-$(CONFIG_SND_SOC_TEGRA20_SPDIF) += snd-soc-tegra20-spdif.o obj-$(CONFIG_SND_SOC_TEGRA30_SPDIF) += snd-soc-tegra30-spdif.o diff --git a/sound/soc/tegra/tegra30_dam.c b/sound/soc/tegra/tegra30_dam.c new file mode 100644 index 000000000000..0828b014d9a7 --- /dev/null +++ b/sound/soc/tegra/tegra30_dam.c @@ -0,0 +1,634 @@ +/* + * tegra30_dam.c - Tegra 30 DAM driver + * + * Author: Nikesh Oswal <noswal@nvidia.com> + * Copyright (C) 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/clk.h> +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <sound/soc.h> +#include "tegra30_dam.h" +#include "tegra30_ahub.h" + +#define DRV_NAME "tegra30-dam" + +static struct tegra30_dam_context *dams_cont_info[TEGRA30_NR_DAM_IFC]; + +enum { + dam_ch_in0 = 0x0, + dam_ch_in1, + dam_ch_out, + dam_ch_maxnum +} tegra30_dam_chtype; + +struct tegra30_dam_src_step_table step_table[] = { + { 8000, 44100, 80 }, + { 8000, 48000, 0 }, + { 16000, 44100, 160 }, + { 16000, 48000, 1 }, + { 44100, 8000, 441 }, + { 48000, 8000, 0 }, + { 44100, 16000, 441 }, + { 48000, 16000, 0 }, +}; + +static void tegra30_dam_set_output_samplerate(struct tegra30_dam_context *dam, + int fsout); +static void tegra30_dam_set_input_samplerate(struct tegra30_dam_context *dam, + int fsin); +static int tegra30_dam_set_step_reset(struct tegra30_dam_context *dam, + int insample, int outsample); +static void tegra30_dam_ch0_set_step(struct tegra30_dam_context *dam, int step); + +static inline void tegra30_dam_writel(struct tegra30_dam_context *dam, + u32 val, u32 reg) +{ + dam->ctrlreg_cache[(reg >> 2)] = val; + __raw_writel(val, dam->damregs + reg); +} + +static inline u32 tegra30_dam_readl(struct tegra30_dam_context *dam, u32 reg) +{ + u32 val = __raw_readl(dam->damregs + reg); + + return val; +} + +#ifdef CONFIG_PM +int tegra30_dam_suspend(int ifc) +{ + int i = 0; + struct tegra30_dam_context *dam; + + if (ifc >= TEGRA30_NR_DAM_IFC) + return -EINVAL; + + return 0; +} + +int tegra30_dam_resume(int ifc) +{ + int i = 0; + struct tegra30_dam_context *dam; + + if (ifc >= TEGRA30_NR_DAM_IFC) + return -EINVAL; + + dam = dams_cont_info[ifc]; + + if (dam->in_use) { + tegra30_dam_enable_clock(ifc); + + for (i = 0; i <= TEGRA30_DAM_CTRL_REGINDEX; i++) { + if ((i == TEGRA30_DAM_CTRL_RSVD_6) || + (i == TEGRA30_DAM_CTRL_RSVD_10)) + continue; + + tegra30_dam_writel(dam, dam->ctrlreg_cache[i], + (i << 2)); + } + + tegra30_dam_disable_clock(ifc); + } + + return 0; +} +#endif + +void tegra30_dam_disable_clock(int ifc) +{ + struct tegra30_dam_context *dam; + + if (ifc >= TEGRA30_NR_DAM_IFC) + return -EINVAL; + + dam = dams_cont_info[ifc]; + clk_disable(dam->dam_clk); + tegra30_ahub_disable_clocks(); +} + +int tegra30_dam_enable_clock(int ifc) +{ + struct tegra30_dam_context *dam; + + if (ifc >= TEGRA30_NR_DAM_IFC) + return -EINVAL; + + dam = dams_cont_info[ifc]; + tegra30_ahub_enable_clocks(); + clk_enable(dam->dam_clk); + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static int tegra30_dam_show(struct seq_file *s, void *unused) +{ +#define REG(r) { r, #r } + static const struct { + int offset; + const char *name; + } regs[] = { + REG(TEGRA30_DAM_CTRL), + REG(TEGRA30_DAM_CLIP), + REG(TEGRA30_DAM_CLIP_THRESHOLD), + REG(TEGRA30_DAM_AUDIOCIF_OUT_CTRL), + REG(TEGRA30_DAM_CH0_CTRL), + REG(TEGRA30_DAM_CH0_CONV), + REG(TEGRA30_DAM_AUDIOCIF_CH0_CTRL), + REG(TEGRA30_DAM_CH1_CTRL), + REG(TEGRA30_DAM_CH1_CONV), + REG(TEGRA30_DAM_AUDIOCIF_CH1_CTRL), + }; +#undef REG + + struct tegra30_dam_context *dam = s->private; + int i; + + clk_enable(dam->dam_clk); + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + u32 val = tegra30_dam_readl(dam, regs[i].offset); + seq_printf(s, "%s = %08x\n", regs[i].name, val); + } + + clk_disable(dam->dam_clk); + + return 0; +} + +static int tegra30_dam_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, tegra30_dam_show, inode->i_private); +} + +static const struct file_operations tegra30_dam_debug_fops = { + .open = tegra30_dam_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void tegra30_dam_debug_add(struct tegra30_dam_context *dam, int id) +{ + char name[] = DRV_NAME ".0"; + + snprintf(name, sizeof(name), DRV_NAME".%1d", id); + dam->debug = debugfs_create_file(name, S_IRUGO, snd_soc_debugfs_root, + dam, &tegra30_dam_debug_fops); +} + +static void tegra30_dam_debug_remove(struct tegra30_dam_context *dam) +{ + if (dam->debug) + debugfs_remove(dam->debug); +} +#else +static inline void tegra30_dam_debug_add(struct tegra30_dam_context *dam, + int id) +{ +} + +static inline void tegra30_dam_debug_remove(struct tegra30_dam_context *dam) +{ +} +#endif + +int tegra30_dam_allocate_controller() +{ + int i = 0; + struct tegra30_dam_context *dam = NULL; + + for (i = 0; i < TEGRA30_NR_DAM_IFC; i++) { + + dam = dams_cont_info[i]; + + if (!dam->in_use) { + dam->in_use = true; + return i; + } + } + + return -ENOENT; +} + +int tegra30_dam_allocate_channel(int ifc, int chid) +{ + int i = 0; + struct tegra30_dam_context *dam = NULL; + + if (ifc >= TEGRA30_NR_DAM_IFC) + return -EINVAL; + + dam = dams_cont_info[ifc]; + + if (!dam->ch_alloc[chid]) { + dam->ch_alloc[chid] = true; + return 0; + } + + return -ENOENT; +} + +int tegra30_dam_free_channel(int ifc, int chid) +{ + int i = 0; + struct tegra30_dam_context *dam = NULL; + + if (ifc >= TEGRA30_NR_DAM_IFC) + return -EINVAL; + + dam = dams_cont_info[ifc]; + + if (dam->ch_alloc[chid]) { + dam->ch_alloc[chid] = false; + return 0; + } + + return -EINVAL; +} + +int tegra30_dam_free_controller(int ifc) +{ + struct tegra30_dam_context *dam = NULL; + + if (ifc >= TEGRA30_NR_DAM_IFC) + return -EINVAL; + + dam = dams_cont_info[ifc]; + + if (!dam->ch_alloc[dam_ch_in0] && + !dam->ch_alloc[dam_ch_in1]) { + dam->in_use = false; + return 0; + } + + return -EINVAL; +} + +void tegra30_dam_set_samplerate(int ifc, int chid, int samplerate) +{ + struct tegra30_dam_context *dam = dams_cont_info[ifc]; + + if (ifc >= TEGRA30_NR_DAM_IFC) + return -EINVAL; + + switch (chid) { + case dam_ch_in0: + tegra30_dam_set_input_samplerate(dam, samplerate); + dam->ch_insamplerate[dam_ch_in0] = samplerate; + tegra30_dam_set_step_reset(dam, samplerate, dam->outsamplerate); + break; + case dam_ch_in1: + if (samplerate != dam->outsamplerate) + return -EINVAL; + dam->ch_insamplerate[dam_ch_in1] = samplerate; + break; + case dam_ch_out: + tegra30_dam_set_output_samplerate(dam, samplerate); + dam->outsamplerate = samplerate; + break; + default: + break; + } +} + +void tegra30_dam_set_output_samplerate(struct tegra30_dam_context *dam, + int fsout) +{ + u32 val; + + val = tegra30_dam_readl(dam, TEGRA30_DAM_CTRL); + val &= ~TEGRA30_DAM_CTRL_FSOUT_MASK; + + switch (fsout) { + case TEGRA30_AUDIO_SAMPLERATE_8000: + val |= TEGRA30_DAM_CTRL_FSOUT_FS8; + break; + case TEGRA30_AUDIO_SAMPLERATE_16000: + val |= TEGRA30_DAM_CTRL_FSOUT_FS16; + break; + case TEGRA30_AUDIO_SAMPLERATE_44100: + val |= TEGRA30_DAM_CTRL_FSOUT_FS44; + break; + case TEGRA30_AUDIO_SAMPLERATE_48000: + val |= TEGRA30_DAM_CTRL_FSOUT_FS48; + break; + default: + break; + } + + tegra30_dam_writel(dam, val, TEGRA30_DAM_CTRL); +} + +void tegra30_dam_set_input_samplerate(struct tegra30_dam_context *dam, int fsin) +{ + u32 val; + + val = tegra30_dam_readl(dam, TEGRA30_DAM_CH0_CTRL); + val &= ~TEGRA30_DAM_CH0_CTRL_FSIN_MASK; + + switch (fsin) { + case TEGRA30_AUDIO_SAMPLERATE_8000: + val |= TEGRA30_DAM_CH0_CTRL_FSIN_FS8; + break; + case TEGRA30_AUDIO_SAMPLERATE_16000: + val |= TEGRA30_DAM_CH0_CTRL_FSIN_FS16; + break; + case TEGRA30_AUDIO_SAMPLERATE_44100: + val |= TEGRA30_DAM_CH0_CTRL_FSIN_FS44; + break; + case TEGRA30_AUDIO_SAMPLERATE_48000: + val |= TEGRA30_DAM_CH0_CTRL_FSIN_FS48; + break; + default: + break; + } + + tegra30_dam_writel(dam, val, TEGRA30_DAM_CH0_CTRL); +} + +int tegra30_dam_set_step_reset(struct tegra30_dam_context *dam, + int insample, int outsample) +{ + int step_reset = 0; + int i = 0; + + for (i = 0; i < ARRAY_SIZE(step_table); i++) { + if ((insample == step_table[i].insample) && + (outsample == step_table[i].outsample)) + step_reset = step_table[i].stepreset; + } + + tegra30_dam_ch0_set_step(dam, step_reset); + + return 0; +} + +void tegra30_dam_ch0_set_step(struct tegra30_dam_context *dam, int step) +{ + u32 val; + + val = tegra30_dam_readl(dam, TEGRA30_DAM_CH0_CTRL); + val &= ~TEGRA30_DAM_CH0_CTRL_STEP_MASK; + val |= step << TEGRA30_DAM_CH0_CTRL_STEP_SHIFT; + tegra30_dam_writel(dam, val, TEGRA30_DAM_CH0_CTRL); +} + +int tegra30_dam_set_gain(int ifc, int chid, int gain) +{ + + if (ifc >= TEGRA30_NR_DAM_IFC) + return -EINVAL; + + switch (chid) { + case dam_ch_in0: + tegra30_dam_writel(dams_cont_info[ifc], gain, + TEGRA30_DAM_CH0_CONV); + break; + case dam_ch_in1: + tegra30_dam_writel(dams_cont_info[ifc], gain, + TEGRA30_DAM_CH1_CONV); + break; + default: + break; + } + + return 0; +} + +int tegra30_dam_set_acif(int ifc, int chid, unsigned int audio_channels, + unsigned int audio_bits, unsigned int client_channels, + unsigned int client_bits) +{ + unsigned int reg; + unsigned int value = 0; + + if (ifc >= TEGRA30_NR_DAM_IFC) + return -EINVAL; + + /*ch0 takes input as mono/16bit always*/ + if ((chid == dam_ch_in0) && + ((client_channels != 1) || (client_bits != 16))) + return -EINVAL; + + value |= TEGRA30_CIF_MONOCONV_COPY; + value |= TEGRA30_CIF_STEREOCONV_CH0; + value |= (audio_channels-1) << TEGRA30_AUDIO_CHANNELS_SHIFT; + value |= (((audio_bits>>2)-1)<<TEGRA30_AUDIO_BITS_SHIFT); + value |= (client_channels-1) << TEGRA30_CLIENT_CHANNELS_SHIFT; + value |= (((client_bits>>2)-1)<<TEGRA30_CLIENT_BITS_SHIFT); + + switch (chid) { + case dam_ch_out: + value |= TEGRA30_CIF_DIRECTION_TX; + reg = TEGRA30_DAM_AUDIOCIF_OUT_CTRL; + break; + case dam_ch_in0: + value |= TEGRA30_CIF_DIRECTION_RX; + reg = TEGRA30_DAM_AUDIOCIF_CH0_CTRL; + break; + case dam_ch_in1: + value |= TEGRA30_CIF_DIRECTION_RX; + reg = TEGRA30_DAM_AUDIOCIF_CH1_CTRL; + break; + } + + tegra30_dam_writel(dams_cont_info[ifc], value, reg); + + return 0; +} + +void tegra30_dam_enable(int ifc, int on, int chid) +{ + u32 old_val, val, enreg; + struct tegra30_dam_context *dam = dams_cont_info[ifc]; + + if (ifc >= TEGRA30_NR_DAM_IFC) + return -EINVAL; + + if (chid == dam_ch_in0) + enreg = TEGRA30_DAM_CH0_CTRL; + else + enreg = TEGRA30_DAM_CH1_CTRL; + + old_val = val = tegra30_dam_readl(dam, enreg); + + if (on) { + if (!dam->ch_enable_refcnt[chid]++) + val |= TEGRA30_DAM_CH0_CTRL_EN; + } else if (dam->ch_enable_refcnt[chid]) { + dam->ch_enable_refcnt[chid]--; + if (!dam->ch_enable_refcnt[chid]) + val &= ~TEGRA30_DAM_CH0_CTRL_EN; + } + + if (val != old_val) + tegra30_dam_writel(dam, val, enreg); + + old_val = val = tegra30_dam_readl(dam, TEGRA30_DAM_CTRL); + + if (dam->ch_enable_refcnt[dam_ch_in0] || + dam->ch_enable_refcnt[dam_ch_in1]) + val |= TEGRA30_DAM_CTRL_DAM_EN; + else + val &= ~TEGRA30_DAM_CTRL_DAM_EN; + + if (old_val != val) + tegra30_dam_writel(dam, val, TEGRA30_DAM_CTRL); +} + +void tegra30_dam_ch0_set_datasync(struct tegra30_dam_context *dam, int datasync) +{ + u32 val; + + val = tegra30_dam_readl(dam, TEGRA30_DAM_CH0_CTRL); + val &= ~TEGRA30_DAM_CH0_CTRL_DATA_SYNC_MASK; + val |= datasync << TEGRA30_DAM_DATA_SYNC_SHIFT; + tegra30_dam_writel(dam, val, TEGRA30_DAM_CH0_CTRL); +} + +void tegra30_dam_ch1_set_datasync(struct tegra30_dam_context *dam, int datasync) +{ + u32 val; + + val = tegra30_dam_readl(dam, TEGRA30_DAM_CH1_CTRL); + val &= ~TEGRA30_DAM_CH1_CTRL_DATA_SYNC_MASK; + val |= datasync << TEGRA30_DAM_DATA_SYNC_SHIFT; + tegra30_dam_writel(dam, val, TEGRA30_DAM_CH1_CTRL); +} + +void tegra30_dam_enable_clip_counter(struct tegra30_dam_context *dam, int on) +{ + u32 val; + + val = tegra30_dam_readl(dam, TEGRA30_DAM_CLIP); + val &= ~TEGRA30_DAM_CLIP_COUNTER_ENABLE; + val |= on ? TEGRA30_DAM_CLIP_COUNTER_ENABLE : 0; + tegra30_dam_writel(dam, val, TEGRA30_DAM_CLIP); +} + +static int __devinit tegra30_dam_probe(struct platform_device *pdev) +{ + struct resource *res, *region; + struct tegra30_dam_context *dam; + int ret = 0; + + if ((pdev->id < 0) || + (pdev->id >= TEGRA30_NR_DAM_IFC)) { + dev_err(&pdev->dev, "ID %d out of range\n", pdev->id); + return -EINVAL; + } + + dams_cont_info[pdev->id] = devm_kzalloc(&pdev->dev, + sizeof(struct tegra30_dam_context), + GFP_KERNEL); + if (!dams_cont_info[pdev->id]) { + dev_err(&pdev->dev, "Can't allocate dam context\n"); + ret = -ENOMEM; + goto exit; + } + dam = dams_cont_info[pdev->id]; + + dam->dam_clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(dam->dam_clk)) { + dev_err(&pdev->dev, "Can't retrieve dam clock\n"); + ret = PTR_ERR(dam->dam_clk); + goto err_free; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No memory 0 resource\n"); + ret = -ENODEV; + goto err_clk_put_dam; + } + + region = devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name); + if (!region) { + dev_err(&pdev->dev, "Memory region 0 already claimed\n"); + ret = -EBUSY; + goto err_clk_put_dam; + } + + dam->damregs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!dam->damregs) { + dev_err(&pdev->dev, "ioremap 0 failed\n"); + ret = -ENOMEM; + goto err_clk_put_dam; + } + + platform_set_drvdata(pdev, dam); + + tegra30_dam_debug_add(dam, pdev->id); + + return 0; + +err_clk_put_dam: + clk_put(dam->dam_clk); +err_free: + dams_cont_info[pdev->id] = NULL; +exit: + return ret; +} + +static int __devexit tegra30_dam_remove(struct platform_device *pdev) +{ + struct tegra30_dam_context *dam; + + dam = platform_get_drvdata(pdev); + clk_put(dam->dam_clk); + tegra30_dam_debug_remove(dam); + dams_cont_info[pdev->id] = NULL; + + return 0; +} + +static struct platform_driver tegra30_dam_driver = { + .probe = tegra30_dam_probe, + .remove = __devexit_p(tegra30_dam_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init tegra30_dam_modinit(void) +{ + return platform_driver_register(&tegra30_dam_driver); +} +module_init(tegra30_dam_modinit); + +static void __exit tegra30_dam_modexit(void) +{ + platform_driver_unregister(&tegra30_dam_driver); +} +module_exit(tegra30_dam_modexit); + +MODULE_AUTHOR("Nikesh Oswal <noswal@nvidia.com>"); +MODULE_DESCRIPTION("Tegra 30 DAM driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra30_dam.h b/sound/soc/tegra/tegra30_dam.h new file mode 100644 index 000000000000..199730fc1563 --- /dev/null +++ b/sound/soc/tegra/tegra30_dam.h @@ -0,0 +1,162 @@ +/* + * tegra30_dam.h - Tegra 30 DAM driver. + * + * Author: Nikesh Oswal <noswal@nvidia.com> + * Copyright (C) 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 + * + */ + +#ifndef __TEGRA30_DAM_H +#define __TEGRA30_DAM_H + +/* Register offsets from TEGRA30_DAM*_BASE */ +#define TEGRA30_DAM_CTRL 0 +#define TEGRA30_DAM_CLIP 4 +#define TEGRA30_DAM_CLIP_THRESHOLD 8 +#define TEGRA30_DAM_AUDIOCIF_OUT_CTRL 0x0C +#define TEGRA30_DAM_CH0_CTRL 0x10 +#define TEGRA30_DAM_CH0_CONV 0x14 +#define TEGRA30_DAM_AUDIOCIF_CH0_CTRL 0x1C +#define TEGRA30_DAM_CH1_CTRL 0x20 +#define TEGRA30_DAM_CH1_CONV 0x24 +#define TEGRA30_DAM_AUDIOCIF_CH1_CTRL 0x2C +#define TEGRA30_DAM_CTRL_REGINDEX (TEGRA30_DAM_AUDIOCIF_CH1_CTRL >> 2) +#define TEGRA30_DAM_CTRL_RSVD_6 6 +#define TEGRA30_DAM_CTRL_RSVD_10 10 + +#define TEGRA30_NR_DAM_IFC 3 + +#define TEGRA30_DAM_NUM_INPUT_CHANNELS 2 + +/* Fields in TEGRA30_DAM_CTRL */ +#define TEGRA30_DAM_CTRL_SOFT_RESET_ENABLE (1 << 31) +#define TEGRA30_DAM_CTRL_FSOUT_SHIFT 4 +#define TEGRA30_DAM_CTRL_FSOUT_MASK (0xf << TEGRA30_DAM_CTRL_FSOUT_SHIFT) +#define TEGRA30_DAM_FS_8KHZ 0 +#define TEGRA30_DAM_FS_16KHZ 1 +#define TEGRA30_DAM_FS_44KHZ 2 +#define TEGRA30_DAM_FS_48KHZ 3 +#define TEGRA30_DAM_CTRL_FSOUT_FS8 (TEGRA30_DAM_FS_8KHZ << TEGRA30_DAM_CTRL_FSOUT_SHIFT) +#define TEGRA30_DAM_CTRL_FSOUT_FS16 (TEGRA30_DAM_FS_16KHZ << TEGRA30_DAM_CTRL_FSOUT_SHIFT) +#define TEGRA30_DAM_CTRL_FSOUT_FS44 (TEGRA30_DAM_FS_44KHZ << TEGRA30_DAM_CTRL_FSOUT_SHIFT) +#define TEGRA30_DAM_CTRL_FSOUT_FS48 (TEGRA30_DAM_FS_48KHZ << TEGRA30_DAM_CTRL_FSOUT_SHIFT) +#define TEGRA30_DAM_CTRL_CG_EN (1 << 1) +#define TEGRA30_DAM_CTRL_DAM_EN (1 << 0) + + +/* Fields in TEGRA30_DAM_CLIP */ +#define TEGRA30_DAM_CLIP_COUNTER_ENABLE (1 << 31) +#define TEGRA30_DAM_CLIP_COUNT_MASK 0x7fffffff + + +/* Fields in TEGRA30_DAM_CH0_CTRL */ +#define TEGRA30_STEP_RESET 1 +#define TEGRA30_DAM_DATA_SYNC 1 +#define TEGRA30_DAM_DATA_SYNC_SHIFT 4 +#define TEGRA30_DAM_CH0_CTRL_FSIN_SHIFT 8 +#define TEGRA30_DAM_CH0_CTRL_STEP_SHIFT 16 +#define TEGRA30_DAM_CH0_CTRL_STEP_MASK (0xffff << 16) +#define TEGRA30_DAM_CH0_CTRL_STEP_RESET (TEGRA30_STEP_RESET << 16) +#define TEGRA30_DAM_CH0_CTRL_FSIN_MASK (0xf << 8) +#define TEGRA30_DAM_CH0_CTRL_FSIN_FS8 (TEGRA30_DAM_FS_8KHZ << 8) +#define TEGRA30_DAM_CH0_CTRL_FSIN_FS16 (TEGRA30_DAM_FS_16KHZ << 8) +#define TEGRA30_DAM_CH0_CTRL_FSIN_FS44 (TEGRA30_DAM_FS_44KHZ << 8) +#define TEGRA30_DAM_CH0_CTRL_FSIN_FS48 (TEGRA30_DAM_FS_48KHZ << 8) +#define TEGRA30_DAM_CH0_CTRL_DATA_SYNC_MASK (0xf << TEGRA30_DAM_DATA_SYNC_SHIFT) +#define TEGRA30_DAM_CH0_CTRL_DATA_SYNC (TEGRA30_DAM_DATA_SYNC << TEGRA30_DAM_DATA_SYNC_SHIFT) +#define TEGRA30_DAM_CH0_CTRL_EN (1 << 0) + + +/* Fields in TEGRA30_DAM_CH0_CONV */ +#define TEGRA30_DAM_GAIN 1 +#define TEGRA30_DAM_GAIN_SHIFT 0 +#define TEGRA30_DAM_CH0_CONV_GAIN (TEGRA30_DAM_GAIN << TEGRA30_DAM_GAIN_SHIFT) + +/* Fields in TEGRA30_DAM_CH1_CTRL */ +#define TEGRA30_DAM_CH1_CTRL_DATA_SYNC_MASK (0xf << TEGRA30_DAM_DATA_SYNC_SHIFT) +#define TEGRA30_DAM_CH1_CTRL_DATA_SYNC (TEGRA30_DAM_DATA_SYNC << TEGRA30_DAM_DATA_SYNC_SHIFT) +#define TEGRA30_DAM_CH1_CTRL_EN (1 << 0) + +/* Fields in TEGRA30_DAM_CH1_CONV */ +#define TEGRA30_DAM_CH1_CONV_GAIN (TEGRA30_DAM_GAIN << TEGRA30_DAM_GAIN_SHIFT) + +#define TEGRA30_AUDIO_CHANNELS_SHIFT 24 +#define TEGRA30_AUDIO_CHANNELS_MASK (7 << TEGRA30_AUDIO_CHANNELS_SHIFT) +#define TEGRA30_CLIENT_CHANNELS_SHIFT 16 +#define TEGRA30_CLIENT_CHANNELS_MASK (7 << TEGRA30_CLIENT_CHANNELS_SHIFT) +#define TEGRA30_AUDIO_BITS_SHIFT 12 +#define TEGRA30_AUDIO_BITS_MASK (7 << TEGRA30_AUDIO_BITS_SHIFT) +#define TEGRA30_CLIENT_BITS_SHIFT 8 +#define TEGRA30_CLIENT_BITS_MASK (7 << TEGRA30_CLIENT_BITS_SHIFT) +#define TEGRA30_CIF_DIRECTION_TX (0 << 2) +#define TEGRA30_CIF_DIRECTION_RX (1 << 2) +#define TEGRA30_CIF_BIT24 5 +#define TEGRA30_CIF_BIT16 3 +#define TEGRA30_CIF_CH1 0 +#define TEGRA30_CIF_MONOCONV_COPY (1<<0) +#define TEGRA30_CIF_STEREOCONV_CH0 (0<<4) + +/* +* Audio Samplerates +*/ +#define TEGRA30_AUDIO_SAMPLERATE_8000 8000 +#define TEGRA30_AUDIO_SAMPLERATE_16000 16000 +#define TEGRA30_AUDIO_SAMPLERATE_44100 44100 +#define TEGRA30_AUDIO_SAMPLERATE_48000 48000 + +#define TEGRA30_DAM_CHIN0_SRC 0 +#define TEGRA30_DAM_CHIN1 1 +#define TEGRA30_DAM_CHOUT 2 +#define TEGRA30_DAM_ENABLE 1 +#define TEGRA30_DAM_DISABLE 0 + +struct tegra30_dam_context { + int outsamplerate; + bool ch_alloc[TEGRA30_DAM_NUM_INPUT_CHANNELS]; + int ch_enable_refcnt[TEGRA30_DAM_NUM_INPUT_CHANNELS]; + int ch_insamplerate[TEGRA30_DAM_NUM_INPUT_CHANNELS]; + int ctrlreg_cache[TEGRA30_DAM_CTRL_REGINDEX + 1]; + struct clk *dam_clk; + bool in_use; + void __iomem *damregs; + struct dentry *debug; +}; + +struct tegra30_dam_src_step_table { + int insample; + int outsample; + int stepreset; +}; + +#ifdef CONFIG_PM +int tegra30_dam_suspend(int ifc); +int tegra30_dam_resume(int ifc); +#endif +void tegra30_dam_disable_clock(int ifc); +int tegra30_dam_enable_clock(int ifc); +int tegra30_dam_allocate_controller(); +int tegra30_dam_allocate_channel(int ifc, int chid); +int tegra30_dam_free_channel(int ifc, int chid); +int tegra30_dam_free_controller(int ifc); +void tegra30_dam_set_samplerate(int ifc, int chtype, int samplerate); +int tegra30_dam_set_gain(int ifc, int chtype, int gain); +int tegra30_dam_set_acif(int ifc, int chtype, unsigned int audio_channels, + unsigned int audio_bits, unsigned int client_channels, + unsigned int client_bits); +void tegra30_dam_enable(int ifc, int on, int chtype); + +#endif |