summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/Kconfig7
-rw-r--r--arch/arm/mach-tegra/Makefile2
-rw-r--r--arch/arm/mach-tegra/tegra_i2s_audio.c1965
-rw-r--r--arch/arm/mach-tegra/tegra_spdif_audio.c1187
-rw-r--r--sound/soc/tegra/Kconfig1
5 files changed, 0 insertions, 3162 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index c98080b3c64f..2de37ff9db93 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -392,13 +392,6 @@ config TEGRA_CONVSERVATIVE_GOV_ON_EARLYSUPSEND
help
Also will restore to original cpu frequency governor when device is resumed
-config TEGRA_LEGACY_AUDIO
- bool "Enable Tegra Legacy Audio APIs"
- default n
- help
- Say Y if you want to add support legacy (non-ALSA) audio APIs on
- Tegra. This will disable ALSA (ASoC) support.
-
config TEGRA_STAT_MON
bool "Enable H/W statistics monitor"
depends on ARCH_TEGRA_2x_SOC
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index c54d056bb801..290ac3300f37 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -35,8 +35,6 @@ obj-y += kfuse.o
obj-y += csi.o
obj-$(CONFIG_TEGRA_SILICON_PLATFORM) += tegra_odm_fuses.o
obj-y += i2c_error_recovery.o
-obj-$(CONFIG_TEGRA_LEGACY_AUDIO) += tegra_i2s_audio.o
-obj-$(CONFIG_TEGRA_LEGACY_AUDIO) += tegra_spdif_audio.o
obj-y += mc.o
obj-$(CONFIG_TEGRA_STAT_MON) += tegra2_statmon.o
obj-$(CONFIG_USB_SUPPORT) += usb_phy.o
diff --git a/arch/arm/mach-tegra/tegra_i2s_audio.c b/arch/arm/mach-tegra/tegra_i2s_audio.c
deleted file mode 100644
index fc790066694a..000000000000
--- a/arch/arm/mach-tegra/tegra_i2s_audio.c
+++ /dev/null
@@ -1,1965 +0,0 @@
-/*
- * arch/arm/mach-tegra/tegra_i2s_audio.c
- *
- * Copyright (C) 2010 Google, Inc.
- *
- * Author:
- * Iliyan Malchev <malchev@google.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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.
- *
- */
-
-/* TODO:
- -- replace make I2S_MAX_NUM_BUFS configurable through an ioctl
-*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/miscdevice.h>
-#include <linux/fs.h>
-#include <linux/mutex.h>
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <linux/spinlock.h>
-#include <linux/uaccess.h>
-#include <linux/dma-mapping.h>
-#include <linux/dmapool.h>
-#include <linux/err.h>
-#include <linux/spi/spi.h>
-#include <linux/kfifo.h>
-#include <linux/debugfs.h>
-#include <linux/completion.h>
-#include <linux/platform_device.h>
-#include <linux/device.h>
-#include <linux/io.h>
-#include <linux/ktime.h>
-#include <linux/sysfs.h>
-#include <linux/wakelock.h>
-#include <linux/delay.h>
-#include <linux/tegra_audio.h>
-#include <linux/pm.h>
-#include <linux/workqueue.h>
-
-#include <mach/dma.h>
-#include <mach/iomap.h>
-#include <mach/i2s.h>
-#include <mach/audio.h>
-#include <mach/irqs.h>
-
-#include "clock.h"
-
-#define PCM_BUFFER_MAX_SIZE_ORDER PAGE_SHIFT
-
-#define TEGRA_AUDIO_DSP_NONE 0
-#define TEGRA_AUDIO_DSP_PCM 1
-#define TEGRA_AUDIO_DSP_NETWORK 2
-#define TEGRA_AUDIO_DSP_TDM 3
-
-#define I2S_MAX_NUM_BUFS 4
-#define I2S_DEFAULT_TX_NUM_BUFS 2
-#define I2S_DEFAULT_RX_NUM_BUFS 2
-
-/* per stream (input/output) */
-struct audio_stream {
- int opened;
- struct mutex lock;
-
- bool active; /* is DMA in progress? */
- int num_bufs;
- void *buffer[I2S_MAX_NUM_BUFS];
- dma_addr_t buf_phy[I2S_MAX_NUM_BUFS];
- struct completion comp[I2S_MAX_NUM_BUFS];
- struct tegra_dma_req dma_req[I2S_MAX_NUM_BUFS];
- int last_queued;
-
- int i2s_fifo_atn_level;
-
- struct tegra_dma_channel *dma_chan;
- bool stop;
- struct completion stop_completion;
- spinlock_t dma_req_lock;
-
- struct work_struct allow_suspend_work;
- struct wake_lock wake_lock;
- char wake_lock_name[100];
-};
-
-/* per i2s controller */
-struct audio_driver_state {
- struct list_head next;
-
- struct platform_device *pdev;
- struct tegra_audio_platform_data *pdata;
- phys_addr_t i2s_phys;
- unsigned long i2s_base;
-
- unsigned long dma_req_sel;
-
- int irq;
- struct tegra_audio_in_config in_config;
-
- struct miscdevice misc_out;
- struct miscdevice misc_out_ctl;
- struct audio_stream out;
-
- struct miscdevice misc_in;
- struct miscdevice misc_in_ctl;
- struct audio_stream in;
-
- /* Control for whole I2S (Data format, etc.) */
- struct miscdevice misc_ctl;
- unsigned int bit_format;
-};
-
-static inline bool pending_buffer_requests(struct audio_stream *stream)
-{
- int i;
- for (i = 0; i < stream->num_bufs; i++)
- if (!completion_done(&stream->comp[i]))
- return true;
- return false;
-}
-
-static inline int buf_size(struct audio_stream *s __attribute__((unused)))
-{
- return 1 << PCM_BUFFER_MAX_SIZE_ORDER;
-}
-
-static inline struct audio_driver_state *ads_from_misc_out(struct file *file)
-{
- struct miscdevice *m = file->private_data;
- struct audio_driver_state *ads =
- container_of(m, struct audio_driver_state, misc_out);
- BUG_ON(!ads);
- return ads;
-}
-
-static inline struct audio_driver_state *ads_from_misc_out_ctl(
- struct file *file)
-{
- struct miscdevice *m = file->private_data;
- struct audio_driver_state *ads =
- container_of(m, struct audio_driver_state,
- misc_out_ctl);
- BUG_ON(!ads);
- return ads;
-}
-
-static inline struct audio_driver_state *ads_from_misc_in(struct file *file)
-{
- struct miscdevice *m = file->private_data;
- struct audio_driver_state *ads =
- container_of(m, struct audio_driver_state, misc_in);
- BUG_ON(!ads);
- return ads;
-}
-
-static inline struct audio_driver_state *ads_from_misc_in_ctl(
- struct file *file)
-{
- struct miscdevice *m = file->private_data;
- struct audio_driver_state *ads =
- container_of(m, struct audio_driver_state,
- misc_in_ctl);
- BUG_ON(!ads);
- return ads;
-}
-
-static inline struct audio_driver_state *ads_from_misc_ctl(
- struct file *file)
-{
- struct miscdevice *m = file->private_data;
- struct audio_driver_state *ads =
- container_of(m, struct audio_driver_state,
- misc_ctl);
- BUG_ON(!ads);
- return ads;
-}
-
-static inline struct audio_driver_state *ads_from_out(
- struct audio_stream *aos)
-{
- return container_of(aos, struct audio_driver_state, out);
-}
-
-static inline struct audio_driver_state *ads_from_in(
- struct audio_stream *ais)
-{
- return container_of(ais, struct audio_driver_state, in);
-}
-
-static inline void prevent_suspend(struct audio_stream *as)
-{
- pr_debug("%s\n", __func__);
- cancel_work_sync(&as->allow_suspend_work);
- wake_lock(&as->wake_lock);
-}
-
-static void allow_suspend_worker(struct work_struct *w)
-{
- struct audio_stream *as = container_of(w,
- struct audio_stream, allow_suspend_work);
- pr_debug("%s\n", __func__);
- wake_unlock(&as->wake_lock);
-}
-
-static inline void allow_suspend(struct audio_stream *as)
-{
- schedule_work(&as->allow_suspend_work);
-}
-
-#define I2S_I2S_FIFO_TX_BUSY I2S_I2S_STATUS_FIFO1_BSY
-#define I2S_I2S_FIFO_TX_QS I2S_I2S_STATUS_QS_FIFO1
-#define I2S_I2S_FIFO_TX_ERR I2S_I2S_STATUS_FIFO1_ERR
-
-#define I2S_I2S_FIFO_RX_BUSY I2S_I2S_STATUS_FIFO2_BSY
-#define I2S_I2S_FIFO_RX_QS I2S_I2S_STATUS_QS_FIFO2
-#define I2S_I2S_FIFO_RX_ERR I2S_I2S_STATUS_FIFO2_ERR
-
-#define I2S_FIFO_ERR (I2S_I2S_STATUS_FIFO1_ERR | I2S_I2S_STATUS_FIFO2_ERR)
-
-static inline void i2s_writel(unsigned long base, u32 val, u32 reg)
-{
- writel(val, base + reg);
-}
-
-static inline u32 i2s_readl(unsigned long base, u32 reg)
-{
- return readl(base + reg);
-}
-
-static inline void i2s_fifo_write(unsigned long base, int fifo, u32 data)
-{
- i2s_writel(base, data, fifo ? I2S_I2S_FIFO2_0 : I2S_I2S_FIFO1_0);
-}
-
-static inline u32 i2s_fifo_read(unsigned long base, int fifo)
-{
- return i2s_readl(base, fifo ? I2S_I2S_FIFO2_0 : I2S_I2S_FIFO1_0);
-}
-
-static int i2s_set_channel_bit_count(unsigned long base,
- int sampling, int bitclk)
-{
- u32 val;
- int bitcnt = bitclk / (2 * sampling) - 1;
-
- if (bitcnt < 0 || bitcnt >= 1<<11) {
- pr_err("%s: bit count %d is out of bounds\n", __func__,
- bitcnt);
- return -EINVAL;
- }
-
- val = bitcnt;
- if (bitclk % (2 * sampling)) {
- pr_info("%s: enabling non-symmetric mode\n", __func__);
- val |= I2S_I2S_TIMING_NON_SYM_ENABLE;
- }
-
- pr_debug("%s: I2S_I2S_TIMING_0 = %08x\n", __func__, val);
- i2s_writel(base, val, I2S_I2S_TIMING_0);
- return 0;
-}
-
-static void i2s_set_fifo_mode(unsigned long base, int fifo, int tx)
-{
- u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
- if (fifo == 0) {
- val &= ~I2S_I2S_CTRL_FIFO1_RX_ENABLE;
- val |= (!tx) ? I2S_I2S_CTRL_FIFO1_RX_ENABLE : 0;
- } else {
- val &= ~I2S_I2S_CTRL_FIFO2_TX_ENABLE;
- val |= tx ? I2S_I2S_CTRL_FIFO2_TX_ENABLE : 0;
- }
- i2s_writel(base, val, I2S_I2S_CTRL_0);
-}
-
-static int i2s_fifo_set_attention_level(unsigned long base,
- int fifo, unsigned level)
-{
- u32 val;
-
- if (level > I2S_FIFO_ATN_LVL_TWELVE_SLOTS) {
- pr_err("%s: invalid fifo level selector %d\n", __func__,
- level);
- return -EINVAL;
- }
-
- val = i2s_readl(base, I2S_I2S_FIFO_SCR_0);
-
- if (!fifo) {
- val &= ~I2S_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK;
- val |= level << I2S_FIFO1_ATN_LVL_SHIFT;
- } else {
- val &= ~I2S_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK;
- val |= level << I2S_FIFO2_ATN_LVL_SHIFT;
- }
-
- i2s_writel(base, val, I2S_I2S_FIFO_SCR_0);
- return 0;
-}
-
-static void i2s_fifo_enable(unsigned long base, int fifo, int on)
-{
- u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
- if (!fifo) {
- val &= ~I2S_I2S_CTRL_FIFO1_ENABLE;
- val |= on ? I2S_I2S_CTRL_FIFO1_ENABLE : 0;
- } else {
- val &= ~I2S_I2S_CTRL_FIFO2_ENABLE;
- val |= on ? I2S_I2S_CTRL_FIFO2_ENABLE : 0;
- }
-
- i2s_writel(base, val, I2S_I2S_CTRL_0);
-}
-
-#if 0
-static bool i2s_is_fifo_enabled(unsigned long base, int fifo)
-{
- u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
- if (!fifo)
- return !!(val & I2S_I2S_CTRL_FIFO1_ENABLE);
- return !!(val & I2S_I2S_CTRL_FIFO2_ENABLE);
-}
-#endif
-
-static void i2s_fifo_clear(unsigned long base, int fifo)
-{
- u32 val = i2s_readl(base, I2S_I2S_FIFO_SCR_0);
- if (!fifo) {
- val &= ~I2S_I2S_FIFO_SCR_FIFO1_CLR;
- val |= I2S_I2S_FIFO_SCR_FIFO1_CLR;
-#if 0
- /* Per Nvidia, reduces pop on the next run. */
- if (!(val & I2S_I2S_CTRL_FIFO1_RX_ENABLE)) {
- int cnt = 16;
- while (cnt--)
- i2s_writel(base, 0, I2S_I2S_FIFO1_0);
- }
-#endif
- } else {
- val &= ~I2S_I2S_FIFO_SCR_FIFO2_CLR;
- val |= I2S_I2S_FIFO_SCR_FIFO2_CLR;
- }
-
- i2s_writel(base, val, I2S_I2S_FIFO_SCR_0);
-}
-
-static void i2s_set_master(unsigned long base, int master)
-{
- u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
- val &= ~I2S_I2S_CTRL_MASTER_ENABLE;
- val |= master ? I2S_I2S_CTRL_MASTER_ENABLE : 0;
- i2s_writel(base, val, I2S_I2S_CTRL_0);
-}
-
-static int i2s_set_dsp_mode(unsigned long base, unsigned int mode)
-{
- u32 val;
- if (mode > TEGRA_AUDIO_DSP_TDM) {
- pr_err("%s: invalid mode %d.\n", __func__, mode);
- return -EINVAL;
- }
- if (mode == TEGRA_AUDIO_DSP_TDM) {
- pr_err("TEGRA_AUDIO_DSP_TDM not implemented.\n");
- return -EINVAL;
- }
-
- /* Disable unused modes */
- if (mode != TEGRA_AUDIO_DSP_PCM) {
- /* Disable PCM mode */
- val = i2s_readl(base, I2S_I2S_PCM_CTRL_0);
- val &= ~(I2S_I2S_PCM_CTRL_TRM_MODE |
- I2S_I2S_PCM_CTRL_RCV_MODE);
- i2s_writel(base, val, I2S_I2S_PCM_CTRL_0);
- }
- if (mode != TEGRA_AUDIO_DSP_NETWORK) {
- /* Disable Network mode */
- val = i2s_readl(base, I2S_I2S_NW_CTRL_0);
- val &= ~(I2S_I2S_NW_CTRL_TRM_TLPHY_MODE |
- I2S_I2S_NW_CTRL_RCV_TLPHY_MODE);
- i2s_writel(base, val, I2S_I2S_NW_CTRL_0);
- }
-
- /* Enable the selected mode. */
- switch (mode) {
- case TEGRA_AUDIO_DSP_NETWORK:
- /* Set DSP Network (Telephony) Mode */
- val = i2s_readl(base, I2S_I2S_NW_CTRL_0);
- val |= I2S_I2S_NW_CTRL_TRM_TLPHY_MODE |
- I2S_I2S_NW_CTRL_RCV_TLPHY_MODE;
- i2s_writel(base, val, I2S_I2S_NW_CTRL_0);
- break;
- case TEGRA_AUDIO_DSP_PCM:
- /* Set DSP PCM Mode */
- val = i2s_readl(base, I2S_I2S_PCM_CTRL_0);
- val |= I2S_I2S_PCM_CTRL_TRM_MODE |
- I2S_I2S_PCM_CTRL_RCV_MODE;
- i2s_writel(base, val, I2S_I2S_PCM_CTRL_0);
- break;
- }
-
- return 0;
-}
-
-static int i2s_set_bit_format(unsigned long base, unsigned fmt)
-{
- u32 val;
-
- if (fmt > I2S_BIT_FORMAT_DSP) {
- pr_err("%s: invalid bit-format selector %d\n", __func__, fmt);
- return -EINVAL;
- }
-
- val = i2s_readl(base, I2S_I2S_CTRL_0);
- val &= ~I2S_I2S_CTRL_BIT_FORMAT_MASK;
- val |= fmt << I2S_BIT_FORMAT_SHIFT;
- i2s_writel(base, val, I2S_I2S_CTRL_0);
- /* For DSP format, select DSP PCM mode. */
- /* PCM mode and Network Mode slot 0 are effectively identical. */
- if (fmt == I2S_BIT_FORMAT_DSP)
- i2s_set_dsp_mode(base, TEGRA_AUDIO_DSP_PCM);
- else
- i2s_set_dsp_mode(base, TEGRA_AUDIO_DSP_NONE);
-
- return 0;
-}
-
-static int i2s_set_bit_size(unsigned long base, unsigned bit_size)
-{
- u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
- val &= ~I2S_I2S_CTRL_BIT_SIZE_MASK;
-
- if (bit_size > I2S_BIT_SIZE_32) {
- pr_err("%s: invalid bit_size selector %d\n", __func__,
- bit_size);
- return -EINVAL;
- }
-
- val |= bit_size << I2S_BIT_SIZE_SHIFT;
-
- i2s_writel(base, val, I2S_I2S_CTRL_0);
- return 0;
-}
-
-static int i2s_set_fifo_format(unsigned long base, unsigned fmt)
-{
- u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
- val &= ~I2S_I2S_CTRL_FIFO_FORMAT_MASK;
-
- if (fmt > I2S_FIFO_32 && fmt != I2S_FIFO_PACKED) {
- pr_err("%s: invalid fmt selector %d\n", __func__, fmt);
- return -EINVAL;
- }
-
- val |= fmt << I2S_FIFO_SHIFT;
-
- i2s_writel(base, val, I2S_I2S_CTRL_0);
- return 0;
-}
-
-static void i2s_set_left_right_control_polarity(unsigned long base,
- int high_low)
-{
- u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
- val &= ~I2S_I2S_CTRL_L_R_CTRL;
- val |= high_low ? I2S_I2S_CTRL_L_R_CTRL : 0;
- i2s_writel(base, val, I2S_I2S_CTRL_0);
-}
-
-#if 0
-static void i2s_set_fifo_irq_on_err(unsigned long base, int fifo, int on)
-{
- u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
- if (!fifo) {
- val &= ~I2S_I2S_IE_FIFO1_ERR;
- val |= on ? I2S_I2S_IE_FIFO1_ERR : 0;
- } else {
- val &= ~I2S_I2S_IE_FIFO2_ERR;
- val |= on ? I2S_I2S_IE_FIFO2_ERR : 0;
- }
- i2s_writel(base, val, I2S_I2S_CTRL_0);
-}
-
-static void i2s_set_fifo_irq_on_qe(unsigned long base, int fifo, int on)
-{
- u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
- if (!fifo) {
- val &= ~I2S_I2S_QE_FIFO1;
- val |= on ? I2S_I2S_QE_FIFO1 : 0;
- } else {
- val &= ~I2S_I2S_QE_FIFO2;
- val |= on ? I2S_I2S_QE_FIFO2 : 0;
- }
- i2s_writel(base, val, I2S_I2S_CTRL_0);
-}
-#endif
-
-static void i2s_enable_fifos(unsigned long base, int on)
-{
- u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
- if (on)
- val |= I2S_I2S_QE_FIFO1 | I2S_I2S_QE_FIFO2 |
- I2S_I2S_IE_FIFO1_ERR | I2S_I2S_IE_FIFO2_ERR;
- else
- val &= ~(I2S_I2S_QE_FIFO1 | I2S_I2S_QE_FIFO2 |
- I2S_I2S_IE_FIFO1_ERR | I2S_I2S_IE_FIFO2_ERR);
-
- i2s_writel(base, val, I2S_I2S_CTRL_0);
-}
-
-static inline u32 i2s_get_status(unsigned long base)
-{
- return i2s_readl(base, I2S_I2S_STATUS_0);
-}
-
-static inline u32 i2s_get_control(unsigned long base)
-{
- return i2s_readl(base, I2S_I2S_CTRL_0);
-}
-
-static inline void i2s_ack_status(unsigned long base)
-{
- return i2s_writel(base, i2s_readl(base, I2S_I2S_STATUS_0),
- I2S_I2S_STATUS_0);
-}
-
-static inline u32 i2s_get_fifo_scr(unsigned long base)
-{
- return i2s_readl(base, I2S_I2S_FIFO_SCR_0);
-}
-
-static inline phys_addr_t i2s_get_fifo_phy_base(unsigned long phy_base,
- int fifo)
-{
- return phy_base + (fifo ? I2S_I2S_FIFO2_0 : I2S_I2S_FIFO1_0);
-}
-
-static inline u32 i2s_get_fifo_full_empty_count(unsigned long base, int fifo)
-{
- u32 val = i2s_readl(base, I2S_I2S_FIFO_SCR_0);
-
- if (!fifo)
- val = val >> I2S_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT;
- else
- val = val >> I2S_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT;
-
- return val & I2S_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK;
-}
-
-static int i2s_configure(struct platform_device *pdev)
-{
- struct tegra_audio_platform_data *pdata = pdev->dev.platform_data;
- struct audio_driver_state *state = pdata->driver_data;
- bool master;
- struct clk *i2s_clk;
- int master_clk;
-
- /* dev_info(&pdev->dev, "%s\n", __func__); */
-
- if (!state)
- return -ENOMEM;
-
- /* disable interrupts from I2S */
- i2s_enable_fifos(state->i2s_base, 0);
- i2s_fifo_clear(state->i2s_base, I2S_FIFO_TX);
- i2s_fifo_clear(state->i2s_base, I2S_FIFO_RX);
- i2s_set_left_right_control_polarity(state->i2s_base, 0); /* default */
-
- i2s_clk = clk_get(&pdev->dev, NULL);
- if (!i2s_clk) {
- dev_err(&pdev->dev, "%s: could not get i2s clock\n",
- __func__);
- return -EIO;
- }
-
- master = state->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP ?
- state->pdata->dsp_master : state->pdata->i2s_master;
-
-
- master_clk = state->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP ?
- state->pdata->dsp_master_clk :
- state->pdata->i2s_master_clk;
-#define I2S_CLK_TO_BITCLK_RATIO 2 /* Todo, Bitclk based on 2X clock? */
- if (master)
- i2s_set_channel_bit_count(state->i2s_base, master_clk,
- clk_get_rate(i2s_clk)*I2S_CLK_TO_BITCLK_RATIO);
- i2s_set_master(state->i2s_base, master);
-
- i2s_set_fifo_mode(state->i2s_base, I2S_FIFO_TX, 1);
- i2s_set_fifo_mode(state->i2s_base, I2S_FIFO_RX, 0);
-
- if (state->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP)
- i2s_set_bit_format(state->i2s_base, I2S_BIT_FORMAT_DSP);
- else
- i2s_set_bit_format(state->i2s_base, state->pdata->mode);
- i2s_set_bit_size(state->i2s_base, state->pdata->bit_size);
- i2s_set_fifo_format(state->i2s_base, state->pdata->fifo_fmt);
-
- return 0;
-}
-
-static int init_stream_buffer(struct audio_stream *, int);
-
-static int setup_dma(struct audio_driver_state *, int);
-static void tear_down_dma(struct audio_driver_state *, int);
-static void stop_dma_playback(struct audio_stream *);
-static int start_dma_recording(struct audio_stream *, int);
-static void stop_dma_recording(struct audio_stream *);
-
-struct sound_ops {
- int (*setup)(struct audio_driver_state *, int);
- void (*tear_down)(struct audio_driver_state *, int);
- void (*stop_playback)(struct audio_stream *);
- int (*start_recording)(struct audio_stream *, int);
- void (*stop_recording)(struct audio_stream *);
-};
-
-static const struct sound_ops dma_sound_ops = {
- .setup = setup_dma,
- .tear_down = tear_down_dma,
- .stop_playback = stop_dma_playback,
- .start_recording = start_dma_recording,
- .stop_recording = stop_dma_recording,
-};
-
-static const struct sound_ops *sound_ops = &dma_sound_ops;
-
-static int start_recording_if_necessary(struct audio_stream *ais, int size)
-{
- int rc = 0;
- unsigned long flags;
- prevent_suspend(ais);
- spin_lock_irqsave(&ais->dma_req_lock, flags);
- if (!ais->stop && !pending_buffer_requests(ais)) {
- /* pr_debug("%s: starting recording\n", __func__); */
- rc = sound_ops->start_recording(ais, size);
- if (rc) {
- pr_err("%s start_recording() failed\n", __func__);
- allow_suspend(ais);
- }
- }
- spin_unlock_irqrestore(&ais->dma_req_lock, flags);
- return rc;
-}
-
-static bool stop_playback_if_necessary(struct audio_stream *aos)
-{
- unsigned long flags;
- spin_lock_irqsave(&aos->dma_req_lock, flags);
- pr_debug("%s\n", __func__);
- if (!pending_buffer_requests(aos)) {
- pr_debug("%s: no more data to play back\n", __func__);
- sound_ops->stop_playback(aos);
- spin_unlock_irqrestore(&aos->dma_req_lock, flags);
- allow_suspend(aos);
- return true;
- }
- spin_unlock_irqrestore(&aos->dma_req_lock, flags);
-
- return false;
-}
-
-/* playback and recording */
-static bool wait_till_stopped(struct audio_stream *as)
-{
- int rc;
- pr_debug("%s: wait for completion\n", __func__);
- rc = wait_for_completion_timeout(
- &as->stop_completion, HZ);
- if (!rc)
- pr_err("%s: wait timed out", __func__);
- if (rc < 0)
- pr_err("%s: wait error %d\n", __func__, rc);
- allow_suspend(as);
- pr_debug("%s: done: %d\n", __func__, rc);
- return true;
-}
-
-/* Ask for playback and recording to stop. The _nosync means that
- * as->lock has to be locked by the caller.
- */
-static void request_stop_nosync(struct audio_stream *as)
-{
- int i;
- pr_debug("%s\n", __func__);
- if (!as->stop) {
- as->stop = true;
- if (pending_buffer_requests(as))
- wait_till_stopped(as);
- for (i = 0; i < as->num_bufs; i++) {
- init_completion(&as->comp[i]);
- complete(&as->comp[i]);
- }
- }
- if (!tegra_dma_is_empty(as->dma_chan))
- pr_err("%s: DMA not empty!\n", __func__);
- /* Stop the DMA then dequeue anything that's in progress. */
- tegra_dma_cancel(as->dma_chan);
- as->active = false; /* applies to recording only */
- pr_debug("%s: done\n", __func__);
-}
-
-static void setup_dma_tx_request(struct tegra_dma_req *req,
- struct audio_stream *aos);
-
-static void setup_dma_rx_request(struct tegra_dma_req *req,
- struct audio_stream *ais);
-
-static int setup_dma(struct audio_driver_state *ads, int mask)
-{
- int rc, i;
- pr_info("%s\n", __func__);
-
- if (mask & TEGRA_AUDIO_ENABLE_TX) {
- /* setup audio playback */
- for (i = 0; i < ads->out.num_bufs; i++) {
- ads->out.buf_phy[i] = dma_map_single(&ads->pdev->dev,
- ads->out.buffer[i],
- 1 << PCM_BUFFER_MAX_SIZE_ORDER,
- DMA_TO_DEVICE);
- BUG_ON(!ads->out.buf_phy[i]);
- setup_dma_tx_request(&ads->out.dma_req[i], &ads->out);
- ads->out.dma_req[i].source_addr = ads->out.buf_phy[i];
- }
- ads->out.dma_chan = tegra_dma_allocate_channel(
- TEGRA_DMA_MODE_CONTINUOUS_SINGLE,
- "i2s_tx_req_%d", ads->dma_req_sel);
- if (!ads->out.dma_chan) {
- pr_err("%s: error alloc output DMA channel: %ld\n",
- __func__, PTR_ERR(ads->out.dma_chan));
- rc = -ENODEV;
- goto fail_tx;
- }
- }
-
- if (mask & TEGRA_AUDIO_ENABLE_RX) {
- /* setup audio recording */
- for (i = 0; i < ads->in.num_bufs; i++) {
- ads->in.buf_phy[i] = dma_map_single(&ads->pdev->dev,
- ads->in.buffer[i],
- 1 << PCM_BUFFER_MAX_SIZE_ORDER,
- DMA_FROM_DEVICE);
- BUG_ON(!ads->in.buf_phy[i]);
- setup_dma_rx_request(&ads->in.dma_req[i], &ads->in);
- ads->in.dma_req[i].dest_addr = ads->in.buf_phy[i];
- }
- ads->in.dma_chan = tegra_dma_allocate_channel(
- TEGRA_DMA_MODE_CONTINUOUS_SINGLE,
- "i2s_rx_req_%d", ads->dma_req_sel);
- if (!ads->in.dma_chan) {
- pr_err("%s: error allocating input DMA channel: %ld\n",
- __func__, PTR_ERR(ads->in.dma_chan));
- rc = -ENODEV;
- goto fail_rx;
- }
- }
-
- return 0;
-
-fail_rx:
- if (mask & TEGRA_AUDIO_ENABLE_RX) {
- for (i = 0; i < ads->in.num_bufs; i++) {
- dma_unmap_single(&ads->pdev->dev, ads->in.buf_phy[i],
- 1 << PCM_BUFFER_MAX_SIZE_ORDER,
- DMA_FROM_DEVICE);
- ads->in.buf_phy[i] = 0;
- }
- tegra_dma_free_channel(ads->in.dma_chan);
- ads->in.dma_chan = 0;
- }
-fail_tx:
- if (mask & TEGRA_AUDIO_ENABLE_TX) {
- for (i = 0; i < ads->out.num_bufs; i++) {
- dma_unmap_single(&ads->pdev->dev, ads->out.buf_phy[i],
- 1 << PCM_BUFFER_MAX_SIZE_ORDER,
- DMA_TO_DEVICE);
- ads->out.buf_phy[i] = 0;
- }
- tegra_dma_free_channel(ads->out.dma_chan);
- ads->out.dma_chan = 0;
- }
-
- return rc;
-}
-
-static void tear_down_dma(struct audio_driver_state *ads, int mask)
-{
- int i;
- pr_info("%s\n", __func__);
-
- if (mask & TEGRA_AUDIO_ENABLE_TX) {
- tegra_dma_free_channel(ads->out.dma_chan);
- for (i = 0; i < ads->out.num_bufs; i++) {
- dma_unmap_single(&ads->pdev->dev, ads->out.buf_phy[i],
- buf_size(&ads->out),
- DMA_TO_DEVICE);
- ads->out.buf_phy[i] = 0;
- }
- }
- ads->out.dma_chan = NULL;
-
- if (mask & TEGRA_AUDIO_ENABLE_RX) {
- tegra_dma_free_channel(ads->in.dma_chan);
- for (i = 0; i < ads->in.num_bufs; i++) {
- dma_unmap_single(&ads->pdev->dev, ads->in.buf_phy[i],
- buf_size(&ads->in),
- DMA_FROM_DEVICE);
- ads->in.buf_phy[i] = 0;
- }
- }
- ads->in.dma_chan = NULL;
-}
-
-static void dma_tx_complete_callback(struct tegra_dma_req *req)
-{
- unsigned long flags;
- struct audio_stream *aos = req->dev;
- unsigned req_num;
-
- spin_lock_irqsave(&aos->dma_req_lock, flags);
-
- req_num = req - aos->dma_req;
- pr_debug("%s: completed buffer %d size %d\n", __func__,
- req_num, req->bytes_transferred);
- BUG_ON(req_num >= aos->num_bufs);
-
- complete(&aos->comp[req_num]);
-
- if (!pending_buffer_requests(aos)) {
- pr_debug("%s: Playback underflow\n", __func__);
- complete(&aos->stop_completion);
- }
-
- spin_unlock_irqrestore(&aos->dma_req_lock, flags);
-}
-
-static void dma_rx_complete_callback(struct tegra_dma_req *req)
-{
- unsigned long flags;
- struct audio_stream *ais = req->dev;
- unsigned req_num;
-
- spin_lock_irqsave(&ais->dma_req_lock, flags);
-
- req_num = req - ais->dma_req;
- pr_debug("%s: completed buffer %d size %d\n", __func__,
- req_num, req->bytes_transferred);
- BUG_ON(req_num >= ais->num_bufs);
-
- complete(&ais->comp[req_num]);
-
- if (!pending_buffer_requests(ais))
- pr_debug("%s: Capture overflow\n", __func__);
-
- spin_unlock_irqrestore(&ais->dma_req_lock, flags);
-}
-
-static void setup_dma_tx_request(struct tegra_dma_req *req,
- struct audio_stream *aos)
-{
- struct audio_driver_state *ads = ads_from_out(aos);
-
- memset(req, 0, sizeof(*req));
-
- req->complete = dma_tx_complete_callback;
- req->dev = aos;
- req->to_memory = false;
- req->dest_addr = i2s_get_fifo_phy_base(ads->i2s_phys, I2S_FIFO_TX);
- req->dest_wrap = 4;
- if (ads->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP)
- req->dest_bus_width = ads->pdata->dsp_bus_width;
- else
- req->dest_bus_width = ads->pdata->i2s_bus_width;
- req->source_bus_width = 32;
- req->source_wrap = 0;
- req->req_sel = ads->dma_req_sel;
-}
-
-static void setup_dma_rx_request(struct tegra_dma_req *req,
- struct audio_stream *ais)
-{
- struct audio_driver_state *ads = ads_from_in(ais);
-
- memset(req, 0, sizeof(*req));
-
- req->complete = dma_rx_complete_callback;
- req->dev = ais;
- req->to_memory = true;
- req->source_addr = i2s_get_fifo_phy_base(ads->i2s_phys, I2S_FIFO_RX);
- req->source_wrap = 4;
- if (ads->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP)
- req->source_bus_width = ads->pdata->dsp_bus_width;
- else
- req->source_bus_width = ads->pdata->i2s_bus_width;
- req->dest_bus_width = 32;
- req->dest_wrap = 0;
- req->req_sel = ads->dma_req_sel;
-}
-
-static int start_playback(struct audio_stream *aos,
- struct tegra_dma_req *req)
-{
- int rc;
- unsigned long flags;
- struct audio_driver_state *ads = ads_from_out(aos);
-
- pr_debug("%s: (writing %d)\n",
- __func__, req->size);
-
- spin_lock_irqsave(&aos->dma_req_lock, flags);
-#if 0
- i2s_fifo_clear(ads->i2s_base, I2S_FIFO_TX);
-#endif
- i2s_fifo_set_attention_level(ads->i2s_base,
- I2S_FIFO_TX, aos->i2s_fifo_atn_level);
-
- i2s_fifo_enable(ads->i2s_base, I2S_FIFO_TX, 1);
-
- rc = tegra_dma_enqueue_req(aos->dma_chan, req);
- spin_unlock_irqrestore(&aos->dma_req_lock, flags);
-
- if (rc)
- pr_err("%s: could not enqueue TX DMA req\n", __func__);
- return rc;
-}
-
-/* Called with aos->dma_req_lock taken. */
-static void stop_dma_playback(struct audio_stream *aos)
-{
- int spin = 0;
- struct audio_driver_state *ads = ads_from_out(aos);
- pr_debug("%s\n", __func__);
- i2s_fifo_enable(ads->i2s_base, I2S_FIFO_TX, 0);
- while ((i2s_get_status(ads->i2s_base) & I2S_I2S_FIFO_TX_BUSY) &&
- spin < 100) {
- udelay(10);
- if (spin++ > 50)
- pr_info("%s: spin %d\n", __func__, spin);
- }
- if (spin == 100)
- pr_warn("%s: spinny\n", __func__);
-}
-
-/* This function may be called from either interrupt or process context. */
-/* Called with ais->dma_req_lock taken. */
-static int start_dma_recording(struct audio_stream *ais, int size)
-{
- int i;
- struct audio_driver_state *ads = ads_from_in(ais);
-
- pr_debug("%s\n", __func__);
-
- BUG_ON(pending_buffer_requests(ais));
-
- for (i = 0; i < ais->num_bufs; i++) {
- init_completion(&ais->comp[i]);
- ais->dma_req[i].dest_addr = ais->buf_phy[i];
- ais->dma_req[i].size = size;
- tegra_dma_enqueue_req(ais->dma_chan, &ais->dma_req[i]);
- }
-
- ais->last_queued = ais->num_bufs - 1;
-
-#if 0
- i2s_fifo_clear(ads->i2s_base, I2S_FIFO_RX);
-#endif
- i2s_fifo_set_attention_level(ads->i2s_base,
- I2S_FIFO_RX, ais->i2s_fifo_atn_level);
- i2s_fifo_enable(ads->i2s_base, I2S_FIFO_RX, 1);
- return 0;
-}
-
-static void stop_dma_recording(struct audio_stream *ais)
-{
- int spin = 0;
- struct audio_driver_state *ads = ads_from_in(ais);
- pr_debug("%s\n", __func__);
- tegra_dma_cancel(ais->dma_chan);
- i2s_fifo_enable(ads->i2s_base, I2S_FIFO_RX, 0);
- i2s_fifo_clear(ads->i2s_base, I2S_FIFO_RX);
- while ((i2s_get_status(ads->i2s_base) & I2S_I2S_FIFO_RX_BUSY) &&
- spin < 100) {
- udelay(10);
- if (spin++ > 50)
- pr_info("%s: spin %d\n", __func__, spin);
- }
- if (spin == 100)
- pr_warn("%s: spinny\n", __func__);
-}
-
-static irqreturn_t i2s_interrupt(int irq, void *data)
-{
- struct audio_driver_state *ads = data;
- u32 status = i2s_get_status(ads->i2s_base);
-
- pr_debug("%s: %08x\n", __func__, status);
-
- if (status & I2S_FIFO_ERR)
- i2s_ack_status(ads->i2s_base);
-
- pr_debug("%s: done %08x\n", __func__, i2s_get_status(ads->i2s_base));
- return IRQ_HANDLED;
-}
-
-static ssize_t tegra_audio_write(struct file *file,
- const char __user *buf, size_t size, loff_t *off)
-{
- ssize_t rc = 0;
- int out_buf;
- struct tegra_dma_req *req;
- struct audio_driver_state *ads = ads_from_misc_out(file);
-
- mutex_lock(&ads->out.lock);
-
- if (!IS_ALIGNED(size, 4) || size < 4 || size > buf_size(&ads->out)) {
- pr_err("%s: invalid user size %d\n", __func__, size);
- rc = -EINVAL;
- goto done;
- }
-
- pr_debug("%s: write %d bytes\n", __func__, size);
-
- if (ads->out.stop) {
- pr_debug("%s: playback has been cancelled\n", __func__);
- goto done;
- }
-
- /* Decide which buf is next. */
- out_buf = (ads->out.last_queued + 1) % ads->out.num_bufs;
- req = &ads->out.dma_req[out_buf];
-
- /* Wait for the buffer to be emptied (complete). The maximum timeout
- * value could be calculated dynamically based on buf_size(&ads->out).
- * For a buffer size of 16k, at 44.1kHz/stereo/16-bit PCM, you would
- * have ~93ms.
- */
- pr_debug("%s: waiting for buffer %d\n", __func__, out_buf);
- rc = wait_for_completion_interruptible_timeout(
- &ads->out.comp[out_buf], HZ);
- if (!rc) {
- pr_err("%s: timeout", __func__);
- rc = -ETIMEDOUT;
- goto done;
- } else if (rc < 0) {
- pr_err("%s: wait error %d", __func__, rc);
- goto done;
- }
-
- /* Fill the buffer and enqueue it. */
- pr_debug("%s: acquired buffer %d, copying data\n", __func__, out_buf);
- rc = copy_from_user(ads->out.buffer[out_buf], buf, size);
- if (rc) {
- rc = -EFAULT;
- goto done;
- }
-
- prevent_suspend(&ads->out);
-
- req->size = size;
- dma_sync_single_for_device(NULL,
- req->source_addr, req->size, DMA_TO_DEVICE);
- ads->out.last_queued = out_buf;
- init_completion(&ads->out.stop_completion);
-
- rc = start_playback(&ads->out, req);
- if (!rc)
- rc = size;
- else
- allow_suspend(&ads->out);
-
-done:
- mutex_unlock(&ads->out.lock);
- return rc;
-}
-
-static long tegra_audio_out_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- int rc = 0;
- struct audio_driver_state *ads = ads_from_misc_out_ctl(file);
- struct audio_stream *aos = &ads->out;
-
- mutex_lock(&aos->lock);
-
- switch (cmd) {
- case TEGRA_AUDIO_OUT_FLUSH:
- if (pending_buffer_requests(aos)) {
- pr_debug("%s: flushing\n", __func__);
- request_stop_nosync(aos);
- pr_debug("%s: flushed\n", __func__);
- }
- if (stop_playback_if_necessary(aos))
- pr_debug("%s: done (stopped)\n", __func__);
- aos->stop = false;
- break;
- case TEGRA_AUDIO_OUT_SET_NUM_BUFS: {
- unsigned int num;
- if (copy_from_user(&num, (const void __user *)arg,
- sizeof(num))) {
- rc = -EFAULT;
- break;
- }
- if (!num || num > I2S_MAX_NUM_BUFS) {
- pr_err("%s: invalid buffer count %d\n", __func__, num);
- rc = -EINVAL;
- break;
- }
- if (pending_buffer_requests(aos)) {
- pr_err("%s: playback in progress\n", __func__);
- rc = -EBUSY;
- break;
- }
- rc = init_stream_buffer(aos, num);
- if (rc < 0)
- break;
- aos->num_bufs = num;
- sound_ops->tear_down(ads, TEGRA_AUDIO_ENABLE_TX);
- sound_ops->setup(ads, TEGRA_AUDIO_ENABLE_TX);
- pr_debug("%s: num buf set to %d\n", __func__, num);
- }
- break;
- case TEGRA_AUDIO_OUT_GET_NUM_BUFS:
- if (copy_to_user((void __user *)arg,
- &aos->num_bufs, sizeof(aos->num_bufs)))
- rc = -EFAULT;
- break;
- default:
- rc = -EINVAL;
- }
-
- mutex_unlock(&aos->lock);
- return rc;
-}
-
-static long tegra_audio_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- int rc = 0;
- struct audio_driver_state *ads = ads_from_misc_ctl(file);
- unsigned int mode;
- bool dma_restart = false;
-
- mutex_lock(&ads->out.lock);
- mutex_lock(&ads->in.lock);
-
- switch (cmd) {
- case TEGRA_AUDIO_SET_BIT_FORMAT:
- if (copy_from_user(&mode, (const void __user *)arg,
- sizeof(mode))) {
- rc = -EFAULT;
- goto done;
- }
- dma_restart = (mode != ads->bit_format);
- switch (mode) {
- case TEGRA_AUDIO_BIT_FORMAT_DEFAULT:
- i2s_set_bit_format(ads->i2s_base, ads->pdata->mode);
- ads->bit_format = mode;
- break;
- case TEGRA_AUDIO_BIT_FORMAT_DSP:
- i2s_set_bit_format(ads->i2s_base, I2S_BIT_FORMAT_DSP);
- ads->bit_format = mode;
- break;
- default:
- pr_err("%s: Invald PCM mode %d", __func__, mode);
- rc = -EINVAL;
- goto done;
- }
- break;
- case TEGRA_AUDIO_GET_BIT_FORMAT:
- if (copy_to_user((void __user *)arg, &ads->bit_format,
- sizeof(mode)))
- rc = -EFAULT;
- goto done;
- }
-
- if (dma_restart) {
- pr_debug("%s: Restarting DMA due to configuration change.\n",
- __func__);
- if (pending_buffer_requests(&ads->out) || ads->in.active) {
- pr_err("%s: dma busy, cannot restart.\n", __func__);
- rc = -EBUSY;
- goto done;
- }
- sound_ops->tear_down(ads, ads->pdata->mask);
- i2s_configure(ads->pdev);
- sound_ops->setup(ads, ads->pdata->mask);
- }
-
-done:
- mutex_unlock(&ads->in.lock);
- mutex_unlock(&ads->out.lock);
- return rc;
-}
-
-static long tegra_audio_in_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- int rc = 0;
- struct audio_driver_state *ads = ads_from_misc_in_ctl(file);
- struct audio_stream *ais = &ads->in;
-
- mutex_lock(&ais->lock);
-
- switch (cmd) {
- case TEGRA_AUDIO_IN_START:
- pr_debug("%s: start recording\n", __func__);
- ais->stop = false;
- break;
- case TEGRA_AUDIO_IN_STOP:
- pr_debug("%s: stop recording\n", __func__);
- if (ais->active) {
- /* Clean up DMA/I2S, and complete the completion */
- sound_ops->stop_recording(ais);
- complete(&ais->stop_completion);
- /* Set stop flag and allow suspend. */
- request_stop_nosync(ais);
- }
- break;
- case TEGRA_AUDIO_IN_SET_CONFIG: {
- struct tegra_audio_in_config cfg;
-
- if (ais->active) {
- pr_err("%s: recording in progress\n", __func__);
- rc = -EBUSY;
- break;
- }
- if (copy_from_user(&cfg, (const void __user *)arg,
- sizeof(cfg))) {
- rc = -EFAULT;
- break;
- }
-
- if (cfg.stereo && !ads->pdata->stereo_capture) {
- pr_err("%s: not capable of stereo capture.",
- __func__);
- rc = -EINVAL;
- }
- if (!rc) {
- pr_info("%s: setting input sampling rate to %d, %s\n",
- __func__, cfg.rate,
- cfg.stereo ? "stereo" : "mono");
- ads->in_config = cfg;
- ads->in_config.stereo = !!ads->in_config.stereo;
- }
- }
- break;
- case TEGRA_AUDIO_IN_GET_CONFIG:
- if (copy_to_user((void __user *)arg, &ads->in_config,
- sizeof(ads->in_config)))
- rc = -EFAULT;
- break;
- case TEGRA_AUDIO_IN_SET_NUM_BUFS: {
- unsigned int num;
- if (copy_from_user(&num, (const void __user *)arg,
- sizeof(num))) {
- rc = -EFAULT;
- break;
- }
- if (!num || num > I2S_MAX_NUM_BUFS) {
- pr_err("%s: invalid buffer count %d\n", __func__,
- num);
- rc = -EINVAL;
- break;
- }
- if (ais->active || pending_buffer_requests(ais)) {
- pr_err("%s: recording in progress\n", __func__);
- rc = -EBUSY;
- break;
- }
- rc = init_stream_buffer(ais, num);
- if (rc < 0)
- break;
- ais->num_bufs = num;
- sound_ops->tear_down(ads, TEGRA_AUDIO_ENABLE_RX);
- sound_ops->setup(ads, TEGRA_AUDIO_ENABLE_RX);
- }
- break;
- case TEGRA_AUDIO_IN_GET_NUM_BUFS:
- if (copy_to_user((void __user *)arg,
- &ais->num_bufs, sizeof(ais->num_bufs)))
- rc = -EFAULT;
- break;
- default:
- rc = -EINVAL;
- }
-
- mutex_unlock(&ais->lock);
- return rc;
-}
-
-static ssize_t tegra_audio_read(struct file *file, char __user *buf,
- size_t size, loff_t *off)
-{
- ssize_t rc;
- ssize_t nr = 0;
- int in_buf;
- struct tegra_dma_req *req;
- struct audio_driver_state *ads = ads_from_misc_in(file);
-
- mutex_lock(&ads->in.lock);
-
- if (!IS_ALIGNED(size, 4) || size < 4 || size > buf_size(&ads->in)) {
- pr_err("%s: invalid size %d.\n", __func__, size);
- rc = -EINVAL;
- goto done;
- }
-
- pr_debug("%s: size %d\n", __func__, size);
-
- /* If we want recording to stop immediately after it gets cancelled,
- * then we do not want to wait for the fifo to get drained.
- */
- if (ads->in.stop) {
- pr_debug("%s: recording has been cancelled\n", __func__);
- rc = 0;
- goto done;
- }
-
- /* This function calls prevent_suspend() internally */
- rc = start_recording_if_necessary(&ads->in, size);
- if (rc < 0 && rc != -EALREADY) {
- pr_err("%s: could not start recording\n", __func__);
- goto done;
- }
-
- ads->in.active = true;
-
- /* Note that when tegra_audio_read() is called for the first time (or
- * when all the buffers are empty), then it queues up all
- * ads->in.num_bufs buffers, and in_buf is set to zero below.
- */
- in_buf = (ads->in.last_queued + 1) % ads->in.num_bufs;
-
- /* Wait for the buffer to be filled (complete). The maximum timeout
- * value could be calculated dynamically based on buf_size(&ads->in).
- * For a buffer size of 16k, at 44.1kHz/stereo/16-bit PCM, you would
- * have ~93ms.
- */
- rc = wait_for_completion_interruptible_timeout(
- &ads->in.comp[in_buf], HZ);
- if (!rc) {
- pr_err("%s: timeout", __func__);
- rc = -ETIMEDOUT;
- goto done;
- } else if (rc < 0) {
- pr_err("%s: wait error %d", __func__, rc);
- goto done;
- }
-
- req = &ads->in.dma_req[in_buf];
-
- nr = size > req->size ? req->size : size;
- req->size = size;
- dma_sync_single_for_cpu(NULL, ads->in.dma_req[in_buf].dest_addr,
- ads->in.dma_req[in_buf].size, DMA_FROM_DEVICE);
- rc = copy_to_user(buf, ads->in.buffer[in_buf], nr);
- if (rc) {
- rc = -EFAULT;
- goto done;
- }
-
- init_completion(&ads->in.stop_completion);
-
- ads->in.last_queued = in_buf;
- rc = tegra_dma_enqueue_req(ads->in.dma_chan, req);
- /* We've successfully enqueued this request before. */
- BUG_ON(rc);
-
- rc = nr;
- *off += nr;
-done:
- mutex_unlock(&ads->in.lock);
- pr_debug("%s: done %d\n", __func__, rc);
- return rc;
-}
-
-static int tegra_audio_out_open(struct inode *inode, struct file *file)
-{
- int rc = 0;
- int i;
- struct audio_driver_state *ads = ads_from_misc_out(file);
-
- pr_debug("%s\n", __func__);
-
- mutex_lock(&ads->out.lock);
-
- if (ads->out.opened) {
- rc = -EBUSY;
- goto done;
- }
-
- ads->out.opened = 1;
- ads->out.stop = false;
-
- for (i = 0; i < I2S_MAX_NUM_BUFS; i++) {
- init_completion(&ads->out.comp[i]);
- /* TX buf rest state is unqueued, complete. */
- complete(&ads->out.comp[i]);
- }
-
-done:
- mutex_unlock(&ads->out.lock);
- return rc;
-}
-
-static int tegra_audio_out_release(struct inode *inode, struct file *file)
-{
- struct audio_driver_state *ads = ads_from_misc_out(file);
-
- pr_debug("%s\n", __func__);
-
- mutex_lock(&ads->out.lock);
- ads->out.opened = 0;
- request_stop_nosync(&ads->out);
- if (stop_playback_if_necessary(&ads->out))
- pr_debug("%s: done (stopped)\n", __func__);
- allow_suspend(&ads->out);
- mutex_unlock(&ads->out.lock);
- pr_debug("%s: done\n", __func__);
- return 0;
-}
-
-static int tegra_audio_in_open(struct inode *inode, struct file *file)
-{
- int rc = 0;
- int i;
- struct audio_driver_state *ads = ads_from_misc_in(file);
-
- pr_debug("%s\n", __func__);
-
- mutex_lock(&ads->in.lock);
- if (ads->in.opened) {
- rc = -EBUSY;
- goto done;
- }
-
- ads->in.opened = 1;
- ads->in.stop = false;
-
- for (i = 0; i < I2S_MAX_NUM_BUFS; i++) {
- init_completion(&ads->in.comp[i]);
- /* RX buf rest state is unqueued, complete. */
- complete(&ads->in.comp[i]);
- }
-
-done:
- mutex_unlock(&ads->in.lock);
- return rc;
-}
-
-static int tegra_audio_in_release(struct inode *inode, struct file *file)
-{
- struct audio_driver_state *ads = ads_from_misc_in(file);
-
- pr_debug("%s\n", __func__);
-
- mutex_lock(&ads->in.lock);
- ads->in.opened = 0;
- if (ads->in.active) {
- sound_ops->stop_recording(&ads->in);
- complete(&ads->in.stop_completion);
- request_stop_nosync(&ads->in);
- }
- allow_suspend(&ads->in);
- mutex_unlock(&ads->in.lock);
- pr_debug("%s: done\n", __func__);
- return 0;
-}
-
-static const struct file_operations tegra_audio_out_fops = {
- .owner = THIS_MODULE,
- .open = tegra_audio_out_open,
- .release = tegra_audio_out_release,
- .write = tegra_audio_write,
-};
-
-static const struct file_operations tegra_audio_in_fops = {
- .owner = THIS_MODULE,
- .open = tegra_audio_in_open,
- .read = tegra_audio_read,
- .release = tegra_audio_in_release,
-};
-
-static int tegra_audio_ctl_open(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static int tegra_audio_ctl_release(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static const struct file_operations tegra_audio_out_ctl_fops = {
- .owner = THIS_MODULE,
- .open = tegra_audio_ctl_open,
- .release = tegra_audio_ctl_release,
- .unlocked_ioctl = tegra_audio_out_ioctl,
-};
-
-static const struct file_operations tegra_audio_in_ctl_fops = {
- .owner = THIS_MODULE,
- .open = tegra_audio_ctl_open,
- .release = tegra_audio_ctl_release,
- .unlocked_ioctl = tegra_audio_in_ioctl,
-};
-
-static const struct file_operations tegra_audio_ctl_fops = {
- .owner = THIS_MODULE,
- .open = tegra_audio_ctl_open,
- .release = tegra_audio_ctl_release,
- .unlocked_ioctl = tegra_audio_ioctl,
-};
-
-static int init_stream_buffer(struct audio_stream *s, int num)
-{
- int i, j;
- pr_debug("%s (num %d)\n", __func__, num);
-
- for (i = 0; i < num; i++) {
- kfree(s->buffer[i]);
- s->buffer[i] =
- kmalloc((1 << PCM_BUFFER_MAX_SIZE_ORDER),
- GFP_KERNEL | GFP_DMA);
- if (!s->buffer[i]) {
- pr_err("%s: could not allocate buffer\n", __func__);
- for (j = i - 1; j >= 0; j--) {
- kfree(s->buffer[j]);
- s->buffer[j] = 0;
- }
- return -ENOMEM;
- }
- }
- return 0;
-}
-
-
-static int setup_misc_device(struct miscdevice *misc,
- const struct file_operations *fops,
- const char *fmt, ...)
-{
- int rc = 0;
- va_list args;
- const int sz = 64;
-
- va_start(args, fmt);
-
- memset(misc, 0, sizeof(*misc));
- misc->minor = MISC_DYNAMIC_MINOR;
- misc->name = kmalloc(sz, GFP_KERNEL);
- if (!misc->name) {
- rc = -ENOMEM;
- goto done;
- }
-
- vsnprintf((char *)misc->name, sz, fmt, args);
- misc->fops = fops;
- if (misc_register(misc)) {
- pr_err("%s: could not register %s\n", __func__, misc->name);
- kfree(misc->name);
- rc = -EIO;
- goto done;
- }
-
-done:
- va_end(args);
- return rc;
-}
-
-static ssize_t dma_toggle_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return sprintf(buf, "dma\n");
-}
-
-static ssize_t dma_toggle_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- pr_err("%s: Not implemented.", __func__);
- return 0;
-}
-
-static DEVICE_ATTR(dma_toggle, 0644, dma_toggle_show, dma_toggle_store);
-
-static ssize_t __attr_fifo_atn_read(char *buf, int atn_lvl)
-{
- switch (atn_lvl) {
- case I2S_FIFO_ATN_LVL_ONE_SLOT:
- strncpy(buf, "1\n", 2);
- return 2;
- case I2S_FIFO_ATN_LVL_FOUR_SLOTS:
- strncpy(buf, "4\n", 2);
- return 2;
- case I2S_FIFO_ATN_LVL_EIGHT_SLOTS:
- strncpy(buf, "8\n", 2);
- return 2;
- case I2S_FIFO_ATN_LVL_TWELVE_SLOTS:
- strncpy(buf, "12\n", 3);
- return 3;
- default:
- BUG_ON(1);
- return -EIO;
- }
-}
-
-static ssize_t __attr_fifo_atn_write(struct audio_driver_state *ads,
- struct audio_stream *as,
- int *fifo_lvl,
- const char *buf, size_t size)
-{
- int lvl;
-
- if (size > 3) {
- pr_err("%s: buffer size %d too big\n", __func__, size);
- return -EINVAL;
- }
-
- if (sscanf(buf, "%d", &lvl) != 1) {
- pr_err("%s: invalid input string [%s]\n", __func__, buf);
- return -EINVAL;
- }
-
- switch (lvl) {
- case 1:
- lvl = I2S_FIFO_ATN_LVL_ONE_SLOT;
- break;
- case 4:
- lvl = I2S_FIFO_ATN_LVL_FOUR_SLOTS;
- break;
- case 8:
- lvl = I2S_FIFO_ATN_LVL_EIGHT_SLOTS;
- break;
- case 12:
- lvl = I2S_FIFO_ATN_LVL_TWELVE_SLOTS;
- break;
- default:
- pr_err("%s: invalid attention level %d\n", __func__, lvl);
- return -EINVAL;
- }
-
- *fifo_lvl = lvl;
- pr_info("%s: fifo level %d\n", __func__, *fifo_lvl);
-
- return size;
-}
-
-static ssize_t tx_fifo_atn_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct tegra_audio_platform_data *pdata = dev->platform_data;
- struct audio_driver_state *ads = pdata->driver_data;
- return __attr_fifo_atn_read(buf, ads->out.i2s_fifo_atn_level);
-}
-
-static ssize_t tx_fifo_atn_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- ssize_t rc;
- struct tegra_audio_platform_data *pdata = dev->platform_data;
- struct audio_driver_state *ads = pdata->driver_data;
- mutex_lock(&ads->out.lock);
- if (pending_buffer_requests(&ads->out)) {
- pr_err("%s: playback in progress.\n", __func__);
- rc = -EBUSY;
- goto done;
- }
- rc = __attr_fifo_atn_write(ads, &ads->out,
- &ads->out.i2s_fifo_atn_level,
- buf, count);
-done:
- mutex_unlock(&ads->out.lock);
- return rc;
-}
-
-static DEVICE_ATTR(tx_fifo_atn, 0644, tx_fifo_atn_show, tx_fifo_atn_store);
-
-static ssize_t rx_fifo_atn_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct tegra_audio_platform_data *pdata = dev->platform_data;
- struct audio_driver_state *ads = pdata->driver_data;
- return __attr_fifo_atn_read(buf, ads->in.i2s_fifo_atn_level);
-}
-
-static ssize_t rx_fifo_atn_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- ssize_t rc;
- struct tegra_audio_platform_data *pdata = dev->platform_data;
- struct audio_driver_state *ads = pdata->driver_data;
- mutex_lock(&ads->in.lock);
- if (ads->in.active) {
- pr_err("%s: recording in progress.\n", __func__);
- rc = -EBUSY;
- goto done;
- }
- rc = __attr_fifo_atn_write(ads, &ads->in,
- &ads->in.i2s_fifo_atn_level,
- buf, count);
-done:
- mutex_unlock(&ads->in.lock);
- return rc;
-}
-
-static DEVICE_ATTR(rx_fifo_atn, 0644, rx_fifo_atn_show, rx_fifo_atn_store);
-
-static int tegra_audio_probe(struct platform_device *pdev)
-{
- int rc, i;
- struct resource *res;
- struct clk *i2s_clk, *dap_mclk;
- struct audio_driver_state *state;
-
- pr_info("%s\n", __func__);
-
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
- return -ENOMEM;
-
- state->pdev = pdev;
- state->pdata = pdev->dev.platform_data;
- state->pdata->driver_data = state;
- BUG_ON(!state->pdata);
-
- if (!(state->pdata->mask &
- (TEGRA_AUDIO_ENABLE_TX | TEGRA_AUDIO_ENABLE_RX))) {
- dev_err(&pdev->dev, "neither tx nor rx is enabled!\n");
- return -EIO;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "no mem resource!\n");
- return -ENODEV;
- }
-
- if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
- dev_err(&pdev->dev, "memory region already claimed!\n");
- return -ENOMEM;
- }
-
- state->i2s_phys = res->start;
- state->i2s_base = (unsigned long)ioremap(res->start,
- res->end - res->start + 1);
- if (!state->i2s_base) {
- dev_err(&pdev->dev, "cannot remap iomem!\n");
- return -EIO;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!res) {
- dev_err(&pdev->dev, "no dma resource!\n");
- return -ENODEV;
- }
- state->dma_req_sel = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(&pdev->dev, "no irq resource!\n");
- return -ENODEV;
- }
- state->irq = res->start;
-
- i2s_clk = clk_get(&pdev->dev, NULL);
- if (!i2s_clk) {
- dev_err(&pdev->dev, "%s: could not get i2s clock\n",
- __func__);
- return -EIO;
- }
-
- clk_set_rate(i2s_clk, state->pdata->i2s_clk_rate);
- if (clk_enable(i2s_clk)) {
- dev_err(&pdev->dev, "%s: failed to enable i2s clock\n",
- __func__);
- return -EIO;
- }
- pr_info("%s: i2s_clk rate %ld\n", __func__, clk_get_rate(i2s_clk));
-
- dap_mclk = tegra_get_clock_by_name(state->pdata->dap_clk);
- if (!dap_mclk) {
- dev_err(&pdev->dev, "%s: could not get DAP clock\n",
- __func__);
- return -EIO;
- }
- clk_enable(dap_mclk);
-
- rc = i2s_configure(pdev);
- if (rc < 0)
- return rc;
-
- if ((state->pdata->mask & TEGRA_AUDIO_ENABLE_TX)) {
- state->out.opened = 0;
- state->out.active = false;
- mutex_init(&state->out.lock);
- init_completion(&state->out.stop_completion);
- spin_lock_init(&state->out.dma_req_lock);
- state->out.dma_chan = NULL;
- state->out.i2s_fifo_atn_level = I2S_FIFO_ATN_LVL_FOUR_SLOTS;
- state->out.num_bufs = I2S_DEFAULT_TX_NUM_BUFS;
- for (i = 0; i < I2S_MAX_NUM_BUFS; i++) {
- init_completion(&state->out.comp[i]);
- /* TX buf rest state is unqueued, complete. */
- complete(&state->out.comp[i]);
- state->out.buffer[i] = 0;
- state->out.buf_phy[i] = 0;
- }
- state->out.last_queued = 0;
- rc = init_stream_buffer(&state->out, state->out.num_bufs);
- if (rc < 0)
- return rc;
-
- INIT_WORK(&state->out.allow_suspend_work, allow_suspend_worker);
-
- snprintf(state->out.wake_lock_name,
- sizeof(state->out.wake_lock_name),
- "i2s.%d-audio-out", state->pdev->id);
- wake_lock_init(&state->out.wake_lock, WAKE_LOCK_SUSPEND,
- state->out.wake_lock_name);
-
- rc = setup_misc_device(&state->misc_out,
- &tegra_audio_out_fops,
- "audio%d_out", state->pdev->id);
- if (rc < 0)
- return rc;
-
- rc = setup_misc_device(&state->misc_out_ctl,
- &tegra_audio_out_ctl_fops,
- "audio%d_out_ctl", state->pdev->id);
- if (rc < 0)
- return rc;
- }
-
- if ((state->pdata->mask & TEGRA_AUDIO_ENABLE_RX)) {
- state->in.opened = 0;
- state->in.active = false;
- mutex_init(&state->in.lock);
- init_completion(&state->in.stop_completion);
- spin_lock_init(&state->in.dma_req_lock);
- state->in.dma_chan = NULL;
- state->in.i2s_fifo_atn_level = I2S_FIFO_ATN_LVL_FOUR_SLOTS;
- state->in.num_bufs = I2S_DEFAULT_RX_NUM_BUFS;
- for (i = 0; i < I2S_MAX_NUM_BUFS; i++) {
- init_completion(&state->in.comp[i]);
- /* RX buf rest state is unqueued, complete. */
- complete(&state->in.comp[i]);
- state->in.buffer[i] = 0;
- state->in.buf_phy[i] = 0;
- }
- state->in.last_queued = 0;
- rc = init_stream_buffer(&state->in, state->in.num_bufs);
- if (rc < 0)
- return rc;
-
- INIT_WORK(&state->in.allow_suspend_work, allow_suspend_worker);
-
- snprintf(state->in.wake_lock_name,
- sizeof(state->in.wake_lock_name),
- "i2s.%d-audio-in", state->pdev->id);
- wake_lock_init(&state->in.wake_lock, WAKE_LOCK_SUSPEND,
- state->in.wake_lock_name);
-
- rc = setup_misc_device(&state->misc_in,
- &tegra_audio_in_fops,
- "audio%d_in", state->pdev->id);
- if (rc < 0)
- return rc;
-
- rc = setup_misc_device(&state->misc_in_ctl,
- &tegra_audio_in_ctl_fops,
- "audio%d_in_ctl", state->pdev->id);
- if (rc < 0)
- return rc;
- }
-
- if (request_irq(state->irq, i2s_interrupt,
- IRQF_DISABLED, state->pdev->name, state) < 0) {
- dev_err(&pdev->dev,
- "%s: could not register handler for irq %d\n",
- __func__, state->irq);
- return -EIO;
- }
-
- rc = setup_misc_device(&state->misc_ctl,
- &tegra_audio_ctl_fops,
- "audio%d_ctl", state->pdev->id);
- if (rc < 0)
- return rc;
-
- sound_ops->setup(state, state->pdata->mask);
-
- rc = device_create_file(&pdev->dev, &dev_attr_dma_toggle);
- if (rc < 0) {
- dev_err(&pdev->dev, "%s: could not create sysfs entry %s: %d\n",
- __func__, dev_attr_dma_toggle.attr.name, rc);
- return rc;
- }
-
- rc = device_create_file(&pdev->dev, &dev_attr_tx_fifo_atn);
- if (rc < 0) {
- dev_err(&pdev->dev, "%s: could not create sysfs entry %s: %d\n",
- __func__, dev_attr_tx_fifo_atn.attr.name, rc);
- return rc;
- }
-
- rc = device_create_file(&pdev->dev, &dev_attr_rx_fifo_atn);
- if (rc < 0) {
- dev_err(&pdev->dev, "%s: could not create sysfs entry %s: %d\n",
- __func__, dev_attr_rx_fifo_atn.attr.name, rc);
- return rc;
- }
-
- state->in_config.rate = 11025;
- state->in_config.stereo = false;
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int tegra_audio_suspend(struct platform_device *pdev, pm_message_t mesg)
-{
- /* dev_info(&pdev->dev, "%s\n", __func__); */
- return 0;
-}
-
-static int tegra_audio_resume(struct platform_device *pdev)
-{
- return i2s_configure(pdev);
-}
-#endif /* CONFIG_PM_SLEEP */
-
-static struct platform_driver tegra_audio_driver = {
- .driver = {
- .name = "tegra-i2s",
- .owner = THIS_MODULE,
- },
- .probe = tegra_audio_probe,
-#ifdef CONFIG_PM_SLEEP
- .suspend = tegra_audio_suspend,
- .resume = tegra_audio_resume,
-#endif
-};
-
-static int __init tegra_audio_init(void)
-{
- return platform_driver_register(&tegra_audio_driver);
-}
-
-module_init(tegra_audio_init);
-MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-tegra/tegra_spdif_audio.c b/arch/arm/mach-tegra/tegra_spdif_audio.c
deleted file mode 100644
index 3765633606cd..000000000000
--- a/arch/arm/mach-tegra/tegra_spdif_audio.c
+++ /dev/null
@@ -1,1187 +0,0 @@
-/*
- * arch/arm/mach-tegra/tegra_spdif_audio.c
- *
- * S/PDIF audio driver for NVIDIA Tegra SoCs
- *
- * 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 as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/miscdevice.h>
-#include <linux/fs.h>
-#include <linux/mutex.h>
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <linux/spinlock.h>
-#include <linux/uaccess.h>
-#include <linux/dma-mapping.h>
-#include <linux/dmapool.h>
-#include <linux/err.h>
-#include <linux/spi/spi.h>
-#include <linux/kfifo.h>
-#include <linux/debugfs.h>
-#include <linux/completion.h>
-#include <linux/platform_device.h>
-#include <linux/device.h>
-#include <linux/io.h>
-#include <linux/ktime.h>
-#include <linux/sysfs.h>
-#include <linux/wakelock.h>
-#include <linux/delay.h>
-#include <linux/tegra_audio.h>
-#include <linux/pm.h>
-#include <linux/workqueue.h>
-
-#include <mach/dma.h>
-#include <mach/iomap.h>
-#include <mach/spdif.h>
-#include <mach/audio.h>
-#include <mach/irqs.h>
-
-#include "clock.h"
-
-#define PCM_BUFFER_MAX_SIZE_ORDER (PAGE_SHIFT)
-
-#define SPDIF_MAX_NUM_BUFS 4
-/* Todo: Add IOCTL to configure the number of buffers. */
-#define SPDIF_DEFAULT_TX_NUM_BUFS 2
-#define SPDIF_DEFAULT_RX_NUM_BUFS 2
-/* per stream (input/output) */
-struct audio_stream {
- int opened;
- struct mutex lock;
-
- bool active; /* is DMA in progress? */
- int num_bufs;
- void *buffer[SPDIF_MAX_NUM_BUFS];
- dma_addr_t buf_phy[SPDIF_MAX_NUM_BUFS];
- struct completion comp[SPDIF_MAX_NUM_BUFS];
- struct tegra_dma_req dma_req[SPDIF_MAX_NUM_BUFS];
- int last_queued;
-
- int spdif_fifo_atn_level;
-
- struct tegra_dma_channel *dma_chan;
- bool stop;
- struct completion stop_completion;
- spinlock_t dma_req_lock;
-
- struct work_struct allow_suspend_work;
- struct wake_lock wake_lock;
- char wake_lock_name[100];
-};
-
-struct audio_driver_state {
- struct list_head next;
-
- struct platform_device *pdev;
- struct tegra_audio_platform_data *pdata;
- phys_addr_t spdif_phys;
- unsigned long spdif_base;
-
- unsigned long dma_req_sel;
- bool fifo_init;
-
- int irq;
-
- struct miscdevice misc_out;
- struct miscdevice misc_out_ctl;
- struct audio_stream out;
-};
-
-static inline bool pending_buffer_requests(struct audio_stream *stream)
-{
- int i;
- for (i = 0; i < stream->num_bufs; i++)
- if (!completion_done(&stream->comp[i]))
- return true;
- return false;
-}
-
-static inline int buf_size(struct audio_stream *s __attribute__((unused)))
-{
- return 1 << PCM_BUFFER_MAX_SIZE_ORDER;
-}
-
-static inline struct audio_driver_state *ads_from_misc_out(struct file *file)
-{
- struct miscdevice *m = file->private_data;
- struct audio_driver_state *ads =
- container_of(m, struct audio_driver_state, misc_out);
- BUG_ON(!ads);
- return ads;
-}
-
-static inline struct audio_driver_state *ads_from_misc_out_ctl(
- struct file *file)
-{
- struct miscdevice *m = file->private_data;
- struct audio_driver_state *ads =
- container_of(m, struct audio_driver_state,
- misc_out_ctl);
- BUG_ON(!ads);
- return ads;
-}
-
-static inline struct audio_driver_state *ads_from_out(
- struct audio_stream *aos)
-{
- return container_of(aos, struct audio_driver_state, out);
-}
-
-static inline void prevent_suspend(struct audio_stream *as)
-{
- pr_debug("%s\n", __func__);
- cancel_work_sync(&as->allow_suspend_work);
- wake_lock(&as->wake_lock);
-}
-
-static void allow_suspend_worker(struct work_struct *w)
-{
- struct audio_stream *as = container_of(w,
- struct audio_stream, allow_suspend_work);
- pr_debug("%s\n", __func__);
- wake_unlock(&as->wake_lock);
-}
-
-static inline void allow_suspend(struct audio_stream *as)
-{
- schedule_work(&as->allow_suspend_work);
-}
-
-#define I2S_I2S_FIFO_TX_BUSY I2S_I2S_STATUS_FIFO1_BSY
-#define I2S_I2S_FIFO_TX_QS I2S_I2S_STATUS_QS_FIFO1
-#define I2S_I2S_FIFO_TX_ERR I2S_I2S_STATUS_FIFO1_ERR
-
-#define I2S_I2S_FIFO_RX_BUSY I2S_I2S_STATUS_FIFO2_BSY
-#define I2S_I2S_FIFO_RX_QS I2S_I2S_STATUS_QS_FIFO2
-#define I2S_I2S_FIFO_RX_ERR I2S_I2S_STATUS_FIFO2_ERR
-
-#define I2S_FIFO_ERR (I2S_I2S_STATUS_FIFO1_ERR | I2S_I2S_STATUS_FIFO2_ERR)
-
-
-static inline void spdif_writel(unsigned long base, u32 val, u32 reg)
-{
- writel(val, base + reg);
-}
-
-static inline u32 spdif_readl(unsigned long base, u32 reg)
-{
- return readl(base + reg);
-}
-
-static inline void spdif_fifo_write(unsigned long base, u32 data)
-{
- spdif_writel(base, data, SPDIF_DATA_OUT_0);
-}
-
-static int spdif_fifo_set_attention_level(unsigned long base,
- unsigned level)
-{
- u32 val;
-
- if (level > SPDIF_FIFO_ATN_LVL_TWELVE_SLOTS) {
- pr_err("%s: invalid fifo level selector %d\n", __func__,
- level);
- return -EINVAL;
- }
-
- val = spdif_readl(base, SPDIF_DATA_FIFO_CSR_0);
-
- val &= ~SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_MASK;
- val |= level << SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_SHIFT;
-
-
- spdif_writel(base, val, SPDIF_DATA_FIFO_CSR_0);
- return 0;
-}
-
-static void spdif_fifo_enable(unsigned long base, int on)
-{
- u32 val = spdif_readl(base, SPDIF_CTRL_0);
- val &= ~(SPDIF_CTRL_0_TX_EN | SPDIF_CTRL_0_TC_EN | SPDIF_CTRL_0_TU_EN);
- val |= on ? (SPDIF_CTRL_0_TX_EN) : 0;
- val |= on ? (SPDIF_CTRL_0_TC_EN) : 0;
-
- spdif_writel(base, val, SPDIF_CTRL_0);
-}
-#if 0
-static bool spdif_is_fifo_enabled(unsigned long base)
-{
- u32 val = spdif_readl(base, SPDIF_CTRL_0);
- return !!(val & SPDIF_CTRL_0_TX_EN);
-}
-#endif
-
-static void spdif_fifo_clear(unsigned long base)
-{
- u32 val = spdif_readl(base, SPDIF_DATA_FIFO_CSR_0);
- val &= ~(SPDIF_DATA_FIFO_CSR_0_TX_CLR | SPDIF_DATA_FIFO_CSR_0_TU_CLR);
- val |= SPDIF_DATA_FIFO_CSR_0_TX_CLR | SPDIF_DATA_FIFO_CSR_0_TU_CLR;
- spdif_writel(base, val, SPDIF_DATA_FIFO_CSR_0);
-}
-
-
-static int spdif_set_bit_mode(unsigned long base, unsigned mode)
-{
- u32 val = spdif_readl(base, SPDIF_CTRL_0);
- val &= ~SPDIF_CTRL_0_BIT_MODE_MASK;
-
- if (mode > SPDIF_BIT_MODE_MODERAW) {
- pr_err("%s: invalid bit_size selector %d\n", __func__,
- mode);
- return -EINVAL;
- }
-
- val |= mode << SPDIF_CTRL_0_BIT_MODE_SHIFT;
-
- spdif_writel(base, val, SPDIF_CTRL_0);
- return 0;
-}
-
-static int spdif_set_fifo_packed(unsigned long base, unsigned on)
-{
- u32 val = spdif_readl(base, SPDIF_CTRL_0);
- val &= ~SPDIF_CTRL_0_PACK;
- val |= on ? SPDIF_CTRL_0_PACK : 0;
- spdif_writel(base, val, SPDIF_CTRL_0);
- return 0;
-}
-
-#if 0
-static void spdif_set_fifo_irq_on_err(unsigned long base, int on)
-{
- u32 val = spdif_readl(base, SPDIF_CTRL_0);
- val &= ~SPDIF_CTRL_0_IE_TXE;
- val |= on ? SPDIF_CTRL_0_IE_TXE : 0;
- spdif_writel(base, val, SPDIF_CTRL_0);
-}
-#endif
-
-
-static void spdif_enable_fifos(unsigned long base, int on)
-{
- u32 val = spdif_readl(base, SPDIF_CTRL_0);
- if (on)
- val |= SPDIF_CTRL_0_TX_EN | SPDIF_CTRL_0_TC_EN |
- SPDIF_CTRL_0_IE_TXE;
- else
- val &= ~(SPDIF_CTRL_0_TX_EN | SPDIF_CTRL_0_TC_EN |
- SPDIF_CTRL_0_IE_TXE);
-
- spdif_writel(base, val, SPDIF_CTRL_0);
-}
-
-static inline u32 spdif_get_status(unsigned long base)
-{
- return spdif_readl(base, SPDIF_STATUS_0);
-}
-
-static inline u32 spdif_get_control(unsigned long base)
-{
- return spdif_readl(base, SPDIF_CTRL_0);
-}
-
-static inline void spdif_ack_status(unsigned long base)
-{
- return spdif_writel(base, spdif_readl(base, SPDIF_STATUS_0),
- SPDIF_STATUS_0);
-}
-
-static inline u32 spdif_get_fifo_scr(unsigned long base)
-{
- return spdif_readl(base, SPDIF_DATA_FIFO_CSR_0);
-}
-
-static inline phys_addr_t spdif_get_fifo_phy_base(unsigned long phy_base)
-{
- return phy_base + SPDIF_DATA_OUT_0;
-}
-
-static inline u32 spdif_get_fifo_full_empty_count(unsigned long base)
-{
- u32 val = spdif_readl(base, SPDIF_DATA_FIFO_CSR_0);
- val = val >> SPDIF_DATA_FIFO_CSR_0_TD_EMPTY_COUNT_SHIFT;
- return val & SPDIF_DATA_FIFO_CSR_0_TD_EMPTY_COUNT_MASK;
-}
-
-
-static int spdif_set_sample_rate(struct audio_driver_state *state,
- unsigned int sample_rate)
-{
- unsigned int clock_freq = 0;
- struct clk *spdif_clk;
-
- unsigned int ch_sta[] = {
- 0x0, /* 44.1, default values */
- 0x0,
- 0x0,
- 0x0,
- 0x0,
- 0x0,
- };
-
- switch (sample_rate) {
- case 32000:
- clock_freq = 4096000; /* 4.0960 MHz */
- ch_sta[0] = 0x3 << 24;
- ch_sta[1] = 0xC << 4;
- break;
- case 44100:
- clock_freq = 5644800; /* 5.6448 MHz */
- ch_sta[0] = 0x0;
- ch_sta[1] = 0xF << 4;
- break;
- case 48000:
- clock_freq = 6144000; /* 6.1440MHz */
- ch_sta[0] = 0x2 << 24;
- ch_sta[1] = 0xD << 4;
- break;
- case 88200:
- clock_freq = 11289600; /* 11.2896 MHz */
- break;
- case 96000:
- clock_freq = 12288000; /* 12.288 MHz */
- break;
- case 176400:
- clock_freq = 22579200; /* 22.5792 MHz */
- break;
- case 192000:
- clock_freq = 24576000; /* 24.5760 MHz */
- break;
- default:
- return -1;
- }
-
- spdif_clk = clk_get(&state->pdev->dev, NULL);
- if (!spdif_clk) {
- dev_err(&state->pdev->dev, "%s: could not get spdif clock\n",
- __func__);
- return -EIO;
- }
-
- clk_set_rate(spdif_clk, clock_freq);
- if (clk_enable(spdif_clk)) {
- dev_err(&state->pdev->dev,
- "%s: failed to enable spdif_clk clock\n", __func__);
- return -EIO;
- }
- pr_info("%s: spdif_clk rate %ld\n", __func__, clk_get_rate(spdif_clk));
-
- spdif_writel(state->spdif_base, ch_sta[0], SPDIF_CH_STA_TX_A_0);
- spdif_writel(state->spdif_base, ch_sta[1], SPDIF_CH_STA_TX_B_0);
- spdif_writel(state->spdif_base, ch_sta[2], SPDIF_CH_STA_TX_C_0);
- spdif_writel(state->spdif_base, ch_sta[3], SPDIF_CH_STA_TX_D_0);
- spdif_writel(state->spdif_base, ch_sta[4], SPDIF_CH_STA_TX_E_0);
- spdif_writel(state->spdif_base, ch_sta[5], SPDIF_CH_STA_TX_F_0);
-
- return 0;
-}
-
-static int init_stream_buffer(struct audio_stream *, int);
-
-static int setup_dma(struct audio_driver_state *);
-static void tear_down_dma(struct audio_driver_state *);
-static void stop_dma_playback(struct audio_stream *);
-
-
-struct sound_ops {
- int (*setup)(struct audio_driver_state *);
- void (*tear_down)(struct audio_driver_state *);
- void (*stop_playback)(struct audio_stream *);
-};
-
-static const struct sound_ops dma_sound_ops = {
- .setup = setup_dma,
- .tear_down = tear_down_dma,
- .stop_playback = stop_dma_playback,
-};
-
-static const struct sound_ops *sound_ops = &dma_sound_ops;
-
-
-
-static bool stop_playback_if_necessary(struct audio_stream *aos)
-{
- unsigned long flags;
- spin_lock_irqsave(&aos->dma_req_lock, flags);
- pr_debug("%s\n", __func__);
- if (!pending_buffer_requests(aos)) {
- pr_debug("%s: no more data to play back\n", __func__);
- sound_ops->stop_playback(aos);
- spin_unlock_irqrestore(&aos->dma_req_lock, flags);
- allow_suspend(aos);
- return true;
- }
- spin_unlock_irqrestore(&aos->dma_req_lock, flags);
-
- return false;
-}
-
-/* playback */
-static bool wait_till_stopped(struct audio_stream *as)
-{
- int rc;
- pr_debug("%s: wait for completion\n", __func__);
- rc = wait_for_completion_timeout(
- &as->stop_completion, HZ);
- if (!rc)
- pr_err("%s: wait timed out", __func__);
- if (rc < 0)
- pr_err("%s: wait error %d\n", __func__, rc);
- allow_suspend(as);
- pr_debug("%s: done: %d\n", __func__, rc);
- return true;
-}
-
-/* Ask for playback to stop. The _nosync means that
- * as->lock has to be locked by the caller.
- */
-static void request_stop_nosync(struct audio_stream *as)
-{
- int i;
- pr_debug("%s\n", __func__);
- if (!as->stop) {
- as->stop = true;
- if (pending_buffer_requests(as))
- wait_till_stopped(as);
- for (i = 0; i < as->num_bufs; i++) {
- init_completion(&as->comp[i]);
- complete(&as->comp[i]);
- }
- }
- if (!tegra_dma_is_empty(as->dma_chan))
- pr_err("%s: DMA not empty!\n", __func__);
- /* Stop the DMA then dequeue anything that's in progress. */
- tegra_dma_cancel(as->dma_chan);
- as->active = false; /* applies to recording only */
- pr_debug("%s: done\n", __func__);
-}
-
-static void setup_dma_tx_request(struct tegra_dma_req *req,
- struct audio_stream *aos);
-
-static int setup_dma(struct audio_driver_state *ads)
-{
- int rc, i;
- pr_info("%s\n", __func__);
-
- /* setup audio playback */
- for (i = 0; i < ads->out.num_bufs; i++) {
- ads->out.buf_phy[i] = dma_map_single(&ads->pdev->dev,
- ads->out.buffer[i],
- buf_size(&ads->out),
- DMA_TO_DEVICE);
- BUG_ON(!ads->out.buf_phy[i]);
- setup_dma_tx_request(&ads->out.dma_req[i], &ads->out);
- ads->out.dma_req[i].source_addr = ads->out.buf_phy[i];
- }
- ads->out.dma_chan =
- tegra_dma_allocate_channel(TEGRA_DMA_MODE_CONTINUOUS_SINGLE,
- "spdif_tx_req_%d", ads->dma_req_sel);
- if (!ads->out.dma_chan) {
- pr_err("%s: error alloc output DMA channel: %ld\n",
- __func__, PTR_ERR(ads->out.dma_chan));
- rc = -ENODEV;
- goto fail_tx;
- }
- return 0;
-
-
-fail_tx:
-
- for (i = 0; i < ads->out.num_bufs; i++) {
- dma_unmap_single(&ads->pdev->dev, ads->out.buf_phy[i],
- buf_size(&ads->out),
- DMA_TO_DEVICE);
- ads->out.buf_phy[i] = 0;
- }
- tegra_dma_free_channel(ads->out.dma_chan);
- ads->out.dma_chan = 0;
-
-
- return rc;
-}
-
-static void tear_down_dma(struct audio_driver_state *ads)
-{
- int i;
- pr_info("%s\n", __func__);
-
-
- tegra_dma_free_channel(ads->out.dma_chan);
- for (i = 0; i < ads->out.num_bufs; i++) {
- dma_unmap_single(&ads->pdev->dev, ads->out.buf_phy[i],
- buf_size(&ads->out),
- DMA_TO_DEVICE);
- ads->out.buf_phy[i] = 0;
- }
-
- ads->out.dma_chan = NULL;
-}
-
-static void dma_tx_complete_callback(struct tegra_dma_req *req)
-{
- struct audio_stream *aos = req->dev;
- unsigned req_num;
-
- req_num = req - aos->dma_req;
- pr_debug("%s: completed buffer %d size %d\n", __func__,
- req_num, req->bytes_transferred);
- BUG_ON(req_num >= aos->num_bufs);
-
- complete(&aos->comp[req_num]);
-
- if (!pending_buffer_requests(aos)) {
- pr_debug("%s: Playback underflow", __func__);
- complete(&aos->stop_completion);
- }
-}
-
-
-static void setup_dma_tx_request(struct tegra_dma_req *req,
- struct audio_stream *aos)
-{
- struct audio_driver_state *ads = ads_from_out(aos);
-
- memset(req, 0, sizeof(*req));
-
- req->complete = dma_tx_complete_callback;
- req->dev = aos;
- req->to_memory = false;
- req->dest_addr = spdif_get_fifo_phy_base(ads->spdif_phys);
- req->dest_bus_width = 32;
- req->dest_wrap = 4;
- req->source_wrap = 0;
- req->source_bus_width = 32;
- req->req_sel = ads->dma_req_sel;
-}
-
-
-static int start_playback(struct audio_stream *aos,
- struct tegra_dma_req *req)
-{
- int rc;
- unsigned long flags;
- struct audio_driver_state *ads = ads_from_out(aos);
-
- pr_debug("%s: (writing %d)\n",
- __func__, req->size);
-
- spin_lock_irqsave(&aos->dma_req_lock, flags);
-#if 0
- spdif_fifo_clear(ads->spdif_base);
-#endif
-
- spdif_fifo_set_attention_level(ads->spdif_base,
- ads->out.spdif_fifo_atn_level);
-
- if (ads->fifo_init) {
- spdif_set_bit_mode(ads->spdif_base, SPDIF_BIT_MODE_MODE16BIT);
- spdif_set_fifo_packed(ads->spdif_base, 1);
- ads->fifo_init = false;
- }
-
- spdif_fifo_enable(ads->spdif_base, 1);
-
- rc = tegra_dma_enqueue_req(aos->dma_chan, req);
- spin_unlock_irqrestore(&aos->dma_req_lock, flags);
-
- if (rc)
- pr_err("%s: could not enqueue TX DMA req\n", __func__);
- return rc;
-}
-
-/* Called with aos->dma_req_lock taken. */
-static void stop_dma_playback(struct audio_stream *aos)
-{
- int spin = 0;
- struct audio_driver_state *ads = ads_from_out(aos);
- pr_debug("%s\n", __func__);
- spdif_fifo_enable(ads->spdif_base, 0);
- while ((spdif_get_status(ads->spdif_base) & SPDIF_STATUS_0_TX_BSY) &&
- spin < 100) {
- udelay(10);
- if (spin++ > 50)
- pr_info("%s: spin %d\n", __func__, spin);
- }
- if (spin == 100)
- pr_warn("%s: spinny\n", __func__);
- ads->fifo_init = true;
-}
-
-
-
-static irqreturn_t spdif_interrupt(int irq, void *data)
-{
- struct audio_driver_state *ads = data;
- u32 status = spdif_get_status(ads->spdif_base);
-
- pr_debug("%s: %08x\n", __func__, status);
-
-/* if (status & SPDIF_STATUS_0_TX_ERR) */
- spdif_ack_status(ads->spdif_base);
-
- pr_debug("%s: done %08x\n", __func__,
- spdif_get_status(ads->spdif_base));
- return IRQ_HANDLED;
-}
-
-static ssize_t tegra_spdif_write(struct file *file,
- const char __user *buf, size_t size, loff_t *off)
-{
- ssize_t rc = 0;
- int out_buf;
- struct tegra_dma_req *req;
- struct audio_driver_state *ads = ads_from_misc_out(file);
-
- mutex_lock(&ads->out.lock);
-
- if (!IS_ALIGNED(size, 4) || size < 4 || size > buf_size(&ads->out)) {
- pr_err("%s: invalid user size %d\n", __func__, size);
- rc = -EINVAL;
- goto done;
- }
-
- pr_debug("%s: write %d bytes\n", __func__, size);
-
- if (ads->out.stop) {
- pr_debug("%s: playback has been cancelled\n", __func__);
- goto done;
- }
-
- /* Decide which buf is next. */
- out_buf = (ads->out.last_queued + 1) % ads->out.num_bufs;
- req = &ads->out.dma_req[out_buf];
-
- /* Wait for the buffer to be emptied (complete). The maximum timeout
- * value could be calculated dynamically based on buf_size(&ads->out).
- * For a buffer size of 16k, at 44.1kHz/stereo/16-bit PCM, you would
- * have ~93ms.
- */
- pr_debug("%s: waiting for buffer %d\n", __func__, out_buf);
- rc = wait_for_completion_interruptible_timeout(
- &ads->out.comp[out_buf], HZ);
- if (!rc) {
- pr_err("%s: timeout", __func__);
- rc = -ETIMEDOUT;
- goto done;
- } else if (rc < 0) {
- pr_err("%s: wait error %d", __func__, rc);
- goto done;
- }
-
- /* Fill the buffer and enqueue it. */
- pr_debug("%s: acquired buffer %d, copying data\n", __func__, out_buf);
- rc = copy_from_user(ads->out.buffer[out_buf], buf, size);
- if (rc) {
- rc = -EFAULT;
- goto done;
- }
-
- prevent_suspend(&ads->out);
-
- req->size = size;
- dma_sync_single_for_device(NULL,
- req->source_addr, req->size, DMA_TO_DEVICE);
- ads->out.last_queued = out_buf;
- init_completion(&ads->out.stop_completion);
-
- rc = start_playback(&ads->out, req);
- if (!rc)
- rc = size;
- else
- allow_suspend(&ads->out);
-
-done:
- mutex_unlock(&ads->out.lock);
- return rc;
-}
-
-static long tegra_spdif_out_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- int rc = 0;
- struct audio_driver_state *ads = ads_from_misc_out_ctl(file);
- struct audio_stream *aos = &ads->out;
-
- mutex_lock(&aos->lock);
-
- switch (cmd) {
- case TEGRA_AUDIO_OUT_FLUSH:
- if (pending_buffer_requests(aos)) {
- pr_debug("%s: flushing\n", __func__);
- request_stop_nosync(aos);
- pr_debug("%s: flushed\n", __func__);
- }
- if (stop_playback_if_necessary(aos))
- pr_debug("%s: done (stopped)\n", __func__);
- aos->stop = false;
- break;
- case TEGRA_AUDIO_OUT_SET_NUM_BUFS: {
- unsigned int num;
- if (copy_from_user(&num, (const void __user *)arg,
- sizeof(num))) {
- rc = -EFAULT;
- break;
- }
- if (!num || num > SPDIF_MAX_NUM_BUFS) {
- pr_err("%s: invalid buffer count %d\n", __func__, num);
- rc = -EINVAL;
- break;
- }
- if (pending_buffer_requests(aos)) {
- pr_err("%s: playback in progress\n", __func__);
- rc = -EBUSY;
- break;
- }
- rc = init_stream_buffer(aos, num);
- if (rc < 0)
- break;
- aos->num_bufs = num;
- sound_ops->setup(ads);
- }
- break;
- case TEGRA_AUDIO_OUT_GET_NUM_BUFS:
- if (copy_to_user((void __user *)arg,
- &aos->num_bufs, sizeof(aos->num_bufs)))
- rc = -EFAULT;
- break;
- default:
- rc = -EINVAL;
- }
-
- mutex_unlock(&aos->lock);
- return rc;
-}
-
-
-static int tegra_spdif_out_open(struct inode *inode, struct file *file)
-{
- int rc = 0;
- int i;
- struct audio_driver_state *ads = ads_from_misc_out(file);
-
- pr_debug("%s\n", __func__);
-
- mutex_lock(&ads->out.lock);
-
- if (ads->out.opened) {
- rc = -EBUSY;
- goto done;
- }
-
- ads->out.opened = 1;
- ads->out.stop = false;
-
- for (i = 0; i < SPDIF_MAX_NUM_BUFS; i++) {
- init_completion(&ads->out.comp[i]);
- /* TX buf rest state is unqueued, complete. */
- complete(&ads->out.comp[i]);
- }
-
-done:
- mutex_unlock(&ads->out.lock);
- return rc;
-}
-
-static int tegra_spdif_out_release(struct inode *inode, struct file *file)
-{
- struct audio_driver_state *ads = ads_from_misc_out(file);
-
- pr_debug("%s\n", __func__);
-
- mutex_lock(&ads->out.lock);
- ads->out.opened = 0;
- request_stop_nosync(&ads->out);
- if (stop_playback_if_necessary(&ads->out))
- pr_debug("%s: done (stopped)\n", __func__);
- allow_suspend(&ads->out);
- mutex_unlock(&ads->out.lock);
- pr_debug("%s: done\n", __func__);
- return 0;
-}
-
-
-static const struct file_operations tegra_spdif_out_fops = {
- .owner = THIS_MODULE,
- .open = tegra_spdif_out_open,
- .release = tegra_spdif_out_release,
- .write = tegra_spdif_write,
-};
-
-static int tegra_spdif_ctl_open(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static int tegra_spdif_ctl_release(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static const struct file_operations tegra_spdif_out_ctl_fops = {
- .owner = THIS_MODULE,
- .open = tegra_spdif_ctl_open,
- .release = tegra_spdif_ctl_release,
- .unlocked_ioctl = tegra_spdif_out_ioctl,
-};
-
-static int init_stream_buffer(struct audio_stream *s, int num)
-{
- int i, j;
- pr_debug("%s (num %d)\n", __func__, num);
-
- for (i = 0; i < num; i++) {
- kfree(s->buffer[i]);
- s->buffer[i] =
- kmalloc(buf_size(s), GFP_KERNEL | GFP_DMA);
- if (!s->buffer[i]) {
- pr_err("%s: could not allocate buffer\n", __func__);
- for (j = i - 1; j >= 0; j--) {
- kfree(s->buffer[j]);
- s->buffer[j] = 0;
- }
- return -ENOMEM;
- }
- }
- return 0;
-}
-
-
-static int setup_misc_device(struct miscdevice *misc,
- const struct file_operations *fops,
- const char *fmt, ...)
-{
- int rc = 0;
- va_list args;
- const int sz = 64;
-
- va_start(args, fmt);
-
- memset(misc, 0, sizeof(*misc));
- misc->minor = MISC_DYNAMIC_MINOR;
- misc->name = kmalloc(sz, GFP_KERNEL);
- if (!misc->name) {
- rc = -ENOMEM;
- goto done;
- }
-
- vsnprintf((char *)misc->name, sz, fmt, args);
- misc->fops = fops;
- if (misc_register(misc)) {
- pr_err("%s: could not register %s\n", __func__, misc->name);
- kfree(misc->name);
- rc = -EIO;
- goto done;
- }
-
-done:
- va_end(args);
- return rc;
-}
-
-static ssize_t dma_toggle_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return sprintf(buf, "dma\n");
-}
-
-static ssize_t dma_toggle_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- pr_err("%s: Not implemented.", __func__);
- return 0;
-}
-
-static DEVICE_ATTR(dma_toggle, 0644, dma_toggle_show, dma_toggle_store);
-
-static ssize_t __attr_fifo_atn_read(char *buf, int atn_lvl)
-{
- switch (atn_lvl) {
- case SPDIF_FIFO_ATN_LVL_ONE_SLOT:
- strncpy(buf, "1\n", 2);
- return 2;
- case SPDIF_FIFO_ATN_LVL_FOUR_SLOTS:
- strncpy(buf, "4\n", 2);
- return 2;
- case SPDIF_FIFO_ATN_LVL_EIGHT_SLOTS:
- strncpy(buf, "8\n", 2);
- return 2;
- case SPDIF_FIFO_ATN_LVL_TWELVE_SLOTS:
- strncpy(buf, "12\n", 3);
- return 3;
- default:
- BUG_ON(1);
- return -EIO;
- }
-}
-
-static ssize_t __attr_fifo_atn_write(struct audio_driver_state *ads,
- struct audio_stream *as,
- int *fifo_lvl,
- const char *buf, size_t size)
-{
- int lvl;
-
- if (size > 3) {
- pr_err("%s: buffer size %d too big\n", __func__, size);
- return -EINVAL;
- }
-
- if (sscanf(buf, "%d", &lvl) != 1) {
- pr_err("%s: invalid input string [%s]\n", __func__, buf);
- return -EINVAL;
- }
-
- switch (lvl) {
- case 1:
- lvl = SPDIF_FIFO_ATN_LVL_ONE_SLOT;
- break;
- case 4:
- lvl = SPDIF_FIFO_ATN_LVL_FOUR_SLOTS;
- break;
- case 8:
- lvl = SPDIF_FIFO_ATN_LVL_EIGHT_SLOTS;
- break;
- case 12:
- lvl = SPDIF_FIFO_ATN_LVL_TWELVE_SLOTS;
- break;
- default:
- pr_err("%s: invalid attention level %d\n", __func__, lvl);
- return -EINVAL;
- }
-
- *fifo_lvl = lvl;
- pr_info("%s: fifo level %d\n", __func__, *fifo_lvl);
-
- return size;
-}
-
-static ssize_t tx_fifo_atn_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct tegra_audio_platform_data *pdata = dev->platform_data;
- struct audio_driver_state *ads = pdata->driver_data;
- return __attr_fifo_atn_read(buf, ads->out.spdif_fifo_atn_level);
-}
-
-static ssize_t tx_fifo_atn_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- ssize_t rc;
- struct tegra_audio_platform_data *pdata = dev->platform_data;
- struct audio_driver_state *ads = pdata->driver_data;
- mutex_lock(&ads->out.lock);
- if (pending_buffer_requests(&ads->out)) {
- pr_err("%s: playback in progress.\n", __func__);
- rc = -EBUSY;
- goto done;
- }
- rc = __attr_fifo_atn_write(ads, &ads->out,
- &ads->out.spdif_fifo_atn_level,
- buf, count);
-done:
- mutex_unlock(&ads->out.lock);
- return rc;
-}
-
-static DEVICE_ATTR(tx_fifo_atn, 0644, tx_fifo_atn_show, tx_fifo_atn_store);
-
-
-static int spdif_configure(struct platform_device *pdev)
-{
- struct tegra_audio_platform_data *pdata = pdev->dev.platform_data;
- struct audio_driver_state *state = pdata->driver_data;
-
- if (!state)
- return -ENOMEM;
-
- /* disable interrupts from SPDIF */
- spdif_writel(state->spdif_base, 0x0, SPDIF_CTRL_0);
- spdif_fifo_clear(state->spdif_base);
- spdif_enable_fifos(state->spdif_base, 0);
-
- spdif_set_bit_mode(state->spdif_base, SPDIF_BIT_MODE_MODE16BIT);
- spdif_set_fifo_packed(state->spdif_base, 1);
-
- spdif_fifo_set_attention_level(state->spdif_base,
- state->out.spdif_fifo_atn_level);
-
- spdif_set_sample_rate(state, 44100);
-
- state->fifo_init = true;
- return 0;
-}
-
-static int tegra_spdif_probe(struct platform_device *pdev)
-{
- int rc, i;
- struct resource *res;
- struct audio_driver_state *state;
-
- pr_info("%s\n", __func__);
-
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
- return -ENOMEM;
-
- state->pdev = pdev;
- state->pdata = pdev->dev.platform_data;
- state->pdata->driver_data = state;
- BUG_ON(!state->pdata);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "no mem resource!\n");
- return -ENODEV;
- }
-
- if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
- dev_err(&pdev->dev, "memory region already claimed!\n");
- return -ENOMEM;
- }
-
- state->spdif_phys = res->start;
- state->spdif_base = (unsigned long)ioremap(res->start,
- res->end - res->start + 1);
- if (!state->spdif_base) {
- dev_err(&pdev->dev, "cannot remap iomem!\n");
- return -EIO;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!res) {
- dev_err(&pdev->dev, "no dma resource!\n");
- return -ENODEV;
- }
- state->dma_req_sel = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(&pdev->dev, "no irq resource!\n");
- return -ENODEV;
- }
- state->irq = res->start;
-
- rc = spdif_configure(pdev);
- if (rc < 0)
- return rc;
-
- state->out.opened = 0;
- state->out.active = false;
- mutex_init(&state->out.lock);
- init_completion(&state->out.stop_completion);
- spin_lock_init(&state->out.dma_req_lock);
- state->out.dma_chan = NULL;
- state->out.num_bufs = SPDIF_DEFAULT_TX_NUM_BUFS;
- for (i = 0; i < SPDIF_MAX_NUM_BUFS; i++) {
- init_completion(&state->out.comp[i]);
- /* TX buf rest state is unqueued, complete. */
- complete(&state->out.comp[i]);
- state->out.buffer[i] = 0;
- state->out.buf_phy[i] = 0;
- }
- state->out.last_queued = 0;
- rc = init_stream_buffer(&state->out, state->out.num_bufs);
- if (rc < 0)
- return rc;
-
- INIT_WORK(&state->out.allow_suspend_work, allow_suspend_worker);
- snprintf(state->out.wake_lock_name, sizeof(state->out.wake_lock_name),
- "tegra-audio-spdif");
- wake_lock_init(&state->out.wake_lock, WAKE_LOCK_SUSPEND,
- state->out.wake_lock_name);
-
- if (request_irq(state->irq, spdif_interrupt,
- IRQF_DISABLED, state->pdev->name, state) < 0) {
- dev_err(&pdev->dev,
- "%s: could not register handler for irq %d\n",
- __func__, state->irq);
- return -EIO;
- }
-
- rc = setup_misc_device(&state->misc_out,
- &tegra_spdif_out_fops,
- "spdif_out");
- if (rc < 0)
- return rc;
-
- rc = setup_misc_device(&state->misc_out_ctl,
- &tegra_spdif_out_ctl_fops,
- "spdif_out_ctl");
- if (rc < 0)
- return rc;
-
- sound_ops->setup(state);
-
- rc = device_create_file(&pdev->dev, &dev_attr_dma_toggle);
- if (rc < 0) {
- dev_err(&pdev->dev, "%s: could not create sysfs entry %s: %d\n",
- __func__, dev_attr_dma_toggle.attr.name, rc);
- return rc;
- }
-
- rc = device_create_file(&pdev->dev, &dev_attr_tx_fifo_atn);
- if (rc < 0) {
- dev_err(&pdev->dev, "%s: could not create sysfs entry %s: %d\n",
- __func__, dev_attr_tx_fifo_atn.attr.name, rc);
- return rc;
- }
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int tegra_spdif_suspend(struct platform_device *pdev, pm_message_t mesg)
-{
- /* dev_info(&pdev->dev, "%s\n", __func__); */
- return 0;
-}
-
-static int tegra_spdif_resume(struct platform_device *pdev)
-{
- return spdif_configure(pdev);
-}
-#endif /* CONFIG_PM_SLEEP */
-
-static struct platform_driver tegra_spdif_driver = {
- .driver = {
- .name = "spdif_out",
- .owner = THIS_MODULE,
- },
- .probe = tegra_spdif_probe,
-#ifdef CONFIG_PM_SLEEP
- .suspend = tegra_spdif_suspend,
- .resume = tegra_spdif_resume,
-#endif
-};
-
-static int __init tegra_spdif_init(void)
-{
- return platform_driver_register(&tegra_spdif_driver);
-}
-
-module_init(tegra_spdif_init);
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index 5f37ba27a1ea..2a8b4f1cef64 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -1,7 +1,6 @@
config SND_SOC_TEGRA
tristate "SoC Audio for the Tegra System-on-Chip"
depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA
- depends on !TEGRA_LEGACY_AUDIO
help
Say Y or M here if you want support for SoC audio on Tegra.