diff options
author | Iliyan Malchev <malchev@google.com> | 2010-10-06 19:05:13 -0700 |
---|---|---|
committer | Iliyan Malchev <malchev@google.com> | 2010-10-06 19:09:59 -0700 |
commit | 20366f5aa7ba6121d987635a2a7c05b3bb84309a (patch) | |
tree | d65c8bc35c7d7d5b4339bdc4bfa71ffb595a5aeb /arch/arm | |
parent | d335791e5b8112389f387c99892ab55c4994b94c (diff) |
Revert "[ARM] tegra: driver for spdif audio"
This reverts commit 3543d53703c5a1ed0b987c77e7a79226c3a24f45.
Signed-off-by: Iliyan Malchev <malchev@google.com>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/mach-tegra/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/spdif.h | 392 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra_spdif_audio.c | 1326 |
3 files changed, 0 insertions, 1719 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index ca13a3835d92..bc049fbe80c8 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -11,7 +11,6 @@ obj-y += powergate.o obj-y += suspend.o obj-y += fuse.o obj-y += tegra_i2s_audio.o -obj-y += tegra_spdif_audio.o obj-$(CONFIG_USB_SUPPORT) += usb_phy.o obj-$(CONFIG_FIQ) += fiq.o obj-$(CONFIG_TEGRA_PWM) += pwm.o diff --git a/arch/arm/mach-tegra/include/mach/spdif.h b/arch/arm/mach-tegra/include/mach/spdif.h deleted file mode 100644 index cbfaba73c6ca..000000000000 --- a/arch/arm/mach-tegra/include/mach/spdif.h +++ /dev/null @@ -1,392 +0,0 @@ -/* - * arch/arm/mach-tegra/include/mach/spdif.h - * - * - * 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. - */ - - -#ifndef __ARCH_ARM_MACH_TEGRA_SPDIF_H -#define __ARCH_ARM_MACH_TEGRA_SPDIF_H - -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/platform_device.h> - -/* Offsets from TEGRA_SPDIF_BASE */ - -#define SPDIF_CTRL_0 0x0 -#define SPDIF_STATUS_0 0x4 -#define SPDIF_STROBE_CTRL_0 0x8 -#define SPDIF_DATA_FIFO_CSR_0 0x0C -#define SPDIF_DATA_OUT_0 0x40 -#define SPDIF_DATA_IN_0 0x80 -#define SPDIF_CH_STA_RX_A_0 0x100 -#define SPDIF_CH_STA_RX_B_0 0x104 -#define SPDIF_CH_STA_RX_C_0 0x108 -#define SPDIF_CH_STA_RX_D_0 0x10C -#define SPDIF_CH_STA_RX_E_0 0x110 -#define SPDIF_CH_STA_RX_F_0 0x114 -#define SPDIF_CH_STA_TX_A_0 0x140 -#define SPDIF_CH_STA_TX_B_0 0x144 -#define SPDIF_CH_STA_TX_C_0 0x148 -#define SPDIF_CH_STA_TX_D_0 0x14C -#define SPDIF_CH_STA_TX_E_0 0x150 -#define SPDIF_CH_STA_TX_F_0 0x154 -#define SPDIF_USR_STA_RX_A_0 0x180 -#define SPDIF_USR_DAT_TX_A_0 0x1C0 - -/* - * Register SPDIF_CTRL_0 - */ - -/* - * 1=start capturing from left channel,0=start - * capturing from right channel. - */ -#define SPDIF_CTRL_0_CAP_LC (1<<30) - -/* SPDIF receiver(RX): 1=enable, 0=disable. */ -#define SPDIF_CTRL_0_RX_EN (1<<29) - -/* SPDIF Transmitter(TX): 1=enable, 0=disable. */ -#define SPDIF_CTRL_0_TX_EN (1<<28) - -/* Transmit Channel status: 1=enable, 0=disable. */ -#define SPDIF_CTRL_0_TC_EN (1<<27) - -/* Transmit user Data: 1=enable, 0=disable. */ -#define SPDIF_CTRL_0_TU_EN (1<<26) - -/* Interrupt on transmit error: 1=enable, 0=disable. */ -#define SPDIF_CTRL_0_IE_TXE (1<<25) - -/* Interrupt on receive error: 1=enable, 0=disable. */ -#define SPDIF_CTRL_0_IE_RXE (1<<24) - -/* Interrupt on invalid preamble: 1=enable, 0=disable. */ -#define SPDIF_CTRL_0_IE_P (1<<23) - -/* Interrupt on "B" preamble: 1=enable, 0=disable. */ -#define SPDIF_CTRL_0_IE_B (1<<22) - -/* - * Interrupt when block of channel status received: - * 1=enable, 0=disable. - */ -#define SPDIF_CTRL_0_IE_C (1<<21) - -/* - * Interrupt when a valid information unit (IU) recieve: - * 1=enable, 0=disable. - */ -#define SPDIF_CTRL_0_IE_U (1<<20) - -/* - * Interrupt when RX user FIFO attn. level is reached: - * 1=enable, 0=disable. - */ -#define SPDIF_CTRL_0_QE_RU (1<<19) - -/* - * Interrupt when TX user FIFO attn. level is reached: - * 1=enable, 0=disable. - */ -#define SPDIF_CTRL_0_QE_TU (1<<18) - -/* - * Interrupt when RX data FIFO attn. level is reached: - * 1=enable, 0=disable. - */ -#define SPDIF_CTRL_0_QE_RX (1<<17) - -/* - * Interrupt when TX data FIFO attn. level is reached: - * 1=enable, 0=disable. - */ -#define SPDIF_CTRL_0_QE_TX (1<<16) - -/* Loopback test mode: 1=enable internal loopback, 0=Normal mode. */ -#define SPDIF_CTRL_0_LBK_EN (1<<15) - -/* - * Pack data mode: - * 1=Packeted left/right channel data into a single word, - * 0=Single data (16 bit needs to be padded to match the - * interface data bit size) - */ -#define SPDIF_CTRL_0_PACK (1<<14) - -/* - * 00=16bit data - * 01=20bit data - * 10=24bit data - * 11=raw data - */ -#define SPDIF_BIT_MODE_MODE16BIT (0) -#define SPDIF_BIT_MODE_MODE20BIT (1) -#define SPDIF_BIT_MODE_MODE24BIT (2) -#define SPDIF_BIT_MODE_MODERAW (3) -#define SPDIF_CTRL_0_BIT_MODE_SHIFT (12) - -#define SPDIF_CTRL_0_BIT_MODE_MASK \ - ((0x3) << SPDIF_CTRL_0_BIT_MODE_SHIFT) -#define SPDIF_CTRL_0_BIT_MODE_MODE16BIT \ - (SPDIF_BIT_MODE_MODE16BIT << SPDIF_CTRL_0_BIT_MODE_SHIFT) -#define SPDIF_CTRL_0_BIT_MODE_MODE20BIT \ - (SPDIF_BIT_MODE_MODE20BIT << SPDIF_CTRL_0_BIT_MODE_SHIFT) -#define SPDIF_CTRL_0_BIT_MODE_MODE24BIT \ - (SPDIF_BIT_MODE_MODE24BIT << SPDIF_CTRL_0_BIT_MODE_SHIFT) -#define SPDIF_CTRL_0_BIT_MODE_MODERAW \ - (SPDIF_BIT_MODE_MODERAW << SPDIF_CTRL_0_BIT_MODE_SHIFT) - - -/* - * SPDIF Status Register - * ------------------------- - * Note: IS_P, IS_B, IS_C, and IS_U are sticky bits. - * Software must write a 1 to the corresponding bit location - * to clear the status. - */ - -/* Register SPDIF_STATUS_0 */ - -/* - * Receiver(RX) shifter is busy receiving data. 1=busy, 0=not busy. - * This bit is asserted when the receiver first locked onto the - * preamble of the data stream after RX_EN is asserted. This bit is - * deasserted when either, - * (a) the end of a frame is reached after RX_EN is deeasserted, or - * (b) the SPDIF data stream becomes inactive. - */ -#define SPDIF_STATUS_0_RX_BSY (1<<29) - - -/* - * Transmitter(TX) shifter is busy transmitting data. - * 1=busy, 0=not busy. - * This bit is asserted when TX_EN is asserted. - * This bit is deasserted when the end of a frame is reached after - * TX_EN is deasserted. - */ -#define SPDIF_STATUS_0_TX_BSY (1<<28) - -/* - * TX is busy shifting out channel status. 1=busy, 0=not busy. - * This bit is asserted when both TX_EN and TC_EN are asserted and - * data from CH_STA_TX_A register is loaded into the internal shifter. - * This bit is deasserted when either, - * (a) the end of a frame is reached after TX_EN is deasserted, or - * (b) CH_STA_TX_F register is loaded into the internal shifter. - */ -#define SPDIF_STATUS_0_TC_BSY (1<<27) - -/* - * TX User data FIFO busy. 1=busy, 0=not busy. - * This bit is asserted when TX_EN and TXU_EN are asserted and - * there's data in the TX user FIFO. This bit is deassert when either, - * (a) the end of a frame is reached after TX_EN is deasserted, or - * (b) there's no data left in the TX user FIFO. - */ -#define SPDIF_STATUS_0_TU_BSY (1<<26) - -/* Tx FIFO Underrun error status: 1=error, 0=no error */ -#define SPDIF_STATUS_0_TX_ERR (1<<25) - -/* Rx FIFO Overrun error status: 1=error, 0=no error */ -#define SPDIF_STATUS_0_RX_ERR (1<<24) - -/* Preamble status: 1=bad/missing preamble, 0=Preamble ok */ -#define SPDIF_STATUS_0_IS_P (1<<23) - -/* B-preamble detection status: 0=not detected, 1=B-preamble detected */ -#define SPDIF_STATUS_0_IS_B (1<<22) - -/* - * RX channel block data receive status: - * 1=received entire block of channel status, - * 0=entire block not recieved yet. - */ -#define SPDIF_STATUS_0_IS_C (1<<21) - -/* RX User Data Valid flag: 1=valid IU detected, 0 = no IU detected. */ -#define SPDIF_STATUS_0_IS_U (1<<20) - -/* - * RX User FIFO Status: - * 1=attention level reached, 0=attention level not reached. - */ -#define SPDIF_STATUS_0_QS_RU (1<<19) - -/* - * TX User FIFO Status: - * 1=attention level reached, 0=attention level not reached. - */ -#define SPDIF_STATUS_0_QS_TU (1<<18) - -/* - * RX Data FIFO Status: - * 1=attention level reached, 0=attention level not reached. - */ -#define SPDIF_STATUS_0_QS_RX (1<<17) - -/* - * TX Data FIFO Status: - * 1=attention level reached, 0=attention level not reached. - */ -#define SPDIF_STATUS_0_QS_TX (1<<16) - - -/* SPDIF FIFO Configuration and Status Register */ - -/* Register SPDIF_DATA_FIFO_CSR_0 */ - -#define SPDIF_FIFO_ATN_LVL_ONE_SLOT 0 -#define SPDIF_FIFO_ATN_LVL_FOUR_SLOTS 1 -#define SPDIF_FIFO_ATN_LVL_EIGHT_SLOTS 2 -#define SPDIF_FIFO_ATN_LVL_TWELVE_SLOTS 3 - - -/* Clear Receiver User FIFO (RX USR.FIFO) */ -#define SPDIF_DATA_FIFO_CSR_0_RU_CLR (1<<31) - -/* - * RX USR.FIFO Attention Level: - * 00=1-slot-full, 01=2-slots-full, 10=3-slots-full, 11=4-slots-full. - */ - -#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU1 (0) -#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU2 (1) -#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU3 (2) -#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU4 (3) - -#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_SHIFT (29) -#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_MASK \ - (0x3 << SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_SHIFT) -#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU1_WORD_FULL \ - (SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU1 << \ - SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_SHIF) -#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU2_WORD_FULL \ - (SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU2 << \ - SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_SHIF) -#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU3_WORD_FULL \ - (SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU3 << \ - SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_SHIF) -#define SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU4_WORD_FULL \ - (SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_RU4 << \ - SPDIF_DATA_FIFO_CSR_0_RU_ATN_LVL_SHIF) - -/* Number of RX USR.FIFO levels with valid data. */ -#define SPDIF_DATA_FIFO_CSR_0_FULL_COUNT_SHIFT (24) -#define SPDIF_DATA_FIFO_CSR_0_FULL_COUNT_MASK (0x1f << \ - SPDIF_DATA_FIFO_CSR_0_FULL_COUNT_SHIFT) - -/* Clear Transmitter User FIFO (TX USR.FIFO) */ -#define SPDIF_DATA_FIFO_CSR_0_TU_CLR (1<<23) - -/* - * TxUSR.FIFO Attention Level: - * 11=4-slots-empty, 10=3-slots-empty, 01=2-slots-empty, 00=1-slot-empty. - */ - -#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU1 (0) -#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU2 (1) -#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU3 (2) -#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU4 (3) - -#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_SHIFT (21) -#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_MASK (0x3 << \ - SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_SHIFT) -#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU1_WORD_EMPTY \ - (SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU1 << \ - SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_SHIFT) -#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU2_WORD_EMPTY \ - (SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU2 << \ - SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_SHIFT) -#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU3_WORD_EMPTY \ - (SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU3 << \ - SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_SHIFT) -#define SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU4_WORD_EMPTY \ - (SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_TU4 << \ - SPDIF_DATA_FIFO_CSR_0_TU_ATN_LVL_SHIFT) - -/* Number of Tx USR.FIFO levels that could be filled. */ -#define SPDIF_DATA_FIFO_CSR_0_TU_EMPTY_COUNT_SHIFT (16) -#define SPDIF_DATA_FIFO_CSR_0_TU_EMPTY_COUNT_FIELD \ - ((0x1f) << SPDIF_DATA_FIFO_CSR_0_TU_EMPTY_COUNT_SHIFT) - -/* Clear Receiver Data FIFO (RX DATA.FIFO). */ -#define SPDIF_DATA_FIFO_CSR_0_RX_CLR (1<<15) - -/* - * Rx FIFO Attention Level: - * 11=12-slots-full, 10=8-slots-full, 01=4-slots-full, 00=1-slot-full. - */ -#define SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_SHIFT (13) -#define SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_MASK \ - (0x3 << SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_SHIFT) -#define SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_RX1_WORD_FULL \ - (SPDIF_FIFO_ATN_LVL_ONE_SLOT << \ - SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_SHIFT) -#define SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_RX4_WORD_FULL \ - (SPDIF_FIFO_ATN_LVL_FOUR_SLOTS << \ - SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_SHIFT) -#define SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_RX8_WORD_FULL \ - (SPDIF_FIFO_ATN_LVL_EIGHT_SLOTS << \ - SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_SHIFT) -#define SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_RX12_WORD_FULL \ - (SPDIF_FIFO_ATN_LVL_TWELVE_SLOTS << \ - SPDIF_DATA_FIFO_CSR_0_RX_ATN_LVL_SHIFT) - - -/* Number of RX DATA.FIFO levels with valid data */ -#define SPDIF_DATA_FIFO_CSR_0_RX_DATA_FIFO_FULL_COUNT_SHIFT (8) -#define SPDIF_DATA_FIFO_CSR_0_RX_DATA_FIFO_FULL_COUNT_FIELD \ - ((0x1f) << SPDIF_DATA_FIFO_CSR_0_RX_DATA_FIFO_FULL_COUNT_SHIFT) - -/* Clear Transmitter Data FIFO (TX DATA.FIFO) */ -#define SPDIF_DATA_FIFO_CSR_0_TX_CLR (1<<7) - -/* - * Tx FIFO Attention Level: - * 11=12-slots-empty, 10=8-slots-empty, 01=4-slots-empty, 00=1-slot-empty - */ -#define SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_SHIFT (5) -#define SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_MASK \ - (0x3 << SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_SHIFT) -#define SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_TX1_WORD_FULL \ - (SPDIF_FIFO_ATN_LVL_ONE_SLOT << \ - SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_SHIFT) -#define SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_TX4_WORD_FULL \ - (SPDIF_FIFO_ATN_LVL_FOUR_SLOTS << \ - SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_SHIFT) -#define SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_TX8_WORD_FULL \ - (SPDIF_FIFO_ATN_LVL_EIGHT_SLOTS << \ - SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_SHIFT) -#define SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_TX12_WORD_FULL \ - (SPDIF_FIFO_ATN_LVL_TWELVE_SLOTS << \ - SPDIF_DATA_FIFO_CSR_0_TX_ATN_LVL_SHIFT) - - -/* Number of Tx DATA.FIFO levels that could be filled. */ -#define SPDIF_DATA_FIFO_CSR_0_TD_EMPTY_COUNT_SHIFT (0) -#define SPDIF_DATA_FIFO_CSR_0_TD_EMPTY_COUNT_MASK \ - ((0x1f) << SPDIF_DATA_FIFO_CSR_0_TD_EMPTY_COUNT_SHIFT) - - -#endif /* __ARCH_ARM_MACH_TEGRA_SPDIF_H */ 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 ff182079863e..000000000000 --- a/arch/arm/mach-tegra/tegra_spdif_audio.c +++ /dev/null @@ -1,1326 +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/tegra_audio.h> - -#include <mach/dma.h> -#include <mach/iomap.h> -#include <mach/spdif.h> -#include <mach/audio.h> -#include <mach/irqs.h> - -#include "clock.h" - - -/* per stream (input/output) */ -struct audio_stream { - int opened; - struct mutex lock; - - struct tegra_audio_buf_config buf_config; - bool active; /* is DMA or PIO in progress? */ - void *buffer; - dma_addr_t buf_phys; - struct kfifo fifo; - struct completion fifo_completion; - - unsigned errors; - - int spdif_fifo_atn_level; - - ktime_t last_dma_ts; - struct tegra_dma_channel *dma_chan; - struct completion stop_completion; - spinlock_t dma_req_lock; /* guards dma_has_it */ - int dma_has_it; - struct tegra_dma_req dma_req; -}; - -struct spdif_pio_stats { - u32 spdif_interrupt_count; - u32 tx_fifo_errors; - u32 tx_fifo_written; -}; - - -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; - - bool using_dma; - unsigned long dma_req_sel; - - int irq; /* for pio mode */ - struct spdif_pio_stats pio_stats; - const int *in_divs; - int in_divs_len; - - struct miscdevice misc_out; - struct miscdevice misc_out_ctl; - struct audio_stream out; -}; - -static inline int buf_size(struct audio_stream *s) -{ - return 1 << s->buf_config.size; -} - -static inline int chunk_size(struct audio_stream *s) -{ - return 1 << s->buf_config.chunk; -} - -static inline int threshold_size(struct audio_stream *s) -{ - return 1 << s->buf_config.threshold; -} - -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 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); - val |= on ? (SPDIF_CTRL_0_TX_EN) : 0; - val |= on ? (SPDIF_CTRL_0_TC_EN) : 0; - - spdif_writel(base, val, SPDIF_CTRL_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); -} - -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; -} - - -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); -} - - - -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; - unsigned int parent_clock_freq = 0; - struct clk *spdif_clk; - - unsigned int ch_sta[] = { - 0x0, /* 44.1, default values */ - 0xf << 4, /* bits 36-39, original sample freq -- 44.1 */ - 0x0, - 0x0, - 0x0, - 0x0, - }; - - ch_sta[0] = 0x0; - ch_sta[1] = 0xf << 4; - - switch (sample_rate) { - case 32000: - clock_freq = 4096000; /* 4.0960 MHz */ - parent_clock_freq = 12288000; - ch_sta[0] = 0x3 << 24; - ch_sta[1] = 0xC << 4; - break; - case 44100: - clock_freq = 5644800; /* 5.6448 MHz */ - parent_clock_freq = 11289600; - ch_sta[0] = 0x0; - ch_sta[1] = 0xF << 4; - break; - case 48000: - clock_freq = 6144000; /* 6.1440MHz */ - parent_clock_freq = 12288000; - ch_sta[0] = 0x2 << 24; - ch_sta[1] = 0xD << 4; - break; - case 88200: - clock_freq = 11289600; /* 11.2896 MHz */ - parent_clock_freq = 11289600; - break; - case 96000: - clock_freq = 12288000; /* 12.288 MHz */ - parent_clock_freq = 12288000; - break; - case 176400: - clock_freq = 22579200; /* 22.5792 MHz */ - parent_clock_freq = 11289600; - break; - case 192000: - clock_freq = 24576000; /* 24.5760 MHz */ - parent_clock_freq = 12288000; - 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; -} - - -#define PCM_IN_BUFFER_PADDING (1<<6) /* bytes */ -#define PCM_BUFFER_MAX_SIZE_ORDER (PAGE_SHIFT + 2) -#define PCM_BUFFER_DMA_CHUNK_SIZE_ORDER (PCM_BUFFER_MAX_SIZE_ORDER - 1) -#define PCM_BUFFER_THRESHOLD_ORDER PCM_BUFFER_DMA_CHUNK_SIZE_ORDER -#define PCM_DMA_CHUNK_MIN_SIZE_ORDER 3 - -static int init_stream_buffer(struct audio_stream *, - struct tegra_audio_buf_config *cfg, unsigned); - -static int setup_dma(struct audio_driver_state *); -static void tear_down_dma(struct audio_driver_state *); -static int start_dma_playback(struct audio_stream *); -static void stop_dma_playback(struct audio_stream *); - -static int setup_pio(struct audio_driver_state *); -static void tear_down_pio(struct audio_driver_state *); -static int start_pio_playback(struct audio_stream *); -static void stop_pio_playback(struct audio_stream *); - - -struct sound_ops { - int (*setup)(struct audio_driver_state *); - void (*tear_down)(struct audio_driver_state *); - int (*start_playback)(struct audio_stream *); - void (*stop_playback)(struct audio_stream *); -}; - -static const struct sound_ops dma_sound_ops = { - .setup = setup_dma, - .tear_down = tear_down_dma, - .start_playback = start_dma_playback, - .stop_playback = stop_dma_playback, -}; - -static const struct sound_ops pio_sound_ops = { - .setup = setup_pio, - .tear_down = tear_down_pio, - .start_playback = start_pio_playback, - .stop_playback = stop_pio_playback, -}; - -static const struct sound_ops *sound_ops = &dma_sound_ops; - -static int start_playback(struct audio_stream *aos) -{ - int rc; - unsigned long flags; - spin_lock_irqsave(&aos->dma_req_lock, flags); - pr_debug("%s: starting playback\n", __func__); - rc = sound_ops->start_playback(aos); - spin_unlock_irqrestore(&aos->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); - if (kfifo_is_empty(&aos->fifo)) { - sound_ops->stop_playback(aos); - if (aos->active) - aos->errors++; - spin_unlock_irqrestore(&aos->dma_req_lock, flags); - return true; - } - spin_unlock_irqrestore(&aos->dma_req_lock, flags); - - return false; -} - - -static void toggle_dma(struct audio_driver_state *ads) -{ - pr_info("%s: %s\n", __func__, ads->using_dma ? "pio" : "dma"); - sound_ops->tear_down(ads); - sound_ops = ads->using_dma ? &pio_sound_ops : &dma_sound_ops; - sound_ops->setup(ads); - ads->using_dma = !ads->using_dma; -} - -/* DMA */ - -static int resume_dma_playback(struct audio_stream *aos); - -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; - pr_info("%s\n", __func__); - - /* setup audio playback */ - ads->out.buf_phys = dma_map_single(&ads->pdev->dev, ads->out.buffer, - 1 << PCM_BUFFER_MAX_SIZE_ORDER, DMA_TO_DEVICE); - BUG_ON(!ads->out.buf_phys); - setup_dma_tx_request(&ads->out.dma_req, &ads->out); - ads->out.dma_chan = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); - if (!ads->out.dma_chan) { - pr_err("%s: could not allocate output SPDIF DMA channel: %ld\n", - __func__, PTR_ERR(ads->out.dma_chan)); - rc = -ENODEV; - goto fail_tx; - } - return 0; - - -fail_tx: - dma_unmap_single(&ads->pdev->dev, ads->out.buf_phys, - 1 << PCM_BUFFER_MAX_SIZE_ORDER, DMA_TO_DEVICE); - 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) -{ - pr_info("%s\n", __func__); - - tegra_dma_free_channel(ads->out.dma_chan); - ads->out.dma_chan = NULL; - dma_unmap_single(&ads->pdev->dev, ads->out.buf_phys, - buf_size(&ads->out), - DMA_TO_DEVICE); - ads->out.buf_phys = 0; - -} - -static void dma_tx_complete_callback(struct tegra_dma_req *req) -{ - unsigned long flags; - struct audio_stream *aos = req->dev; - int count = req->bytes_transferred; - u64 delta_us; - u64 max_delay_us = count * 10000 / (4 * 441); - - pr_debug("%s bytes transferred %d\n", __func__, count); - - aos->dma_has_it = false; - delta_us = ktime_to_us(ktime_sub(ktime_get_real(), aos->last_dma_ts)); - - if (delta_us > max_delay_us) { - pr_debug("%s: too late by %lld us\n", __func__, - delta_us - max_delay_us); - aos->errors++; - } - - kfifo_skip(&aos->fifo, count); - - if (kfifo_avail(&aos->fifo) >= threshold_size(aos) && - !completion_done(&aos->fifo_completion)) { - pr_debug("%s: complete (%d avail)\n", __func__, - kfifo_avail(&aos->fifo)); - complete(&aos->fifo_completion); - } - - if (stop_playback_if_necessary(aos)) - return; - spin_lock_irqsave(&aos->dma_req_lock, flags); - resume_dma_playback(aos); - spin_unlock_irqrestore(&aos->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 = spdif_get_fifo_phy_base(ads->spdif_phys); - req->dest_wrap = 4; - req->dest_bus_width = 16; - req->source_bus_width = 32; - req->source_wrap = 0; - req->req_sel = ads->dma_req_sel; -} - - -/* Called with aos->dma_req_lock taken. */ -static int resume_dma_playback(struct audio_stream *aos) -{ - int rc; - struct audio_driver_state *ads = ads_from_out(aos); - struct tegra_dma_req *req = &aos->dma_req; - - unsigned out, in; - - out = __kfifo_off(&aos->fifo, aos->fifo.out); - in = __kfifo_off(&aos->fifo, aos->fifo.in); - - /* stop_playback_if_necessary() already checks to see if the fifo is - * empty. - */ - BUG_ON(!kfifo_len(&aos->fifo)); - - if (aos->dma_has_it) { - pr_debug("%s: playback already in progress\n", __func__); - return 0; - } - -#if 0 - spdif_fifo_clear(ads->spdif_base); -#endif - spdif_fifo_set_attention_level(ads->spdif_base, - aos->spdif_fifo_atn_level); - - req->source_addr = aos->buf_phys + out; - if (out < in) - req->size = in - out; - else - req->size = kfifo_size(&aos->fifo) - out; - - dma_sync_single_for_device(NULL, - aos->buf_phys + out, req->size, DMA_TO_DEVICE); - - /* Don't send all the data yet. */ - if (req->size > chunk_size(aos)) - req->size = chunk_size(aos); - pr_debug("%s resume playback (%d in fifo, writing %d, in %d out %d)\n", - __func__, kfifo_len(&aos->fifo), req->size, in, out); - - spdif_fifo_enable(ads->spdif_base, 1); - - aos->last_dma_ts = ktime_get_real(); - rc = tegra_dma_enqueue_req(aos->dma_chan, req); - aos->dma_has_it = !rc; - if (!aos->dma_has_it) - pr_err("%s: could not enqueue TX DMA req\n", __func__); - return rc; -} - -/* Called with aos->dma_req_lock taken. */ -static int start_dma_playback(struct audio_stream *aos) -{ - return resume_dma_playback(aos); -} - -/* 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) - if (spin++ > 50) - pr_info("%s: spin %d\n", __func__, spin); - if (spin == 100) - pr_warn("%s: spinny\n", __func__); -} - -/* PIO (non-DMA) */ - -static int setup_pio(struct audio_driver_state *ads) -{ - pr_info("%s\n", __func__); - enable_irq(ads->irq); - return 0; -} - -static void tear_down_pio(struct audio_driver_state *ads) -{ - pr_info("%s\n", __func__); - disable_irq(ads->irq); -} - -static int start_pio_playback(struct audio_stream *aos) -{ - struct audio_driver_state *ads = ads_from_out(aos); - - if (spdif_is_fifo_enabled(ads->spdif_base)) { - pr_debug("%s: playback is already in progress\n", __func__); - return 0; - } - - pr_debug("%s\n", __func__); - - spdif_fifo_set_attention_level(ads->spdif_base, - aos->spdif_fifo_atn_level); -#if 0 - spdif_fifo_clear(ads->spdif_base); -#endif - - spdif_set_fifo_irq_on_err(ads->spdif_base, 1); - spdif_fifo_enable(ads->spdif_base, 1); - - return 0; -} - -static void stop_pio_playback(struct audio_stream *aos) -{ - struct audio_driver_state *ads = ads_from_out(aos); - - spdif_set_fifo_irq_on_err(ads->spdif_base, 0); - spdif_fifo_enable(ads->spdif_base, 0); - while (spdif_get_status(ads->spdif_base) & SPDIF_STATUS_0_TX_BSY) - /* spin */; - - pr_info("%s: interrupts %d\n", __func__, - ads->pio_stats.spdif_interrupt_count); - pr_info("%s: sent %d\n", __func__, - ads->pio_stats.tx_fifo_written); - pr_info("%s: tx errors %d\n", __func__, - ads->pio_stats.tx_fifo_errors); - - memset(&ads->pio_stats, 0, sizeof(ads->pio_stats)); -} - - -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); - - ads->pio_stats.spdif_interrupt_count++; - - if (status & SPDIF_CTRL_0_IE_TXE) - ads->pio_stats.tx_fifo_errors++; - -#if 0 - if (status & SPDIF_STATUS_0_TX_ERR) -#endif - spdif_ack_status(ads->spdif_base); - - if (status & SPDIF_STATUS_0_QS_TX) { - int written; - int empty; - int len; - u16 fifo_buffer[32]; - - struct audio_stream *out = &ads->out; - - if (!spdif_is_fifo_enabled(ads->spdif_base)) { - pr_debug("%s: tx fifo not enabled, skipping\n", - __func__); - goto done; - } - - pr_debug("%s tx fifo is ready\n", __func__); - - if (kfifo_avail(&out->fifo) > threshold_size(out) && - !completion_done(&out->fifo_completion)) { - pr_debug("%s: tx complete (%d avail)\n", __func__, - kfifo_avail(&out->fifo)); - complete(&out->fifo_completion); - } - - if (stop_playback_if_necessary(out)) - goto done; - - empty = spdif_get_fifo_full_empty_count(ads->spdif_base); - - len = kfifo_out(&out->fifo, fifo_buffer, - empty * sizeof(u16)); - len /= sizeof(u16); - - written = 0; - while (empty-- && written < len) { - ads->pio_stats.tx_fifo_written += written * sizeof(u16); - spdif_fifo_write(ads->spdif_base, - fifo_buffer[written++]); - } - - /* TODO: Should we check to see if we wrote less than the - * FIFO threshold and adjust it if so? - */ - - if (written) { - /* start the transaction */ - pr_debug("%s: enabling fifo (%d samples written)\n", - __func__, written); - spdif_fifo_enable(ads->spdif_base, 1); - } - } - -done: - 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, total = 0; - unsigned nw = 0; - - struct audio_driver_state *ads = ads_from_misc_out(file); - - mutex_lock(&ads->out.lock); - - ads->out.active = true; - - if (!IS_ALIGNED(size, 4)) { - pr_err("%s: user size request %d not aligned to 4\n", - __func__, size); - rc = -EINVAL; - goto done; - } - - pr_debug("%s: write %d bytes, %d available\n", __func__, - size, kfifo_avail(&ads->out.fifo)); - -again: - rc = kfifo_from_user(&ads->out.fifo, buf + total, size - total, &nw); - if (rc < 0) { - pr_err("%s: error copying from user\n", __func__); - goto done; - } - - rc = start_playback(&ads->out); - if (rc < 0) { - pr_err("%s: could not start playback: %d\n", __func__, rc); - goto done; - } - - total += nw; - if (total < size) { - pr_debug("%s: sleep (user %d total %d nw %d)\n", __func__, - size, total, nw); - mutex_unlock(&ads->out.lock); - rc = wait_for_completion_interruptible( - &ads->out.fifo_completion); - mutex_lock(&ads->out.lock); - if (rc == -ERESTARTSYS) { - pr_warn("%s: interrupted\n", __func__); - goto done; - } - pr_debug("%s: awake\n", __func__); - goto again; - } - - rc = total; - *off += total; - -done: - ads->out.active = false; - 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_SET_BUF_CONFIG: { - struct tegra_audio_buf_config cfg; - if (copy_from_user(&cfg, (void __user *)arg, sizeof(cfg))) { - rc = -EFAULT; - break; - } - if (aos->active) { - pr_err("%s: playback in progress\n", __func__); - rc = -EBUSY; - break; - } - rc = init_stream_buffer(aos, &cfg, 0); - if (rc < 0) - break; - aos->buf_config = cfg; - } - break; - case TEGRA_AUDIO_OUT_GET_BUF_CONFIG: - if (copy_to_user((void __user *)arg, &aos->buf_config, - sizeof(aos->buf_config))) - rc = -EFAULT; - break; - case TEGRA_AUDIO_OUT_GET_ERROR_COUNT: - if (copy_to_user((void __user *)arg, &aos->errors, - sizeof(aos->errors))) - rc = -EFAULT; - if (!rc) - aos->errors = 0; - break; - case TEGRA_AUDIO_OUT_PRELOAD_FIFO: { - struct tegra_audio_out_preload preload; - if (copy_from_user(&preload, (void __user *)arg, - sizeof(preload))) { - rc = -EFAULT; - break; - } - rc = kfifo_from_user(&ads->out.fifo, - (void __user *)preload.data, preload.len, - &preload.len_written); - if (rc < 0) { - pr_err("%s: error copying from user\n", __func__); - break; - } - if (copy_to_user((void __user *)arg, &preload, sizeof(preload))) - rc = -EFAULT; - pr_info("%s: preloaded output fifo with %d bytes\n", __func__, - preload.len_written); - } - 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; - struct audio_driver_state *ads = ads_from_misc_out(file); - - pr_info("%s\n", __func__); - - mutex_lock(&ads->out.lock); - if (!ads->out.opened++) { - pr_info("%s: resetting fifo and error count\n", __func__); - ads->out.errors = 0; - kfifo_reset(&ads->out.fifo); - } - rc = spdif_set_sample_rate(ads, 44100); - - 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); - struct clk *spdif_clk; - - pr_info("%s\n", __func__); - - mutex_lock(&ads->out.lock); - if (ads->out.opened) - ads->out.opened--; - if (!ads->out.opened) - stop_playback_if_necessary(&ads->out); - - spdif_clk = clk_get(&ads->pdev->dev, NULL); - if (!spdif_clk) { - dev_err(&ads->pdev->dev, "%s: could not get spdif clock\n", - __func__); - return -EIO; - } - clk_disable(spdif_clk); - mutex_unlock(&ads->out.lock); - - 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, - struct tegra_audio_buf_config *cfg, - unsigned padding) -{ - pr_info("%s (size %d threshold %d chunk %d)\n", __func__, - cfg->size, cfg->threshold, cfg->chunk); - - if (cfg->chunk < PCM_DMA_CHUNK_MIN_SIZE_ORDER) { - pr_err("%s: chunk %d too small (%d min)\n", __func__, - cfg->chunk, PCM_DMA_CHUNK_MIN_SIZE_ORDER); - return -EINVAL; - } - - if (cfg->chunk > cfg->size) { - pr_err("%s: chunk %d > size %d\n", __func__, - cfg->chunk, cfg->size); - return -EINVAL; - } - - if (cfg->threshold > cfg->size) { - pr_err("%s: threshold %d > size %d\n", __func__, - cfg->threshold, cfg->size); - return -EINVAL; - } - - if ((1 << cfg->size) < padding) { - pr_err("%s: size %d < buffer padding %d (bytes)\n", __func__, - cfg->size, padding); - return -EINVAL; - } - - if (cfg->size > PCM_BUFFER_MAX_SIZE_ORDER) { - pr_err("%s: size %d exceeds max %d\n", __func__, - cfg->size, PCM_BUFFER_MAX_SIZE_ORDER); - return -EINVAL; - } - - if (!s->buffer) { - pr_debug("%s: allocating buffer (size %d, padding %d)\n", - __func__, 1 << cfg->size, padding); - s->buffer = kmalloc((1 << cfg->size) + padding, - GFP_KERNEL | GFP_DMA); - } - if (!s->buffer) { - pr_err("%s: could not allocate output buffer\n", __func__); - return -ENOMEM; - } - - kfifo_init(&s->fifo, s->buffer, 1 << cfg->size); - 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) -{ - struct tegra_audio_platform_data *pdata = dev->platform_data; - struct audio_driver_state *ads = pdata->driver_data; - return sprintf(buf, "%s\n", ads->using_dma ? "dma" : "pio"); -} - -static ssize_t dma_toggle_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int use_dma; - struct tegra_audio_platform_data *pdata = dev->platform_data; - struct audio_driver_state *ads = pdata->driver_data; - - if (count < 4) - return -EINVAL; - - use_dma = 0; - if (!strncmp(buf, "dma", 3)) - use_dma = 1; - else if (strncmp(buf, "pio", 3)) { - dev_err(dev, "%s: invalid string [%s]\n", __func__, buf); - return -EINVAL; - } - - mutex_lock(&ads->out.lock); - if (ads->out.active) { - dev_err(dev, "%s: playback or recording in progress.\n", - __func__); - mutex_unlock(&ads->out.lock); - return -EBUSY; - } - if (!!use_dma ^ !!ads->using_dma) - toggle_dma(ads); - else - dev_info(dev, "%s: no change\n", __func__); - mutex_unlock(&ads->out.lock); - - return count; -} - -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; - } - - mutex_lock(&as->lock); - if (as->active) { - pr_err("%s: in progress.\n", __func__); - mutex_unlock(&as->lock); - return -EBUSY; - } - *fifo_lvl = lvl; - pr_info("%s: fifo level %d\n", __func__, *fifo_lvl); - mutex_unlock(&as->lock); - - 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) -{ - struct tegra_audio_platform_data *pdata = dev->platform_data; - struct audio_driver_state *ads = pdata->driver_data; - return __attr_fifo_atn_write(ads, &ads->out, - &ads->out.spdif_fifo_atn_level, buf, count); -} - -static DEVICE_ATTR(tx_fifo_atn, 0644, tx_fifo_atn_show, tx_fifo_atn_store); - - -static int tegra_spdif_probe(struct platform_device *pdev) -{ - int rc; - 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; - } - - state->out.spdif_fifo_atn_level = SPDIF_FIFO_ATN_LVL_FOUR_SLOTS; - - 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; - - memset(&state->pio_stats, 0, sizeof(state->pio_stats)); - - /* disable interrupts from SPDIF */ - spdif_fifo_clear(state->spdif_base); - spdif_enable_fifos(state->spdif_base, 0); - - spdif_set_bit_mode(state->spdif_base, state->pdata->mode); - spdif_set_fifo_packed(state->spdif_base, state->pdata->fifo_fmt); - - state->out.opened = 0; - state->out.active = false; - mutex_init(&state->out.lock); - init_completion(&state->out.fifo_completion); - init_completion(&state->out.stop_completion); - spin_lock_init(&state->out.dma_req_lock); - state->out.buf_phys = 0; - state->out.dma_chan = NULL; - state->out.dma_has_it = false; - - state->out.buffer = 0; - state->out.buf_config.size = PCM_BUFFER_MAX_SIZE_ORDER; - state->out.buf_config.threshold = PCM_BUFFER_THRESHOLD_ORDER; - state->out.buf_config.chunk = PCM_BUFFER_DMA_CHUNK_SIZE_ORDER; - rc = init_stream_buffer(&state->out, &state->out.buf_config, 0); - if (rc < 0) - return rc; - - 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; - - - state->using_dma = state->pdata->dma_on; - if (!state->using_dma) - sound_ops = &pio_sound_ops; - 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; -} - -static struct platform_driver tegra_spdif_driver = { - .driver = { - .name = "spdif_out", - .owner = THIS_MODULE, - }, - .probe = tegra_spdif_probe, -}; - -static int __init tegra_spdif_init(void) -{ - return platform_driver_register(&tegra_spdif_driver); -} - -module_init(tegra_spdif_init); -MODULE_LICENSE("GPL"); |