Adds OMAP audio driver.
Signed-off-by: Tony Lindgren <tony@atomide.com>
--- /dev/null
+/*
+ *
+ * TI TSC2101 Audio CODEC and TS control registers definition
+ *
+ *
+ * Copyright 2003 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ * source@mvista.com
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ASM_HARDWARE_TSC2101_H
+#define __ASM_HARDWARE_TSC2101_H
+
+/* Page 0 Touch Screen Data Registers */
+#define TSC2101_TS_X (0x00)
+#define TSC2101_TS_Y (0x01)
+#define TSC2101_TS_Z1 (0x02)
+#define TSC2101_TS_Z2 (0x03)
+#define TSC2101_TS_BAT (0x05)
+#define TSC2101_TS_AUX1 (0x07)
+#define TSC2101_TS_AUX2 (0x08)
+#define TSC2101_TS_TEMP1 (0x09)
+#define TSC2101_TS_TEMP2 (0x0A)
+
+/* Page 1 Touch Screen Control registers */
+#define TSC2101_TS_ADC_CTRL (0x00)
+#define TSC2101_TS_STATUS (0x01)
+#define TSC2101_TS_BUFFER_CTRL (0x02)
+#define TSC2101_TS_REF_CTRL (0x03)
+#define TSC2101_TS_RESET_CTRL (0x04)
+#define TSC2101_TS_CONFIG_CTRL (0x05)
+#define TSC2101_TS_TEMP_MAX_THRESHOLD (0x06)
+#define TSC2101_TS_TEMP_MIN_THRESHOLD (0x07)
+#define TSC2101_TS_AUX1_MAX_THRESHOLD (0x08)
+#define TSC2101_TS_AUX1_MIN_THRESHOLD (0x09)
+#define TSC2101_TS_AUX2_MAX_THRESHOLD (0x0A)
+#define TSC2101_TS_AUX2_MIN_THRESHOLD (0x0B)
+#define TSC2101_TS_MEASURE_CONFIG (0x0C)
+#define TSC2101_TS_PROG_DELAY (0x0D)
+
+/* Page 2 Audio codec Control registers */
+#define TSC2101_AUDIO_CTRL_1 (0x00)
+#define TSC2101_HEADSET_GAIN_CTRL (0x01)
+#define TSC2101_DAC_GAIN_CTRL (0x02)
+#define TSC2101_MIXER_PGA_CTRL (0x03)
+#define TSC2101_AUDIO_CTRL_2 (0x04)
+#define TSC2101_CODEC_POWER_CTRL (0x05)
+#define TSC2101_AUDIO_CTRL_3 (0x06)
+#define TSC2101_LCH_BASS_BOOST_N0 (0x07)
+#define TSC2101_LCH_BASS_BOOST_N1 (0x08)
+#define TSC2101_LCH_BASS_BOOST_N2 (0x09)
+#define TSC2101_LCH_BASS_BOOST_N3 (0x0A)
+#define TSC2101_LCH_BASS_BOOST_N4 (0x0B)
+#define TSC2101_LCH_BASS_BOOST_N5 (0x0C)
+#define TSC2101_LCH_BASS_BOOST_D1 (0x0D)
+#define TSC2101_LCH_BASS_BOOST_D2 (0x0E)
+#define TSC2101_LCH_BASS_BOOST_D4 (0x0F)
+#define TSC2101_LCH_BASS_BOOST_D5 (0x10)
+#define TSC2101_RCH_BASS_BOOST_N0 (0x11)
+#define TSC2101_RCH_BASS_BOOST_N1 (0x12)
+#define TSC2101_RCH_BASS_BOOST_N2 (0x13)
+#define TSC2101_RCH_BASS_BOOST_N3 (0x14)
+#define TSC2101_RCH_BASS_BOOST_N4 (0x15)
+#define TSC2101_RCH_BASS_BOOST_N5 (0x16)
+#define TSC2101_RCH_BASS_BOOST_D1 (0x17)
+#define TSC2101_RCH_BASS_BOOST_D2 (0x18)
+#define TSC2101_RCH_BASS_BOOST_D4 (0x19)
+#define TSC2101_RCH_BASS_BOOST_D5 (0x1A)
+#define TSC2101_PLL_PROG_1 (0x1B)
+#define TSC2101_PLL_PROG_2 (0x1C)
+#define TSC2101_AUDIO_CTRL_4 (0x1D)
+#define TSC2101_HANDSET_GAIN_CTRL (0x1E)
+#define TSC2101_BUZZER_GAIN_CTRL (0x1F)
+#define TSC2101_AUDIO_CTRL_5 (0x20)
+#define TSC2101_AUDIO_CTRL_6 (0x21)
+#define TSC2101_AUDIO_CTRL_7 (0x22)
+#define TSC2101_GPIO_CTRL (0x23)
+#define TSC2101_AGC_CTRL (0x24)
+#define TSC2101_POWERDOWN_STS (0x25)
+#define TSC2101_MIC_AGC_CONTROL (0x26)
+#define TSC2101_CELL_AGC_CONTROL (0x27)
+
+/* Bit field definitions for TS Control */
+#define TSC2101_DATA_AVAILABLE 0x4000
+#define TSC2101_BUFFERMODE_DISABLE 0x0
+#define TSC2101_REF_POWERUP 0x16
+#define TSC2101_ENABLE_TOUCHDETECT 0x08
+#define TSC2101_PRG_DELAY 0x0900
+#define TSC2101_ADC_CONTROL 0x8874
+#define TSC2101_ADC_POWERDOWN 0x4000
+
+/* Bit position */
+#define TSC2101_BIT(ARG) ((0x01)<<(ARG))
+
+/* Field masks for Audio Control 1 */
+#define AC1_ADCHPF(ARG) (((ARG) & 0x03) << 14)
+#define AC1_WLEN(ARG) (((ARG) & 0x03) << 10)
+#define AC1_DATFM(ARG) (((ARG) & 0x03) << 8)
+#define AC1_DACFS(ARG) (((ARG) & 0x07) << 3)
+#define AC1_ADCFS(ARG) (((ARG) & 0x07))
+
+/* Field masks for TSC2101_HEADSET_GAIN_CTRL */
+#define HGC_ADMUT_HED TSC2101_BIT(15)
+#define HGC_ADPGA_HED(ARG) (((ARG) & 0x7F) << 8)
+#define HGC_AGCTG_HED(ARG) (((ARG) & 0x07) << 5)
+#define HGC_AGCTC_HED(ARG) (((ARG) & 0x0F) << 1)
+#define HGC_AGCEN_HED (0x01)
+
+/* Field masks for TSC2101_DAC_GAIN_CTRL */
+#define DGC_DALMU TSC2101_BIT(15)
+#define DGC_DALVL(ARG) (((ARG) & 0x7F) << 8)
+#define DGC_DARMU TSC2101_BIT(7)
+#define DGC_DARVL(ARG) (((ARG) & 0x7F))
+
+/* Field masks for TSC2101_MIXER_PGA_CTRL */
+#define MPC_ASTMU TSC2101_BIT(15)
+#define MPC_ASTG(ARG) (((ARG) & 0x7F) << 8)
+#define MPC_MICSEL(ARG) (((ARG) & 0x07) << 5)
+#define MPC_MICADC TSC2101_BIT(4)
+#define MPC_CPADC TSC2101_BIT(3)
+#define MPC_ASTGF (0x01)
+
+/* Field formats for TSC2101_AUDIO_CTRL_2 */
+#define AC2_KCLEN TSC2101_BIT(15)
+#define AC2_KCLAC(ARG) (((ARG) & 0x07) << 12)
+#define AC2_APGASS TSC2101_BIT(11)
+#define AC2_KCLFRQ(ARG) (((ARG) & 0x07) << 8)
+#define AC2_KCLLN(ARG) (((ARG) & 0x0F) << 4)
+#define AC2_DLGAF TSC2101_BIT(3)
+#define AC2_DRGAF TSC2101_BIT(2)
+#define AC2_DASTC TSC2101_BIT(1)
+#define AC2_ADGAF (0x01)
+
+/* Field masks for TSC2101_CODEC_POWER_CTRL */
+#define CPC_MBIAS_HND TSC2101_BIT(15)
+#define CPC_MBIAS_HED TSC2101_BIT(14)
+#define CPC_ASTPWD TSC2101_BIT(13)
+#define CPC_SP1PWDN TSC2101_BIT(12)
+#define CPC_SP2PWDN TSC2101_BIT(11)
+#define CPC_DAPWDN TSC2101_BIT(10)
+#define CPC_ADPWDN TSC2101_BIT(9)
+#define CPC_VGPWDN TSC2101_BIT(8)
+#define CPC_COPWDN TSC2101_BIT(7)
+#define CPC_LSPWDN TSC2101_BIT(6)
+#define CPC_ADPWDF TSC2101_BIT(5)
+#define CPC_LDAPWDF TSC2101_BIT(4)
+#define CPC_RDAPWDF TSC2101_BIT(3)
+#define CPC_ASTPWF TSC2101_BIT(2)
+#define CPC_BASSBC TSC2101_BIT(1)
+#define CPC_DEEMPF (0x01)
+
+/* Field masks for TSC2101_AUDIO_CTRL_3 */
+#define AC3_DMSVOL(ARG) (((ARG) & 0x03) << 14)
+#define AC3_REFFS TSC2101_BIT(13)
+#define AC3_DAXFM TSC2101_BIT(12)
+#define AC3_SLVMS TSC2101_BIT(11)
+#define AC3_ADCOVF TSC2101_BIT(8)
+#define AC3_DALOVF TSC2101_BIT(7)
+#define AC3_DAROVF TSC2101_BIT(6)
+#define AC3_CLPST TSC2101_BIT(3)
+#define AC3_REVID(ARG) (((ARG) & 0x07))
+
+/* Field masks for TSC2101_PLL_PROG_1 */
+#define PLL1_PLLSEL TSC2101_BIT(15)
+#define PLL1_QVAL(ARG) (((ARG) & 0x0F) << 11)
+#define PLL1_PVAL(ARG) (((ARG) & 0x07) << 8)
+#define PLL1_I_VAL(ARG) (((ARG) & 0x3F) << 2)
+
+/* Field masks of TSC2101_PLL_PROG_2 */
+#define PLL2_D_VAL(ARG) (((ARG) & 0x3FFF) << 2)
+
+/* Field masks for TSC2101_AUDIO_CTRL_4 */
+#define AC4_ADSTPD TSC2101_BIT(15)
+#define AC4_DASTPD TSC2101_BIT(14)
+#define AC4_ASSTPD TSC2101_BIT(13)
+#define AC4_CISTPD TSC2101_BIT(12)
+#define AC4_BISTPD TSC2101_BIT(11)
+#define AC4_AGCHYS(ARG) (((ARG) & 0x03) << 9)
+#define AC4_MB_HED(ARG) (((ARG) & 0x03) << 7)
+#define AC4_MB_HND TSC2101_BIT(6)
+#define AC4_SCPFL TSC2101_BIT(1)
+
+/* Field masks settings for TSC2101_HANDSET_GAIN_CTRL */
+#define HNGC_ADMUT_HND TSC2101_BIT(15)
+#define HNGC_ADPGA_HND(ARG) (((ARG) & 0x7F) << 8)
+#define HNGC_AGCTG_HND(ARG) (((ARG) & 0x07) << 5)
+#define HNGC_AGCTC_HND(ARG) (((ARG) & 0x0F) << 1)
+#define HNGC_AGCEN_HND (0x01)
+
+/* Field masks settings for TSC2101_BUZZER_GAIN_CTRL */
+#define BGC_MUT_CP TSC2101_BIT(15)
+#define BGC_CPGA(ARG) (((ARG) & 0x7F) << 8)
+#define BGC_CPGF TSC2101_BIT(7)
+#define BGC_MUT_BU TSC2101_BIT(6)
+#define BGC_BPGA(ARG) (((ARG) & 0x0F) << 2)
+#define BGC_BUGF TSC2101_BIT(1)
+
+/* Field masks settings for TSC2101_AUDIO_CTRL_5 */
+#define AC5_DIFFIN TSC2101_BIT(15)
+#define AC5_DAC2SPK1(ARG) (((ARG) & 0x03) << 13)
+#define AC5_AST2SPK1 TSC2101_BIT(12)
+#define AC5_BUZ2SPK1 TSC2101_BIT(11)
+#define AC5_KCL2SPK1 TSC2101_BIT(10)
+#define AC5_CPI2SPK1 TSC2101_BIT(9)
+#define AC5_DAC2SPK2(ARG) (((ARG) & 0x03) << 7)
+#define AC5_AST2SPK2 TSC2101_BIT(6)
+#define AC5_BUZ2SPK2 TSC2101_BIT(5)
+#define AC5_KCL2SPK2 TSC2101_BIT(4)
+#define AC5_CPI2SPK2 TSC2101_BIT(3)
+#define AC5_MUTSPK1 TSC2101_BIT(2)
+#define AC5_MUTSPK2 TSC2101_BIT(1)
+#define AC5_HDSCPTC (0x01)
+
+/* Field masks settings for TSC2101_AUDIO_CTRL_6 */
+#define AC6_SPL2LSK TSC2101_BIT(15)
+#define AC6_AST2LSK TSC2101_BIT(14)
+#define AC6_BUZ2LSK TSC2101_BIT(13)
+#define AC6_KCL2LSK TSC2101_BIT(12)
+#define AC6_CPI2LSK TSC2101_BIT(11)
+#define AC6_MIC2CPO TSC2101_BIT(10)
+#define AC6_SPL2CPO TSC2101_BIT(9)
+#define AC6_SPR2CPO TSC2101_BIT(8)
+#define AC6_MUTLSPK TSC2101_BIT(7)
+#define AC6_MUTSPK2 TSC2101_BIT(6)
+#define AC6_LDSCPTC TSC2101_BIT(5)
+#define AC6_VGNDSCPTC TSC2101_BIT(4)
+#define AC6_CAPINTF TSC2101_BIT(3)
+
+/* Field masks settings for TSC2101_AUDIO_CTRL_7 */
+#define AC7_DETECT TSC2101_BIT(15)
+#define AC7_HESTYPE(ARG) (((ARG) & 0x03) << 13)
+#define AC7_HDDETFL TSC2101_BIT(12)
+#define AC7_BDETFL TSC2101_BIT(11)
+#define AC7_HDDEBNPG(ARG) (((ARG) & 0x03) << 9)
+#define AC7_BDEBNPG(ARG) (((ARG) & 0x03) << 6)
+#define AC7_DGPIO2 TSC2101_BIT(4)
+#define AC7_DGPIO1 TSC2101_BIT(3)
+#define AC7_CLKGPIO2 TSC2101_BIT(2)
+#define AC7_ADWSF(ARG) (((ARG) & 0x03))
+
+/* Field masks settings for TSC2101_GPIO_CTRL */
+#define GC_GPO2EN TSC2101_BIT(15)
+#define GC_GPO2SG TSC2101_BIT(14)
+#define GC_GPI2EN TSC2101_BIT(13)
+#define GC_GPI2SGF TSC2101_BIT(12)
+#define GC_GPO1EN TSC2101_BIT(11)
+#define GC_GPO1SG TSC2101_BIT(10)
+#define GC_GPI1EN TSC2101_BIT(9)
+#define GC_GPI1SGF TSC2101_BIT(8)
+
+/* Field masks for TSC2101_AGC_CTRL */
+#define AC_AGCNF_CELL TSC2101_BIT(14)
+#define AC_AGCNL(ARG) (((ARG) & 0x07) << 11)
+#define AC_AGCHYS_CELL(ARG) (((ARG) & 0x03) << 9)
+#define AC_CLPST_CELL TSC2101_BIT(8)
+#define AC_AGCTG_CELL(ARG) (((ARG) & 0x07) << 5)
+#define AC_AGCTC_CELL(ARG) (((ARG) & 0x0F) << 1)
+#define AC_AGCEN_CELL (0x01)
+
+/* Field masks for TSC2101_POWERDOWN_STS */
+#define PS_SPK1FL TSC2101_BIT(15)
+#define PS_SPK2FL TSC2101_BIT(14)
+#define PS_HNDFL TSC2101_BIT(13)
+#define PS_VGNDFL TSC2101_BIT(12)
+#define PS_LSPKFL TSC2101_BIT(11)
+#define PS_CELLFL TSC2101_BIT(10)
+#define PS_PSEQ TSC2101_BIT(5)
+#define PS_PSTIME TSC2101_BIT(4)
+
+/* Field masks for Register Mic AGC Control */
+#define MAC_MMPGA(ARG) (((ARG) & 0x7F) << 9)
+#define MAC_MDEBNS(ARG) (((ARG) & 0x07) << 6)
+#define MAC_MDEBSN(ARG) (((ARG) & 0x07) << 3)
+
+/* Field masks for Register Cellphone AGC Control */
+#define CAC_CMPGA(ARG) (((ARG) & 0x7F) << 9)
+#define CAC_CDEBNS(ARG) (((ARG) & 0x07) << 6)
+#define CAC_CDEBSN(ARG) (((ARG) & 0x07) << 3)
+
+#endif /* __ASM_HARDWARE_TSC2101_H */
# More hacking for modularisation.
#
# Prompt user for primary drivers.
+config SOUND_OMAP
+ tristate "OMAP Sound Driver"
+ depends on SOUND_PRIME!=n && SOUND && ARCH_OMAP
+ ---help---
+ OMAP Audio driver
+
+config SOUND_OMAP_TSC2101
+ tristate "TSC2101 Stereo Codec"
+ depends on SOUND_OMAP && ( MACH_OMAP_H2 || MACH_OMAP_H3 )
+ select OMAP_TSC2101
+ select OMAP_UWIRE if ARCH_OMAP
+ ---help---
+ Tsc2101 Audio Codec Driver for OMAP will be enabled.
+ Will also Enable the following:
+ 1. uWire Driver based on Platform
+ 2. TSC2101 Glue driver
+
+config SOUND_OMAP_AIC23
+ tristate "AIC23 Stereo Codec"
+ depends on SOUND_OMAP && ( MACH_OMAP_INNOVATOR || MACH_OMAP_OSK )
+ select OMAP_DSP if ARCH_OMAP
+ select SENSORS_TLV320AIC23 if ARCH_OMAP
+ ---help---
+ AIC23 Audio Codec Driver for OMAP will be enabled.
+ This will also enable OMAP DSP support because McBSP needed for
+ this is a DSP peripheral. Additionally, AIC23 I2C support is enabled.
+
config SOUND_BT878
tristate "BT878 audio dma"
depends on SOUND_PRIME!=n && SOUND
obj-$(CONFIG_SOUND_OSS) += sound.o
obj-$(CONFIG_SOUND_CS4232) += cs4232.o ad1848.o
+obj-$(CONFIG_SOUND_OMAP) += omap-audio-dma-intfc.o omap-audio.o
+obj-$(CONFIG_SOUND_OMAP_TSC2101)+= omap-audio-tsc2101.o
+obj-$(CONFIG_SOUND_OMAP_AIC23) += omap-audio-aic23.o
+
# Please leave it as is, cause the link order is significant !
obj-$(CONFIG_SOUND_SH_DAC_AUDIO) += sh_dac_audio.o
--- /dev/null
+/*
+ * linux/sound/oss/omap-audio-aic23.c
+ *
+ * Glue audio driver for TI TLV320AIC23 codec
+ *
+ * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
+ * Copyright (C) 2001, Steve Johnson <stevej@ridgerun.com>
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ * Copyright (C) 2005 Dirk Behme <dirk.behme@de.bosch.com>
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/errno.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+
+#include <asm/uaccess.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/mcbsp.h>
+#include <asm/arch/dsp_common.h>
+#include <asm/arch/fpga.h>
+#include <asm/arch/aic23.h>
+
+#include <asm/hardware/clock.h>
+
+#include "omap-audio.h"
+#include "omap-audio-dma-intfc.h"
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#define PROC_START_FILE "driver/aic23-audio-start"
+#define PROC_STOP_FILE "driver/aic23-audio-stop"
+#endif
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define DPRINTK(ARGS...) printk("<%s>: ",__FUNCTION__);printk(ARGS)
+#else
+#define DPRINTK( x... )
+#endif
+
+#define CODEC_NAME "AIC23"
+
+#if CONFIG_MACH_OMAP_OSK
+#define PLATFORM_NAME "OMAP OSK"
+#elif CONFIG_MACH_OMAP_INNOVATOR
+#define PLATFORM_NAME "OMAP INNOVATOR"
+#else
+#error "Unsupported plattform"
+#endif
+
+/* Define to set the AIC23 as the master w.r.t McBSP */
+#define AIC23_MASTER
+
+#define CODEC_CLOCK 12000000
+
+/*
+ * AUDIO related MACROS
+ */
+#define DEFAULT_BITPERSAMPLE 16
+#define AUDIO_RATE_DEFAULT 44100
+
+/* Select the McBSP For Audio */
+#define AUDIO_MCBSP OMAP_MCBSP1
+
+#define REC_MASK (SOUND_MASK_LINE | SOUND_MASK_MIC)
+#define DEV_MASK (REC_MASK | SOUND_MASK_VOLUME)
+
+#define SET_VOLUME 1
+#define SET_LINE 2
+
+#define DEFAULT_OUTPUT_VOLUME 93
+#define DEFAULT_INPUT_VOLUME 0 /* 0 ==> mute line in */
+
+#define OUTPUT_VOLUME_MIN LHV_MIN
+#define OUTPUT_VOLUME_MAX LHV_MAX
+#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN)
+#define OUTPUT_VOLUME_MASK OUTPUT_VOLUME_MAX
+
+#define INPUT_VOLUME_MIN LIV_MIN
+#define INPUT_VOLUME_MAX LIV_MAX
+#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
+#define INPUT_VOLUME_MASK INPUT_VOLUME_MAX
+
+#define NUMBER_SAMPLE_RATES_SUPPORTED 9
+
+static audio_stream_t output_stream = {
+ .id = "AIC23 out",
+ .dma_dev = OMAP_DMA_MCBSP1_TX,
+ .input_or_output = FMODE_WRITE
+};
+
+static audio_stream_t input_stream = {
+ .id = "AIC23 in",
+ .dma_dev = OMAP_DMA_MCBSP1_RX,
+ .input_or_output = FMODE_READ
+};
+
+static struct clk *aic23_mclk = 0;
+
+static int audio_dev_id, mixer_dev_id;
+
+static struct aic23_local_info {
+ u8 volume;
+ u16 volume_reg;
+ u8 line;
+ u8 mic;
+ u16 input_volume_reg;
+ int mod_cnt;
+} aic23_local;
+
+struct sample_rate_reg_info {
+ u32 sample_rate;
+ u8 control; /* SR3, SR2, SR1, SR0 and BOSR */
+ u8 divider; /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */
+};
+
+/* To Store the default sample rate */
+static long audio_samplerate = AUDIO_RATE_DEFAULT;
+
+/* DAC USB-mode sampling rates (MCLK = 12 MHz) */
+static const struct sample_rate_reg_info
+reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = {
+ {96000, 0x0E, 0},
+ {88200, 0x1F, 0},
+ {48000, 0x00, 0},
+ {44100, 0x11, 0},
+ {32000, 0x0C, 0},
+ {24000, 0x00, 1},
+ {16000, 0x0C, 1},
+ { 8000, 0x06, 0},
+ { 4000, 0x06, 1},
+};
+
+static struct omap_mcbsp_reg_cfg initial_config = {
+ .spcr2 = FREE | FRST | GRST | XRST | XINTM(3),
+ .spcr1 = RINTM(3) | RRST,
+ .rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) |
+ RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(0),
+ .rcr1 = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16),
+ .xcr2 = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) |
+ XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(0) | XFIG,
+ .xcr1 = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16),
+ .srgr1 = FWID(DEFAULT_BITPERSAMPLE - 1),
+ .srgr2 = GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1),
+#ifndef AIC23_MASTER
+ /* configure McBSP to be the I2S master */
+ .pcr0 = FSXM | FSRM | CLKXM | CLKRM | CLKXP | CLKRP,
+#else
+ /* configure McBSP to be the I2S slave */
+ .pcr0 = CLKXP | CLKRP,
+#endif /* AIC23_MASTER */
+};
+
+static void omap_aic23_initialize(void *dummy);
+static void omap_aic23_shutdown(void *dummy);
+static int omap_aic23_ioctl(struct inode *inode, struct file *file,
+ uint cmd, ulong arg);
+static int omap_aic23_probe(void);
+#ifdef MODULE
+static void omap_aic23_remove(void);
+#endif
+static int omap_aic23_suspend(void);
+static int omap_aic23_resume(void);
+static inline void aic23_configure(void);
+static int mixer_open(struct inode *inode, struct file *file);
+static int mixer_release(struct inode *inode, struct file *file);
+static int mixer_ioctl(struct inode *inode, struct file *file, uint cmd,
+ ulong arg);
+
+#ifdef CONFIG_PROC_FS
+static int codec_start(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data);
+static int codec_stop(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data);
+#endif
+
+
+/* File Op structure for mixer */
+static struct file_operations omap_mixer_fops = {
+ .open = mixer_open,
+ .release = mixer_release,
+ .ioctl = mixer_ioctl,
+ .owner = THIS_MODULE
+};
+
+/* To store characteristic info regarding the codec for the audio driver */
+static audio_state_t aic23_state = {
+ .output_stream = &output_stream,
+ .input_stream = &input_stream,
+/* .need_tx_for_rx = 1, //Once the Full Duplex works */
+ .need_tx_for_rx = 0,
+ .hw_init = omap_aic23_initialize,
+ .hw_shutdown = omap_aic23_shutdown,
+ .client_ioctl = omap_aic23_ioctl,
+ .hw_probe = omap_aic23_probe,
+ .hw_remove = __exit_p(omap_aic23_remove),
+ .hw_suspend = omap_aic23_suspend,
+ .hw_resume = omap_aic23_resume,
+ .sem = __MUTEX_INITIALIZER(aic23_state.sem),
+};
+
+/* This will be defined in the audio.h */
+static struct file_operations *omap_audio_fops;
+
+extern int tlv320aic23_write_value(u8 reg, u16 value);
+
+/* TLV320AIC23 is a write only device */
+static __inline__ void audio_aic23_write(u8 address, u16 data)
+{
+ tlv320aic23_write_value(address, data);
+}
+
+static int aic23_update(int flag, int val)
+{
+ u16 volume;
+
+ /* Ignore separate left/right channel for now,
+ even the codec does support it. */
+ val &= 0xff;
+
+ if (val < 0 || val > 100) {
+ printk(KERN_ERR "Trying a bad volume value(%d)!\n",val);
+ return -EPERM;
+ }
+
+ switch (flag) {
+ case SET_VOLUME:
+ // Convert 0 -> 100 volume to 0x00 (LHV_MIN) -> 0x7f (LHV_MAX)
+ // volume range
+ volume = ((val * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MIN;
+
+ // R/LHV[6:0] 1111111 (+6dB) to 0000000 (-73dB) in 1db steps,
+ // default 1111001 (0dB)
+ aic23_local.volume_reg &= ~OUTPUT_VOLUME_MASK;
+ aic23_local.volume_reg |= volume;
+ audio_aic23_write(LEFT_CHANNEL_VOLUME_ADDR, aic23_local.volume_reg);
+ audio_aic23_write(RIGHT_CHANNEL_VOLUME_ADDR, aic23_local.volume_reg);
+ break;
+
+ case SET_LINE:
+ // Convert 0 -> 100 volume to 0x0 (LIV_MIN) -> 0x1f (LIV_MAX)
+ // volume range
+ volume = ((val * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN;
+
+ // R/LIV[4:0] 11111 (+12dB) to 00000 (-34.5dB) in 1.5dB steps,
+ // default 10111 (0dB)
+ aic23_local.input_volume_reg &= ~INPUT_VOLUME_MASK;
+ aic23_local.input_volume_reg |= volume;
+ audio_aic23_write(LEFT_LINE_VOLUME_ADDR, aic23_local.input_volume_reg);
+ audio_aic23_write(RIGHT_LINE_VOLUME_ADDR, aic23_local.input_volume_reg);
+ break;
+ }
+ return 0;
+}
+
+static int mixer_open(struct inode *inode, struct file *file)
+{
+ /* Any mixer specific initialization */
+
+ return 0;
+}
+
+static int mixer_release(struct inode *inode, struct file *file)
+{
+ /* Any mixer specific Un-initialization */
+
+ return 0;
+}
+
+static int
+mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+ int val;
+ int ret = 0;
+ int nr = _IOC_NR(cmd);
+
+ /*
+ * We only accept mixer (type 'M') ioctls.
+ */
+ if (_IOC_TYPE(cmd) != 'M')
+ return -EINVAL;
+
+ DPRINTK(" 0x%08x\n", cmd);
+
+ if (cmd == SOUND_MIXER_INFO) {
+ struct mixer_info mi;
+
+ strncpy(mi.id, "AIC23", sizeof(mi.id));
+ strncpy(mi.name, "TI AIC23", sizeof(mi.name));
+ mi.modify_counter = aic23_local.mod_cnt;
+ return copy_to_user((void *)arg, &mi, sizeof(mi));
+ }
+
+ if (_IOC_DIR(cmd) & _IOC_WRITE) {
+ ret = get_user(val, (int *)arg);
+ if (ret)
+ goto out;
+
+
+ switch (nr) {
+ case SOUND_MIXER_VOLUME:
+ aic23_local.volume = val;
+ aic23_local.mod_cnt++;
+ ret = aic23_update(SET_VOLUME, val);
+ break;
+
+ case SOUND_MIXER_LINE:
+ aic23_local.line = val;
+ aic23_local.mod_cnt++;
+ ret = aic23_update(SET_LINE, val);
+ break;
+
+ case SOUND_MIXER_MIC:
+ aic23_local.mic = val;
+ aic23_local.mod_cnt++;
+ ret = aic23_update(SET_LINE, val);
+ break;
+
+ case SOUND_MIXER_RECSRC:
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+ }
+
+ if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
+ ret = 0;
+
+ switch (nr) {
+ case SOUND_MIXER_VOLUME:
+ val = aic23_local.volume;
+ break;
+ case SOUND_MIXER_LINE:
+ val = aic23_local.line;
+ break;
+ case SOUND_MIXER_MIC:
+ val = aic23_local.mic;
+ break;
+ case SOUND_MIXER_RECSRC:
+ val = REC_MASK;
+ break;
+ case SOUND_MIXER_RECMASK:
+ val = REC_MASK;
+ break;
+ case SOUND_MIXER_DEVMASK:
+ val = DEV_MASK;
+ break;
+ case SOUND_MIXER_CAPS:
+ val = 0;
+ break;
+ case SOUND_MIXER_STEREODEVS:
+ val = 0;
+ break;
+ default:
+ val = 0;
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret == 0)
+ ret = put_user(val, (int *)arg);
+ }
+out:
+ return ret;
+
+}
+
+int omap_set_samplerate(long sample_rate)
+{
+ u8 count = 0;
+ u16 data = 0;
+ /* wait for any frame to complete */
+ udelay(125);
+
+ /* Search for the right sample rate */
+ while ((reg_info[count].sample_rate != sample_rate) &&
+ (count < NUMBER_SAMPLE_RATES_SUPPORTED)) {
+ count++;
+ }
+ if (count == NUMBER_SAMPLE_RATES_SUPPORTED) {
+ printk(KERN_ERR "Invalid Sample Rate %d requested\n",
+ (int)sample_rate);
+ return -EPERM;
+ }
+
+ if (machine_is_omap_innovator()) {
+ /* set the CODEC clock input source to 12.000MHz */
+ fpga_write(fpga_read(OMAP1510_FPGA_POWER) & ~0x01,
+ OMAP1510_FPGA_POWER);
+ }
+
+ data = (reg_info[count].divider << CLKIN_SHIFT) |
+ (reg_info[count].control << BOSR_SHIFT) | USB_CLK_ON;
+
+ audio_aic23_write(SAMPLE_RATE_CONTROL_ADDR, data);
+
+ audio_samplerate = sample_rate;
+
+#ifndef AIC23_MASTER
+ {
+ int clkgdv = 0;
+ /*
+ Set Sample Rate at McBSP
+
+ Formula :
+ Codec System Clock = CODEC_CLOCK, or half if clock_divider = 1;
+ clkgdv = ((Codec System Clock / (SampleRate * BitsPerSample * 2)) - 1);
+
+ FWID = BitsPerSample - 1;
+ FPER = (BitsPerSample * 2) - 1;
+ */
+ if (reg_info[count].divider)
+ clkgdv = CODEC_CLOCK / 2;
+ else
+ clkgdv = CODEC_CLOCK;
+
+ clkgdv = (clkgdv / (sample_rate * DEFAULT_BITPERSAMPLE * 2)) - 1;
+
+ initial_config.srgr1 = (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
+
+ initial_config.srgr2 =
+ (CLKSM | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1));
+
+ omap_mcbsp_config(AUDIO_MCBSP, &initial_config);
+ }
+#endif /* AIC23_MASTER */
+
+ return 0;
+}
+
+static void omap_aic23_initialize(void *dummy)
+{
+ DPRINTK("entry\n");
+
+ /* initialize with default sample rate */
+ audio_samplerate = AUDIO_RATE_DEFAULT;
+
+ omap_mcbsp_request(AUDIO_MCBSP);
+
+ /* if configured, then stop mcbsp */
+ omap_mcbsp_stop(AUDIO_MCBSP);
+
+ omap_mcbsp_config(AUDIO_MCBSP, &initial_config);
+ omap_mcbsp_start(AUDIO_MCBSP);
+ aic23_configure();
+
+ DPRINTK("exit\n");
+}
+
+static void omap_aic23_shutdown(void *dummy)
+{
+ /*
+ Turn off codec after it is done.
+ Can't do it immediately, since it may still have
+ buffered data.
+
+ Wait 20ms (arbitrary value) and then turn it off.
+ */
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(2);
+
+ omap_mcbsp_stop(AUDIO_MCBSP);
+ omap_mcbsp_free(AUDIO_MCBSP);
+
+ audio_aic23_write(RESET_CONTROL_ADDR, 0);
+ audio_aic23_write(POWER_DOWN_CONTROL_ADDR, 0xff);
+}
+
+static inline void aic23_configure()
+{
+ /* Reset codec */
+ audio_aic23_write(RESET_CONTROL_ADDR, 0);
+
+ /* Initialize the AIC23 internal state */
+
+ /* Left/Right line input volume control */
+ aic23_local.line = DEFAULT_INPUT_VOLUME;
+ aic23_local.mic = DEFAULT_INPUT_VOLUME;
+ aic23_update(SET_LINE, DEFAULT_INPUT_VOLUME);
+
+ /* Left/Right headphone channel volume control */
+ /* Zero-cross detect on */
+ aic23_local.volume_reg = LZC_ON;
+ aic23_update(SET_VOLUME, aic23_local.volume);
+
+ /* Analog audio path control, DAC selected, delete INSEL_MIC for line in */
+ audio_aic23_write(ANALOG_AUDIO_CONTROL_ADDR, DAC_SELECTED | INSEL_MIC);
+
+ /* Digital audio path control, de-emphasis control 44.1kHz */
+ audio_aic23_write(DIGITAL_AUDIO_CONTROL_ADDR, DEEMP_44K);
+
+ /* Power control, everything is on */
+ audio_aic23_write(POWER_DOWN_CONTROL_ADDR, 0);
+
+ /* Digital audio interface, master/slave mode, I2S, 16 bit */
+#ifdef AIC23_MASTER
+ audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR, MS_MASTER | IWL_16 | FOR_DSP);
+#else
+ audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR, IWL_16 | FOR_DSP);
+#endif /* AIC23_MASTER */
+
+ /* Enable digital interface */
+ audio_aic23_write(DIGITAL_INTERFACE_ACT_ADDR, ACT_ON);
+
+ /* clock configuration */
+ omap_set_samplerate(audio_samplerate);
+}
+
+static int
+omap_aic23_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+ long val;
+ int ret = 0;
+
+ DPRINTK(" 0x%08x\n", cmd);
+
+ /*
+ * These are platform dependent ioctls which are not handled by the
+ * generic omap-audio module.
+ */
+ switch (cmd) {
+ case SNDCTL_DSP_STEREO:
+ ret = get_user(val, (int *)arg);
+ if (ret)
+ return ret;
+ /* the AIC23 is stereo only */
+ ret = (val == 0) ? -EINVAL : 1;
+ return put_user(ret, (int *)arg);
+
+ case SNDCTL_DSP_CHANNELS:
+ case SOUND_PCM_READ_CHANNELS:
+ /* the AIC23 is stereo only */
+ return put_user(2, (long *)arg);
+
+ case SNDCTL_DSP_SPEED:
+ ret = get_user(val, (long *)arg);
+ if (ret)
+ break;
+ ret = omap_set_samplerate(val);
+ if (ret)
+ break;
+ /* fall through */
+
+ case SOUND_PCM_READ_RATE:
+ return put_user(audio_samplerate, (long *)arg);
+
+ case SOUND_PCM_READ_BITS:
+ case SNDCTL_DSP_SETFMT:
+ case SNDCTL_DSP_GETFMTS:
+ /* we can do 16-bit only */
+ return put_user(AFMT_S16_LE, (long *)arg);
+
+ default:
+ /* Maybe this is meant for the mixer (As per OSS Docs) */
+ return mixer_ioctl(inode, file, cmd, arg);
+ }
+
+ return ret;
+}
+
+static int omap_aic23_probe(void)
+{
+ /* Get the fops from audio oss driver */
+ if (!(omap_audio_fops = audio_get_fops())) {
+ printk(KERN_ERR "Unable to get the file operations for AIC23 OSS driver\n");
+ audio_unregister_codec(&aic23_state);
+ return -EPERM;
+ }
+
+ aic23_local.volume = DEFAULT_OUTPUT_VOLUME;
+
+ /* register devices */
+ audio_dev_id = register_sound_dsp(omap_audio_fops, -1);
+ mixer_dev_id = register_sound_mixer(&omap_mixer_fops, -1);
+
+#ifdef CONFIG_PROC_FS
+ create_proc_read_entry(PROC_START_FILE, 0 /* default mode */ ,
+ NULL /* parent dir */ ,
+ codec_start, NULL /* client data */ );
+
+ create_proc_read_entry(PROC_STOP_FILE, 0 /* default mode */ ,
+ NULL /* parent dir */ ,
+ codec_stop, NULL /* client data */ );
+#endif
+
+ /* Announcement Time */
+ printk(KERN_INFO PLATFORM_NAME " " CODEC_NAME
+ " audio support initialized\n");
+ return 0;
+}
+
+#ifdef MODULE
+static void __exit omap_aic23_remove(void)
+{
+ /* Un-Register the codec with the audio driver */
+ unregister_sound_dsp(audio_dev_id);
+ unregister_sound_mixer(mixer_dev_id);
+
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry(PROC_START_FILE, NULL);
+ remove_proc_entry(PROC_STOP_FILE, NULL);
+#endif
+}
+#endif /* MODULE */
+
+static int omap_aic23_suspend(void)
+{
+ /* Empty for the moment */
+ return 0;
+}
+
+static int omap_aic23_resume(void)
+{
+ /* Empty for the moment */
+ return 0;
+}
+
+static int __init audio_aic23_init(void)
+{
+
+ int err = 0;
+
+ if (machine_is_omap_h2() || machine_is_omap_h3())
+ return -ENODEV;
+
+ if (machine_is_omap_osk()) {
+ /* Set MCLK to be clock input for AIC23 */
+ aic23_mclk = clk_get(0, "mclk");
+
+ if(clk_get_rate( aic23_mclk) != CODEC_CLOCK){
+ /* MCLK ist not at CODEC_CLOCK */
+ if( clk_get_usecount(aic23_mclk) > 0 ){
+ /* MCLK is already in use */
+ printk(KERN_WARNING "MCLK in use at %d Hz. We change it to %d Hz\n",
+ (uint)clk_get_rate( aic23_mclk), CODEC_CLOCK);
+ }
+ if( clk_set_rate( aic23_mclk, CODEC_CLOCK ) ){
+ printk(KERN_ERR "Cannot set MCLK for AIC23 CODEC\n");
+ return -ECANCELED;
+ }
+ }
+
+ clk_use( aic23_mclk );
+
+ DPRINTK("MCLK = %d [%d], usecount = %d\n",(uint)clk_get_rate( aic23_mclk ),
+ CODEC_CLOCK, clk_get_usecount( aic23_mclk));
+ }
+
+ if (machine_is_omap_innovator()) {
+ u8 fpga;
+ /*
+ Turn on chip select for CODEC (shared with touchscreen).
+ Don't turn it back off, in case touch screen needs it.
+ */
+ fpga = fpga_read(OMAP1510_FPGA_TOUCHSCREEN);
+ fpga |= 0x4;
+ fpga_write(fpga, OMAP1510_FPGA_TOUCHSCREEN);
+ }
+
+ /* register the codec with the audio driver */
+ if ((err = audio_register_codec(&aic23_state))) {
+ printk(KERN_ERR
+ "Failed to register AIC23 driver with Audio OSS Driver\n");
+ }
+
+ return err;
+}
+
+static void __exit audio_aic23_exit(void)
+{
+ (void)audio_unregister_codec(&aic23_state);
+ return;
+}
+
+#ifdef CONFIG_PROC_FS
+static int codec_start(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ void *foo = NULL;
+
+ omap_aic23_initialize(foo);
+
+ printk("AIC23 codec initialization done.\n");
+ return 0;
+}
+static int codec_stop(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ void *foo = NULL;
+
+ omap_aic23_shutdown(foo);
+
+ printk("AIC23 codec shutdown.\n");
+ return 0;
+}
+#endif /* CONFIG_PROC_FS */
+
+module_init(audio_aic23_init);
+module_exit(audio_aic23_exit);
+
+MODULE_AUTHOR("Dirk Behme <dirk.behme@de.bosch.com>");
+MODULE_DESCRIPTION("Glue audio driver for the TI AIC23 codec.");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * linux/sound/oss/omap-audio-dma-intfc.c
+ *
+ * Common audio DMA handling for the OMAP processors
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * History:
+ *
+ * 2004-06-07 Sriram Kannan - Created new file from omap_audio_dma_intfc.c. This file
+ * will contain only the DMA interface and buffer handling of OMAP
+ * audio driver.
+ *
+ * 2004-06-22 Sriram Kannan - removed legacy code (auto-init). Self-linking of DMA logical channel.
+ *
+ * 2004-08-12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms
+ *
+ * 2004-11-01 Nishanth Menon - 16xx platform code base modified to support multi channel chaining.
+ *
+ * 2004-12-15 Nishanth Menon - Improved 16xx platform channel logic introduced - tasklets, queue handling updated
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/pm.h>
+#include <linux/errno.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+#include <linux/sysrq.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/semaphore.h>
+
+#include <asm/arch/dma.h>
+#include "omap-audio-dma-intfc.h"
+
+#include <asm/arch/mcbsp.h>
+
+#include "omap-audio.h"
+
+#undef DEBUG
+//#define DEBUG
+#ifdef DEBUG
+#define DPRINTK(ARGS...) printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS)
+#define FN_IN printk(KERN_INFO "[%s]: start\n", __FUNCTION__)
+#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n",__FUNCTION__, n)
+#else
+
+#define DPRINTK( x... )
+#define FN_IN
+#define FN_OUT(x)
+#endif
+
+#define ERR(ARGS...) printk(KERN_ERR "{%s}-ERROR: ", __FUNCTION__);printk(ARGS);
+
+#define AUDIO_NAME "omap-audio"
+#define AUDIO_NBFRAGS_DEFAULT 8
+#define AUDIO_FRAGSIZE_DEFAULT 8192
+
+#define AUDIO_ACTIVE(state) ((state)->rd_ref || (state)->wr_ref)
+
+#define SPIN_ADDR (dma_addr_t)0
+#define SPIN_SIZE 2048
+
+/* Channel Queue Handling macros
+ * tail always points to the current free entry
+ * Head always points to the current entry being used
+ * end is either head or tail
+ */
+
+#define AUDIO_QUEUE_INIT(s) s->dma_q_head = s->dma_q_tail = s->dma_q_count = 0;
+#define AUDIO_QUEUE_FULL(s) (nr_linked_channels == s->dma_q_count)
+#define AUDIO_QUEUE_LAST(s) (1 == s->dma_q_count)
+#define AUDIO_QUEUE_EMPTY(s) (0 == s->dma_q_count)
+#define __AUDIO_INCREMENT_QUEUE(end) ((end)=((end)+1) % nr_linked_channels)
+#define AUDIO_INCREMENT_HEAD(s) __AUDIO_INCREMENT_QUEUE(s->dma_q_head); s->dma_q_count--;
+#define AUDIO_INCREMENT_TAIL(s) __AUDIO_INCREMENT_QUEUE(s->dma_q_tail); s->dma_q_count++;
+
+/* DMA buffer fragmentation sizes */
+#define MAX_DMA_SIZE 0x1000000
+#define CUT_DMA_SIZE 0x1000
+/* TODO: To be moved to more appropriate location */
+#define DCSR_ERROR 0x3
+#define DCSR_SYNC_SET (1 << 6)
+
+#define DCCR_FS (1 << 5)
+#define DCCR_PRIO (1 << 6)
+#define DCCR_EN (1 << 7)
+#define DCCR_AI (1 << 8)
+#define DCCR_REPEAT (1 << 9)
+/* if 0 the channel works in 3.1 compatible mode*/
+#define DCCR_N31COMP (1 << 10)
+#define DCCR_EP (1 << 11)
+#define DCCR_SRC_AMODE_BIT 12
+#define DCCR_SRC_AMODE_MASK (0x3<<12)
+#define DCCR_DST_AMODE_BIT 14
+#define DCCR_DST_AMODE_MASK (0x3<<14)
+#define AMODE_CONST 0x0
+#define AMODE_POST_INC 0x1
+#define AMODE_SINGLE_INDEX 0x2
+#define AMODE_DOUBLE_INDEX 0x3
+
+/**************************** DATA STRUCTURES *****************************************/
+
+static spinlock_t dma_list_lock = SPIN_LOCK_UNLOCKED;
+
+struct audio_isr_work_item {
+ int current_lch;
+ u16 ch_status;
+ audio_stream_t *s;
+};
+
+static char work_item_running = 0;
+static char nr_linked_channels = 1;
+static struct audio_isr_work_item work1, work2;
+
+
+/*********************************** MODULE SPECIFIC FUNCTIONS PROTOTYPES *************/
+
+static void audio_dsr_handler(unsigned long);
+static DECLARE_TASKLET(audio_isr_work1, audio_dsr_handler,
+ (unsigned long)&work1);
+static DECLARE_TASKLET(audio_isr_work2, audio_dsr_handler,
+ (unsigned long)&work2);
+
+static void sound_dma_irq_handler(int lch, u16 ch_status, void *data);
+static void audio_dma_callback(int lch, u16 ch_status, void *data);
+static int omap_start_sound_dma(audio_stream_t * s, dma_addr_t dma_ptr,
+ u_int size);
+static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr,
+ u_int dma_size);
+static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr,
+ u_int dma_size);
+static int audio_start_dma_chain(audio_stream_t * s);
+
+/*********************************** GLOBAL FUNCTIONS DEFINTIONS ***********************/
+
+/***************************************************************************************
+ *
+ * Buffer creation/destruction
+ *
+ **************************************************************************************/
+int audio_setup_buf(audio_stream_t * s)
+{
+ int frag;
+ int dmasize = 0;
+ char *dmabuf = NULL;
+ dma_addr_t dmaphys = 0;
+ FN_IN;
+ if (s->buffers) {
+ FN_OUT(1);
+ return -EBUSY;
+ }
+ s->buffers = kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL);
+ if (!s->buffers)
+ goto err;
+ memset(s->buffers, 0, sizeof(audio_buf_t) * s->nbfrags);
+ for (frag = 0; frag < s->nbfrags; frag++) {
+ audio_buf_t *b = &s->buffers[frag];
+ /*
+ * Let's allocate non-cached memory for DMA buffers.
+ * We try to allocate all memory at once.
+ * If this fails (a common reason is memory fragmentation),
+ * then we allocate more smaller buffers.
+ */
+ if (!dmasize) {
+ dmasize = (s->nbfrags - frag) * s->fragsize;
+ do {
+ dmabuf =
+ dma_alloc_coherent(NULL, dmasize, &dmaphys,
+ 0);
+ if (!dmabuf)
+ dmasize -= s->fragsize;
+ }
+ while (!dmabuf && dmasize);
+ if (!dmabuf)
+ goto err;
+ b->master = dmasize;
+ memzero(dmabuf, dmasize);
+ }
+ b->data = dmabuf;
+ b->dma_addr = dmaphys;
+ dmabuf += s->fragsize;
+ dmaphys += s->fragsize;
+ dmasize -= s->fragsize;
+ }
+ s->usr_head = s->dma_head = s->dma_tail = 0;
+ AUDIO_QUEUE_INIT(s);
+ s->started = 0;
+ s->bytecount = 0;
+ s->fragcount = 0;
+ sema_init(&s->sem, s->nbfrags);
+ FN_OUT(0);
+ return 0;
+ err:
+ audio_discard_buf(s);
+ FN_OUT(1);
+ return -ENOMEM;
+}
+
+void audio_discard_buf(audio_stream_t * s)
+{
+ FN_IN;
+ /* ensure DMA isn't using those buffers */
+ audio_reset(s);
+ if (s->buffers) {
+ int frag;
+ for (frag = 0; frag < s->nbfrags; frag++) {
+ if (!s->buffers[frag].master)
+ continue;
+ dma_free_coherent(NULL,
+ s->buffers[frag].master,
+ s->buffers[frag].data,
+ s->buffers[frag].dma_addr);
+ }
+ kfree(s->buffers);
+ s->buffers = NULL;
+ }
+ FN_OUT(0);
+}
+
+/***************************************************************************************
+ *
+ * DMA channel requests
+ *
+ **************************************************************************************/
+static void omap_sound_dma_link_lch(void *data)
+{
+ audio_stream_t *s = (audio_stream_t *) data;
+ int *chan = s->lch;
+ int i;
+
+ FN_IN;
+ if (s->linked) {
+ FN_OUT(1);
+ return;
+ }
+ for (i = 0; i < nr_linked_channels; i++) {
+ int cur_chan = chan[i];
+ int nex_chan =
+ ((nr_linked_channels - 1 ==
+ i) ? chan[0] : chan[i + 1]);
+ omap_dma_link_lch(cur_chan, nex_chan);
+ }
+ s->linked = 1;
+ FN_OUT(0);
+}
+
+int
+omap_request_sound_dma(int device_id, const char *device_name, void *data,
+ int **channels)
+{
+ int i, err = 0;
+ int *chan = NULL;
+ FN_IN;
+ if (unlikely((NULL == channels) || (NULL == device_name))) {
+ BUG();
+ return -EPERM;
+ }
+ /* Try allocate memory for the num channels */
+ *channels =
+ (int *)kmalloc(sizeof(int) * nr_linked_channels,
+ GFP_KERNEL);
+ chan = *channels;
+ if (NULL == chan) {
+ ERR("No Memory for channel allocs!\n");
+ FN_OUT(-ENOMEM);
+ return -ENOMEM;
+ }
+ spin_lock(&dma_list_lock);
+ for (i = 0; i < nr_linked_channels; i++) {
+ err =
+ omap_request_dma(device_id, device_name,
+ sound_dma_irq_handler, data, &chan[i]);
+ /* Handle Failure condition here */
+ if (err < 0) {
+ int j;
+ for (j = 0; j < i; j++) {
+ omap_free_dma(chan[j]);
+ }
+ spin_unlock(&dma_list_lock);
+ kfree(chan);
+ *channels = NULL;
+ ERR("Error in requesting channel %d=0x%x\n", i, err);
+ FN_OUT(err);
+ return err;
+ }
+ }
+
+ /* Chain the channels together */
+ if (!cpu_is_omap1510())
+ omap_sound_dma_link_lch(data);
+
+ spin_unlock(&dma_list_lock);
+ FN_OUT(0);
+ return 0;
+}
+
+/***************************************************************************************
+ *
+ * DMA channel requests Freeing
+ *
+ **************************************************************************************/
+static void omap_sound_dma_unlink_lch(void *data)
+{
+ audio_stream_t *s = (audio_stream_t *) data;
+ int *chan = s->lch;
+ int i;
+
+ FN_IN;
+ if (!s->linked) {
+ FN_OUT(1);
+ return;
+ }
+ for (i = 0; i < nr_linked_channels; i++) {
+ int cur_chan = chan[i];
+ int nex_chan =
+ ((nr_linked_channels - 1 ==
+ i) ? chan[0] : chan[i + 1]);
+ omap_dma_unlink_lch(cur_chan, nex_chan);
+ }
+ s->linked = 0;
+ FN_OUT(0);
+}
+
+int omap_free_sound_dma(void *data, int **channels)
+{
+ int i;
+ int *chan = NULL;
+ FN_IN;
+ if (unlikely(NULL == channels)) {
+ BUG();
+ return -EPERM;
+ }
+ if (unlikely(NULL == *channels)) {
+ BUG();
+ return -EPERM;
+ }
+ chan = (*channels);
+
+ if (!cpu_is_omap1510())
+ omap_sound_dma_unlink_lch(data);
+ for (i = 0; i < nr_linked_channels; i++) {
+ int cur_chan = chan[i];
+ omap_stop_dma(cur_chan);
+ omap_free_dma(cur_chan);
+ }
+ kfree(*channels);
+ *channels = NULL;
+ FN_OUT(0);
+ return 0;
+}
+
+/***************************************************************************************
+ *
+ * Process DMA requests - This will end up starting the transfer. Proper fragments of
+ * Transfers will be initiated.
+ *
+ **************************************************************************************/
+int audio_process_dma(audio_stream_t * s)
+{
+ int ret = 0;
+ unsigned long flags;
+ FN_IN;
+
+ /* Dont let the ISR over ride touching the in_use flag */
+ local_irq_save(flags);
+ if (1 == s->in_use) {
+ local_irq_restore(flags);
+ ERR("Called again while In Use\n");
+ return 0;
+ }
+ s->in_use = 1;
+ local_irq_restore(flags);
+
+ if (s->stopped)
+ goto spin;
+
+ if (s->dma_spinref > 0 && s->pending_frags) {
+ s->dma_spinref = 0;
+ DMA_CLEAR(s);
+ }
+ while (s->pending_frags) {
+ audio_buf_t *b = &s->buffers[s->dma_head];
+ u_int dma_size = s->fragsize - b->offset;
+ if (dma_size > MAX_DMA_SIZE)
+ dma_size = CUT_DMA_SIZE;
+ ret =
+ omap_start_sound_dma(s, b->dma_addr + b->offset, dma_size);
+ if (ret) {
+ goto process_out;
+ }
+ b->dma_ref++;
+ b->offset += dma_size;
+ if (b->offset >= s->fragsize) {
+ s->pending_frags--;
+ if (++s->dma_head >= s->nbfrags)
+ s->dma_head = 0;
+ }
+ }
+ spin:
+ if (s->spin_idle) {
+ int spincnt = 0;
+ ERR("we are spinning\n");
+ while (omap_start_sound_dma(s, SPIN_ADDR, SPIN_SIZE) == 0)
+ spincnt++;
+ /*
+ * Note: if there is still a data buffer being
+ * processed then the ref count is negative. This
+ * allows for the DMA termination to be accounted in
+ * the proper order. Of course dma_spinref can't be
+ * greater than 0 if dma_ref is not 0 since we kill
+ * the spinning above as soon as there is real data to process.
+ */
+ if (s->buffers && s->buffers[s->dma_tail].dma_ref)
+ spincnt = -spincnt;
+ s->dma_spinref += spincnt;
+ }
+
+ process_out:
+ s->in_use = 0;
+
+ FN_OUT(ret);
+ return ret;
+}
+
+/***************************************************************************************
+ *
+ * Prime Rx - Since the recieve buffer has no time limit as to when it would arrive,
+ * we need to prime it
+ *
+ **************************************************************************************/
+void audio_prime_rx(audio_state_t * state)
+{
+ audio_stream_t *is = state->input_stream;
+
+ FN_IN;
+ if (state->need_tx_for_rx) {
+ /*
+ * With some codecs like the Philips UDA1341 we must ensure
+ * there is an output stream at any time while recording since
+ * this is how the UDA1341 gets its clock from the SA1100.
+ * So while there is no playback data to send, the output DMA
+ * will spin with all zeroes. We use the cache flush special
+ * area for that.
+ */
+ state->output_stream->spin_idle = 1;
+ audio_process_dma(state->output_stream);
+ }
+ is->pending_frags = is->nbfrags;
+ sema_init(&is->sem, 0);
+ is->active = 1;
+ audio_process_dma(is);
+
+ FN_OUT(0);
+ return;
+}
+
+/***************************************************************************************
+ *
+ * set the fragment size
+ *
+ **************************************************************************************/
+int audio_set_fragments(audio_stream_t * s, int val)
+{
+ FN_IN;
+ if (s->active)
+ return -EBUSY;
+ if (s->buffers)
+ audio_discard_buf(s);
+ s->nbfrags = (val >> 16) & 0x7FFF;
+ val &= 0xFFFF;
+ if (val < 4)
+ val = 4;
+ if (val > 15)
+ val = 15;
+ s->fragsize = 1 << val;
+ if (s->nbfrags < 2)
+ s->nbfrags = 2;
+ if (s->nbfrags * s->fragsize > 128 * 1024)
+ s->nbfrags = 128 * 1024 / s->fragsize;
+ FN_OUT(0);
+ if (audio_setup_buf(s))
+ return -ENOMEM;
+ return val | (s->nbfrags << 16);
+
+}
+
+/***************************************************************************************
+ *
+ * Sync up the buffers before we shutdown, else under-run errors will happen
+ *
+ **************************************************************************************/
+int audio_sync(struct file *file)
+{
+ audio_state_t *state = file->private_data;
+ audio_stream_t *s = state->output_stream;
+ audio_buf_t *b;
+ u_int shiftval = 0;
+ unsigned long flags;
+
+ DECLARE_WAITQUEUE(wait, current);
+
+ FN_IN;
+
+ if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped) {
+ FN_OUT(1);
+ return 0;
+ }
+
+ /*
+ * Send current buffer if it contains data. Be sure to send
+ * a full sample count.
+ */
+ b = &s->buffers[s->usr_head];
+ if (b->offset &= ~3) {
+ down(&s->sem);
+ /*
+ * HACK ALERT !
+ * To avoid increased complexity in the rest of the code
+ * where full fragment sizes are assumed, we cheat a little
+ * with the start pointer here and don't forget to restore
+ * it later.
+ */
+
+ /* As this is a last frag we need only one dma channel
+ * to complete. So it's need to unlink dma channels
+ * to avoid empty dma work.
+ */
+ if (!cpu_is_omap1510())
+ omap_sound_dma_unlink_lch(s);
+
+ shiftval = s->fragsize - b->offset;
+ b->offset = shiftval;
+ b->dma_addr -= shiftval;
+ b->data -= shiftval;
+ local_irq_save(flags);
+ s->bytecount -= shiftval;
+ if (++s->usr_head >= s->nbfrags)
+ s->usr_head = 0;
+
+ s->pending_frags++;
+ audio_process_dma(s);
+ local_irq_restore(flags);
+ }
+
+ /* Let's wait for all buffers to complete */
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&s->wq, &wait);
+ while ((s->pending_frags || (atomic_read(&s->sem.count) < s->nbfrags))
+ && !signal_pending(current)) {
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&s->wq, &wait);
+
+ /* undo the pointer hack above */
+ if (shiftval) {
+ local_irq_save(flags);
+ b->dma_addr += shiftval;
+ b->data += shiftval;
+ /* ensure sane DMA code behavior if not yet processed */
+ if (b->offset != 0)
+ b->offset = s->fragsize;
+ local_irq_restore(flags);
+ }
+
+ FN_OUT(0);
+ return 0;
+}
+
+/***************************************************************************************
+ *
+ * Stop all the DMA channels of the stream
+ *
+ **************************************************************************************/
+void audio_stop_dma(audio_stream_t * s)
+{
+ int *chan = s->lch;
+ int i;
+ FN_IN;
+ if (unlikely(NULL == chan)) {
+ BUG();
+ return;
+ }
+ for (i = 0; i < nr_linked_channels; i++) {
+ int cur_chan = chan[i];
+ omap_stop_dma(cur_chan);
+ }
+ s->started = 0;
+ FN_OUT(0);
+ return;
+}
+
+/***************************************************************************************
+ *
+ * Get the dma posn
+ *
+ **************************************************************************************/
+u_int audio_get_dma_pos(audio_stream_t * s)
+{
+ audio_buf_t *b = &s->buffers[s->dma_tail];
+ u_int offset;
+
+ FN_IN;
+ if (b->dma_ref) {
+ offset = omap_get_dma_src_pos(s->lch[s->dma_q_head]) - b->dma_addr;
+ if (offset >= s->fragsize)
+ offset = s->fragsize - 4;
+ } else if (s->pending_frags) {
+ offset = b->offset;
+ } else {
+ offset = 0;
+ }
+ FN_OUT(offset);
+ return offset;
+}
+
+/***************************************************************************************
+ *
+ * Reset the audio buffers
+ *
+ **************************************************************************************/
+void audio_reset(audio_stream_t * s)
+{
+ FN_IN;
+ if (s->buffers) {
+ audio_stop_dma(s);
+ s->buffers[s->dma_head].offset = 0;
+ s->buffers[s->usr_head].offset = 0;
+ s->usr_head = s->dma_head;
+ s->pending_frags = 0;
+ sema_init(&s->sem, s->nbfrags);
+ }
+ s->active = 0;
+ s->stopped = 0;
+ s->started = 0;
+ FN_OUT(0);
+ return;
+}
+
+/***************************************************************************************
+ *
+ * Clear any pending transfers
+ *
+ **************************************************************************************/
+void omap_clear_sound_dma(audio_stream_t * s)
+{
+ FN_IN;
+ omap_clear_dma(s->lch[s->dma_q_head]);
+ FN_OUT(0);
+ return;
+}
+
+/*********************************** MODULE FUNCTIONS DEFINTIONS ***********************/
+
+#ifdef OMAP1610_MCBSP1_BASE
+#undef OMAP1610_MCBSP1_BASE
+#endif
+#define OMAP1610_MCBSP1_BASE 0xE1011000
+
+/***************************************************************************************
+ *
+ * DMA related functions
+ *
+ **************************************************************************************/
+static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr,
+ u_int dma_size)
+{
+ int dt = 0x1; /* data type 16 */
+ int cen = 32; /* Stereo */
+ int cfn = dma_size / (2 * cen);
+ FN_IN;
+ omap_set_dma_dest_params(channel, 0x05, 0x00,
+ (OMAP1610_MCBSP1_BASE + 0x806));
+ omap_set_dma_src_params(channel, 0x00, 0x01, dma_ptr);
+ omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00);
+ FN_OUT(0);
+ return 0;
+}
+
+static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr,
+ u_int dma_size)
+{
+ int dt = 0x1; /* data type 16 */
+ int cen = 16; /* mono */
+ int cfn = dma_size / (2 * cen);
+ FN_IN;
+ omap_set_dma_src_params(channel, 0x05, 0x00,
+ (OMAP1610_MCBSP1_BASE + 0x802));
+ omap_set_dma_dest_params(channel, 0x00, 0x01, dma_ptr);
+ omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00);
+ FN_OUT(0);
+ return 0;
+}
+
+static int audio_start_dma_chain(audio_stream_t * s)
+{
+ int channel = s->lch[s->dma_q_head];
+ FN_IN;
+ if (!s->started) {
+ omap_start_dma(channel);
+ s->started = 1;
+ }
+ /* else the dma itself will progress forward with out our help */
+ FN_OUT(0);
+ return 0;
+}
+
+/* Start DMA -
+ * Do the initial set of work to initialize all the channels as required.
+ * We shall then initate a transfer
+ */
+static int omap_start_sound_dma(audio_stream_t * s, dma_addr_t dma_ptr,
+ u_int dma_size)
+{
+ int ret = -EPERM;
+
+ FN_IN;
+ if (unlikely(dma_size > MAX_DMA_SIZE)) {
+ ERR("DmaSoundDma: Start: overflowed %d-%d\n", dma_size,
+ MAX_DMA_SIZE);
+ return -EOVERFLOW;
+ }
+
+ if (AUDIO_QUEUE_FULL(s)) {
+ ret = -2;
+ goto sound_out;
+ }
+
+ if (s->input_or_output == FMODE_WRITE)
+ /*playback */
+ {
+ ret =
+ audio_set_dma_params_play(s->lch[s->dma_q_tail], dma_ptr,
+ dma_size);
+ } else {
+ ret =
+ audio_set_dma_params_capture(s->lch[s->dma_q_tail], dma_ptr,
+ dma_size);
+ }
+ if (ret != 0) {
+ ret = -2; /* indicate queue full */
+ goto sound_out;
+ }
+ AUDIO_INCREMENT_TAIL(s);
+ ret = audio_start_dma_chain(s);
+ if (ret) {
+ ERR("dma start failed");
+ }
+ sound_out:
+ FN_OUT(ret);
+ return ret;
+
+}
+
+/***************************************************************************************
+ *
+ * ISR related functions
+ *
+ **************************************************************************************/
+/* The work item handler */
+static void audio_dsr_handler(unsigned long inData)
+{
+ void *data = (void *)inData;
+ struct audio_isr_work_item *work = data;
+ audio_stream_t *s = (work->s);
+ int sound_curr_lch = work->current_lch;
+ u16 ch_status = work->ch_status;
+
+ FN_IN;
+ DPRINTK("lch=%d,status=0x%x, data=%p as=%p\n", sound_curr_lch,
+ ch_status, data, s);
+ if (AUDIO_QUEUE_EMPTY(s)) {
+ ERR("Interrupt(%d) for empty queue(h=%d, T=%d)???\n",
+ sound_curr_lch, s->dma_q_head, s->dma_q_tail);
+ ERR("nbfrag=%d,pendfrags=%d,USR-H=%d, QH-%d QT-%d\n",
+ s->nbfrags, s->pending_frags, s->usr_head, s->dma_head,
+ s->dma_tail);
+ FN_OUT(-1);
+ return;
+ }
+
+ AUDIO_INCREMENT_HEAD(s); /* Empty the queue */
+
+ /* Try to fill again */
+ audio_dma_callback(sound_curr_lch, ch_status, s);
+ FN_OUT(0);
+
+}
+
+/* Macro to trace the IRQ calls - checks for multi-channel irqs */
+//#define IRQ_TRACE
+#ifdef IRQ_TRACE
+#define MAX_UP 10
+static char xyz[MAX_UP] = { 0 };
+static int h = 0;
+#endif
+
+/* ISRs have to be short and smart.. So we transfer every heavy duty stuff to the
+ * work item
+ */
+static void sound_dma_irq_handler(int sound_curr_lch, u16 ch_status, void *data)
+{
+ int dma_status = ch_status;
+ audio_stream_t *s = (audio_stream_t *) data;
+ FN_IN;
+#ifdef IRQ_TRACE
+ xyz[h++] = '0' + sound_curr_lch;
+ if (h == MAX_UP - 1) {
+ printk("%s-", xyz);
+ h = 0;
+ }
+#endif
+ DPRINTK("lch=%d,status=0x%x, dma_status=%d, data=%p\n", sound_curr_lch,
+ ch_status, dma_status, data);
+
+ if (dma_status & (DCSR_ERROR)) {
+ omap_writew(omap_readw(OMAP_DMA_CCR(sound_curr_lch)) & ~DCCR_EN,
+ OMAP_DMA_CCR(sound_curr_lch));
+ ERR("DCSR_ERROR!\n");
+ FN_OUT(-1);
+ return;
+ }
+
+ if (AUDIO_QUEUE_LAST(s))
+ audio_stop_dma(s);
+
+ /* Start the work item - we ping pong the work items */
+ if (!work_item_running) {
+ work1.current_lch = sound_curr_lch;
+ work1.ch_status = ch_status;
+ work1.s = s;
+ /* schedule tasklet 1 */
+ tasklet_schedule(&audio_isr_work1);
+ work_item_running = 1;
+ } else {
+ work2.current_lch = sound_curr_lch;
+ work2.ch_status = ch_status;
+ work2.s = s;
+ /* schedule tasklet 2 */
+ tasklet_schedule(&audio_isr_work2);
+ work_item_running = 0;
+ }
+
+ FN_OUT(0);
+ return;
+}
+
+/* The call back that handles buffer stuff */
+static void audio_dma_callback(int lch, u16 ch_status, void *data)
+{
+ audio_stream_t *s = data;
+ audio_buf_t *b = &s->buffers[s->dma_tail];
+ FN_IN;
+
+ if (s->dma_spinref > 0) {
+ s->dma_spinref--;
+ } else if (!s->buffers) {
+ printk(KERN_CRIT
+ "omap_audio: received DMA IRQ for non existent buffers!\n");
+ return;
+ } else if (b->dma_ref && --b->dma_ref == 0 && b->offset >= s->fragsize) {
+ /* This fragment is done */
+ b->offset = 0;
+ s->bytecount += s->fragsize;
+ s->fragcount++;
+ s->dma_spinref = -s->dma_spinref;
+
+ if (++s->dma_tail >= s->nbfrags)
+ s->dma_tail = 0;
+
+ if (!s->mapped)
+ up(&s->sem);
+ else
+ s->pending_frags++;
+
+ wake_up(&s->wq);
+ }
+
+ audio_process_dma(s);
+
+ FN_OUT(0);
+ return;
+}
+
+/*********************************************************************************
+ *
+ * audio_get_dma_callback(): return the dma interface call back function
+ *
+ *********************************************************************************/
+dma_callback_t audio_get_dma_callback(void)
+{
+ FN_IN;
+ FN_OUT(0);
+ return audio_dma_callback;
+}
+
+static int __init audio_dma_init(void)
+{
+ if (!cpu_is_omap1510())
+ nr_linked_channels = 2;
+
+ return 0;
+}
+
+static void __exit audio_dma_exit(void)
+{
+ /* Nothing */
+}
+
+module_init(audio_dma_init);
+module_exit(audio_dma_exit);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("Common DMA handling for Audio driver on OMAP processors");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(omap_clear_sound_dma);
+EXPORT_SYMBOL(omap_request_sound_dma);
+EXPORT_SYMBOL(omap_free_sound_dma);
+
+EXPORT_SYMBOL(audio_get_dma_callback);
+EXPORT_SYMBOL(audio_setup_buf);
+EXPORT_SYMBOL(audio_process_dma);
+EXPORT_SYMBOL(audio_prime_rx);
+EXPORT_SYMBOL(audio_set_fragments);
+EXPORT_SYMBOL(audio_sync);
+EXPORT_SYMBOL(audio_stop_dma);
+EXPORT_SYMBOL(audio_get_dma_pos);
+EXPORT_SYMBOL(audio_reset);
+EXPORT_SYMBOL(audio_discard_buf);
--- /dev/null
+/*
+ * linux/sound/oss/omap-audio-dma-intfc.h
+ *
+ * Common audio DMA handling for the OMAP processors
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * History:
+ *
+ * 2004/08/12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms
+ */
+
+#ifndef __OMAP_AUDIO_DMA_INTFC_H
+#define __OMAP_AUDIO_DMA_INTFC_H
+
+/************************** INCLUDES *************************************/
+
+/* Requires omap-audio.h */
+#include "omap-audio.h"
+
+/************************** GLOBAL MACROS *************************************/
+
+/* Provide the Macro interfaces common across platforms */
+#define DMA_REQUEST(e,s, cb) {e=omap_request_sound_dma(s->dma_dev, s->id, s, &s->lch);}
+#define DMA_FREE(s) omap_free_sound_dma(s, &s->lch)
+#define DMA_CLEAR(s) omap_clear_sound_dma(s)
+
+/************************** GLOBAL DATA STRUCTURES *********************************/
+
+typedef void (*dma_callback_t) (int lch, u16 ch_status, void *data);
+
+/************************** GLOBAL FUNCTIONS ***************************************/
+
+dma_callback_t audio_get_dma_callback(void);
+int audio_setup_buf(audio_stream_t * s);
+int audio_process_dma(audio_stream_t * s);
+void audio_prime_rx(audio_state_t * state);
+int audio_set_fragments(audio_stream_t * s, int val);
+int audio_sync(struct file *file);
+void audio_stop_dma(audio_stream_t * s);
+u_int audio_get_dma_pos(audio_stream_t * s);
+void audio_reset(audio_stream_t * s);
+void audio_discard_buf(audio_stream_t * s);
+
+/**************** ARCH SPECIFIC FUNCIONS *******************************************/
+
+void omap_clear_sound_dma(audio_stream_t * s);
+
+int omap_request_sound_dma(int device_id, const char *device_name, void *data,
+ int **channels);
+int omap_free_sound_dma(void *data, int **channels);
+
+#endif /* #ifndef __OMAP_AUDIO_DMA_INTFC_H */
--- /dev/null
+/*
+ * linux/sound/oss/omap-audio-tsc2101.c
+ *
+ * Glue driver for TSC2101 for OMAP processors
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * History:
+ * -------
+ * 2004-08-12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms.
+ * 2004-09-14 Sriram Kannan - Added /proc support for asynchronous starting/stopping the codec
+ * (without affecting the normal driver flow).
+ * 2004-11-04 Nishanth Menon - Support for power management
+ * 2004-11-07 Nishanth Menon - Support for Common TSC access b/w Touchscreen and audio drivers
+ */
+
+/***************************** INCLUDES ************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/errno.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <asm/hardware.h>
+#include <asm/arch/dma.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+
+#include <asm/arch/mux.h>
+#include <asm/arch/io.h>
+#include <asm/mach-types.h>
+
+#include "omap-audio.h"
+#include "omap-audio-dma-intfc.h"
+#include <asm/arch/mcbsp.h>
+#if CONFIG_ARCH_OMAP16XX
+#include <../drivers/ssi/omap-uwire.h>
+#include <asm/arch/dsp_common.h>
+#else
+#error "Unsupported configuration"
+#endif
+
+#include <asm/hardware/tsc2101.h>
+#include <../drivers/ssi/omap-tsc2101.h>
+
+/***************************** MACROS ************************************/
+
+#define PROC_SUPPORT
+
+#ifdef PROC_SUPPORT
+#include <linux/proc_fs.h>
+#define PROC_START_FILE "driver/tsc2101-audio-start"
+#define PROC_STOP_FILE "driver/tsc2101-audio-stop"
+#endif
+
+#define CODEC_NAME "TSC2101"
+
+#if CONFIG_ARCH_OMAP16XX
+#define PLATFORM_NAME "OMAP16XX"
+#endif
+
+#if CONFIG_ARCH_OMAP16XX
+#define OMAP_DSP_BASE 0xE0000000
+#endif
+
+/* Define to set the tsc as the master w.r.t McBSP */
+#define TSC_MASTER
+
+/*
+ * AUDIO related MACROS
+ */
+#define DEFAULT_BITPERSAMPLE 16
+#define AUDIO_RATE_DEFAULT 44100
+#define PAGE2_AUDIO_CODEC_REGISTERS (2)
+#define LEAVE_CS 0x80
+
+/* Select the McBSP For Audio */
+#if CONFIG_ARCH_OMAP16XX
+#define AUDIO_MCBSP OMAP_MCBSP1
+#else
+#error "UnSupported Configuration"
+#endif
+
+#define REC_MASK (SOUND_MASK_LINE | SOUND_MASK_MIC)
+#define DEV_MASK (REC_MASK | SOUND_MASK_VOLUME)
+
+#define SET_VOLUME 1
+#define SET_LINE 2
+#define SET_MIC 3
+#define SET_RECSRC 4
+
+#define DEFAULT_VOLUME 93
+#define DEFAULT_INPUT_VOLUME 20 /* An minimal volume */
+
+/* Tsc Audio Specific */
+#define NUMBER_SAMPLE_RATES_SUPPORTED 16
+#define OUTPUT_VOLUME_MIN 0x7F
+#define OUTPUT_VOLUME_MAX 0x32
+#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MIN - OUTPUT_VOLUME_MAX)
+#define OUTPUT_VOLUME_MASK OUTPUT_VOLUME_MIN
+#define DEFAULT_VOLUME_LEVEL OUTPUT_VOLUME_MAX
+
+/* use input vol of 75 for 0dB gain */
+#define INPUT_VOLUME_MIN 0x0
+#define INPUT_VOLUME_MAX 0x7D
+#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
+#define INPUT_VOLUME_MASK INPUT_VOLUME_MAX
+
+/*********** Debug Macros ********/
+/* To Generate a rather shrill tone -test the entire path */
+//#define TONE_GEN
+/* To Generate a tone for each keyclick - test the tsc,spi paths*/
+//#define TEST_KEYCLICK
+/* To dump the tsc registers for debug */
+//#define TSC_DUMP_REGISTERS
+
+#ifdef DPRINTK
+#undef DPRINTK
+#endif
+#undef DEBUG
+
+//#define DEBUG
+#ifdef DEBUG
+#define DPRINTK(ARGS...) printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS)
+#define FN_IN printk(KERN_INFO "[%s]: start\n", __FUNCTION__)
+#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n",__FUNCTION__, n)
+#else
+#define DPRINTK( x... )
+#define FN_IN
+#define FN_OUT(n)
+#endif
+
+/***************************** Data Structures **********************************/
+
+static audio_stream_t output_stream = {
+ .id = "TSC2101 out",
+ .dma_dev = OMAP_DMA_MCBSP1_TX,
+ .input_or_output = FMODE_WRITE
+};
+
+static audio_stream_t input_stream = {
+ .id = "TSC2101 in",
+ .dma_dev = OMAP_DMA_MCBSP1_RX,
+ .input_or_output = FMODE_READ
+};
+
+static int audio_dev_id, mixer_dev_id;
+
+typedef struct {
+ u8 volume;
+ u8 line;
+ u8 mic;
+ int recsrc;
+ int mod_cnt;
+} tsc2101_local_info;
+
+static tsc2101_local_info tsc2101_local = {
+ volume: DEFAULT_VOLUME,
+ line: DEFAULT_INPUT_VOLUME,
+ mic: DEFAULT_INPUT_VOLUME,
+ recsrc: SOUND_MASK_LINE,
+ mod_cnt: 0
+};
+
+struct sample_rate_reg_info {
+ u16 sample_rate;
+ u8 divisor;
+ u8 fs_44kHz; /* if 0 48 khz, if 1 44.1 khz fsref */
+};
+
+/* To Store the default sample rate */
+static long audio_samplerate = AUDIO_RATE_DEFAULT;
+
+static const struct sample_rate_reg_info
+ reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = {
+ /* Div 1 */
+ {48000, 0, 0},
+ {44100, 0, 1},
+ /* Div 1.5 */
+ {32000, 1, 0},
+ {29400, 1, 1},
+ /* Div 2 */
+ {24000, 2, 0},
+ {22050, 2, 1},
+ /* Div 3 */
+ {16000, 3, 0},
+ {14700, 3, 1},
+ /* Div 4 */
+ {12000, 4, 0},
+ {11025, 4, 1},
+ /* Div 5 */
+ {9600, 5, 0},
+ {8820, 5, 1},
+ /* Div 5.5 */
+ {8727, 6, 0},
+ {8018, 6, 1},
+ /* Div 6 */
+ {8000, 7, 0},
+ {7350, 7, 1},
+};
+
+static struct omap_mcbsp_reg_cfg initial_config = {
+ .spcr2 = FREE | FRST | GRST | XRST | XINTM(3),
+ .spcr1 = RINTM(3) | RRST,
+ .rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) |
+ RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(1),
+ .rcr1 = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16),
+ .xcr2 = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) |
+ XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(1) | XFIG,
+ .xcr1 = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16),
+ .srgr1 = FWID(15),
+ .srgr2 = GSYNC | CLKSP | FSGM | FPER(31),
+
+ /* platform specific initialization */
+#if CONFIG_MACH_OMAP_H2
+ .pcr0 = CLKXM | CLKRM | FSXP | FSRP | CLKXP | CLKRP,
+#elif CONFIG_MACH_OMAP_H3
+
+#ifndef TSC_MASTER
+ .pcr0 = FSXM | FSRM | CLKXM | CLKRM | CLKXP | CLKRP,
+#else
+ .pcr0 = CLKRM | SCLKME | FSXP | FSRP | CLKXP | CLKRP,
+#endif /* tsc Master defs */
+
+#endif /* platform specific inits */
+};
+
+/***************************** MODULES SPECIFIC FUNCTION PROTOTYPES ********************/
+
+static void omap_tsc2101_initialize(void *dummy);
+
+static void omap_tsc2101_shutdown(void *dummy);
+
+static int omap_tsc2101_ioctl(struct inode *inode, struct file *file,
+ uint cmd, ulong arg);
+
+static int omap_tsc2101_probe(void);
+
+static void omap_tsc2101_remove(void);
+
+static int omap_tsc2101_suspend(void);
+
+static int omap_tsc2101_resume(void);
+
+static void tsc2101_configure(void);
+
+static int mixer_open(struct inode *inode, struct file *file);
+
+static int mixer_release(struct inode *inode, struct file *file);
+
+static int mixer_ioctl(struct inode *inode, struct file *file, uint cmd,
+ ulong arg);
+
+#ifdef TEST_KEYCLICK
+void tsc2101_testkeyclick(void);
+#endif
+
+#ifdef TONE_GEN
+void toneGen(void);
+#endif
+
+#ifdef TSC_DUMP_REGISTERS
+static void tsc2101_dumpRegisters(void);
+#endif
+
+#ifdef PROC_SUPPORT
+static int codec_start(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data);
+
+static int codec_stop(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data);
+
+static void tsc2101_start(void);
+#endif
+
+/******************** DATA STRUCTURES USING FUNCTION POINTERS **************************/
+
+/* File Op structure for mixer */
+static struct file_operations omap_mixer_fops = {
+ .open = mixer_open,
+ .release = mixer_release,
+ .ioctl = mixer_ioctl,
+ .owner = THIS_MODULE
+};
+
+/* To store characteristic info regarding the codec for the audio driver */
+static audio_state_t tsc2101_state = {
+ .output_stream = &output_stream,
+ .input_stream = &input_stream,
+/* .need_tx_for_rx = 1, //Once the Full Duplex works */
+ .need_tx_for_rx = 0,
+ .hw_init = omap_tsc2101_initialize,
+ .hw_shutdown = omap_tsc2101_shutdown,
+ .client_ioctl = omap_tsc2101_ioctl,
+ .hw_probe = omap_tsc2101_probe,
+ .hw_remove = omap_tsc2101_remove,
+ .hw_suspend = omap_tsc2101_suspend,
+ .hw_resume = omap_tsc2101_resume,
+ .sem = __MUTEX_INITIALIZER(tsc2101_state.sem),
+};
+
+/* This will be defined in the Audio.h */
+static struct file_operations *omap_audio_fops;
+
+/***************************** MODULES SPECIFIC FUNCTIONs *******************************/
+
+/*********************************************************************************
+ *
+ * Simplified write for tsc Audio
+ *
+ *********************************************************************************/
+static __inline__ void audio_tsc2101_write(u8 address, u16 data)
+{
+ omap_tsc2101_write(PAGE2_AUDIO_CODEC_REGISTERS, address, data);
+}
+
+/*********************************************************************************
+ *
+ * Simplified read for tsc Audio
+ *
+ *********************************************************************************/
+static __inline__ u16 audio_tsc2101_read(u8 address)
+{
+ return (omap_tsc2101_read(PAGE2_AUDIO_CODEC_REGISTERS, address));
+}
+
+/*********************************************************************************
+ *
+ * tsc2101_update()
+ * Volume Adj etc
+ *
+ ********************************************************************************/
+static int tsc2101_update(int flag, int val)
+{
+ u16 volume;
+ u16 data;
+
+ FN_IN;
+ switch (flag) {
+ case SET_VOLUME:
+ if (val < 0 || val > 100) {
+ printk(KERN_ERR "Trying a bad volume value(%d)!\n", val);
+ return -EPERM;
+ }
+ /* Convert 0 -> 100 volume to 0x7F(min) -> y(max) volume range */
+ volume =
+ ((val * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MAX;
+ /* invert the value for getting the proper range 0 min and 100 max */
+ volume = OUTPUT_VOLUME_MIN - volume;
+ data = audio_tsc2101_read(TSC2101_DAC_GAIN_CTRL);
+ data &=
+ ~(DGC_DALVL(OUTPUT_VOLUME_MIN) |
+ DGC_DARVL(OUTPUT_VOLUME_MIN));
+ data |= DGC_DALVL(volume) | DGC_DARVL(volume);
+ audio_tsc2101_write(TSC2101_DAC_GAIN_CTRL, data);
+ data = audio_tsc2101_read(TSC2101_DAC_GAIN_CTRL);
+
+ break;
+
+ case SET_LINE:
+ if (val < 0 || val > 100) {
+ printk(KERN_ERR "Trying a bad volume value(%d)!\n", val);
+ return -EPERM;
+ }
+ /* Convert 0 -> 100 volume to 0x0(min) -> 0x7D(max) volume range */
+ /* NOTE: 0 is minimum volume and not mute */
+ volume = ((val * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN;
+ /* Handset Input not muted, AGC for Handset In off */
+ audio_tsc2101_write(TSC2101_HEADSET_GAIN_CTRL,
+ HGC_ADPGA_HED(volume));
+ break;
+
+ case SET_MIC:
+ if (val < 0 || val > 100) {
+ printk(KERN_ERR "Trying a bad volume value(%d)!\n", val);
+ return -EPERM;
+ }
+ /* Convert 0 -> 100 volume to 0x0(min) -> 0x7D(max) volume range */
+ /* NOTE: 0 is minimum volume and not mute */
+ volume = ((val * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN;
+ /* Handset Input not muted, AGC for Handset In off */
+ audio_tsc2101_write(TSC2101_HANDSET_GAIN_CTRL,
+ HNGC_ADPGA_HND(volume));
+ break;
+
+ case SET_RECSRC:
+ /*
+ * If more than one recording device selected,
+ * disable the device that is currently in use.
+ */
+ if (hweight32(val) > 1)
+ val &= ~tsc2101_local.recsrc;
+
+ data = audio_tsc2101_read(TSC2101_MIXER_PGA_CTRL);
+ data &= ~MPC_MICSEL(7); /* clear all MICSEL bits */
+
+ if (val == SOUND_MASK_MIC) {
+ data |= MPC_MICSEL(1);
+ audio_tsc2101_write(TSC2101_MIXER_PGA_CTRL, data);
+ }
+ else if (val == SOUND_MASK_LINE) {
+ data |= MPC_MICSEL(0);
+ audio_tsc2101_write(TSC2101_MIXER_PGA_CTRL, data);
+ }
+ else {
+ printk(KERN_WARNING "omap1610-tsc2101: Wrong RECSRC"
+ " value specified\n");
+ return -EINVAL;
+ }
+ tsc2101_local.recsrc = val;
+ break;
+ default:
+ printk(KERN_WARNING "omap1610-tsc2101: Wrong tsc2101_update "
+ "flag specified\n");
+ break;
+ }
+
+ FN_OUT(0);
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * mixer_open()
+ *
+ ********************************************************************************/
+static int mixer_open(struct inode *inode, struct file *file)
+{
+ /* Any mixer specific initialization */
+
+ /* Initalize the tsc2101 */
+ omap_tsc2101_enable();
+
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * mixer_release()
+ *
+ ********************************************************************************/
+static int mixer_release(struct inode *inode, struct file *file)
+{
+ /* Any mixer specific Un-initialization */
+ omap_tsc2101_disable();
+
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * mixer_ioctl()
+ *
+ ********************************************************************************/
+static int
+mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+ int val;
+ int gain;
+ int ret = 0;
+ int nr = _IOC_NR(cmd);
+
+ /*
+ * We only accept mixer (type 'M') ioctls.
+ */
+ FN_IN;
+ if (_IOC_TYPE(cmd) != 'M')
+ return -EINVAL;
+
+ DPRINTK(" 0x%08x\n", cmd);
+
+ if (cmd == SOUND_MIXER_INFO) {
+ struct mixer_info mi;
+
+ strncpy(mi.id, "TSC2101", sizeof(mi.id));
+ strncpy(mi.name, "TI TSC2101", sizeof(mi.name));
+ mi.modify_counter = tsc2101_local.mod_cnt;
+ FN_OUT(1);
+ return copy_to_user((void __user *)arg, &mi, sizeof(mi));
+ }
+
+ if (_IOC_DIR(cmd) & _IOC_WRITE) {
+ ret = get_user(val, (int __user *)arg);
+ if (ret)
+ goto out;
+
+ /* Ignore separate left/right channel for now,
+ * even the codec does support it.
+ */
+ gain = val & 255;
+
+ switch (nr) {
+ case SOUND_MIXER_VOLUME:
+ tsc2101_local.volume = val;
+ tsc2101_local.mod_cnt++;
+ ret = tsc2101_update(SET_VOLUME, gain);
+ break;
+
+ case SOUND_MIXER_LINE:
+ tsc2101_local.line = val;
+ tsc2101_local.mod_cnt++;
+ ret = tsc2101_update(SET_LINE, gain);
+ break;
+
+ case SOUND_MIXER_MIC:
+ tsc2101_local.mic = val;
+ tsc2101_local.mod_cnt++;
+ ret = tsc2101_update(SET_MIC, gain);
+ break;
+
+ case SOUND_MIXER_RECSRC:
+ if ((val & SOUND_MASK_LINE) ||
+ (val & SOUND_MASK_MIC)) {
+ if (tsc2101_local.recsrc != val) {
+ tsc2101_local.mod_cnt++;
+ tsc2101_update(SET_RECSRC, val);
+ }
+ }
+ else {
+ ret = -EINVAL;
+ }
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+ }
+
+ if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
+ ret = 0;
+
+ switch (nr) {
+ case SOUND_MIXER_VOLUME:
+ val = tsc2101_local.volume;
+ val = (tsc2101_local.volume << 8) |
+ tsc2101_local.volume;
+ break;
+ case SOUND_MIXER_LINE:
+ val = (tsc2101_local.line << 8) |
+ tsc2101_local.line;
+ break;
+ case SOUND_MIXER_MIC:
+ val = (tsc2101_local.mic << 8) |
+ tsc2101_local.mic;
+ break;
+ case SOUND_MIXER_RECSRC:
+ val = tsc2101_local.recsrc;
+ break;
+ case SOUND_MIXER_RECMASK:
+ val = REC_MASK;
+ break;
+ case SOUND_MIXER_DEVMASK:
+ val = DEV_MASK;
+ break;
+ case SOUND_MIXER_CAPS:
+ val = 0;
+ break;
+ case SOUND_MIXER_STEREODEVS:
+ val = SOUND_MASK_VOLUME;
+ break;
+ default:
+ val = 0;
+ printk(KERN_WARNING "omap1610-tsc2101: unknown mixer "
+ "read ioctl flag specified\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret == 0)
+ ret = put_user(val, (int __user *)arg);
+ }
+ out:
+ FN_OUT(0);
+ return ret;
+
+}
+
+/*********************************************************************************
+ *
+ * omap_set_samplerate()
+ *
+ ********************************************************************************/
+static int omap_set_samplerate(long sample_rate)
+{
+ u8 count = 0;
+ u16 data = 0;
+ int clkgdv = 0;
+ /* wait for any frame to complete */
+ udelay(125);
+
+ /* Search for the right sample rate */
+ while ((reg_info[count].sample_rate != sample_rate) &&
+ (count < NUMBER_SAMPLE_RATES_SUPPORTED)) {
+ count++;
+ }
+ if (count == NUMBER_SAMPLE_RATES_SUPPORTED) {
+ printk(KERN_ERR "Invalid Sample Rate %d requested\n",
+ (int)sample_rate);
+ return -EPERM;
+ }
+
+ /* Set AC1 */
+ data = audio_tsc2101_read(TSC2101_AUDIO_CTRL_1);
+ /*Clear prev settings */
+ data &= ~(AC1_DACFS(0x07) | AC1_ADCFS(0x07));
+ data |=
+ AC1_DACFS(reg_info[count].divisor) | AC1_ADCFS(reg_info[count].
+ divisor);
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_1, data);
+
+ /* Set the AC3 */
+ data = audio_tsc2101_read(TSC2101_AUDIO_CTRL_3);
+ /*Clear prev settings */
+ data &= ~(AC3_REFFS | AC3_SLVMS);
+ data |= (reg_info[count].fs_44kHz) ? AC3_REFFS : 0;
+#ifdef TSC_MASTER
+ data |= AC3_SLVMS;
+#endif /* #ifdef TSC_MASTER */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_3, data);
+
+ /* program the PLLs */
+ if (reg_info[count].fs_44kHz) {
+ /* 44.1 khz - 12 MHz Mclk */
+ audio_tsc2101_write(TSC2101_PLL_PROG_1, PLL1_PLLSEL | PLL1_PVAL(1) | PLL1_I_VAL(7)); /* PVAL 1; I_VAL 7 */
+ audio_tsc2101_write(TSC2101_PLL_PROG_2, PLL2_D_VAL(0x1490)); /* D_VAL 5264 */
+ } else {
+ /* 48 khz - 12 Mhz Mclk */
+ audio_tsc2101_write(TSC2101_PLL_PROG_1, PLL1_PLLSEL | PLL1_PVAL(1) | PLL1_I_VAL(8)); /* PVAL 1; I_VAL 8 */
+ audio_tsc2101_write(TSC2101_PLL_PROG_2, PLL2_D_VAL(0x780)); /* D_VAL 1920 */
+ }
+
+ audio_samplerate = sample_rate;
+
+ /* Set the sample rate */
+#ifndef TSC_MASTER
+ clkgdv =
+ DEFAULT_MCBSP_CLOCK / (sample_rate *
+ (DEFAULT_BITPERSAMPLE * 2 - 1));
+ if (clkgdv)
+ initial_config.srgr1 =
+ (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
+ else
+ return (1);
+
+ /* Stereo Mode */
+ initial_config.srgr2 =
+ (CLKSM | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1));
+#else
+ initial_config.srgr1 =
+ (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
+ initial_config.srgr2 =
+ ((GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1)));
+
+#endif /* end of #ifdef TSC_MASTER */
+ omap_mcbsp_config(AUDIO_MCBSP, &initial_config);
+
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * omap_tsc2101_initialize() [hw_init() ]
+ *
+ ********************************************************************************/
+static void omap_tsc2101_initialize(void *dummy)
+{
+
+ DPRINTK("omap_tsc2101_initialize entry\n");
+
+ /* initialize with default sample rate */
+ audio_samplerate = AUDIO_RATE_DEFAULT;
+
+ omap_mcbsp_request(AUDIO_MCBSP);
+
+ /* if configured, then stop mcbsp */
+ omap_mcbsp_stop(AUDIO_MCBSP);
+
+ omap_tsc2101_enable();
+
+ omap_mcbsp_config(AUDIO_MCBSP, &initial_config);
+ omap_mcbsp_start(AUDIO_MCBSP);
+ tsc2101_configure();
+
+#ifdef TEST_KEYCLICK
+ tsc2101_testkeyclick();
+#endif
+
+#ifdef TONE_GEN
+ toneGen();
+#endif
+
+ DPRINTK("omap_tsc2101_initialize exit\n");
+}
+
+/*********************************************************************************
+ *
+ * omap_tsc2101_shutdown() [hw_shutdown() ]
+ *
+ ********************************************************************************/
+static void omap_tsc2101_shutdown(void *dummy)
+{
+ /*
+ Turn off codec after it is done.
+ Can't do it immediately, since it may still have
+ buffered data.
+
+ Wait 20ms (arbitrary value) and then turn it off.
+ */
+
+ FN_IN;
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(2);
+
+ omap_mcbsp_stop(AUDIO_MCBSP);
+ omap_mcbsp_free(AUDIO_MCBSP);
+
+ audio_tsc2101_write(TSC2101_CODEC_POWER_CTRL,
+ ~(CPC_SP1PWDN | CPC_SP2PWDN | CPC_BASSBC));
+
+ omap_tsc2101_disable();
+
+ FN_OUT(0);
+}
+
+/*********************************************************************************
+ *
+ * tsc2101_configure
+ *
+ ********************************************************************************/
+static void tsc2101_configure(void)
+{
+ FN_IN;
+
+ audio_tsc2101_write(TSC2101_CODEC_POWER_CTRL, 0x0000);
+
+ /*Mute Analog Sidetone */
+ /*Select MIC_INHED input for headset */
+ /*Cell Phone In not connected */
+ audio_tsc2101_write(TSC2101_MIXER_PGA_CTRL,
+ MPC_ASTMU | MPC_ASTG(0x40) | MPC_MICADC);
+
+ /* Set record source */
+ tsc2101_update(SET_RECSRC, tsc2101_local.recsrc);
+
+ /* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled */
+ /* 1dB AGC hysteresis */
+ /* MICes bias 2V */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_4, AC4_MB_HED(0));
+
+ /* Set codec output volume */
+ audio_tsc2101_write(TSC2101_DAC_GAIN_CTRL, 0x0000);
+
+ /* DAC left and right routed to SPK2 */
+ /* SPK1/2 unmuted */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_5,
+ AC5_DAC2SPK1(3) | AC5_AST2SPK1 | AC5_KCL2SPK1 |
+ AC5_DAC2SPK2(3) | AC5_AST2SPK2 | AC5_KCL2SPK2 |
+ AC5_HDSCPTC);
+
+ /* OUT8P/N muted, CPOUT muted */
+
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_6,
+ AC6_MUTLSPK | AC6_MUTSPK2 | AC6_LDSCPTC |
+ AC6_VGNDSCPTC);
+
+ /* Headset/Hook switch detect disabled */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_7, 0x0000);
+
+ /* Left line input volume control */
+ tsc2101_update(SET_LINE, tsc2101_local.line);
+
+ /* mic input volume control */
+ tsc2101_update(SET_MIC, tsc2101_local.mic);
+
+ /* Left/Right headphone channel volume control */
+ /* Zero-cross detect on */
+ tsc2101_update(SET_VOLUME, tsc2101_local.volume);
+
+ /* clock configuration */
+ omap_set_samplerate(audio_samplerate);
+
+#ifdef TSC_DUMP_REGISTERS
+ tsc2101_dumpRegisters();
+#endif
+
+ FN_OUT(0);
+}
+
+#ifdef PROC_SUPPORT
+static void tsc2101_start(void)
+{
+ FN_IN;
+
+ audio_tsc2101_write(TSC2101_CODEC_POWER_CTRL, 0x0000);
+
+ /*Mute Analog Sidetone */
+ /*Select MIC_INHED input for headset */
+ /*Cell Phone In not connected */
+ audio_tsc2101_write(TSC2101_MIXER_PGA_CTRL,
+ MPC_ASTMU | MPC_ASTG(0x40) | MPC_MICADC);
+
+ /* Set record source */
+ tsc2101_update(SET_RECSRC, tsc2101_local.recsrc);
+
+ /* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled */
+ /* 1dB AGC hysteresis */
+ /* MICes bias 2V */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_4, AC4_MB_HED(0));
+
+ /* Set codec output volume */
+ audio_tsc2101_write(TSC2101_DAC_GAIN_CTRL, 0x0000);
+
+ /* DAC left and right routed to SPK2 */
+ /* SPK1/2 unmuted */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_5,
+ AC5_DAC2SPK1(3) | AC5_AST2SPK1 | AC5_KCL2SPK1 |
+ AC5_DAC2SPK2(3) | AC5_AST2SPK2 | AC5_KCL2SPK2 |
+ AC5_HDSCPTC);
+
+ /* OUT8P/N muted, CPOUT muted */
+
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_6,
+ AC6_MUTLSPK | AC6_MUTSPK2 | AC6_LDSCPTC |
+ AC6_VGNDSCPTC);
+
+ /* Headset/Hook switch detect disabled */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_7, 0x0000);
+
+ /* Left line input volume control */
+ tsc2101_update(SET_LINE, tsc2101_local.line);
+
+ /* mic input volume control */
+ tsc2101_update(SET_MIC, tsc2101_local.mic);
+
+ /* Left/Right headphone channel volume control */
+ /* Zero-cross detect on */
+ tsc2101_update(SET_VOLUME, tsc2101_local.volume);
+
+ FN_OUT(0);
+
+}
+#endif
+
+/******************************************************************************************
+ *
+ * All generic ioctl's are handled by audio_ioctl() [File: omap-audio.c]. This
+ * routine handles some platform specific ioctl's
+ *
+ ******************************************************************************************/
+static int
+omap_tsc2101_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+ long val;
+ int ret = 0;
+
+ DPRINTK(" 0x%08x\n", cmd);
+
+ /*
+ * These are platform dependent ioctls which are not handled by the
+ * generic omap-audio module.
+ */
+ switch (cmd) {
+ case SNDCTL_DSP_STEREO:
+ ret = get_user(val, (int __user *)arg);
+ if (ret)
+ return ret;
+ /* the AIC23 is stereo only */
+ ret = (val == 0) ? -EINVAL : 1;
+ FN_OUT(1);
+ return put_user(ret, (int __user *)arg);
+
+ case SNDCTL_DSP_CHANNELS:
+ case SOUND_PCM_READ_CHANNELS:
+ /* the AIC23 is stereo only */
+ FN_OUT(2);
+ return put_user(2, (long __user *)arg);
+
+ case SNDCTL_DSP_SPEED:
+ ret = get_user(val, (long __user *)arg);
+ if (ret)
+ break;
+ ret = omap_set_samplerate(val);
+ if (ret)
+ break;
+ /* fall through */
+
+ case SOUND_PCM_READ_RATE:
+ FN_OUT(3);
+ return put_user(audio_samplerate, (long __user *)arg);
+
+ case SOUND_PCM_READ_BITS:
+ case SNDCTL_DSP_SETFMT:
+ case SNDCTL_DSP_GETFMTS:
+ /* we can do 16-bit only */
+ FN_OUT(4);
+ return put_user(AFMT_S16_LE, (long __user *)arg);
+
+ default:
+ /* Maybe this is meant for the mixer (As per OSS Docs) */
+ FN_OUT(5);
+ return mixer_ioctl(inode, file, cmd, arg);
+ }
+
+ FN_OUT(0);
+ return ret;
+}
+
+/*********************************************************************************
+ *
+ * module_probe for TSC2101
+ *
+ ********************************************************************************/
+static int omap_tsc2101_probe(void)
+{
+ FN_IN;
+
+ /* Get the fops from audio oss driver */
+ if (!(omap_audio_fops = audio_get_fops())) {
+ printk(KERN_ERR "Unable to Get the FOPs of Audio OSS driver\n");
+ audio_unregister_codec(&tsc2101_state);
+ return -EPERM;
+ }
+
+ /* register devices */
+ audio_dev_id = register_sound_dsp(omap_audio_fops, -1);
+ mixer_dev_id = register_sound_mixer(&omap_mixer_fops, -1);
+
+#ifdef PROC_SUPPORT
+ create_proc_read_entry(PROC_START_FILE, 0 /* default mode */ ,
+ NULL /* parent dir */ ,
+ codec_start, NULL /* client data */ );
+
+ create_proc_read_entry(PROC_STOP_FILE, 0 /* default mode */ ,
+ NULL /* parent dir */ ,
+ codec_stop, NULL /* client data */ );
+#endif
+
+ /* Announcement Time */
+ printk(KERN_INFO PLATFORM_NAME " " CODEC_NAME
+ " Audio support initialized\n");
+
+ FN_OUT(0);
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * Module Remove for TSC2101
+ *
+ ********************************************************************************/
+static void omap_tsc2101_remove(void)
+{
+ FN_IN;
+ /* Un-Register the codec with the audio driver */
+ unregister_sound_dsp(audio_dev_id);
+ unregister_sound_mixer(mixer_dev_id);
+
+#ifdef PROC_SUPPORT
+ remove_proc_entry(PROC_START_FILE, NULL);
+ remove_proc_entry(PROC_STOP_FILE, NULL);
+#endif
+ FN_OUT(0);
+
+}
+
+/*********************************************************************************
+ *
+ * Module Suspend for TSC2101
+ *
+ ********************************************************************************/
+static int omap_tsc2101_suspend(void)
+{
+
+ FN_OUT(0);
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * Module Resume for TSC2101
+ *
+ ********************************************************************************/
+static int omap_tsc2101_resume(void)
+{
+
+ FN_OUT(0);
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * module_init for TSC2101
+ *
+ ********************************************************************************/
+static int __init audio_tsc2101_init(void)
+{
+
+ int err = 0;
+ FN_IN;
+
+ if (machine_is_omap_osk() || machine_is_omap_innovator())
+ return -ENODEV;
+
+ /* register the codec with the audio driver */
+ if ((err = audio_register_codec(&tsc2101_state))) {
+ printk(KERN_ERR
+ "Failed to register TSC driver with Audio OSS Driver\n");
+ }
+ FN_OUT(err);
+ return err;
+}
+
+/*********************************************************************************
+ *
+ * module_exit for TSC2101
+ *
+ ********************************************************************************/
+static void __exit audio_tsc2101_exit(void)
+{
+
+ FN_IN;
+ (void)audio_unregister_codec(&tsc2101_state);
+ FN_OUT(0);
+ return;
+}
+
+/**************************** DEBUG FUNCTIONS ***********************************/
+
+/*********************************************************************************
+ * TEST_KEYCLICK:
+ * This is a test to generate various keyclick sound on tsc.
+ * verifies if the tsc and the spi interfaces are operational.
+ *
+ ********************************************************************************/
+#ifdef TEST_KEYCLICK
+void tsc2101_testkeyclick(void)
+{
+ u8 freq = 0;
+ u16 old_reg_val, reg_val;
+ u32 uDummyVal = 0;
+ u32 uTryVal = 0;
+
+ old_reg_val = audio_tsc2101_read(TSC2101_AUDIO_CTRL_2);
+
+ /* Keyclick active, max amplitude and longest key click len(32 period) */
+ printk(KERN_INFO " TESTING KEYCLICK\n Listen carefully NOW....\n");
+ printk(KERN_INFO " OLD REG VAL=0x%x\n", old_reg_val);
+ /* try all frequencies */
+ for (; freq < 8; freq++) {
+ /* Keyclick active, max amplitude and longest key click len(32 period) */
+ reg_val = old_reg_val | AC2_KCLAC(0x7) | AC2_KCLLN(0xF);
+ uDummyVal = 0;
+ uTryVal = 0;
+ printk(KERN_INFO "\n\nTrying frequency %d reg val= 0x%x\n",
+ freq, reg_val | AC2_KCLFRQ(freq) | AC2_KCLEN);
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_2,
+ reg_val | AC2_KCLFRQ(freq) | AC2_KCLEN);
+ printk("DONE. Wait 10 ms ...\n");
+ /* wait till the kclk bit is auto cleared! time out also to be considered. */
+ while (audio_tsc2101_read(TSC2101_AUDIO_CTRL_2) & AC2_KCLEN) {
+ udelay(3);
+ uTryVal++;
+ if (uTryVal > 2000) {
+ printk(KERN_ERR
+ "KEYCLICK TIMED OUT! freq val=%d, POSSIBLE ERROR!\n",
+ freq);
+ printk(KERN_INFO
+ "uTryVal == %d: Read back new reg val= 0x%x\n",
+ uTryVal,
+ audio_tsc2101_read
+ (TSC2101_AUDIO_CTRL_2));
+ /* clear */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_2, 0x00);
+ break;
+ }
+ }
+ }
+ /* put the old value back */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_2, old_reg_val);
+ printk(KERN_INFO " KEYCLICK TEST COMPLETE\n");
+
+} /* End of tsc2101_testkeyclick */
+
+#endif /* TEST_KEYCLICK */
+
+/*********************************************************************************
+ * TONEGEN:
+ * This is a test to generate a rather unpleasant sound..
+ * verifies if the mcbsp is active (requires MCBSP_DIRECT_RW to be active on McBSP)
+ *
+ ********************************************************************************/
+#ifdef TONE_GEN
+/* Generates a shrill tone */
+u16 tone[] = {
+ 0x0ce4, 0x0ce4, 0x1985, 0x1985, 0x25A1, 0x25A1, 0x30FD, 0x30FE,
+ 0x3B56, 0x3B55, 0x447A, 0x447A, 0x4C3B, 0x4C3C, 0x526D, 0x526C,
+ 0x56F1, 0x56F1, 0x59B1, 0x59B1, 0x5A9E, 0x5A9D, 0x59B1, 0x59B2,
+ 0x56F3, 0x56F2, 0x526D, 0x526D, 0x4C3B, 0x4C3B, 0x447C, 0x447C,
+ 0x3B5A, 0x3B59, 0x30FE, 0x30FE, 0x25A5, 0x25A6, 0x1989, 0x198A,
+ 0x0CE5, 0x0CE3, 0x0000, 0x0000, 0xF31C, 0xF31C, 0xE677, 0xE676,
+ 0xDA5B, 0xDA5B, 0xCF03, 0xCF03, 0xC4AA, 0xC4AA, 0xBB83, 0xBB83,
+ 0xB3C5, 0xB3C5, 0xAD94, 0xAD94, 0xA90D, 0xA90E, 0xA64F, 0xA64E,
+ 0xA562, 0xA563, 0xA64F, 0xA64F, 0xA910, 0xA90F, 0xAD93, 0xAD94,
+ 0xB3C4, 0xB3C4, 0xBB87, 0xBB86, 0xC4AB, 0xC4AB, 0xCF03, 0xCF03,
+ 0xDA5B, 0xDA5A, 0xE67B, 0xE67B, 0xF31B, 0xF3AC, 0x0000, 0x0000,
+ 0x0CE4, 0x0CE4, 0x1985, 0x1985, 0x25A1, 0x25A1, 0x30FD, 0x30FE,
+ 0x3B56, 0x3B55, 0x447A, 0x447A, 0x4C3B, 0x4C3C, 0x526D, 0x526C,
+ 0x56F1, 0x56F1, 0x59B1, 0x59B1, 0x5A9E, 0x5A9D, 0x59B1, 0x59B2,
+ 0x56F3, 0x56F2, 0x526D, 0x526D, 0x4C3B, 0x4C3B, 0x447C, 0x447C,
+ 0x3B5A, 0x3B59, 0x30FE, 0x30FE, 0x25A5, 0x25A6, 0x1989, 0x198A,
+ 0x0CE5, 0x0CE3, 0x0000, 0x0000, 0xF31C, 0xF31C, 0xE677, 0xE676,
+ 0xDA5B, 0xDA5B, 0xCF03, 0xCF03, 0xC4AA, 0xC4AA, 0xBB83, 0xBB83,
+ 0xB3C5, 0xB3C5, 0xAD94, 0xAD94, 0xA90D, 0xA90E, 0xA64F, 0xA64E,
+ 0xA562, 0xA563, 0xA64F, 0xA64F, 0xA910, 0xA90F, 0xAD93, 0xAD94,
+ 0xB3C4, 0xB3C4, 0xBB87, 0xBB86, 0xC4AB, 0xC4AB, 0xCF03, 0xCF03,
+ 0xDA5B, 0xDA5A, 0xE67B, 0xE67B, 0xF31B, 0xF3AC, 0x0000, 0x0000,
+ 0x0CE4, 0x0CE4, 0x1985, 0x1985, 0x25A1, 0x25A1, 0x30FD, 0x30FE,
+ 0x3B56, 0x3B55, 0x447A, 0x447A, 0x4C3B, 0x4C3C, 0x526D, 0x526C,
+ 0x56F1, 0x56F1, 0x59B1, 0x59B1, 0x5A9E, 0x5A9D, 0x59B1, 0x59B2,
+ 0x56F3, 0x56F2, 0x526D, 0x526D, 0x4C3B, 0x4C3B, 0x447C, 0x447C,
+ 0x3B5A, 0x3B59, 0x30FE, 0x30FE, 0x25A5, 0x25A6, 0x1989, 0x198A,
+ 0x0CE5, 0x0CE3, 0x0000, 0x0000, 0xF31C, 0xF31C, 0xE677, 0xE676,
+ 0xDA5B, 0xDA5B, 0xCF03, 0xCF03, 0xC4AA, 0xC4AA, 0xBB83, 0xBB83,
+ 0xB3C5, 0xB3C5, 0xAD94, 0xAD94, 0xA90D, 0xA90E, 0xA64F, 0xA64E,
+ 0xA562, 0xA563, 0xA64F, 0xA64F, 0xA910, 0xA90F, 0xAD93, 0xAD94,
+ 0xB3C4, 0xB3C4, 0xBB87, 0xBB86, 0xC4AB, 0xC4AB, 0xCF03, 0xCF03,
+ 0xDA5B, 0xDA5A, 0xE67B, 0xE67B, 0xF31B, 0xF3AC, 0x0000, 0x0000
+};
+
+void toneGen(void)
+{
+ int count = 0;
+ int ret = 0;
+ printk(KERN_INFO "TONE GEN TEST :");
+
+ for (count = 0; count < 5000; count++) {
+ int bytes;
+ for (bytes = 0; bytes < sizeof(tone) / 2; bytes++) {
+ ret = omap_mcbsp_pollwrite(AUDIO_MCBSP, tone[bytes]);
+ if (ret == -1) {
+ /* retry */
+ bytes--;
+ } else if (ret == -2) {
+ printk(KERN_INFO "ERROR:bytes=%d\n", bytes);
+ return;
+ }
+ }
+ }
+ printk(KERN_INFO "SUCCESS\n");
+}
+
+#endif /* End of TONE_GEN */
+
+/*********************************************************************************
+ *
+ * TSC_DUMP_REGISTERS:
+ * This will dump the entire register set of Page 2 tsc2101.
+ * Useful for major goof ups
+ *
+ ********************************************************************************/
+#ifdef TSC_DUMP_REGISTERS
+static void tsc2101_dumpRegisters(void)
+{
+ int i = 0;
+ u16 data = 0;
+ printk("TSC 2101 Register dump for Page 2 \n");
+ for (i = 0; i < 0x27; i++) {
+ data = audio_tsc2101_read(i);
+ printk(KERN_INFO "Register[%x]=0x%04x\n", i, data);
+
+ }
+}
+#endif /* End of #ifdef TSC_DUMP_REGISTERS */
+
+#ifdef PROC_SUPPORT
+static int codec_start(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ omap_tsc2101_enable();
+ tsc2101_start();
+ printk("Codec initialization done.\n");
+ return 0;
+}
+static int codec_stop(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+
+ omap_tsc2101_disable();
+ audio_tsc2101_write(TSC2101_CODEC_POWER_CTRL,
+ ~(CPC_SP1PWDN | CPC_SP2PWDN | CPC_BASSBC));
+ printk("Codec shutdown.\n");
+ return 0;
+}
+#endif
+
+/*********************************************************************************
+ *
+ * Other misc management, registration etc
+ *
+ ********************************************************************************/
+module_init(audio_tsc2101_init);
+module_exit(audio_tsc2101_exit);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION
+ ("Glue audio driver for the TI OMAP1610/OMAP1710 TSC2101 codec.");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * linux/sound/oss/omap-audio.c
+ *
+ * Common audio handling for the OMAP processors
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * History:
+ *
+ * 2004/08/12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms
+ *
+ * 2004-11-01 Nishanth Menon - modified to support 16xx and 17xx
+ * platform multi channel chaining.
+ *
+ * 2004-11-04 Nishanth Menon - Added support for power management
+ *
+ * 2004-12-17 Nishanth Menon - Provided proper module handling support
+ */
+
+/***************************** INCLUDES ************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/pm.h>
+#include <linux/errno.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+#include <linux/sysrq.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/semaphore.h>
+
+#include "omap-audio-dma-intfc.h"
+#include "omap-audio.h"
+
+/***************************** MACROS ************************************/
+
+#undef DEBUG
+//#define DEBUG
+#ifdef DEBUG
+#define DPRINTK printk
+#define FN_IN printk("[omap_audio.c:[%s] start\n", __FUNCTION__)
+#define FN_OUT(n) printk("[omap_audio.c:[%s] end(%d)\n", __FUNCTION__ , n)
+#else
+#define DPRINTK( x... )
+#define FN_IN
+#define FN_OUT(x)
+#endif
+
+#define OMAP_AUDIO_NAME "omap-audio"
+#define AUDIO_NBFRAGS_DEFAULT 8
+#define AUDIO_FRAGSIZE_DEFAULT 8192
+
+/* HACK ALERT!: These values will bave to be tuned as this is a trade off b/w
+ * Sampling Rate vs buffer size and delay we are prepared to do before giving up
+ */
+#define MAX_QUEUE_FULL_RETRIES 1000000
+#define QUEUE_WAIT_TIME 10
+
+#define AUDIO_ACTIVE(state) ((state)->rd_ref || (state)->wr_ref)
+
+#define SPIN_ADDR (dma_addr_t)0
+#define SPIN_SIZE 2048
+
+/***************************** MODULES SPECIFIC FUNCTION PROTOTYPES ********************/
+
+static int audio_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t * ppos);
+
+static int audio_read(struct file *file, char __user *buffer, size_t count,
+ loff_t * ppos);
+
+static int audio_mmap(struct file *file, struct vm_area_struct *vma);
+
+static unsigned int audio_poll(struct file *file,
+ struct poll_table_struct *wait);
+
+static loff_t audio_llseek(struct file *file, loff_t offset, int origin);
+
+static int audio_ioctl(struct inode *inode, struct file *file, uint cmd,
+ ulong arg);
+
+static int audio_open(struct inode *inode, struct file *file);
+
+static int audio_release(struct inode *inode, struct file *file);
+
+static int audio_probe(struct device *dev);
+
+static int audio_remove(struct device *dev);
+
+static void audio_shutdown(struct device *dev);
+
+static int audio_suspend(struct device *dev, pm_message_t mesg, u32 level);
+
+static int audio_resume(struct device *dev, u32 level);
+
+static void audio_free(struct device *dev);
+
+/***************************** Data Structures **********************************/
+
+/*
+ * The function pointer set to be registered by the codec.
+ */
+static audio_state_t audio_state = { NULL };
+
+/* DMA Call back function */
+static dma_callback_t audio_dma_callback = NULL;
+
+/* File Ops structure */
+static struct file_operations omap_audio_fops = {
+ .open = audio_open,
+ .release = audio_release,
+ .write = audio_write,
+ .read = audio_read,
+ .mmap = audio_mmap,
+ .poll = audio_poll,
+ .ioctl = audio_ioctl,
+ .llseek = audio_llseek,
+ .owner = THIS_MODULE
+};
+
+/* Driver information */
+static struct device_driver omap_audio_driver = {
+ .name = OMAP_AUDIO_NAME,
+ .bus = &platform_bus_type,
+ .probe = audio_probe,
+ .remove = audio_remove,
+ .suspend = audio_suspend,
+ .resume = audio_resume,
+ .shutdown = audio_shutdown,
+};
+
+/* Device Information */
+static struct platform_device omap_audio_device = {
+ .name = OMAP_AUDIO_NAME,
+ .dev = {
+ .driver_data = &audio_state,
+ .release = audio_free,
+ },
+ .id = 0,
+};
+
+/***************************** GLOBAL FUNCTIONs **********************************/
+
+/* Power Management Functions for Linux Device Model */
+/* DEBUG PUPOSES ONLY! */
+#ifdef CONFIG_PM
+//#undef CONFIG_PM
+#endif
+
+#ifdef CONFIG_PM
+/*********************************************************************************
+ *
+ * audio_ldm_suspend(): Suspend operation
+ *
+ *********************************************************************************/
+static int audio_ldm_suspend(void *data)
+{
+ audio_state_t *state = data;
+
+ FN_IN;
+
+ /*
+ * Reject the suspend request if we are already actively transmitting data
+ * Rationale: We dont want to be suspended while in the middle of a call!
+ */
+ if (AUDIO_ACTIVE(state) && state->hw_init) {
+ printk(KERN_ERR "Audio device Active, Cannot Suspend");
+ return -EPERM;
+#if 0
+ /* NOTE:
+ * This Piece of code is commented out in hope
+ * That one day we would need to suspend the device while
+ * audio operations are in progress and resume the operations
+ * once the resume is done.
+ * This is just a sample implementation of how it could be done.
+ * Currently NOT SUPPORTED
+ */
+ audio_stream_t *is = state->input_stream;
+ audio_stream_t *os = state->output_stream;
+ int stopstate;
+ if (is && is->buffers) {
+ printk("IS Suspend\n");
+ stopstate = is->stopped;
+ audio_stop_dma(is);
+ DMA_CLEAR(is);
+ is->dma_spinref = 0;
+ is->stopped = stopstate;
+ }
+ if (os && os->buffers) {
+ printk("OS Suspend\n");
+ stopstate = os->stopped;
+ audio_stop_dma(os);
+ DMA_CLEAR(os);
+ os->dma_spinref = 0;
+ os->stopped = stopstate;
+ }
+#endif
+ }
+
+ FN_OUT(0);
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * audio_ldm_resume(): Resume Operations
+ *
+ *********************************************************************************/
+static int audio_ldm_resume(void *data)
+{
+ audio_state_t *state = data;
+
+ FN_IN;
+ if (AUDIO_ACTIVE(state) && state->hw_init) {
+ /* Should never occur - since we never suspend with active state */
+ BUG();
+ return -EPERM;
+#if 0
+ /* NOTE:
+ * This Piece of code is commented out in hope
+ * That one day we would need to suspend the device while
+ * audio operations are in progress and resume the operations
+ * once the resume is done.
+ * This is just a sample implementation of how it could be done.
+ * Currently NOT SUPPORTED
+ */
+ audio_stream_t *is = state->input_stream;
+ audio_stream_t *os = state->output_stream;
+ if (os && os->buffers) {
+ printk("OS Resume\n");
+ audio_reset(os);
+ audio_process_dma(os);
+ }
+ if (is && is->buffers) {
+ printk("IS Resume\n");
+ audio_reset(is);
+ audio_process_dma(is);
+ }
+#endif
+ }
+ FN_OUT(0);
+ return 0;
+}
+#endif /* End of #ifdef CONFIG_PM */
+
+/*********************************************************************************
+ *
+ * audio_free(): The Audio driver release function
+ * This is a dummy function required by the platform driver
+ *
+ *********************************************************************************/
+static void audio_free(struct device *dev)
+{
+ /* Nothing to Release! */
+}
+
+/*********************************************************************************
+ *
+ * audio_probe(): The Audio driver probe function
+ * WARNING!!!! : It is expected that the codec would have registered with us by now
+ *
+ *********************************************************************************/
+static int audio_probe(struct device *dev)
+{
+ int ret;
+ FN_IN;
+ if (!audio_state.hw_probe) {
+ printk(KERN_ERR "Probe Function Not Registered\n");
+ return -ENODEV;
+ }
+ ret = audio_state.hw_probe();
+ FN_OUT(ret);
+ return ret;
+}
+
+/*********************************************************************************
+ *
+ * audio_remove() Function to handle removal operations
+ *
+ *********************************************************************************/
+static int audio_remove(struct device *dev)
+{
+ FN_IN;
+ if (audio_state.hw_remove) {
+ audio_state.hw_remove();
+ }
+ FN_OUT(0);
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * audio_shutdown(): Function to handle shutdown operations
+ *
+ *********************************************************************************/
+static void audio_shutdown(struct device *dev)
+{
+ FN_IN;
+ if (audio_state.hw_cleanup) {
+ audio_state.hw_cleanup();
+ }
+ FN_OUT(0);
+ return;
+}
+
+/*********************************************************************************
+ *
+ * audio_suspend(): Function to handle suspend operations
+ *
+ *********************************************************************************/
+static int audio_suspend(struct device *dev, pm_message_t mesg, u32 level)
+{
+ int ret = 0;
+
+#ifdef CONFIG_PM
+ void *data = dev->driver_data;
+ FN_IN;
+ if (level != SUSPEND_POWER_DOWN) {
+ return 0;
+ }
+ if (audio_state.hw_suspend) {
+ ret = audio_ldm_suspend(data);
+ if (ret == 0)
+ ret = audio_state.hw_suspend();
+ }
+ if (ret) {
+ printk(KERN_INFO "Audio Suspend Failed \n");
+ } else {
+ printk(KERN_INFO "Audio Suspend Success \n");
+ }
+#endif /* CONFIG_PM */
+
+ FN_OUT(ret);
+ return ret;
+}
+
+/*********************************************************************************
+ *
+ * audio_resume(): Function to handle resume operations
+ *
+ *********************************************************************************/
+static int audio_resume(struct device *dev, u32 level)
+{
+ int ret = 0;
+
+#ifdef CONFIG_PM
+ void *data = dev->driver_data;
+ FN_IN;
+ if (level != RESUME_POWER_ON) {
+ return 0;
+ }
+ if (audio_state.hw_resume) {
+ ret = audio_ldm_resume(data);
+ if (ret == 0)
+ ret = audio_state.hw_resume();
+ }
+ if (ret) {
+ printk(KERN_INFO " Audio Resume Failed \n");
+ } else {
+ printk(KERN_INFO " Audio Resume Success \n");
+ }
+#endif /* CONFIG_PM */
+
+ FN_OUT(ret);
+ return ret;
+}
+
+/*********************************************************************************
+ *
+ * audio_get_fops(): Return the fops required to get the function pointers of
+ * OMAP Audio Driver
+ *
+ *********************************************************************************/
+struct file_operations *audio_get_fops(void)
+{
+ FN_IN;
+ FN_OUT(0);
+ return &omap_audio_fops;
+}
+
+/*********************************************************************************
+ *
+ * audio_register_codec(): Register a Codec fn points using this function
+ * WARNING!!!!! : Codecs should ensure that they do so! no sanity checks
+ * during runtime is done due to obvious performance
+ * penalties.
+ *
+ *********************************************************************************/
+int audio_register_codec(audio_state_t * codec_state)
+{
+ int ret;
+ FN_IN;
+
+ /* We dont handle multiple codecs now */
+ if (audio_state.hw_init) {
+ printk(KERN_ERR " Codec Already registered\n");
+ return -EPERM;
+ }
+
+ /* Grab the dma Callback */
+ audio_dma_callback = audio_get_dma_callback();
+ if (!audio_dma_callback) {
+ printk(KERN_ERR "Unable to get call back function\n");
+ return -EPERM;
+ }
+
+ /* Sanity checks */
+ if (!codec_state) {
+ printk(KERN_ERR "NULL ARGUMENT!\n");
+ return -EPERM;
+ }
+
+ if (!codec_state->hw_probe || !codec_state->hw_init
+ || !codec_state->hw_shutdown || !codec_state->client_ioctl) {
+ printk(KERN_ERR
+ "Required Fn Entry point Missing probe=%p init=%p,down=%p,ioctl=%p!\n",
+ codec_state->hw_probe, codec_state->hw_init,
+ codec_state->hw_shutdown, codec_state->client_ioctl);
+ return -EPERM;
+ }
+
+ memcpy(&audio_state, codec_state, sizeof(audio_state_t));
+
+ ret = platform_device_register(&omap_audio_device);
+ if (ret != 0) {
+ printk(KERN_ERR "Platform dev_register failed =%d\n", ret);
+ ret = -ENODEV;
+ goto register_out;
+ }
+
+ ret = driver_register(&omap_audio_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Device Register failed =%d\n", ret);
+ ret = -ENODEV;
+ platform_device_unregister(&omap_audio_device);
+ goto register_out;
+ }
+
+ register_out:
+
+ FN_OUT(ret);
+ return ret;
+}
+
+/*********************************************************************************
+ *
+ * audio_unregister_codec(): Un-Register a Codec using this function
+ *
+ *********************************************************************************/
+int audio_unregister_codec(audio_state_t * codec_state)
+{
+ FN_IN;
+
+ /* We dont handle multiple codecs now */
+ if (!audio_state.hw_init) {
+ printk(KERN_ERR " No Codec registered\n");
+ return -EPERM;
+ }
+ /* Security check */
+ if (audio_state.hw_init != codec_state->hw_init) {
+ printk(KERN_ERR
+ " Attempt to unregister codec which was not registered with us\n");
+ return -EPERM;
+ }
+
+ driver_unregister(&omap_audio_driver);
+ platform_device_unregister(&omap_audio_device);
+
+ memset(&audio_state, 0, sizeof(audio_state_t));
+
+ FN_OUT(0);
+ return 0;
+}
+
+/***************************** MODULES SPECIFIC FUNCTION *************************/
+
+/*********************************************************************************
+ *
+ * audio_write(): Exposed to write() call
+ *
+ *********************************************************************************/
+static int
+audio_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t * ppos)
+{
+ const char __user *buffer0 = buffer;
+ audio_state_t *state = file->private_data;
+ audio_stream_t *s = state->output_stream;
+ int chunksize, ret = 0;
+
+ DPRINTK("audio_write: count=%d\n", count);
+ if (*ppos != file->f_pos) {
+ printk("FPOS not ppos ppos=0x%x fpos =0x%x\n", (u32) * ppos,
+ (u32) file->f_pos);
+ return -ESPIPE;
+ }
+ if (s->mapped) {
+ printk("s already mapped\n");
+ return -ENXIO;
+ }
+ if (!s->buffers && audio_setup_buf(s)) {
+ printk("NO MEMORY\n");
+ return -ENOMEM;
+ }
+
+ while (count > 0) {
+ audio_buf_t *b = &s->buffers[s->usr_head];
+
+ /* Wait for a buffer to become free */
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ if (down_trylock(&s->sem))
+ break;
+ } else {
+ ret = -ERESTARTSYS;
+ if (down_interruptible(&s->sem))
+ break;
+ }
+
+ /* Feed the current buffer */
+ chunksize = s->fragsize - b->offset;
+ if (chunksize > count)
+ chunksize = count;
+ DPRINTK("write %d to %d\n", chunksize, s->usr_head);
+ if (copy_from_user(b->data + b->offset, buffer, chunksize)) {
+ printk(KERN_ERR "Audio: CopyFrom User failed \n");
+ up(&s->sem);
+ return -EFAULT;
+ }
+
+ buffer += chunksize;
+ count -= chunksize;
+ b->offset += chunksize;
+
+ if (b->offset < s->fragsize) {
+ up(&s->sem);
+ break;
+ }
+
+ /* Update pointers and send current fragment to DMA */
+ b->offset = 0;
+ if (++s->usr_head >= s->nbfrags)
+ s->usr_head = 0;
+ /* Add the num of frags pending */
+ s->pending_frags++;
+ s->active = 1;
+
+ audio_process_dma(s);
+
+ }
+
+ if ((buffer - buffer0))
+ ret = buffer - buffer0;
+ DPRINTK("audio_write: return=%d\n", ret);
+ return ret;
+}
+
+/*********************************************************************************
+ *
+ * audio_read(): Exposed as read() function
+ *
+ *********************************************************************************/
+static int
+audio_read(struct file *file, char __user *buffer, size_t count, loff_t * ppos)
+{
+ char __user *buffer0 = buffer;
+ audio_state_t *state = file->private_data;
+ audio_stream_t *s = state->input_stream;
+ int chunksize, ret = 0;
+ unsigned long flags;
+
+ DPRINTK("audio_read: count=%d\n", count);
+
+ if (*ppos != file->f_pos) {
+ printk("AudioRead - FPOS not ppos ppos=0x%x fpos =0x%x\n",
+ (u32) * ppos, (u32) file->f_pos);
+ return -ESPIPE;
+ }
+ if (s->mapped) {
+ printk("AudioRead - s already mapped\n");
+ return -ENXIO;
+ }
+
+ if (!s->active) {
+ if (!s->buffers && audio_setup_buf(s)) {
+ printk("AudioRead - No Memory\n");
+ return -ENOMEM;
+ }
+ audio_prime_rx(state);
+ }
+
+ while (count > 0) {
+ audio_buf_t *b = &s->buffers[s->usr_head];
+
+ /* Wait for a buffer to become full */
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ if (down_trylock(&s->sem))
+ break;
+ } else {
+ ret = -ERESTARTSYS;
+ if (down_interruptible(&s->sem))
+ break;
+ }
+
+ /* Grab data from the current buffer */
+ chunksize = s->fragsize - b->offset;
+ if (chunksize > count)
+ chunksize = count;
+ DPRINTK("read %d from %d\n", chunksize, s->usr_head);
+ if (copy_to_user(buffer, b->data + b->offset, chunksize)) {
+ up(&s->sem);
+ return -EFAULT;
+ }
+ buffer += chunksize;
+ count -= chunksize;
+ b->offset += chunksize;
+ if (b->offset < s->fragsize) {
+ up(&s->sem);
+ break;
+ }
+
+ /* Update pointers and return current fragment to DMA */
+ local_irq_save(flags);
+ b->offset = 0;
+ if (++s->usr_head >= s->nbfrags)
+ s->usr_head = 0;
+
+ s->pending_frags++;
+ local_irq_restore(flags);
+ audio_process_dma(s);
+
+ }
+
+ if ((buffer - buffer0))
+ ret = buffer - buffer0;
+ DPRINTK("audio_read: return=%d\n", ret);
+ return ret;
+}
+
+/*********************************************************************************
+ *
+ * audio_mmap(): Exposed as mmap Function
+ * !!WARNING: Still under development
+ *
+ *********************************************************************************/
+static int audio_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ audio_state_t *state = file->private_data;
+ audio_stream_t *s;
+ unsigned long size, vma_addr;
+ int i, ret;
+
+ FN_IN;
+ if (vma->vm_pgoff != 0)
+ return -EINVAL;
+
+ if (vma->vm_flags & VM_WRITE) {
+ if (!state->wr_ref)
+ return -EINVAL;;
+ s = state->output_stream;
+ } else if (vma->vm_flags & VM_READ) {
+ if (!state->rd_ref)
+ return -EINVAL;
+ s = state->input_stream;
+ } else
+ return -EINVAL;
+
+ if (s->mapped)
+ return -EINVAL;
+ size = vma->vm_end - vma->vm_start;
+ if (size != s->fragsize * s->nbfrags)
+ return -EINVAL;
+ if (!s->buffers && audio_setup_buf(s))
+ return -ENOMEM;
+ vma_addr = vma->vm_start;
+ for (i = 0; i < s->nbfrags; i++) {
+ audio_buf_t *buf = &s->buffers[i];
+ if (!buf->master)
+ continue;
+ ret =
+ remap_pfn_range(vma, vma_addr, buf->dma_addr >> PAGE_SHIFT,
+ buf->master, vma->vm_page_prot);
+ if (ret)
+ return ret;
+ vma_addr += buf->master;
+ }
+ s->mapped = 1;
+
+ FN_OUT(0);
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * audio_poll(): Exposed as poll function
+ *
+ *********************************************************************************/
+static unsigned int
+audio_poll(struct file *file, struct poll_table_struct *wait)
+{
+ audio_state_t *state = file->private_data;
+ audio_stream_t *is = state->input_stream;
+ audio_stream_t *os = state->output_stream;
+ unsigned int mask = 0;
+
+ DPRINTK("audio_poll(): mode=%s%s\n",
+ (file->f_mode & FMODE_READ) ? "r" : "",
+ (file->f_mode & FMODE_WRITE) ? "w" : "");
+
+ if (file->f_mode & FMODE_READ) {
+ /* Start audio input if not already active */
+ if (!is->active) {
+ if (!is->buffers && audio_setup_buf(is))
+ return -ENOMEM;
+ audio_prime_rx(state);
+ }
+ poll_wait(file, &is->wq, wait);
+ }
+
+ if (file->f_mode & FMODE_WRITE) {
+ if (!os->buffers && audio_setup_buf(os))
+ return -ENOMEM;
+ poll_wait(file, &os->wq, wait);
+ }
+
+ if (file->f_mode & FMODE_READ)
+ if ((is->mapped && is->bytecount > 0) ||
+ (!is->mapped && atomic_read(&is->sem.count) > 0))
+ mask |= POLLIN | POLLRDNORM;
+
+ if (file->f_mode & FMODE_WRITE)
+ if ((os->mapped && os->bytecount > 0) ||
+ (!os->mapped && atomic_read(&os->sem.count) > 0))
+ mask |= POLLOUT | POLLWRNORM;
+
+ DPRINTK("audio_poll() returned mask of %s%s\n",
+ (mask & POLLIN) ? "r" : "", (mask & POLLOUT) ? "w" : "");
+
+ FN_OUT(mask);
+ return mask;
+}
+
+/*********************************************************************************
+ *
+ * audio_llseek(): Exposed as lseek() function.
+ *
+ *********************************************************************************/
+static loff_t audio_llseek(struct file *file, loff_t offset, int origin)
+{
+ FN_IN;
+ FN_OUT(0);
+ return -ESPIPE;
+}
+
+/*********************************************************************************
+ *
+ * audio_ioctl(): Handles generic ioctls. If there is a request for something this
+ * fn cannot handle, its then given to client specific ioctl routine, that will take
+ * up platform specific requests
+ *
+ *********************************************************************************/
+static int
+audio_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+ audio_state_t *state = file->private_data;
+ audio_stream_t *os = state->output_stream;
+ audio_stream_t *is = state->input_stream;
+ long val;
+
+ DPRINTK(__FILE__ " audio_ioctl 0x%08x\n", cmd);
+
+ /* dispatch based on command */
+ switch (cmd) {
+ case OSS_GETVERSION:
+ return put_user(SOUND_VERSION, (int __user *)arg);
+
+ case SNDCTL_DSP_GETBLKSIZE:
+ if (file->f_mode & FMODE_WRITE)
+ return put_user(os->fragsize, (int __user *)arg);
+ else
+ return put_user(is->fragsize, (int __user *)arg);
+
+ case SNDCTL_DSP_GETCAPS:
+ val = DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP;
+ if (is && os)
+ val |= DSP_CAP_DUPLEX;
+ FN_OUT(1);
+ return put_user(val, (int __user *)arg);
+
+ case SNDCTL_DSP_SETFRAGMENT:
+ if (get_user(val, (long __user *)arg)) {
+ FN_OUT(2);
+ return -EFAULT;
+ }
+ if (file->f_mode & FMODE_READ) {
+ int ret = audio_set_fragments(is, val);
+ if (ret < 0) {
+ FN_OUT(3);
+ return ret;
+ }
+ ret = put_user(ret, (int __user *)arg);
+ if (ret) {
+ FN_OUT(4);
+ return ret;
+ }
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ int ret = audio_set_fragments(os, val);
+ if (ret < 0) {
+ FN_OUT(5);
+ return ret;
+ }
+ ret = put_user(ret, (int __user *)arg);
+ if (ret) {
+ FN_OUT(6);
+ return ret;
+ }
+ }
+ FN_OUT(7);
+ return 0;
+
+ case SNDCTL_DSP_SYNC:
+ FN_OUT(8);
+ return audio_sync(file);
+
+ case SNDCTL_DSP_SETDUPLEX:
+ FN_OUT(9);
+ return 0;
+
+ case SNDCTL_DSP_POST:
+ FN_OUT(10);
+ return 0;
+
+ case SNDCTL_DSP_GETTRIGGER:
+ val = 0;
+ if (file->f_mode & FMODE_READ && is->active && !is->stopped)
+ val |= PCM_ENABLE_INPUT;
+ if (file->f_mode & FMODE_WRITE && os->active && !os->stopped)
+ val |= PCM_ENABLE_OUTPUT;
+ FN_OUT(11);
+ return put_user(val, (int __user *)arg);
+
+ case SNDCTL_DSP_SETTRIGGER:
+ if (get_user(val, (int __user *)arg)) {
+ FN_OUT(12);
+ return -EFAULT;
+ }
+ if (file->f_mode & FMODE_READ) {
+ if (val & PCM_ENABLE_INPUT) {
+ unsigned long flags;
+ if (!is->active) {
+ if (!is->buffers && audio_setup_buf(is)) {
+ FN_OUT(13);
+ return -ENOMEM;
+ }
+ audio_prime_rx(state);
+ }
+ local_irq_save(flags);
+ is->stopped = 0;
+ local_irq_restore(flags);
+ audio_process_dma(is);
+
+ } else {
+ audio_stop_dma(is);
+ }
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ if (val & PCM_ENABLE_OUTPUT) {
+ unsigned long flags;
+ if (!os->buffers && audio_setup_buf(os)) {
+ FN_OUT(14);
+ return -ENOMEM;
+ }
+ local_irq_save(flags);
+ if (os->mapped && !os->pending_frags) {
+ os->pending_frags = os->nbfrags;
+ sema_init(&os->sem, 0);
+ os->active = 1;
+ }
+ os->stopped = 0;
+ local_irq_restore(flags);
+ audio_process_dma(os);
+
+ } else {
+ audio_stop_dma(os);
+ }
+ }
+ FN_OUT(15);
+ return 0;
+
+ case SNDCTL_DSP_GETOPTR:
+ case SNDCTL_DSP_GETIPTR:
+ {
+ count_info inf = { 0, };
+ audio_stream_t *s =
+ (cmd == SNDCTL_DSP_GETOPTR) ? os : is;
+ int bytecount, offset;
+ unsigned long flags;
+
+ if ((s == is && !(file->f_mode & FMODE_READ)) ||
+ (s == os && !(file->f_mode & FMODE_WRITE))) {
+ FN_OUT(16);
+ return -EINVAL;
+ }
+ if (s->active) {
+ local_irq_save(flags);
+ offset = audio_get_dma_pos(s);
+ inf.ptr = s->dma_tail * s->fragsize + offset;
+ bytecount = s->bytecount + offset;
+ s->bytecount = -offset;
+ inf.blocks = s->fragcount;
+ s->fragcount = 0;
+ local_irq_restore(flags);
+ if (bytecount < 0)
+ bytecount = 0;
+ inf.bytes = bytecount;
+ }
+ FN_OUT(17);
+ return copy_to_user((void __user *)arg, &inf, sizeof(inf));
+ }
+
+ case SNDCTL_DSP_GETOSPACE:
+ case SNDCTL_DSP_GETISPACE:
+ {
+ audio_buf_info inf = { 0, };
+ audio_stream_t *s =
+ (cmd == SNDCTL_DSP_GETOSPACE) ? os : is;
+
+ if ((s == is && !(file->f_mode & FMODE_READ)) ||
+ (s == os && !(file->f_mode & FMODE_WRITE))) {
+ FN_OUT(18);
+ return -EINVAL;
+ }
+ if (!s->buffers && audio_setup_buf(s)) {
+ FN_OUT(19);
+ return -ENOMEM;
+ }
+ inf.bytes = atomic_read(&s->sem.count) * s->fragsize;
+
+ inf.fragments = inf.bytes / s->fragsize;
+ inf.fragsize = s->fragsize;
+ inf.fragstotal = s->nbfrags;
+ FN_OUT(20);
+ return copy_to_user((void __user *)arg, &inf, sizeof(inf));
+ }
+
+ case SNDCTL_DSP_NONBLOCK:
+ file->f_flags |= O_NONBLOCK;
+ FN_OUT(21);
+ return 0;
+
+ case SNDCTL_DSP_RESET:
+ if (file->f_mode & FMODE_READ) {
+ audio_reset(is);
+ if (state->need_tx_for_rx) {
+ unsigned long flags;
+ local_irq_save(flags);
+ os->spin_idle = 0;
+ local_irq_restore(flags);
+ }
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ audio_reset(os);
+ }
+ FN_OUT(22);
+ return 0;
+
+ default:
+ /*
+ * Let the client of this module handle the
+ * non generic ioctls
+ */
+ FN_OUT(23);
+ return state->client_ioctl(inode, file, cmd, arg);
+ }
+
+ FN_OUT(0);
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * audio_open(): Exposed as open() function
+ *
+ *********************************************************************************/
+static int audio_open(struct inode *inode, struct file *file)
+{
+ audio_state_t *state = (&audio_state);
+ audio_stream_t *os = state->output_stream;
+ audio_stream_t *is = state->input_stream;
+ int err, need_tx_dma;
+ static unsigned char tsc2101_init_flag = 0;
+
+ FN_IN;
+
+ /* Lock the module */
+ if (!try_module_get(THIS_MODULE)) {
+ printk(KERN_CRIT "Failed to get module\n");
+ return -ESTALE;
+ }
+ /* Lock the codec module */
+ if (!try_module_get(state->owner)) {
+ printk(KERN_CRIT "Failed to get codec module\n");
+ module_put(THIS_MODULE);
+ return -ESTALE;
+ }
+
+ down(&state->sem);
+
+ /* access control */
+ err = -ENODEV;
+ if ((file->f_mode & FMODE_WRITE) && !os)
+ goto out;
+ if ((file->f_mode & FMODE_READ) && !is)
+ goto out;
+ err = -EBUSY;
+ if ((file->f_mode & FMODE_WRITE) && state->wr_ref)
+ goto out;
+ if ((file->f_mode & FMODE_READ) && state->rd_ref)
+ goto out;
+ err = -EINVAL;
+ if ((file->f_mode & FMODE_READ) && state->need_tx_for_rx && !os)
+ goto out;
+
+ /* request DMA channels */
+ need_tx_dma = ((file->f_mode & FMODE_WRITE) ||
+ ((file->f_mode & FMODE_READ) && state->need_tx_for_rx));
+ if (state->wr_ref || (state->rd_ref && state->need_tx_for_rx))
+ need_tx_dma = 0;
+ if (need_tx_dma) {
+ DMA_REQUEST(err, os, audio_dma_callback);
+ if (err < 0)
+ goto out;
+ }
+ if (file->f_mode & FMODE_READ) {
+ DMA_REQUEST(err, is, audio_dma_callback);
+ if (err < 0) {
+ if (need_tx_dma)
+ DMA_FREE(os);
+ goto out;
+ }
+ }
+
+ /* now complete initialisation */
+ if (!AUDIO_ACTIVE(state)) {
+ if (state->hw_init && !tsc2101_init_flag) {
+ state->hw_init(state->data);
+ tsc2101_init_flag = 0;
+
+ }
+
+ }
+
+ if ((file->f_mode & FMODE_WRITE)) {
+ state->wr_ref = 1;
+ audio_reset(os);
+ os->fragsize = AUDIO_FRAGSIZE_DEFAULT;
+ os->nbfrags = AUDIO_NBFRAGS_DEFAULT;
+ os->mapped = 0;
+ init_waitqueue_head(&os->wq);
+ }
+
+ if (file->f_mode & FMODE_READ) {
+ state->rd_ref = 1;
+ audio_reset(is);
+ is->fragsize = AUDIO_FRAGSIZE_DEFAULT;
+ is->nbfrags = AUDIO_NBFRAGS_DEFAULT;
+ is->mapped = 0;
+ init_waitqueue_head(&is->wq);
+ }
+
+ file->private_data = state;
+ err = 0;
+
+ out:
+ up(&state->sem);
+ if (err) {
+ module_put(state->owner);
+ module_put(THIS_MODULE);
+ }
+ FN_OUT(err);
+ return err;
+}
+
+/*********************************************************************************
+ *
+ * audio_release(): Exposed as release function()
+ *
+ *********************************************************************************/
+static int audio_release(struct inode *inode, struct file *file)
+{
+ audio_state_t *state = file->private_data;
+ audio_stream_t *os = state->output_stream;
+ audio_stream_t *is = state->input_stream;
+
+ FN_IN;
+
+ down(&state->sem);
+
+ if (file->f_mode & FMODE_READ) {
+ audio_discard_buf(is);
+ DMA_FREE(is);
+ is->dma_spinref = 0;
+ if (state->need_tx_for_rx) {
+ os->spin_idle = 0;
+ if (!state->wr_ref) {
+ DMA_FREE(os);
+ os->dma_spinref = 0;
+ }
+ }
+ state->rd_ref = 0;
+ }
+
+ if (file->f_mode & FMODE_WRITE) {
+ audio_sync(file);
+ audio_discard_buf(os);
+ if (!state->need_tx_for_rx || !state->rd_ref) {
+ DMA_FREE(os);
+ os->dma_spinref = 0;
+ }
+ state->wr_ref = 0;
+ }
+
+ if (!AUDIO_ACTIVE(state)) {
+ if (state->hw_shutdown)
+ state->hw_shutdown(state->data);
+ }
+
+ up(&state->sem);
+
+ module_put(state->owner);
+ module_put(THIS_MODULE);
+
+ FN_OUT(0);
+ return 0;
+}
+
+EXPORT_SYMBOL(audio_register_codec);
+EXPORT_SYMBOL(audio_unregister_codec);
+EXPORT_SYMBOL(audio_get_fops);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("Common audio handling for OMAP processors");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * linux/sound/oss/omap-audio.h
+ *
+ * Common audio handling for the OMAP processors
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * History
+ * -------
+ * 2004/08/12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms
+ *
+ * 2004/04/04 Nishanth menon - Added hooks for power management
+ */
+
+#ifndef __OMAP_AUDIO_H
+#define __OMAP_AUDIO_H
+
+/* Requires dma.h */
+#include <asm/arch/dma.h>
+
+/*
+ * Buffer Management
+ */
+typedef struct {
+ int offset; /* current offset */
+ char *data; /* points to actual buffer */
+ dma_addr_t dma_addr; /* physical buffer address */
+ int dma_ref; /* DMA refcount */
+ int master; /* owner for buffer allocation, contain size when true */
+} audio_buf_t;
+
+/*
+ * Structure describing the data stream related information
+ */
+typedef struct {
+ char *id; /* identification string */
+ audio_buf_t *buffers; /* pointer to audio buffer structures */
+ u_int usr_head; /* user fragment index */
+ u_int dma_head; /* DMA fragment index to go */
+ u_int dma_tail; /* DMA fragment index to complete */
+ u_int fragsize; /* fragment i.e. buffer size */
+ u_int nbfrags; /* nbr of fragments i.e. buffers */
+ u_int pending_frags; /* Fragments sent to DMA */
+ int dma_dev; /* device identifier for DMA */
+
+#ifdef OMAP_DMA_CHAINING_SUPPORT
+ lch_chain *dma_chain;
+ dma_regs_t *dma_regs; /* points to our DMA registers */
+#else
+ char started; /* to store if the chain was started or not */
+ int dma_q_head; /* DMA Channel Q Head */
+ int dma_q_tail; /* DMA Channel Q Tail */
+ char dma_q_count; /* DMA Channel Q Count */
+ char in_use; /* Is this is use? */
+ int *lch; /* Chain of channels this stream is linked to */
+#endif
+ int input_or_output; /* Direction of this data stream */
+ int bytecount; /* nbr of processed bytes */
+ int fragcount; /* nbr of fragment transitions */
+ struct semaphore sem; /* account for fragment usage */
+ wait_queue_head_t wq; /* for poll */
+ int dma_spinref; /* DMA is spinning */
+ unsigned mapped:1; /* mmap()'ed buffers */
+ unsigned active:1; /* actually in progress */
+ unsigned stopped:1; /* might be active but stopped */
+ unsigned spin_idle:1; /* have DMA spin on zeros when idle */
+ unsigned linked:1; /* dma channels linked */
+} audio_stream_t;
+
+/*
+ * State structure for one instance
+ */
+typedef struct {
+ struct module *owner; /* Codec module ID */
+ audio_stream_t *output_stream;
+ audio_stream_t *input_stream;
+ unsigned rd_ref:1; /* open reference for recording */
+ unsigned wr_ref:1; /* open reference for playback */
+ unsigned need_tx_for_rx:1; /* if data must be sent while receiving */
+ void *data;
+ void (*hw_init) (void *);
+ void (*hw_shutdown) (void *);
+ int (*client_ioctl) (struct inode *, struct file *, uint, ulong);
+ int (*hw_probe) (void);
+ void (*hw_remove) (void);
+ void (*hw_cleanup) (void);
+ int (*hw_suspend) (void);
+ int (*hw_resume) (void);
+ struct pm_dev *pm_dev;
+ struct semaphore sem; /* to protect against races in attach() */
+} audio_state_t;
+
+#ifdef AUDIO_PM
+void audio_ldm_suspend(void *data);
+
+void audio_ldm_resume(void *data);
+
+#endif
+
+/* Register a Codec using this function */
+extern int audio_register_codec(audio_state_t * codec_state);
+/* Un-Register a Codec using this function */
+extern int audio_unregister_codec(audio_state_t * codec_state);
+/* Function to provide fops of omap audio driver */
+extern struct file_operations *audio_get_fops(void);
+/* Function to initialize the device info for audio driver */
+extern int audio_dev_init(void);
+/* Function to un-initialize the device info for audio driver */
+void audio_dev_uninit(void);
+
+#endif /* End of #ifndef __OMAP_AUDIO_H */