--- arch/arm/mach-ep93xx/include/mach/hardware.h | 1 arch/arm/mach-ep93xx/include/mach/regs_ac97.h | 180 + sound/arm/Kconfig | 17 sound/arm/Makefile | 3 sound/arm/ep93xx-ac97.c | 3482 ++++++++++++++++++++++++++ sound/arm/ep93xx-ac97.h | 89 6 files changed, 3772 insertions(+) --- linux-2.6.31.orig/arch/arm/mach-ep93xx/include/mach/hardware.h +++ linux-2.6.31/arch/arm/mach-ep93xx/include/mach/hardware.h @@ -3,10 +3,11 @@ */ #ifndef __ASM_ARCH_HARDWARE_H #define __ASM_ARCH_HARDWARE_H #include "ep93xx-regs.h" +#include "regs_ac97.h" #define pcibios_assign_all_busses() 0 #include "regs_raster.h" #include "regs_touch.h" --- /dev/null +++ linux-2.6.31/arch/arm/mach-ep93xx/include/mach/regs_ac97.h @@ -0,0 +1,180 @@ +/*============================================================================= + * FILE: regs_ac97.h + * + * DESCRIPTION: Ac'97 Register Definition + * + * Copyright Cirrus Logic, 2001-2003 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *============================================================================= + */ +#ifndef _REGS_AC97_H_ +#define _REGS_AC97_H_ + +//----------------------------------------------------------------------------- +// Bit definitionses +//----------------------------------------------------------------------------- +#define AC97ISR_RIS 8 +#define AC97ISR_TIS 4 +#define AC97ISR_RTIS 2 +#define AC97ISR_TCIS 1 + +#define AC97RGIS_SLOT1TXCOMPLETE 0x01 +#define AC97RGIS_SLOT2RXVALID 0x02 +#define AC97RGIS_GPIOTXCOMPLETE 0x04 +#define AC97RGIS_GPIOINTRX 0x08 +#define AC97RGIS_RWIS 0x10 +#define AC97RGIS_CODECREADY 0x20 +#define AC97RGIS_SLOT2TXCOMPLETE 0x40 + +#define AC97SR_RXFE 0x0001 +#define AC97SR_TXFE 0x0002 +#define AC97SR_RXFF 0x0004 +#define AC97SR_TXFF 0x0008 +#define AC97SR_TXBUSY 0x0010 +#define AC97SR_RXOE 0x0020 +#define AC97SR_TXUE 0x0040 + +#define AC97GSR_IFE 0x1 +#define AC97GSR_LOOP 0x2 +#define AC97GSR_OVERRIDECODECREADY 0x4 + +#define AC97RESET_TIMEDRESET 0x1 +#define AC97RESET_FORCEDRESET 0x2 +#define AC97RESET_EFORCER 0x4 + +#define AC97RXCR_REN 0x1 + +#define AC97TXCR_TEN 0x1 + + +//**************************************************************************** +// +// The Ac97 Codec registers, accessable through the Ac-link. +// These are not controller registers and are not memory mapped. +// Includes registers specific to CS4202 (Beavis). +// +//**************************************************************************** +#define AC97_REG_OFFSET_MASK 0x0000007E + +#define AC97_00_RESET 0x00000000 +#define AC97_02_MASTER_VOL 0x00000002 +#define AC97_04_HEADPHONE_VOL 0x00000004 +#define AC97_06_MONO_VOL 0x00000006 +#define AC97_08_TONE 0x00000008 +#define AC97_0A_PC_BEEP_VOL 0x0000000A +#define AC97_0C_PHONE_VOL 0x0000000C +#define AC97_0E_MIC_VOL 0x0000000E +#define AC97_10_LINE_IN_VOL 0x00000010 +#define AC97_12_CD_VOL 0x00000012 +#define AC97_14_VIDEO_VOL 0x00000014 +#define AC97_16_AUX_VOL 0x00000016 +#define AC97_18_PCM_OUT_VOL 0x00000018 +#define AC97_1A_RECORD_SELECT 0x0000001A +#define AC97_1C_RECORD_GAIN 0x0000001C +#define AC97_1E_RESERVED_1E 0x0000001E +#define AC97_20_GENERAL_PURPOSE 0x00000020 +#define AC97_22_3D_CONTROL 0x00000022 +#define AC97_24_MODEM_RATE 0x00000024 +#define AC97_26_POWERDOWN 0x00000026 +#define AC97_28_EXT_AUDIO_ID 0x00000028 +#define AC97_2A_EXT_AUDIO_POWER 0x0000002A +#define AC97_2C_PCM_FRONT_DAC_RATE 0x0000002C +#define AC97_2E_PCM_SURR_DAC_RATE 0x0000002E +#define AC97_30_PCM_LFE_DAC_RATE 0x00000030 +#define AC97_32_PCM_LR_ADC_RATE 0x00000032 +#define AC97_34_MIC_ADC_RATE 0x00000034 +#define AC97_36_6CH_VOL_C_LFE 0x00000036 +#define AC97_38_6CH_VOL_SURROUND 0x00000038 +#define AC97_3A_SPDIF_CONTROL 0x0000003A +#define AC97_3C_EXT_MODEM_ID 0x0000003C +#define AC97_3E_EXT_MODEM_POWER 0x0000003E +#define AC97_40_LINE1_CODEC_RATE 0x00000040 +#define AC97_42_LINE2_CODEC_RATE 0x00000042 +#define AC97_44_HANDSET_CODEC_RATE 0x00000044 +#define AC97_46_LINE1_CODEC_LEVEL 0x00000046 +#define AC97_48_LINE2_CODEC_LEVEL 0x00000048 +#define AC97_4A_HANDSET_CODEC_LEVEL 0x0000004A +#define AC97_4C_GPIO_PIN_CONFIG 0x0000004C +#define AC97_4E_GPIO_PIN_TYPE 0x0000004E +#define AC97_50_GPIO_PIN_STICKY 0x00000050 +#define AC97_52_GPIO_PIN_WAKEUP 0x00000052 +#define AC97_54_GPIO_PIN_STATUS 0x00000054 +#define AC97_56_RESERVED 0x00000056 +#define AC97_58_RESERVED 0x00000058 +#define AC97_5A_CRYSTAL_REV_N_FAB_ID 0x0000005A +#define AC97_5C_TEST_AND_MISC_CTRL 0x0000005C +#define AC97_5E_AC_MODE 0x0000005E +#define AC97_60_MISC_CRYSTAL_CONTROL 0x00000060 +#define AC97_62_VENDOR_RESERVED 0x00000062 +#define AC97_64_DAC_SRC_PHASE_INCR 0x00000064 +#define AC97_66_ADC_SRC_PHASE_INCR 0x00000066 +#define AC97_68_RESERVED_68 0x00000068 +#define AC97_6A_SERIAL_PORT_CONTROL 0x0000006A +#define AC97_6C_VENDOR_RESERVED 0x0000006C +#define AC97_6E_VENDOR_RESERVED 0x0000006E +#define AC97_70_BDI_CONFIG 0x00000070 +#define AC97_72_BDI_WAKEUP 0x00000072 +#define AC97_74_VENDOR_RESERVED 0x00000074 +#define AC97_76_CAL_ADDRESS 0x00000076 +#define AC97_78_CAL_DATA 0x00000078 +#define AC97_7A_VENDOR_RESERVED 0x0000007A +#define AC97_7C_VENDOR_ID1 0x0000007C +#define AC97_7E_VENDOR_ID2 0x0000007E + + +#ifndef __ASSEMBLY__ + +// +// enum type for use with reg AC97_RECORD_SELECT +// +typedef enum{ + RECORD_MIC = 0x0000, + RECORD_CD = 0x0101, + RECORD_VIDEO_IN = 0x0202, + RECORD_AUX_IN = 0x0303, + RECORD_LINE_IN = 0x0404, + RECORD_STEREO_MIX = 0x0505, + RECORD_MONO_MIX = 0x0606, + RECORD_PHONE_IN = 0x0707 +} Ac97RecordSources; + +#endif /* __ASSEMBLY__ */ + +// +// Sample rates supported directly in AC97_PCM_FRONT_DAC_RATE and +// AC97_PCM_LR_ADC_RATE. +// +#define Ac97_Fs_8000 0x1f40 +#define Ac97_Fs_11025 0x2b11 +#define Ac97_Fs_16000 0x3e80 +#define Ac97_Fs_22050 0x5622 +#define Ac97_Fs_32000 0x7d00 +#define Ac97_Fs_44100 0xac44 +#define Ac97_Fs_48000 0xbb80 + +// +// RSIZE and TSIZE in AC97RXCR and AC97TXCR +// +#define Ac97_SIZE_20 2 +#define Ac97_SIZE_18 1 +#define Ac97_SIZE_16 0 +#define Ac97_SIZE_12 3 + +//============================================================================= +//============================================================================= + + +#endif /* _REGS_AC97_H_ */ --- linux-2.6.31.orig/sound/arm/Kconfig +++ linux-2.6.31/sound/arm/Kconfig @@ -9,10 +9,27 @@ menuconfig SND_ARM Drivers that are implemented on ASoC can be found in "ALSA for SoC audio support" section. if SND_ARM +config SND_EP93XX_AC97 + tristate "AC97 driver for the Cirrus EP93xx chip" + depends on ARCH_EP93XX && SND + select SND_EP93XX_PCM + select SND_AC97_CODEC + help + Say Y here to use AC'97 audio with a Cirrus Logic EP93xx chip. + + To compile this driver as a module, choose M here: the module + will be called snd-ep93xx-ac97. + +config SND_EP93XX_PCM + tristate + select SND_PCM + help + Generic PCM module for EP93xx + config SND_ARMAACI tristate "ARM PrimeCell PL041 AC Link support" depends on ARM_AMBA select SND_PCM select SND_AC97_CODEC --- linux-2.6.31.orig/sound/arm/Makefile +++ linux-2.6.31/sound/arm/Makefile @@ -3,10 +3,13 @@ # obj-$(CONFIG_SND_ARMAACI) += snd-aaci.o snd-aaci-objs := aaci.o devdma.o +obj-$(CONFIG_SND_EP93XX_AC97) += snd-ep93xx-ac97.o +snd-ep93xx-ac97-objs := ep93xx-ac97.o + obj-$(CONFIG_SND_PXA2XX_PCM) += snd-pxa2xx-pcm.o snd-pxa2xx-pcm-objs := pxa2xx-pcm.o obj-$(CONFIG_SND_PXA2XX_LIB) += snd-pxa2xx-lib.o snd-pxa2xx-lib-y := pxa2xx-pcm-lib.o --- /dev/null +++ linux-2.6.31/sound/arm/ep93xx-ac97.c @@ -0,0 +1,3482 @@ +/* + * linux/sound/arm/ep93xx-ac97.c -- ALSA PCM interface for the edb93xx ac97 audio + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "ep93xx-ac97.h" + +#define DRIVER_VERSION "01/05/2009" +#define DRIVER_DESC "EP93xx AC97 Audio driver" +static int ac_link_enabled = 0; +static int codec_supported_mixers; + +//#define DEBUG 1 +#ifdef DEBUG +#define DPRINTK( fmt, arg... ) printk( fmt, ##arg ) +#else +#define DPRINTK( fmt, arg... ) +#endif + +#define WL16 0 +#define WL24 1 + +#define AUDIO_NAME "ep93xx-ac97" +#define AUDIO_SAMPLE_RATE_DEFAULT 44100 +#define AUDIO_DEFAULT_VOLUME 0 +#define AUDIO_MAX_VOLUME 181 +#define AUDIO_DEFAULT_DMACHANNELS 3 +#define PLAYBACK_DEFAULT_DMACHANNELS 3 +#define CAPTURE_DEFAULT_DMACHANNELS 3 + +#define CHANNEL_FRONT (1<<0) +#define CHANNEL_REAR (1<<1) +#define CHANNEL_CENTER_LFE (1<<2) + +static void snd_ep93xx_dma_tx_callback( ep93xx_dma_int_t DMAInt, + ep93xx_dma_dev_t device, + unsigned int user_data); +static void snd_ep93xx_dma_rx_callback( ep93xx_dma_int_t DMAInt, + ep93xx_dma_dev_t device, + unsigned int user_data); + +static const struct snd_pcm_hardware ep93xx_ac97_pcm_hardware = { + + + .info = ( SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE ), + .formats = ( SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE ), + .rates = ( SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 ), + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1,/*2,*/ + .channels_max = 2, + + .period_bytes_min = 1 * 1024, + .period_bytes_max = 32 * 1024, + .periods_min = 1, + .periods_max = 32, + .buffer_bytes_max = 32 * 1024, + .fifo_size = 0, +}; + +static audio_stream_t output_stream; +static audio_stream_t input_stream; + +static audio_state_t audio_state = +{ + .output_stream =&output_stream, + .output_dma[0] =DMATx_AAC1, + .output_id[0] ="Ac97 out", + + .input_stream =&input_stream, + .input_dma[0] =DMARx_AAC1, + .input_id[0] ="Ac97 in", + + .sem = __SEMAPHORE_INIT(audio_state.sem,1), + .codec_set_by_playback = 0, + .codec_set_by_capture = 0, + .DAC_bit_width =16, + .bCompactMode =0, +}; + + + +/* + * peek + * + * Reads an AC97 codec register. Returns -1 if there was an error. + */ +static int peek(unsigned int uiAddress) +{ + unsigned int uiAC97RGIS; + + if( !ac_link_enabled ) + { + printk("ep93xx ac97 peek: attempt to peek before enabling ac-link.\n"); + return -1; + } + + /* + * Check to make sure that the address is aligned on a word boundary + * and is 7E or less. + */ + if( ((uiAddress & 0x1)!=0) || (uiAddress > 0x007e)) + { + return -1; + } + + /* + * How it is supposed to work is: + * - The ac97 controller sends out a read addr in slot 1. + * - In the next frame, the codec will echo that address back in slot 1 + * and send the data in slot 2. SLOT2RXVALID will be set to 1. + * + * Read until SLOT2RXVALID goes to 1. Reading the data in AC97S2DATA + * clears SLOT2RXVALID. + */ + + /* + * First, delay one frame in case of back to back peeks/pokes. + */ + mdelay( 1 ); + + /* + * Write the address to AC97S1DATA, delay 1 frame, read the flags. + */ + outl( uiAddress, AC97S1DATA); + udelay( 21 * 4 ); + uiAC97RGIS = inl( AC97RGIS ); + + /* + * Return error if we timed out. + */ + if( ((uiAC97RGIS & AC97RGIS_SLOT1TXCOMPLETE) == 0 ) && + ((uiAC97RGIS & AC97RGIS_SLOT2RXVALID) == 0 ) ) + { + printk( "ep93xx-ac97 - peek failed reading reg 0x%02x.\n", uiAddress ); + return -1; + } + + return ( inl(AC97S2DATA) & 0x000fffff); +} + +/* + * poke + * + * Writes an AC97 codec Register. Return -1 if error. + */ +static int poke(unsigned int uiAddress, unsigned int uiValue) +{ + unsigned int uiAC97RGIS; + + if( !ac_link_enabled ) + { + printk("ep93xx ac97 poke: attempt to poke before enabling ac-link.\n"); + return -1; + } + + /* + * Check to make sure that the address is align on a word boundary and + * is 7E or less. And that the value is a 16 bit value. + */ + if( ((uiAddress & 0x1)!=0) || (uiAddress > 0x007e)) + { + printk("ep93xx ac97 poke: address error.\n"); + return -1; + } + + /*stop the audio loop from the input to the output directly*/ + + if((uiAddress==AC97_0E_MIC_VOL)||(uiAddress==AC97_10_LINE_IN_VOL)) + { + uiValue = (uiValue | 0x8000); + + } + + /* + * First, delay one frame in case of back to back peeks/pokes. + */ + mdelay( 1 ); + + /* + * Write the data to AC97S2DATA, then the address to AC97S1DATA. + */ + outl( uiValue, AC97S2DATA ); + outl( uiAddress, AC97S1DATA ); + + /* + * Wait for the tx to complete, get status. + */ + udelay( 30 );/*21*/ + uiAC97RGIS = inl(AC97RGIS); + + /* + * Return error if we timed out. + */ + if( !(inl(AC97RGIS) & AC97RGIS_SLOT1TXCOMPLETE) ) + { + printk( "ep93xx-ac97: poke failed writing reg 0x%02x value 0x%02x.\n", uiAddress, uiValue ); + return -1; + } + + return 0; +} + + +/* + * When we get to the multichannel case the pre-fill and enable code + * will go to the dma driver's start routine. + */ +static void ep93xx_audio_enable( int input_or_output_stream ) +{ + unsigned int uiTemp; + + DPRINTK("ep93xx_audio_enable :%x\n",input_or_output_stream); + + /* + * Enable the rx or tx channel depending on the value of + * input_or_output_stream + */ + if( input_or_output_stream ) + { + uiTemp = inl(AC97TXCR1); + outl( (uiTemp | AC97TXCR_TEN), AC97TXCR1 ); + } + else + { + uiTemp = inl(AC97RXCR1); + outl( (uiTemp | AC97RXCR_REN), AC97RXCR1 ); + } + + + //DDEBUG("ep93xx_audio_enable - EXIT\n"); +} + +static void ep93xx_audio_disable( int input_or_output_stream ) +{ + unsigned int uiTemp; + + DPRINTK("ep93xx_audio_disable\n"); + + /* + * Disable the rx or tx channel depending on the value of + * input_or_output_stream + */ + if( input_or_output_stream ) + { + uiTemp = inl(AC97TXCR1); + outl( (uiTemp & ~AC97TXCR_TEN), AC97TXCR1 ); + } + else + { + uiTemp = inl(AC97RXCR1); + outl( (uiTemp & ~AC97RXCR_REN), AC97RXCR1 ); + } + + //DDEBUG("ep93xx_audio_disable - EXIT\n"); +} + + + +/*=======================================================================================*/ +/* + * ep93xx_setup_src + * + * Once the ac-link is up and all is good, we want to set the codec to a + * usable mode. + */ +static void ep93xx_setup_src(void) +{ + int iTemp; + + /* + * Set the VRA bit to enable the SRC. + */ + iTemp = peek( AC97_2A_EXT_AUDIO_POWER ); + poke( AC97_2A_EXT_AUDIO_POWER, (iTemp | 0x1) ); + + /* + * Set the DSRC/ASRC bits to enable the variable rate SRC. + */ + iTemp = peek( AC97_60_MISC_CRYSTAL_CONTROL ); + poke( AC97_60_MISC_CRYSTAL_CONTROL, (iTemp | 0x0300) ); +} + +/* + * ep93xx_set_samplerate + * + * lFrequency - Sample Rate in Hz + * bCapture - 0 to set Tx sample rate; 1 to set Rx sample rate + */ +static void ep93xx_set_samplerate( long lSampleRate, int bCapture ) +{ + unsigned short usDivider, usPhase; + + DPRINTK( "ep93xx_set_samplerate - Fs = %d\n", (int)lSampleRate ); + + if( (lSampleRate < 7200) || (lSampleRate > 48000) ) + { + printk( "ep93xx_set_samplerate - invalid Fs = %d\n", + (int)lSampleRate ); + return; + } + + /* + * Calculate divider and phase increment. + * + * divider = round( 0x1770000 / lSampleRate ) + * Note that usually rounding is done by adding 0.5 to a floating + * value and then truncating. To do this without using floating + * point, I multiply the fraction by two, do the division, then add one, + * then divide the whole by 2 and then truncate. + * Same effect, no floating point math. + * + * Ph incr = trunc( (0x1000000 / usDivider) + 1 ) + */ + + usDivider = (unsigned short)( ((2 * 0x1770000 / lSampleRate) + 1) / 2 ); + + usPhase = (0x1000000 / usDivider) + 1; + + /* + * Write them in the registers. Spec says divider must be + * written after phase incr. + */ + if(!bCapture) + { + poke( AC97_2C_PCM_FRONT_DAC_RATE, usDivider); + poke( AC97_64_DAC_SRC_PHASE_INCR, usPhase); + } + else + { + + poke( AC97_32_PCM_LR_ADC_RATE, usDivider); + poke( AC97_66_ADC_SRC_PHASE_INCR, usPhase); + } + + DPRINTK( "ep93xx_set_samplerate - phase = %d, divider = %d\n", + (unsigned int)usPhase, (unsigned int)usDivider ); + + /* + * We sorta should report the actual samplerate back to the calling + * application. But some applications freak out if they don't get + * exactly what they asked for. So we fudge and tell them what + * they want to hear. + */ + //audio_samplerate = lSampleRate; + + DPRINTK( "ep93xx_set_samplerate - EXIT\n" ); +} + +/* + * ep93xx_set_hw_format + * + * Sets up whether the controller is expecting 20 bit data in 32 bit words + * or 16 bit data compacted to have a stereo sample in each 32 bit word. + */ +static void ep93xx_set_hw_format( long format,long channel ) +{ + int bCompactMode; + + switch( format ) + { + /* + * Here's all the <=16 bit formats. We can squeeze both L and R + * into one 32 bit sample so use compact mode. + */ + case SNDRV_PCM_FORMAT_U8: + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_U16_LE: + bCompactMode = 1; + break; + + /* + * Add any other >16 bit formats here... + */ + case SNDRV_PCM_FORMAT_S32_LE: + default: + bCompactMode = 0; + break; + } + + if( bCompactMode ) + { + DPRINTK("ep93xx_set_hw_format - Setting serial mode to 16 bit compact.\n"); + + /* + * Turn on Compact Mode so we can fit each stereo sample into + * a 32 bit word. Twice as efficent for DMA and FIFOs. + */ + if(channel==2){ + outl( 0x00008018, AC97RXCR1 ); + outl( 0x00008018, AC97TXCR1 ); + } + else { + outl( 0x00008018, AC97RXCR1 ); + outl( 0x00008018, AC97TXCR1 ); + } + + + audio_state.DAC_bit_width = 16; + audio_state.bCompactMode = 1; + } + else + { + DPRINTK("ep93xx_set_hw_format - Setting serial mode to 20 bit non-CM.\n"); + + /* + * Turn off Compact Mode so we can do > 16 bits per channel + */ + if(channel==2){ + outl( 0x00004018, AC97RXCR1 ); + outl( 0x00004018, AC97TXCR1 ); + } + else{ + outl( 0x00004018, AC97RXCR1 ); + outl( 0x00004018, AC97TXCR1 ); + } + + audio_state.DAC_bit_width = 20; + audio_state.bCompactMode = 0; + } + +} + +/* + * ep93xx_stop_loop + * + * Once the ac-link is up and all is good, we want to set the codec to a + * usable mode. + */ +static void ep93xx_stop_loop(void) +{ + int iTemp; + + /* + * Set the AC97_0E_MIC_VOL MUTE bit to enable the LOOP. + */ + iTemp = peek( AC97_0E_MIC_VOL ); + poke( AC97_0E_MIC_VOL, (iTemp | 0x8000) ); + + /* + * Set the AC97_10_LINE_IN_VOL MUTE bit to enable the LOOP. + */ + iTemp = peek( AC97_10_LINE_IN_VOL ); + poke( AC97_10_LINE_IN_VOL, (iTemp | 0x8000) ); +} + +/* + * ep93xx_init_ac97_controller + * + * This routine sets up the Ac'97 Controller. + */ +static void ep93xx_init_ac97_controller(void) +{ + unsigned int uiDEVCFG, uiTemp; + + DPRINTK("ep93xx_init_ac97_controller - enter\n"); + + /* + * Configure the multiplexed Ac'97 pins to be Ac97 not I2s. + * Configure the EGPIO4 and EGPIO6 to be GPIOS, not to be + * SDOUT's for the second and third I2S controller channels. + */ + uiDEVCFG = inl(EP93XX_SYSCON_DEVICE_CONFIG); + + uiDEVCFG &= ~(EP93XX_SYSCON_DEVCFG_CONFIG_I2SONAC97 | + EP93XX_SYSCON_DEVCFG_A1onG | + EP93XX_SYSCON_DEVCFG_A2onG); + + SysconSetLocked(EP93XX_SYSCON_DEVICE_CONFIG, uiDEVCFG); + + /* + * Disable the AC97 controller internal loopback. + * Disable Override codec ready. + */ + outl( 0, AC97GCR ); + + /* + * Enable the AC97 Link. + */ + uiTemp = inl(AC97GCR); + outl( (uiTemp | AC97GSR_IFE), AC97GCR ); + + /* + * Set the TIMEDRESET bit. Will cause a > 1uSec reset of the ac-link. + * This bit is self resetting. + */ + outl( AC97RESET_TIMEDRESET, AC97RESET ); + + /* + * Delay briefly, but let's not hog the processor. + */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout( 5 ); /* 50 mSec */ + + /* + * Read the AC97 status register to see if we've seen a CODECREADY + * signal from the AC97 codec. + */ + if( !(inl(AC97RGIS) & AC97RGIS_CODECREADY)) + { + printk( "ep93xx-ac97 - FAIL: CODECREADY still low!\n"); + return; + } + + /* + * Delay for a second, not hogging the processor + */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout( HZ ); /* 1 Sec */ + + /* + * Now the Ac-link is up. We can read and write codec registers. + */ + ac_link_enabled = 1; + + /* + * Set up the rx and tx channels + * Set the CM bit, data size=16 bits, enable tx slots 3 & 4. + */ + ep93xx_set_hw_format( EP93XX_DEFAULT_FORMAT,EP93XX_DEFAULT_NUM_CHANNELS ); + + DPRINTK( "ep93xx-ac97 -- AC97RXCR1: %08x\n", inl(AC97RXCR1) ); + DPRINTK( "ep93xx-ac97 -- AC97TXCR1: %08x\n", inl(AC97TXCR1) ); + + DPRINTK("ep93xx_init_ac97_controller - EXIT - success\n"); + +} + +#ifdef alsa_ac97_debug +static void ep93xx_dump_ac97_regs(void) +{ + int i; + unsigned int reg0, reg1, reg2, reg3, reg4, reg5, reg6, reg7; + + DPRINTK( "---------------------------------------------\n"); + DPRINTK( " : 0 2 4 6 8 A C E\n" ); + + for( i=0 ; i < 0x80 ; i+=0x10 ) + { + reg0 = 0xffff & (unsigned int)peek( i ); + reg1 = 0xffff & (unsigned int)peek( i + 0x2 ); + reg2 = 0xffff & (unsigned int)peek( i + 0x4 ); + reg3 = 0xffff & (unsigned int)peek( i + 0x6 ); + reg4 = 0xffff & (unsigned int)peek( i + 0x8 ); + reg5 = 0xffff & (unsigned int)peek( i + 0xa ); + reg6 = 0xffff & (unsigned int)peek( i + 0xc ); + reg7 = 0xffff & (unsigned int)peek( i + 0xe ); + + DPRINTK( " %02x : %04x %04x %04x %04x %04x %04x %04x %04x\n", + i, reg0, reg1, reg2, reg3, reg4, reg5, reg6, reg7); + } + + DPRINTK( "---------------------------------------------\n"); +} +#endif + + +#define supported_mixer(FOO) \ + ( (FOO >= 0) && \ + (FOO < SOUND_MIXER_NRDEVICES) && \ + codec_supported_mixers & (1< ac97 quickly. */ +enum ac97_recsettings +{ + AC97_REC_MIC=0, + AC97_REC_CD, + AC97_REC_VIDEO, + AC97_REC_AUX, + AC97_REC_LINE, + AC97_REC_STEREO, /* combination of all enabled outputs.. */ + AC97_REC_MONO, /*.. or the mono equivalent */ + AC97_REC_PHONE +}; + +static const unsigned int ac97_rm2oss[] = +{ + [AC97_REC_MIC] = SOUND_MIXER_MIC, + [AC97_REC_CD] = SOUND_MIXER_CD, + [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO, + [AC97_REC_AUX] = SOUND_MIXER_LINE1, + [AC97_REC_LINE] = SOUND_MIXER_LINE, + [AC97_REC_STEREO]= SOUND_MIXER_IGAIN, + [AC97_REC_PHONE] = SOUND_MIXER_PHONEIN +}; + +/* indexed by bit position */ +static const unsigned int ac97_oss_rm[] = +{ + [SOUND_MIXER_MIC] = AC97_REC_MIC, + [SOUND_MIXER_CD] = AC97_REC_CD, + [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO, + [SOUND_MIXER_LINE1] = AC97_REC_AUX, + [SOUND_MIXER_LINE] = AC97_REC_LINE, + [SOUND_MIXER_IGAIN] = AC97_REC_STEREO, + [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE +}; + + +/* + * ep93xx_write_mixer + * + */ +static void ep93xx_write_mixer +( + int oss_channel, + unsigned int left, + unsigned int right +) +{ + u16 val = 0; + ac97_mixer_hw_t * mh = &ac97_hw[oss_channel]; + + DPRINTK("ac97_codec: wrote OSS %2d (ac97 0x%02x), " + "l:%2d, r:%2d:", + oss_channel, mh->offset, left, right); + + if( !mh->scale ) + { + printk( "ep93xx-ac97.c: ep93xx_write_mixer - not a valid OSS channel\n"); + return; + } + + if( AC97_STEREO_MASK & (1 << oss_channel) ) + { + /* stereo mixers */ + if (left == 0 && right == 0) + { + val = 0x8000; + } + else + { + if (oss_channel == SOUND_MIXER_IGAIN) + { + right = (right * mh->scale) / 100; + left = (left * mh->scale) / 100; + if (right >= mh->scale) + right = mh->scale-1; + if (left >= mh->scale) + left = mh->scale-1; + } + else + { + right = ((100 - right) * mh->scale) / 100; + left = ((100 - left) * mh->scale) / 100; + if (right >= mh->scale) + right = mh->scale-1; + if (left >= mh->scale) + left = mh->scale-1; + } + val = (left << 8) | right; + } + } + else if(left == 0) + { + val = 0x8000; + } + else if( (oss_channel == SOUND_MIXER_SPEAKER) || + (oss_channel == SOUND_MIXER_PHONEIN) || + (oss_channel == SOUND_MIXER_PHONEOUT) ) + { + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val = left; + } + else if (oss_channel == SOUND_MIXER_MIC) + { + val = peek( mh->offset) & ~0x801f; + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val |= left; + } + /* + * For bass and treble, the low bit is optional. Masking it + * lets us avoid the 0xf 'bypass'. + * Do a read, modify, write as we have two contols in one reg. + */ + else if (oss_channel == SOUND_MIXER_BASS) + { + val = peek( mh->offset) & ~0x0f00; + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val |= (left << 8) & 0x0e00; + } + else if (oss_channel == SOUND_MIXER_TREBLE) + { + val = peek( mh->offset) & ~0x000f; + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val |= left & 0x000e; + } + + DPRINTK(" 0x%04x", val); + + poke( mh->offset, val ); + +#ifdef alsa_ac97_debug + val = peek( mh->offset ); + DEBUG(" -> 0x%04x\n", val); +#endif + +} + +/* a thin wrapper for write_mixer */ +static void ep93xx_set_mixer +( + unsigned int oss_mixer, + unsigned int val +) +{ + unsigned int left,right; + + /* cleanse input a little */ + right = ((val >> 8) & 0xff) ; + left = (val & 0xff) ; + + if (right > 100) right = 100; + if (left > 100) left = 100; + + /*mixer_state[oss_mixer] = (right << 8) | left;*/ + ep93xx_write_mixer( oss_mixer, left, right); +} + +static void ep93xx_init_mixer(void) +{ + u16 cap; + int i; + + /* mixer masks */ + codec_supported_mixers = AC97_SUPPORTED_MASK; + + cap = peek( AC97_00_RESET ); + if( !(cap & 0x04) ) + { + codec_supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE); + } + if( !(cap & 0x10) ) + { + codec_supported_mixers &= ~SOUND_MASK_ALTPCM; + } + + /* + * Detect bit resolution of output volume controls by writing to the + * 6th bit (not unmuting yet) + */ + poke( AC97_02_MASTER_VOL, 0xa020 ); + if( peek( AC97_02_MASTER_VOL) != 0xa020 ) + { + ac97_hw[SOUND_MIXER_VOLUME].scale = 32; + } + + poke( AC97_04_HEADPHONE_VOL, 0xa020 ); + if( peek( AC97_04_HEADPHONE_VOL) != 0xa020 ) + { + ac97_hw[AC97_04_HEADPHONE_VOL].scale = 32; + } + + poke( AC97_06_MONO_VOL, 0x8020 ); + if( peek( AC97_06_MONO_VOL) != 0x8020 ) + { + ac97_hw[AC97_06_MONO_VOL].scale = 32; + } + + /* initialize mixer channel volumes */ + for( i = 0; + (i < SOUND_MIXER_NRDEVICES) && (mixer_defaults[i].mixer != -1) ; + i++ ) + { + if( !supported_mixer( mixer_defaults[i].mixer) ) + { + continue; + } + + ep93xx_set_mixer( mixer_defaults[i].mixer, mixer_defaults[i].value); + } + +} + +static int ep93xx_set_recsource( int mask ) +{ + unsigned int val; + + /* Arg contains a bit for each recording source */ + if( mask == 0 ) + { + return 0; + } + + mask &= AC97_RECORD_MASK; + + if( mask == 0 ) + { + return -EINVAL; + } + + /* + * May have more than one bit set. So clear out currently selected + * record source value first (AC97 supports only 1 input) + */ + val = (1 << ac97_rm2oss[peek( AC97_1A_RECORD_SELECT ) & 0x07]); + if (mask != val) + mask &= ~val; + + val = ffs(mask); + val = ac97_oss_rm[val-1]; + val |= val << 8; /* set both channels */ + + /* + * + */ + val = peek( AC97_1A_RECORD_SELECT ) & 0x0707; + if ((val&0x0404)!=0) + val=0x0404; + else if((val&0x0000)!=0) + val=0x0101; + + + DPRINTK("ac97_codec: setting ac97 recmask to 0x%04x\n", val); + + poke( AC97_1A_RECORD_SELECT, val); + + return 0; +} + +/* + * ep93xx_init_ac97_codec + * + * Program up the external Ac97 codec. + * + */ +static void ep93xx_init_ac97_codec( void ) +{ + DPRINTK("ep93xx_init_ac97_codec - enter\n"); + + ep93xx_setup_src(); + ep93xx_set_samplerate( AUDIO_SAMPLE_RATE_DEFAULT, 0 ); + ep93xx_set_samplerate( AUDIO_SAMPLE_RATE_DEFAULT, 1 ); + ep93xx_init_mixer(); + + DPRINTK("ep93xx_init_ac97_codec - EXIT\n"); + +} + + +/* + * ep93xx_audio_init + * Audio interface + */ +static void ep93xx_audio_init(void) +{ + DPRINTK("ep93xx_audio_init - enter\n"); + /* + * Init the controller, enable the ac-link. + * Initialize the codec. + */ + ep93xx_init_ac97_controller(); + ep93xx_init_ac97_codec(); + /*stop the audio loop from the input to the output directly*/ + ep93xx_stop_loop(); + +#ifdef alsa_ac97_debug + ep93xx_dump_ac97_regs(); +#endif + DPRINTK("ep93xx_audio_init - EXIT\n"); +} + +/*====================================================================================*/ + + +static void print_audio_format( long format ) +{ + switch( format ){ + case SNDRV_PCM_FORMAT_S8: + DPRINTK( "AFMT_S8\n" ); + break; + + case SNDRV_PCM_FORMAT_U8: + DPRINTK( "AFMT_U8\n" ); + break; + + case SNDRV_PCM_FORMAT_S16_LE: + DPRINTK( "AFMT_S16_LE\n" ); + break; + + case SNDRV_PCM_FORMAT_S16_BE: + DPRINTK( "AFMT_S16_BE\n" ); + break; + + case SNDRV_PCM_FORMAT_U16_LE: + DPRINTK( "AFMT_U16_LE\n" ); + break; + case SNDRV_PCM_FORMAT_U16_BE: + DPRINTK( "AFMT_U16_BE\n" ); + break; + + case SNDRV_PCM_FORMAT_S24_LE: + DPRINTK( "AFMT_S24_LE\n" ); + break; + + case SNDRV_PCM_FORMAT_S24_BE: + DPRINTK( "AFMT_S24_BE\n" ); + break; + + case SNDRV_PCM_FORMAT_U24_LE: + DPRINTK( "AFMT_U24_LE\n" ); + break; + + case SNDRV_PCM_FORMAT_U24_BE: + DPRINTK( "AFMT_U24_BE\n" ); + break; + case SNDRV_PCM_FORMAT_S32_LE: + DPRINTK( "AFMT_S24_LE\n" ); + break; + + case SNDRV_PCM_FORMAT_S32_BE: + DPRINTK( "AFMT_S24_BE\n" ); + break; + + case SNDRV_PCM_FORMAT_U32_LE: + DPRINTK( "AFMT_U24_LE\n" ); + break; + + case SNDRV_PCM_FORMAT_U32_BE: + DPRINTK( "AFMT_U24_BE\n" ); + break; + default: + DPRINTK( "ep93xx_i2s_Unsupported Audio Format\n" ); + break; + } +} + +static void audio_set_format( audio_stream_t * s, long val ) +{ + DPRINTK( "ep93xx_i2s_audio_set_format enter. Format requested (%d) %d ", + (int)val,SNDRV_PCM_FORMAT_S16_LE); + print_audio_format( val ); + + switch( val ){ + case SNDRV_PCM_FORMAT_S8: + s->audio_format = SNDRV_PCM_FORMAT_S8; + s->audio_stream_bitwidth = 8; + break; + + case SNDRV_PCM_FORMAT_U8: + s->audio_format = SNDRV_PCM_FORMAT_U8; + s->audio_stream_bitwidth = 8; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + s->audio_format = SNDRV_PCM_FORMAT_S16_LE; + s->audio_stream_bitwidth = 16; + break; + + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_U16_BE: + s->audio_format = SNDRV_PCM_FORMAT_U16_LE; + s->audio_stream_bitwidth = 16; + break; + + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + s->audio_format = SNDRV_PCM_FORMAT_S24_LE; + s->audio_stream_bitwidth = 24; + break; + + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_U24_BE: + s->audio_format = SNDRV_PCM_FORMAT_U24_LE; + s->audio_stream_bitwidth = 24; + break; + + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_U32_BE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + s->audio_format = SNDRV_PCM_FORMAT_S32_LE; + s->audio_stream_bitwidth = 32; + break; + default: + DPRINTK( "ep93xx_i2s_Unsupported Audio Format\n" ); + break; + } + + DPRINTK( "ep93xx_i2s_audio_set_format EXIT format set to be (%d) ", (int)s->audio_format ); + print_audio_format( (long)s->audio_format ); +} + +static __inline__ unsigned long copy_to_user_S24_LE +( + audio_stream_t *stream, + const char *to, + unsigned long to_count +) +{ + int *dma_buffer_0 = (int *)stream->hwbuf[0]; + int *dma_buffer_1 = (int *)stream->hwbuf[1]; + int *dma_buffer_2 = (int *)stream->hwbuf[2]; + + int total_to_count = to_count; + int *user_ptr = (int *)to; /* 32 bit user buffer */ + int count; + + count = 8 * stream->dma_num_channels; + + while (to_count > 0){ + + __put_user( (int)( *dma_buffer_0++ ), user_ptr++ ); + __put_user( (int)( *dma_buffer_0++ ), user_ptr++ ); + + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __put_user( (int)( *dma_buffer_1++ ), user_ptr++ ); + __put_user( (int)( *dma_buffer_1++ ), user_ptr++ ); + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __put_user( (int)( *dma_buffer_2++ ), user_ptr++ ); + __put_user( (int)( *dma_buffer_2++ ), user_ptr++ ); + } + to_count -= count; + } + return total_to_count; +} + +static __inline__ unsigned long copy_to_user_U24_LE +( + audio_stream_t *stream, + const char *to, + unsigned long to_count +) +{ + int *dma_buffer_0 = (int *)stream->hwbuf[0]; + int *dma_buffer_1 = (int *)stream->hwbuf[1]; + int *dma_buffer_2 = (int *)stream->hwbuf[2]; + + int total_to_count = to_count; + unsigned int * user_ptr = (unsigned int *)to; /* 32 bit user buffer */ + int count; + + count = 8 * stream->dma_num_channels; + + while (to_count > 0){ + __put_user( ((unsigned int)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ ); + __put_user( ((unsigned int)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ ); + + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __put_user( ((unsigned int)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ ); + __put_user( ((unsigned int)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ ); + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __put_user( ((unsigned int)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ ); + __put_user( ((unsigned int)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ ); + } + to_count -= count; + } + return total_to_count; +} + +static __inline__ unsigned long copy_to_user_S16_LE +( + audio_stream_t *stream, + const char *to, + unsigned long to_count +) +{ + int *dma_buffer_0 = (int *)stream->hwbuf[0]; + int *dma_buffer_1 = (int *)stream->hwbuf[1]; + int *dma_buffer_2 = (int *)stream->hwbuf[2]; + int total_to_count = to_count; + short * user_ptr = (short *)to; /* 16 bit user buffer */ + int count; + + count = 4 * stream->dma_num_channels; + + while (to_count > 0){ + + __put_user( (short)( *dma_buffer_0++ ), user_ptr++ ); + __put_user( (short)( *dma_buffer_0++ ), user_ptr++ ); + + if( stream->audio_channels_flag & CHANNEL_REAR ){ + __put_user( (short)( *dma_buffer_1++ ), user_ptr++ ); + __put_user( (short)( *dma_buffer_1++ ), user_ptr++ ); + } + + if( stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __put_user( (short)( *dma_buffer_2++ ), user_ptr++ ); + __put_user( (short)( *dma_buffer_2++ ), user_ptr++ ); + } + to_count -= count; + } + return total_to_count; +} + +static __inline__ unsigned long copy_to_user_U16_LE +( + audio_stream_t *stream, + const char *to, + unsigned long to_count +) +{ + int *dma_buffer_0 = (int *)stream->hwbuf[0]; + int *dma_buffer_1 = (int *)stream->hwbuf[1]; + int *dma_buffer_2 = (int *)stream->hwbuf[2]; + int count; + int total_to_count = to_count; + short * user_ptr = (short *)to; /* 16 bit user buffer */ + + count = 4 * stream->dma_num_channels; + + while (to_count > 0){ + + __put_user( ((unsigned short)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ ); + __put_user( ((unsigned short)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ ); + + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __put_user( ((unsigned short)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ ); + __put_user( ((unsigned short)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ ); + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __put_user( ((unsigned short)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ ); + __put_user( ((unsigned short)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ ); + } + to_count -= count; + } + return total_to_count; +} + +static __inline__ unsigned long copy_to_user_S8 +( + audio_stream_t *stream, + const char *to, + unsigned long to_count +) +{ + char *dma_buffer_0 = (char *)stream->hwbuf[0]; + char *dma_buffer_1 = (char *)stream->hwbuf[1]; + char *dma_buffer_2 = (char *)stream->hwbuf[2]; + int count; + int total_to_count = to_count; + char * user_ptr = (char *)to; /* 8 bit user buffer */ + + count = 2 * stream->dma_num_channels; + + dma_buffer_0++; + dma_buffer_1++; + dma_buffer_2++; + + while (to_count > 0){ + + __put_user( (char)( *dma_buffer_0 ), user_ptr++ ); + dma_buffer_0 += 4; + __put_user( (char)( *dma_buffer_0 ), user_ptr++ ); + dma_buffer_0 += 4; + + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __put_user( (char)( *dma_buffer_1 ), user_ptr++ ); + dma_buffer_1 += 4; + __put_user( (char)( *dma_buffer_1 ), user_ptr++ ); + dma_buffer_1 += 4; + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __put_user( (char)( *dma_buffer_2 ), user_ptr++ ); + dma_buffer_2 += 4; + __put_user( (char)( *dma_buffer_2 ), user_ptr++ ); + dma_buffer_2 += 4; + } + to_count -= count; + } + return total_to_count; +} + +static __inline__ unsigned long copy_to_user_U8 +( + audio_stream_t *stream, + const char *to, + unsigned long to_count +) +{ + char *dma_buffer_0 = (char *)stream->hwbuf[0]; + char *dma_buffer_1 = (char *)stream->hwbuf[1]; + char *dma_buffer_2 = (char *)stream->hwbuf[2]; + int count; + int total_to_count = to_count; + char * user_ptr = (char *)to; /* 8 bit user buffer */ + + count = 2 * stream->dma_num_channels; + + dma_buffer_0++; + dma_buffer_1++; + dma_buffer_2++; + + while (to_count > 0){ + + __put_user( (char)( *dma_buffer_0 ) ^ 0x80, user_ptr++ ); + dma_buffer_0 += 4; + __put_user( (char)( *dma_buffer_0 ) ^ 0x80, user_ptr++ ); + dma_buffer_0 += 4; + + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __put_user( (char)( *dma_buffer_1 ) ^ 0x80, user_ptr++ ); + dma_buffer_1 += 4; + __put_user( (char)( *dma_buffer_1 ) ^ 0x80, user_ptr++ ); + dma_buffer_1 += 4; + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __put_user( (char)( *dma_buffer_2 ) ^ 0x80, user_ptr++ ); + dma_buffer_2 += 4; + __put_user( (char)( *dma_buffer_2 ) ^ 0x80, user_ptr++ ); + dma_buffer_2 += 4; + } + to_count -= count; + } + return total_to_count; +} + + + + +static __inline__ unsigned long copy_to_user_S16_LE_CM +( + audio_stream_t *stream, + const char *to, + unsigned long to_count +) +{ + short *dma_buffer_0 = (short *)stream->hwbuf[0]; + int *dma_buffer_1 = (int *)stream->hwbuf[1]; + int *dma_buffer_2 = (int *)stream->hwbuf[2]; + int total_to_count = to_count; + short * user_ptr = (short *)to; /* 16 bit user buffer */ + int count; + + + count = 4 * stream->dma_num_channels; + + while (to_count > 0){ + if(stream->audio_num_channels == 2){ + __put_user( (short)( *dma_buffer_0++ ), user_ptr++ ); + __put_user( (short)( *dma_buffer_0++ ), user_ptr++ ); + to_count -= count; + } + else{ + dma_buffer_0++; + __put_user( (short)( *dma_buffer_0++ ), user_ptr++ ); + to_count -= 2; + } + + if( stream->audio_channels_flag & CHANNEL_REAR ){ + __put_user( (short)( *dma_buffer_1++ ), user_ptr++ ); + __put_user( (short)( *dma_buffer_1++ ), user_ptr++ ); + } + + if( stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __put_user( (short)( *dma_buffer_2++ ), user_ptr++ ); + __put_user( (short)( *dma_buffer_2++ ), user_ptr++ ); + } + //to_count -= count; + } + return total_to_count; +} + +static __inline__ unsigned long copy_to_user_U16_LE_CM +( + audio_stream_t *stream, + const char *to, + unsigned long to_count +) +{ + unsigned short *dma_buffer_0 = (unsigned short *)stream->hwbuf[0]; + int *dma_buffer_1 = (int *)stream->hwbuf[1]; + int *dma_buffer_2 = (int *)stream->hwbuf[2]; + int count; + int total_to_count = to_count; + unsigned short * user_ptr = (unsigned short *)to; /* 16 bit user buffer */ + + count = 4 * stream->dma_num_channels; + + while (to_count > 0){ + + if(stream->audio_num_channels == 2){ + __put_user( ((unsigned short)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ ); + __put_user( ((unsigned short)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ ); + to_count -= count; + } + else{ + dma_buffer_0++; + __put_user( ((unsigned short)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ ); + to_count -= 2; + } + + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __put_user( ((unsigned short)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ ); + __put_user( ((unsigned short)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ ); + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __put_user( ((unsigned short)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ ); + __put_user( ((unsigned short)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ ); + } + //to_count -= count; + } + return total_to_count; +} + +static __inline__ unsigned long copy_to_user_S8_CM +( + audio_stream_t *stream, + const char *to, + unsigned long to_count +) +{ + unsigned short *dma_buffer_0 = (unsigned short *)stream->hwbuf[0]; + char *dma_buffer_1 = (char *)stream->hwbuf[1]; + char *dma_buffer_2 = (char *)stream->hwbuf[2]; + int count; + int total_to_count = to_count; + char * user_ptr = (char *)to; /* 8 bit user buffer */ + + count = 2 * stream->dma_num_channels; + + dma_buffer_0++; + dma_buffer_1++; + dma_buffer_2++; + + while (to_count > 0){ + if(stream->audio_num_channels == 2){ + __put_user( (char)( *dma_buffer_0++ >> 8), user_ptr++ ); + //dma_buffer_0 += 4; + __put_user( (char)( *dma_buffer_0++ >> 8), user_ptr++ ); + //dma_buffer_0 += 4; + to_count -= count; + } + else{ + dma_buffer_0++ ; + __put_user( (char)( *dma_buffer_0++ >> 8), user_ptr++ ); + + to_count -= 1; + } + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __put_user( (char)( *dma_buffer_1 ), user_ptr++ ); + dma_buffer_1 += 4; + __put_user( (char)( *dma_buffer_1 ), user_ptr++ ); + dma_buffer_1 += 4; + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __put_user( (char)( *dma_buffer_2 ), user_ptr++ ); + dma_buffer_2 += 4; + __put_user( (char)( *dma_buffer_2 ), user_ptr++ ); + dma_buffer_2 += 4; + } + //to_count -= count; + } + return total_to_count; +} + +static __inline__ unsigned long copy_to_user_U8_CM +( + audio_stream_t *stream, + const char *to, + unsigned long to_count +) +{ + unsigned short *dma_buffer_0 = (unsigned short *)stream->hwbuf[0]; + char *dma_buffer_1 = (char *)stream->hwbuf[1]; + char *dma_buffer_2 = (char *)stream->hwbuf[2]; + int count; + int total_to_count = to_count; + char * user_ptr = (char *)to; /* 8 bit user buffer */ + + count = 2 * stream->dma_num_channels; + + dma_buffer_0++; + dma_buffer_1++; + dma_buffer_2++; + + while (to_count > 0){ + if(stream->audio_num_channels == 2){ + __put_user( (char)( *dma_buffer_0++ >>8) ^ 0x80, user_ptr++ ); + //dma_buffer_0 += 4; + __put_user( (char)( *dma_buffer_0++ >>8) ^ 0x80, user_ptr++ ); + //dma_buffer_0 += 4; + to_count -= count; + } + else{ + dma_buffer_0++; + __put_user( (char)( *dma_buffer_0++ >>8) ^ 0x80, user_ptr++ ); + //dma_buffer_0 += 4; + to_count--; + } + + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __put_user( (char)( *dma_buffer_1 ) ^ 0x80, user_ptr++ ); + dma_buffer_1 += 4; + __put_user( (char)( *dma_buffer_1 ) ^ 0x80, user_ptr++ ); + dma_buffer_1 += 4; + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __put_user( (char)( *dma_buffer_2 ) ^ 0x80, user_ptr++ ); + dma_buffer_2 += 4; + __put_user( (char)( *dma_buffer_2 ) ^ 0x80, user_ptr++ ); + dma_buffer_2 += 4; + } + //to_count -= count; + } + return total_to_count; +} + +static __inline__ unsigned long copy_to_user_U32 +( + audio_stream_t *stream, + const char *to, + unsigned long to_count +) +{ + char *dma_buffer_0 = (char *)stream->hwbuf[0]; + + if(__copy_to_user( (char *)to, dma_buffer_0, to_count)) + { + return -EFAULT; + } + return to_count; +} + +static __inline__ int copy_to_user_with_conversion +( + audio_stream_t *stream, + const char *to, + int toCount, + int bCompactMode +) +{ + int ret = 0; + + if( toCount == 0 ){ + DPRINTK("ep93xx_i2s_copy_to_user_with_conversion - nothing to copy!\n"); + } + + if( bCompactMode == 1 ){ + + switch( stream->audio_format ){ + + case SNDRV_PCM_FORMAT_S8: + ret = copy_to_user_S8_CM( stream, to, toCount ); + break; + + case SNDRV_PCM_FORMAT_U8: + ret = copy_to_user_U8_CM( stream, to, toCount ); + break; + + case SNDRV_PCM_FORMAT_S16_LE: + ret = copy_to_user_S16_LE_CM( stream, to, toCount ); + break; + + case SNDRV_PCM_FORMAT_U16_LE: + ret = copy_to_user_U16_LE_CM( stream, to, toCount ); + break; + + case SNDRV_PCM_FORMAT_S24_LE: + //ret = copy_to_user_S24_LE( stream, to, toCount ); + //break; + + case SNDRV_PCM_FORMAT_U24_LE: + //ret = copy_to_user_U24_LE( stream, to, toCount ); + //break; + + case SNDRV_PCM_FORMAT_S32_LE: + default: + DPRINTK( "ep93xx_i2s copy to user unsupported audio format %x\n",stream->audio_format ); + break; + } + + } + else{ + + switch( stream->audio_format ){ + + case SNDRV_PCM_FORMAT_S8: + ret = copy_to_user_S8( stream, to, toCount ); + break; + + case SNDRV_PCM_FORMAT_U8: + ret = copy_to_user_U8( stream, to, toCount ); + break; + + case SNDRV_PCM_FORMAT_S16_LE: + ret = copy_to_user_S16_LE( stream, to, toCount ); + break; + + case SNDRV_PCM_FORMAT_U16_LE: + ret = copy_to_user_U16_LE( stream, to, toCount ); + break; + + case SNDRV_PCM_FORMAT_S24_LE: + //ret = copy_to_user_S24_LE( stream, to, toCount ); + //break; + + case SNDRV_PCM_FORMAT_U24_LE: + //ret = copy_to_user_U24_LE( stream, to, toCount ); + //break; + DPRINTK( "ep93xx_i2s copy to user unsupported audio format %x\n",stream->audio_format ); + break; + + case SNDRV_PCM_FORMAT_S32_LE: + + //__copy_to_user( (char *)to, from, toCount); + ret = copy_to_user_U32( stream, to, toCount ); + break; + default: + DPRINTK( "ep93xx_i2s copy to user unsupported audio format\n" ); + break; + } + + } + return ret; +} + +static __inline__ int copy_from_user_S24_LE +( + audio_stream_t *stream, + const char *from, + int toCount +) +{ + int *dma_buffer_0 = (int *)stream->hwbuf[0]; + int *dma_buffer_1 = (int *)stream->hwbuf[1]; + int *dma_buffer_2 = (int *)stream->hwbuf[2]; + int count; + + unsigned int * user_buffer = (unsigned int *)from; + unsigned int data; + + int toCount0 = toCount; + count = 8 * stream->dma_num_channels; + + while (toCount > 0){ + + __get_user(data, user_buffer++); + *dma_buffer_0++ = (unsigned int)data; + __get_user(data, user_buffer++); + *dma_buffer_0++ = (unsigned int)data; + + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __get_user(data, user_buffer++); + *dma_buffer_1++ = (unsigned int)data; + __get_user(data, user_buffer++); + *dma_buffer_1++ = (unsigned int)data; + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __get_user(data, user_buffer++); + *dma_buffer_2++ = (unsigned int)data; + __get_user(data, user_buffer++); + *dma_buffer_2++ = (unsigned int)data; + } + toCount -= count; + } + return toCount0 / 2; +} + +static __inline__ int copy_from_user_U24_LE +( + audio_stream_t *stream, + const char *from, + int toCount +) +{ + int *dma_buffer_0 = (int *)stream->hwbuf[0]; + int *dma_buffer_1 = (int *)stream->hwbuf[1]; + int *dma_buffer_2 = (int *)stream->hwbuf[2]; + int count; + unsigned int * user_buffer = (unsigned int *)from; + unsigned int data; + + int toCount0 = toCount; + count = 8 * stream->dma_num_channels; + + while (toCount > 0){ + + __get_user(data, user_buffer++); + *dma_buffer_0++ = ((unsigned int)data ^ 0x8000); + __get_user(data, user_buffer++); + *dma_buffer_0++ = ((unsigned int)data ^ 0x8000); + + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __get_user(data, user_buffer++); + *dma_buffer_1++ = ((unsigned int)data ^ 0x8000); + __get_user(data, user_buffer++); + *dma_buffer_1++ = ((unsigned int)data ^ 0x8000); + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __get_user(data, user_buffer++); + *dma_buffer_2++ = ((unsigned int)data ^ 0x8000); + __get_user(data, user_buffer++); + *dma_buffer_2++ = ((unsigned int)data ^ 0x8000); + } + toCount -= count; + } + return toCount0 / 2; +} + +static __inline__ int copy_from_user_S16_LE +( + audio_stream_t *stream, + const char *from, + int toCount +) +{ + int *dma_buffer_0 = (int *)stream->hwbuf[0]; + int *dma_buffer_1 = (int *)stream->hwbuf[1]; + int *dma_buffer_2 = (int *)stream->hwbuf[2]; + unsigned short *user_buffer = (unsigned short *)from; + unsigned short data; + + int toCount0 = toCount; + int count; + count = 8 * stream->dma_num_channels; + + while (toCount > 0){ + + __get_user(data, user_buffer++); + *dma_buffer_0++ = data; + if(stream->audio_num_channels == 2){ + __get_user(data, user_buffer++); + } + *dma_buffer_0++ = data; + + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __get_user(data, user_buffer++); + *dma_buffer_1++ = data; + __get_user(data, user_buffer++); + *dma_buffer_1++ = data; + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __get_user(data, user_buffer++); + *dma_buffer_2++ = data; + __get_user(data, user_buffer++); + *dma_buffer_2++ = data; + } + toCount -= count; + } + + if(stream->audio_num_channels == 1){ + return toCount0 / 4; + } + return toCount0 / 2; +} + +static __inline__ int copy_from_user_U16_LE +( + audio_stream_t *stream, + const char *from, + int toCount +) +{ + int *dma_buffer_0 = (int *)stream->hwbuf[0]; + int *dma_buffer_1 = (int *)stream->hwbuf[1]; + int *dma_buffer_2 = (int *)stream->hwbuf[2]; + int count; + unsigned short * user_buffer = (unsigned short *)from; + unsigned short data; + + int toCount0 = toCount; + count = 8 * stream->dma_num_channels; + + while (toCount > 0){ + + __get_user(data, user_buffer++); + *dma_buffer_0++ = ((unsigned int)data ^ 0x8000); + if(stream->audio_num_channels == 2){ + __get_user(data, user_buffer++); + } + *dma_buffer_0++ = ((unsigned int)data ^ 0x8000); + + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __get_user(data, user_buffer++); + *dma_buffer_1++ = ((unsigned int)data ^ 0x8000); + __get_user(data, user_buffer++); + *dma_buffer_1++ = ((unsigned int)data ^ 0x8000); + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __get_user(data, user_buffer++); + *dma_buffer_2++ = ((unsigned int)data ^ 0x8000); + __get_user(data, user_buffer++); + *dma_buffer_2++ = ((unsigned int)data ^ 0x8000); + } + toCount -= count; + } + + if(stream->audio_num_channels == 1){ + return toCount0 / 4; + } + return toCount0 / 2; +} + +static __inline__ int copy_from_user_S8 +( + audio_stream_t *stream, + const char *from, + int toCount +) +{ + char *dma_buffer_0 = (char *)stream->hwbuf[0]; + char *dma_buffer_1 = (char *)stream->hwbuf[1]; + char *dma_buffer_2 = (char *)stream->hwbuf[2]; + int count; + unsigned char * user_buffer = (unsigned char *)from; + unsigned char data; + + int toCount0 = toCount; + count = 8 * stream->dma_num_channels; + + dma_buffer_0++; + dma_buffer_1++; + dma_buffer_2++; + + while (toCount > 0){ + __get_user(data, user_buffer++); + *dma_buffer_0 = data; + dma_buffer_0 += 4; + if(stream->audio_num_channels == 2){ + __get_user(data, user_buffer++); + } + *dma_buffer_0 = data; + dma_buffer_0 += 4; + + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __get_user(data, user_buffer++); + *dma_buffer_1 = data; + dma_buffer_1 += 4; + __get_user(data, user_buffer++); + *dma_buffer_1 = data; + dma_buffer_1 += 4; + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __get_user(data, user_buffer++); + *dma_buffer_2 = data; + dma_buffer_2 += 4; + __get_user(data, user_buffer++); + *dma_buffer_2 = data; + dma_buffer_2 += 4; + } + toCount -= count; + } + + if(stream->audio_num_channels == 1){ + return toCount0 / 8; + } + return toCount0 / 4; +} + +static __inline__ int copy_from_user_U8 +( + audio_stream_t *stream, + const char *from, + int toCount +) +{ + char *dma_buffer_0 = (char *)stream->hwbuf[0]; + char *dma_buffer_1 = (char *)stream->hwbuf[1]; + char *dma_buffer_2 = (char *)stream->hwbuf[2]; + int count; + unsigned char *user_buffer = (unsigned char *)from; + unsigned char data; + + int toCount0 = toCount; + count = 8 * stream->dma_num_channels; + + dma_buffer_0 ++; + dma_buffer_1 ++; + dma_buffer_2 ++; + + while (toCount > 0){ + + __get_user(data, user_buffer++); + *dma_buffer_0 = ((unsigned char)data ^ 0x80); + dma_buffer_0 += 4; + if(stream->audio_num_channels == 2){ + __get_user(data, user_buffer++); + } + *dma_buffer_0 = ((unsigned char)data ^ 0x80); + dma_buffer_0 += 4; + + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __get_user(data, user_buffer++); + *dma_buffer_1 = ((unsigned char)data ^ 0x80); + dma_buffer_1 += 4; + __get_user(data, user_buffer++); + *dma_buffer_1 = ((unsigned char)data ^ 0x80); + dma_buffer_1 += 4; + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __get_user(data, user_buffer++); + *dma_buffer_2 = ((unsigned char)data ^ 0x80); + dma_buffer_2 += 4; + __get_user(data, user_buffer++); + *dma_buffer_2 = ((unsigned char)data ^ 0x80); + dma_buffer_2 += 4; + } + toCount -= count; + } + + if(stream->audio_num_channels == 1){ + return toCount0 / 8; + } + return toCount0 / 4; +} + +static __inline__ int copy_from_user_S16_LE_CM +( + audio_stream_t *stream, + const char *from, + int toCount +) +{ + unsigned int *dma_buffer_0 = (int *)stream->hwbuf[0]; + unsigned int *dma_buffer_1 = (int *)stream->hwbuf[1]; + unsigned int *dma_buffer_2 = (int *)stream->hwbuf[2]; + unsigned short *user_buffer = (unsigned short *)from; + short data; + unsigned int val; + int toCount0 = toCount; + int count; + count = 4 * stream->dma_num_channels; + + //printk("count=%x tocount\n",count,toCount); + while (toCount > 0){ + + __get_user(data, user_buffer++); + //*dma_buffer_0++ = data; + val = (unsigned int)data & 0x0000ffff; + if(stream->audio_num_channels == 2){ + __get_user(data, user_buffer++); + } + *dma_buffer_0++ = ((unsigned int)data << 16) | val; + + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __get_user(data, user_buffer++); + //*dma_buffer_1++ = data; + val = (unsigned int)data & 0x0000ffff; + __get_user(data, user_buffer++); + *dma_buffer_1++ = ((unsigned int)data << 16) | val; + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __get_user(data, user_buffer++); + //*dma_buffer_2++ = data; + val = (unsigned int)data & 0x0000ffff; + __get_user(data, user_buffer++); + *dma_buffer_2++ = ((unsigned int)data << 16) | val; + } + toCount -= count; + } + + if(stream->audio_num_channels == 1){ + return toCount0 /2 ; + } + + return toCount0 ; +} + +static __inline__ int copy_from_user_U16_LE_CM +( + audio_stream_t *stream, + const char *from, + int toCount +) +{ + int *dma_buffer_0 = (int *)stream->hwbuf[0]; + int *dma_buffer_1 = (int *)stream->hwbuf[1]; + int *dma_buffer_2 = (int *)stream->hwbuf[2]; + int count; + unsigned short * user_buffer = (unsigned short *)from; + unsigned short data; + unsigned int val; + int toCount0 = toCount; + count = 4 * stream->dma_num_channels; + + while (toCount > 0){ + + __get_user(data, user_buffer++); + //*dma_buffer_0++ = ((unsigned int)data ^ 0x8000); + val = (unsigned int)data & 0x0000ffff; + if(stream->audio_num_channels == 2){ + __get_user(data, user_buffer++); + } + //*dma_buffer_0++ = ((unsigned int)data ^ 0x8000); + *dma_buffer_0++ = (((unsigned int)data << 16) | val) ^ 0x80008000; + + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __get_user(data, user_buffer++); + //*dma_buffer_1++ = ((unsigned int)data ^ 0x8000); + val = (unsigned int)data & 0x0000ffff; + __get_user(data, user_buffer++); + //*dma_buffer_1++ = ((unsigned int)data ^ 0x8000); + *dma_buffer_1++ = (((unsigned int)data << 16) | val) ^ 0x80008000; + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __get_user(data, user_buffer++); + //*dma_buffer_2++ = ((unsigned int)data ^ 0x8000); + val = (unsigned int)data & 0x0000ffff; + __get_user(data, user_buffer++); + //*dma_buffer_2++ = ((unsigned int)data ^ 0x8000); + *dma_buffer_2++ = (((unsigned int)data << 16) | val) ^ 0x80008000; + } + toCount -= count; + } + + if(stream->audio_num_channels == 1){ + return toCount0/2; + } + return toCount0 ; +} + +static __inline__ int copy_from_user_S8_CM +( + audio_stream_t *stream, + const char *from, + int toCount +) +{ + char *dma_buffer_0 = (char *)stream->hwbuf[0]; + char *dma_buffer_1 = (char *)stream->hwbuf[1]; + char *dma_buffer_2 = (char *)stream->hwbuf[2]; + int count; + unsigned char * user_buffer = (unsigned char *)from; + unsigned char data; + int toCount0 = toCount; + count = 4 * stream->dma_num_channels; + + dma_buffer_0++; + dma_buffer_1++; + dma_buffer_2++; + + while (toCount > 0){ + __get_user(data, user_buffer++); + *dma_buffer_0 = data; + *(dma_buffer_0 +1 ) = 0; + dma_buffer_0 += 2; + + if(stream->audio_num_channels == 2){ + __get_user(data, user_buffer++); + } + *dma_buffer_0 = data; + *(dma_buffer_0 +1 ) = 0; + dma_buffer_0 += 2; + + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __get_user(data, user_buffer++); + *dma_buffer_1 = data; + dma_buffer_1 += 2; + __get_user(data, user_buffer++); + *dma_buffer_1 = data; + dma_buffer_1 += 2; + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __get_user(data, user_buffer++); + *dma_buffer_2 = data; + dma_buffer_2 += 2; + __get_user(data, user_buffer++); + *dma_buffer_2 = data; + dma_buffer_2 += 2; + } + toCount -= count; + } + + if(stream->audio_num_channels == 1){ + return toCount0 / 4; + } + + return toCount0 / 2; +} + +static __inline__ int copy_from_user_U8_CM +( + audio_stream_t *stream, + const char *from, + int toCount +) +{ + unsigned char *dma_buffer_0 = (unsigned char *)stream->hwbuf[0]; + unsigned char *dma_buffer_1 = (unsigned char *)stream->hwbuf[1]; + unsigned char *dma_buffer_2 = (unsigned char *)stream->hwbuf[2]; + int count; + unsigned char *user_buffer = (unsigned char *)from; + unsigned char data; + + int toCount0 = toCount; + count = 4 * stream->dma_num_channels; + + dma_buffer_0 ++; + dma_buffer_1 ++; + dma_buffer_2 ++; + + while (toCount > 0){ + + __get_user(data, user_buffer++); + *dma_buffer_0 = ((unsigned char)data ^ 0x80); + *(dma_buffer_0 +1 ) = 0; + dma_buffer_0 += 2; + + if(stream->audio_num_channels == 2){ + __get_user(data, user_buffer++); + } + *dma_buffer_0 = ((unsigned char)data ^ 0x80); + *(dma_buffer_0 +1 ) = 0; + dma_buffer_0 += 2; + + + if(stream->audio_channels_flag & CHANNEL_REAR ){ + __get_user(data, user_buffer++); + *dma_buffer_1 = ((unsigned char)data ^ 0x80); + dma_buffer_1 += 2; + __get_user(data, user_buffer++); + *dma_buffer_1 = ((unsigned char)data ^ 0x80); + dma_buffer_1 += 2; + } + + if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){ + __get_user(data, user_buffer++); + *dma_buffer_2 = ((unsigned char)data ^ 0x80); + dma_buffer_2 += 2; + __get_user(data, user_buffer++); + *dma_buffer_2 = ((unsigned char)data ^ 0x80); + dma_buffer_2 += 2; + } + toCount -= count; + } + + if(stream->audio_num_channels == 1){ + return toCount0 / 4; + } + + return toCount0 / 2; +} + +static int copy_from_user_U32 +( + audio_stream_t *stream, + const char *from, + int toCount +) +{ + char *dma_buffer_0 = (char *)stream->hwbuf[0]; + + if (copy_from_user( (char *)dma_buffer_0, from, toCount)) + { + return -EFAULT; + } + + return toCount; + +} + +/* + * Returns negative for error + * Returns # of bytes transferred out of the from buffer + * for success. + */ +static __inline__ int copy_from_user_with_conversion +( + audio_stream_t *stream, + const char *from, + int toCount, + int bCompactMode +) +{ + int ret = 0; +// DPRINTK("copy_from_user_with_conversion\n"); + if( toCount == 0 ){ + DPRINTK("ep93xx_i2s_copy_from_user_with_conversion - nothing to copy!\n"); + } + + if( bCompactMode == 1){ + + switch( stream->audio_format ){ + + case SNDRV_PCM_FORMAT_S8: + DPRINTK("SNDRV_PCM_FORMAT_S8 CM\n"); + ret = copy_from_user_S8_CM( stream, from, toCount ); + break; + + case SNDRV_PCM_FORMAT_U8: + DPRINTK("SNDRV_PCM_FORMAT_U8 CM\n"); + ret = copy_from_user_U8_CM( stream, from, toCount ); + break; + + case SNDRV_PCM_FORMAT_S16_LE: + DPRINTK("SNDRV_PCM_FORMAT_S16_LE CM\n"); + ret = copy_from_user_S16_LE_CM( stream, from, toCount ); + break; + + case SNDRV_PCM_FORMAT_U16_LE: + DPRINTK("SNDRV_PCM_FORMAT_U16_LE CM\n"); + ret = copy_from_user_U16_LE_CM( stream, from, toCount ); + break; + + case SNDRV_PCM_FORMAT_S24_LE: + DPRINTK("SNDRV_PCM_FORMAT_S24_LE CM\n"); + //ret = copy_from_user_S24_LE( stream, from, toCount ); + //break; + + case SNDRV_PCM_FORMAT_U24_LE: + DPRINTK("SNDRV_PCM_FORMAT_U24_LE CM\n"); + //ret = copy_from_user_U24_LE( stream, from, toCount ); + //break; + case SNDRV_PCM_FORMAT_S32_LE: + DPRINTK("SNDRV_PCM_FORMAT_S32_LE CM\n"); + //break; + default: + DPRINTK( "ep93xx_i2s copy from user unsupported audio format\n" ); + break; + } + } + else{ + switch( stream->audio_format ){ + + case SNDRV_PCM_FORMAT_S8: + DPRINTK("SNDRV_PCM_FORMAT_S8\n"); + ret = copy_from_user_S8( stream, from, toCount ); + break; + + case SNDRV_PCM_FORMAT_U8: + DPRINTK("SNDRV_PCM_FORMAT_U8\n"); + ret = copy_from_user_U8( stream, from, toCount ); + break; + + case SNDRV_PCM_FORMAT_S16_LE: + DPRINTK("SNDRV_PCM_FORMAT_S16_LE\n"); + ret = copy_from_user_S16_LE( stream, from, toCount ); + break; + + case SNDRV_PCM_FORMAT_U16_LE: + DPRINTK("SNDRV_PCM_FORMAT_U16_LE\n"); + ret = copy_from_user_U16_LE( stream, from, toCount ); + break; + + case SNDRV_PCM_FORMAT_S24_LE: + DPRINTK("SNDRV_PCM_FORMAT_S24_LE\n"); + //ret = copy_from_user_S24_LE( stream, from, toCount ); + //break; + + case SNDRV_PCM_FORMAT_U24_LE: + DPRINTK("SNDRV_PCM_FORMAT_U24_LE\n"); + //ret = copy_from_user_U24_LE( stream, from, toCount ); + //break; + DPRINTK( "ep93xx_i2s copy from user unsupported audio format\n" ); + break; + case SNDRV_PCM_FORMAT_S32_LE: + DPRINTK("SNDRV_PCM_FORMAT_S32_LE\n"); + ret = copy_from_user_U32( stream, from, toCount ); + break; + default: + DPRINTK( "ep93xx_i2s copy from user unsupported audio format\n" ); + break; + } + } + + return ret; +} + + + +/* + * For audio playback, we convert samples of arbitrary format to be 32 bit + * for our hardware. We're scaling a user buffer to a dma buffer. So when + * report byte counts, we scale them acording to the ratio of DMA sample + * size to user buffer sample size. When we report # of DMA fragments, + * we don't scale that. So: + * + * Also adjust the size and number of dma fragments if sample size changed. + * + * Input format Input sample Output sample size ratio (out:in) + * bits channels size (bytes) CM non-CM CM non-CM + * 8 stereo 2 4 8 2:1 4:1 + * 16 stereo 4 4 8 1:1 2:1 + * 24 stereo 6 4 8 X 8:6 not a real case + * + */ +static void snd_ep93xx_dma2usr_ratio( audio_stream_t * stream,int bCompactMode ) +{ + unsigned int dma_sample_size, user_sample_size; + + if(bCompactMode == 1){ + dma_sample_size = 4; /* each stereo sample is 2 * 32 bits */ + } + else{ + dma_sample_size = 8; + } + + // If stereo 16 bit, user sample is 4 bytes. + // If stereo 8 bit, user sample is 2 bytes. + if(stream->audio_num_channels == 1){ + user_sample_size = stream->audio_stream_bitwidth / 8; + } + else{ + user_sample_size = stream->audio_stream_bitwidth / 4; + } + + stream->dma2usr_ratio = dma_sample_size / user_sample_size; +} + +/*---------------------------------------------------------------------------------------------*/ + +static int snd_ep93xx_dma_free(struct snd_pcm_substream *substream ){ + + + audio_state_t *state = substream->private_data; + audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + state->output_stream:state->input_stream; + int i; + + + DPRINTK("snd_ep93xx_dma_free - enter\n"); + for( i = 0 ; i < stream->dma_num_channels ;i++ ){ + ep93xx_dma_free( stream->dmahandles[i] ); + } + DPRINTK("snd_ep93xx_dma_free - exit\n"); + return 0; +} + +static int snd_ep93xx_dma_config(struct snd_pcm_substream *substream ){ + + audio_state_t *state = substream->private_data; + audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + state->output_stream:state->input_stream; + int i,err = 0; + + DPRINTK("snd_ep93xx_dma_config - enter\n"); + + for( i = 0 ; i < stream->dma_num_channels ;i++ ){ + + err = ep93xx_dma_request(&stream->dmahandles[i], + stream->devicename, + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + state->output_dma[i]:state->input_dma[i] ); + if (err){ + printk("snd_ep93xx_dma_config - exit ERROR dma request failed\n"); + return err; + } + err = ep93xx_dma_config( stream->dmahandles[i], + IGNORE_CHANNEL_ERROR, + 0, + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + snd_ep93xx_dma_tx_callback:snd_ep93xx_dma_rx_callback, + (unsigned int)substream ); + if (err){ + printk("snd_ep93xx_dma_config - exit ERROR dma request failed\n"); + return err; + } + } + + DPRINTK("snd_ep93xx_dma_config - enter\n"); + return err; +} + +static void snd_ep93xx_dma_start( audio_state_t * state, audio_stream_t * stream ) +{ + int err,i; + + DPRINTK("snd_ep93xx_dma_start - enter\n"); + + for(i = 0 ;i < stream->dma_num_channels;i++) + err = ep93xx_dma_start( stream->dmahandles[i], 1,(unsigned int *) stream->dmahandles ); + + stream->active = 1; + + DPRINTK("snd_ep93xx_dma_start - exit\n"); +} + +static void snd_ep93xx_dma_pause( audio_state_t * state, audio_stream_t * stream ) +{ + int i; + + DPRINTK("snd_ep93xx_dma_pause - enter\n"); + + for(i = 0 ;i < stream->dma_num_channels;i++) + ep93xx_dma_pause( stream->dmahandles[i], 1,(unsigned int *)stream->dmahandles ); + + stream->active = 0; + DPRINTK("snd_ep93xx_dma_pause - exit\n"); + +} + +static void snd_ep93xx_dma_flush( audio_state_t * state, audio_stream_t * stream ){ + + int i; + + DPRINTK("snd_ep93xx_dma_flush - enter\n"); + + for( i = 0 ; i < stream->dma_num_channels ; i++ ) + ep93xx_dma_flush( stream->dmahandles[i] ); + + DPRINTK("snd_ep93xx_dma_flush - exit\n"); +} + +static void snd_ep93xx_deallocate_buffers( struct snd_pcm_substream *substream, audio_stream_t *stream ) +{ + int i; + audio_channel_t *dma_chan; + + DPRINTK("snd_ep93xx_deallocate_buffers - enter\n"); + + if( stream->dma_channels ){ + + for(i = 0;i < stream->dma_num_channels;i++){ + + dma_chan = &stream->dma_channels[i]; + + if( dma_chan->area ){ + + if( dma_chan->audio_buffers ){ + + kfree(dma_chan->audio_buffers); + dma_chan->audio_buffers = NULL; + + } + + kfree(dma_chan->area); + dma_chan->area = NULL; + } + } + kfree(stream->dma_channels); + stream->dma_channels = NULL; + } + DPRINTK("snd_ep93xx_deallocate_buffers - exit\n"); +} + +static int snd_ep93xx_allocate_buffers(struct snd_pcm_substream *substream, audio_stream_t *stream) +{ + audio_channel_t *channel; + unsigned int size,tmpsize,bufsize,bufextsize; + int i,j; + + + DPRINTK("snd_ep93xx_allocate_buffers - enter\n" ); + + if (stream->dma_channels){ + printk("ep93xx_i2s %s BUSY\n",__FUNCTION__); + return -EBUSY; + } + + stream->dma_channels = (audio_channel_t *)kmalloc(sizeof(audio_channel_t) * stream->dma_num_channels , GFP_KERNEL); + + if (!stream->dma_channels){ + printk(AUDIO_NAME ": unable to allocate dma_channels memory\n"); + return - ENOMEM; + } + + size = ( stream->dmasize / stream->dma_num_channels ) * stream->dma2usr_ratio; + + for( i = 0; i < stream->dma_num_channels;i++){ + channel = &stream->dma_channels[i]; + + channel->area = kmalloc( size, GFP_DMA ); + + if(!channel->area){ + printk(AUDIO_NAME ": unable to allocate audio memory\n"); + return -ENOMEM; + } + channel->bytes = size; + channel->addr = __virt_to_phys((int) channel->area); + memset( channel->area, 0, channel->bytes ); + + bufsize = ( stream->fragsize / stream->dma_num_channels ) * stream->dma2usr_ratio; + channel->audio_buff_count = size / bufsize; + bufextsize = size % bufsize; + + if( bufextsize > 0 ){ + channel->audio_buff_count++; + } + + channel->audio_buffers = (audio_buf_t *)kmalloc(sizeof(audio_buf_t) * channel->audio_buff_count , GFP_KERNEL); + + if (!channel->audio_buffers){ + printk(AUDIO_NAME ": unable to allocate audio memory\n "); + return -ENOMEM; + } + + tmpsize = size; + + for( j = 0; j < channel->audio_buff_count; j++){ + + channel->audio_buffers[j].dma_addr = channel->addr + j * bufsize; + + if( tmpsize >= bufsize ){ + tmpsize -= bufsize; + channel->audio_buffers[j].bytes = bufsize; + channel->audio_buffers[j].reportedbytes = bufsize / stream->dma2usr_ratio; + } + else{ + channel->audio_buffers[j].bytes = bufextsize; + channel->audio_buffers[j].reportedbytes = bufextsize / stream->dma2usr_ratio; + } + } + } + + DPRINTK("snd_ep93xx_allocate_buffers -- exit SUCCESS\n" ); + return 0; +} + +/* + * DMA callback functions + */ + +static void snd_ep93xx_dma_tx_callback +( + ep93xx_dma_int_t DMAInt, + ep93xx_dma_dev_t device, + unsigned int user_data +) +{ + int handle; + int i,chan; + unsigned int buf_id; + + struct snd_pcm_substream *substream = (struct snd_pcm_substream *)user_data; + audio_state_t *state = (audio_state_t *)(substream->private_data); + audio_stream_t *stream = state->output_stream; + audio_buf_t *buf; + + switch( device ) + { + case DMATx_I2S3: + DPRINTK( "snd_ep93xx_dma_tx_callback - DMATx_I2S3\n"); + i = 2; + break; + case DMATx_I2S2: + DPRINTK( "snd_ep93xx_dma_tx_callback - DMATx_I2S2\n"); + i = 1; + break; + case DMATx_I2S1: + default: + DPRINTK( "snd_ep93xx_dma_tx_callback - DMATx_I2S1\n"); + i = 0; + break; + } + + if(stream->audio_num_channels == 1){ + chan = 0; + } + else{ + chan = stream->audio_num_channels / 2 - 1; + } + handle = stream->dmahandles[i]; + + if(stream->stopped == 0){ + + if( ep93xx_dma_remove_buffer( handle, &buf_id ) >= 0 ){ + + buf = (audio_buf_t *)buf_id; + stream->bytecount += buf->reportedbytes; + ep93xx_dma_add_buffer( stream->dmahandles[i], + (unsigned int)buf->dma_addr, + 0, + buf->bytes, + 0, + (unsigned int) buf ); + if(chan == i) + snd_pcm_period_elapsed(substream); + } + } +} + +static void snd_ep93xx_dma_rx_callback +( + ep93xx_dma_int_t DMAInt, + ep93xx_dma_dev_t device, + unsigned int user_data +) +{ + int handle,i,chan; + unsigned int buf_id; + audio_buf_t *buf; + + struct snd_pcm_substream *substream = (struct snd_pcm_substream *)user_data; + audio_state_t *state = (audio_state_t *)(substream->private_data); + audio_stream_t *stream = state->input_stream; + + switch( device ){ + + case DMARx_I2S3: + DPRINTK( "snd_ep93xx_dma_rx_callback - DMARx_I2S3\n"); + i = 2; + break; + case DMARx_I2S2: + DPRINTK( "snd_ep93xx_dma_rx_callback - DMARx_I2S2\n"); + i = 1; + break; + case DMARx_I2S1: + default: + DPRINTK( "snd_ep93xx_dma_rx_callback - DMARx_I2S1\n"); + i = 0; + break; + } + + if(stream->audio_num_channels == 1){ + chan = 0; + } + else{ + chan = stream->audio_num_channels / 2 - 1; + } + handle = stream->dmahandles[i]; + + if( stream->stopped == 0 ){ + + if( ep93xx_dma_remove_buffer( handle, &buf_id ) >= 0 ){ + + buf = (audio_buf_t *)buf_id; + stream->bytecount += buf->reportedbytes; + ep93xx_dma_add_buffer( stream->dmahandles[i], + (unsigned int)buf->dma_addr, + 0, + buf->bytes, + 0, + (unsigned int) buf ); + if( i == chan ) + snd_pcm_period_elapsed(substream); + } + } +} + +static int snd_ep93xx_release(struct snd_pcm_substream *substream) +{ + audio_state_t *state = (audio_state_t *)substream->private_data; + audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + state->output_stream : state->input_stream; + + DPRINTK("snd_ep93xx_release - enter\n"); + + down(&state->sem); + stream->active = 0; + stream->stopped = 0; + snd_ep93xx_deallocate_buffers(substream, stream); + up(&state->sem); + + DPRINTK("snd_ep93xx_release - exit\n"); + + return 0; +} + +static int ep93xx_ac97_pcm_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int r; + int iTempMasterVol,iTempHeadphoneVol,iTempMonoVol,iTempRecordSelect; + /*save the old mixer*/ + iTempRecordSelect = peek(AC97_1A_RECORD_SELECT); + iTempMasterVol = peek( AC97_02_MASTER_VOL); + iTempHeadphoneVol = peek( AC97_04_HEADPHONE_VOL); + iTempMonoVol = peek( AC97_06_MONO_VOL); + + runtime->hw.channels_min = 1; + runtime->hw.channels_max = 2; + + ep93xx_audio_init(); + /*ep93xx_init_ac97_controller();*/ + + /*reset the old output mixer*/ + poke( AC97_02_MASTER_VOL, iTempMasterVol); + poke( AC97_04_HEADPHONE_VOL,iTempHeadphoneVol ); + poke( AC97_06_MONO_VOL, iTempMonoVol); + poke( AC97_1A_RECORD_SELECT,iTempRecordSelect); + + r = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + AC97_RATES_FRONT_DAC : AC97_RATES_ADC; + + DPRINTK(" ep93xx_ac97_pcm_startup=%x\n",r); + + return 0; +} + + +static int snd_ep93xx_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + DPRINTK("snd_ep93xx_pcm_hw_params - enter\n"); + return snd_pcm_lib_malloc_pages(substream,params_buffer_bytes(params)); +} + +static int snd_ep93xx_pcm_hw_free(struct snd_pcm_substream *substream) +{ + + DPRINTK("snd_ep93xx_pcm_hw_free - enter\n"); + return snd_pcm_lib_free_pages(substream); +} + +/* + *snd_ep93xx_pcm_prepare: need to finish these functions as lower + *chip_set_sample_format + *chip_set_sample_rate + *chip_set_channels + *chip_set_dma_setup + */ + +static int snd_ep93xx_pcm_prepare_playback( struct snd_pcm_substream *substream) +{ + audio_state_t *state = (audio_state_t *) substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + audio_stream_t *stream = state->output_stream; + + DPRINTK("snd_ep93xx_pcm_prepare_playback - enter\n"); + + ep93xx_audio_disable(1); + ep93xx_ac97_pcm_startup(substream); + + snd_ep93xx_deallocate_buffers(substream,stream); + + //if(runtime->channels % 2 != 0) + // return -1; + + DPRINTK("The runtime item : \n"); + DPRINTK("runtime->dma_addr = 0x%x\n", runtime->dma_addr); + DPRINTK("runtime->dma_area = 0x%x\n", runtime->dma_area); + DPRINTK("runtime->dma_bytes = %d\n", runtime->dma_bytes); + DPRINTK("runtime->frame_bits = %d\n", runtime->frame_bits); + DPRINTK("runtime->buffer_size = %d\n", runtime->buffer_size); + DPRINTK("runtime->period_size = %d\n", runtime->period_size); + DPRINTK("runtime->periods = %d\n", runtime->periods); + DPRINTK("runtime->rate = %d\n", runtime->rate); + DPRINTK("runtime->format = %d\n", runtime->format); + DPRINTK("runtime->channels = %d\n", runtime->channels); + + /* set requestd format when available */ + stream->audio_num_channels = runtime->channels; + if(stream->audio_num_channels == 1){ + stream->dma_num_channels = 1; + } + else{ + stream->dma_num_channels = runtime->channels / 2; + } + + stream->audio_channels_flag = CHANNEL_FRONT; + if(stream->dma_num_channels == 2) + stream->audio_channels_flag |= CHANNEL_REAR; + if(stream->dma_num_channels == 3) + stream->audio_channels_flag |= CHANNEL_REAR | CHANNEL_CENTER_LFE; + + stream->dmasize = runtime->dma_bytes; + stream->nbfrags = runtime->periods; + stream->fragsize = frames_to_bytes (runtime, runtime->period_size); + stream->bytecount = 0; + + if( !state->codec_set_by_capture ){ + state->codec_set_by_playback = 1; + + if( stream->audio_rate != runtime->rate ){ + ep93xx_set_samplerate( runtime->rate,0 ); + } + //if( stream->audio_format != runtime->format ){ + // snd_ep93xx_i2s_init((stream->audio_stream_bitwidth == 24)); + //} + } + else{ + audio_stream_t *s = state->input_stream; + if( runtime->format != s->audio_format) + return -1; + if( runtime->rate != s->audio_rate ) + return -1; + } + + stream->audio_format = runtime->format ; + ep93xx_set_hw_format(stream->audio_format,stream->audio_num_channels); + + + stream->audio_rate = runtime->rate; + audio_set_format( stream, runtime->format ); + snd_ep93xx_dma2usr_ratio( stream,state->bCompactMode ); + + if( snd_ep93xx_allocate_buffers( substream, stream ) != 0 ){ + snd_ep93xx_deallocate_buffers( substream, stream ); + return -1; + } + + ep93xx_audio_enable(1); + + DPRINTK("snd_ep93xx_pcm_prepare_playback - exit\n"); + return 0; +} + +static int snd_ep93xx_pcm_prepare_capture( struct snd_pcm_substream *substream) +{ + audio_state_t *state = (audio_state_t *) substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + audio_stream_t *stream = state->input_stream; + + ep93xx_audio_disable(0); + ep93xx_ac97_pcm_startup(substream); + + snd_ep93xx_deallocate_buffers(substream,stream); + + //if(runtime->channels % 2 != 0) + //return -1; + + DPRINTK("snd_ep93xx_pcm_prepare_capture - enter\n"); + +// printk("The runtime item : \n"); +// printk("runtime->dma_addr = 0x%x\n", runtime->dma_addr); +// printk("runtime->dma_area = 0x%x\n", runtime->dma_area); +// printk("runtime->dma_bytes = %d\n", runtime->dma_bytes); +// printk("runtime->frame_bits = %d\n", runtime->frame_bits); +// printk("runtime->buffer_size = %d\n", runtime->buffer_size); +// printk("runtime->period_size = %d\n", runtime->period_size); +// printk("runtime->periods = %d\n", runtime->periods); +// printk("runtime->rate = %d\n", runtime->rate); +// printk("runtime->format = %d\n", runtime->format); +// printk("runtime->channels = %d\n", runtime->channels); + + /* set requestd format when available */ + stream->audio_num_channels = runtime->channels; + if(stream->audio_num_channels == 1){ + stream->dma_num_channels = 1; + } + else{ + stream->dma_num_channels = runtime->channels / 2; + } + + stream->audio_channels_flag = CHANNEL_FRONT; + if(stream->dma_num_channels == 2) + stream->audio_channels_flag |= CHANNEL_REAR; + if(stream->dma_num_channels == 3) + stream->audio_channels_flag |= CHANNEL_REAR | CHANNEL_CENTER_LFE; + + stream->dmasize = runtime->dma_bytes; + stream->nbfrags = runtime->periods; + stream->fragsize = frames_to_bytes (runtime, runtime->period_size); + stream->bytecount = 0; + + if( !state->codec_set_by_playback ){ + state->codec_set_by_capture = 1; + + /*rate*/ + if( stream->audio_rate != runtime->rate ){ + ep93xx_set_samplerate( runtime->rate,1 ); + } + + /*mixer*/ + ep93xx_set_recsource(SOUND_MASK_MIC|SOUND_MASK_LINE1 | SOUND_MASK_LINE); + poke( AC97_1C_RECORD_GAIN, 0); + + /*format*/ + //if( stream->audio_format != runtime->format ){ + // snd_ep93xx_i2s_init((stream->audio_stream_bitwidth == 24)); + //} + } + else{ + audio_stream_t *s = state->output_stream; + if( runtime->format != s->audio_format) + return -1; + if( runtime->rate != s->audio_rate ) + return -1; + } + + stream->audio_format = runtime->format ; + ep93xx_set_hw_format(stream->audio_format,stream->audio_num_channels); + + + stream->audio_rate = runtime->rate; + audio_set_format( stream, runtime->format ); + snd_ep93xx_dma2usr_ratio( stream,state->bCompactMode ); + + if( snd_ep93xx_allocate_buffers( substream, stream ) != 0 ){ + snd_ep93xx_deallocate_buffers( substream, stream ); + return -1; + } + + ep93xx_audio_enable(0); + + DPRINTK("snd_ep93xx_pcm_prepare_capture - exit\n"); + return 0; +} +/* + *start/stop/pause dma translate + */ +static int snd_ep93xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + audio_state_t *state = (audio_state_t *)substream->private_data; + audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + state->output_stream:state->input_stream; + audio_buf_t *buf; + audio_channel_t *dma_channel; + int i,count,ret = 0; + unsigned long flags; + + DPRINTK("snd_ep93xx_pcm_triger %d - enter \n",cmd); + + switch (cmd){ + + case SNDRV_PCM_TRIGGER_START: + + snd_ep93xx_dma_config( substream ); + + stream->stopped = 0; + + if( !stream->active && !stream->stopped ){ + stream->active = 1; + snd_ep93xx_dma_start( state, stream ); + } + + local_irq_save(flags); + + for (i = 0; i < stream->dma_num_channels; i++){ + dma_channel = &stream->dma_channels[i]; + + for(count = 0 ;count < dma_channel->audio_buff_count; count++){ + + buf = &dma_channel->audio_buffers[count]; + ep93xx_dma_add_buffer( stream->dmahandles[i], + (unsigned int)buf->dma_addr, + 0, + buf->bytes, + 0, + (unsigned int) buf ); + } + } + + local_irq_restore(flags); + break; + + case SNDRV_PCM_TRIGGER_STOP: + stream->stopped = 1; + snd_ep93xx_dma_pause( state, stream ); + snd_ep93xx_dma_flush( state, stream ); + snd_ep93xx_dma_free( substream ); + break; + + case SNDRV_PCM_TRIGGER_SUSPEND: + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + break; + + default: + ret = -EINVAL; + } + DPRINTK("snd_ep93xx_pcm_triger %d - exit \n",cmd); + return ret; +} + +static snd_pcm_uframes_t snd_ep93xx_pcm_pointer_playback(struct snd_pcm_substream *substream) +{ + audio_state_t *state = (audio_state_t *)(substream->private_data); + struct snd_pcm_runtime *runtime = substream->runtime; + audio_stream_t *stream = state->output_stream; + snd_pcm_uframes_t pointer = 0; + + pointer = bytes_to_frames( runtime,stream->bytecount ); + + if (pointer >= runtime->buffer_size){ + pointer = 0; + stream->bytecount = 0; + } + + DPRINTK("snd_ep93xx_pcm_pointer_playback - exit\n"); + return pointer; +} + +static snd_pcm_uframes_t snd_ep93xx_pcm_pointer_capture(struct snd_pcm_substream *substream) +{ + audio_state_t *state = (audio_state_t *)(substream->private_data); + struct snd_pcm_runtime *runtime = substream->runtime; + audio_stream_t *stream = state->input_stream; + snd_pcm_uframes_t pointer = 0; + + pointer = bytes_to_frames( runtime,stream->bytecount ); + + if (pointer >= runtime->buffer_size){ + pointer = 0; + stream->bytecount = 0; + } + + DPRINTK("snd_ep93xx_pcm_pointer_capture - exit\n"); + return pointer; +} + +static int snd_ep93xx_pcm_open(struct snd_pcm_substream *substream) +{ + audio_state_t *state = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + state->output_stream:state->input_stream; + + DPRINTK("snd_ep93xx_pcm_open - enter\n"); + + down(&state->sem); + + runtime->hw = ep93xx_ac97_pcm_hardware; + + stream->dma_num_channels = AUDIO_DEFAULT_DMACHANNELS; + stream->dma_channels = NULL; + stream->audio_rate = 0; + stream->audio_stream_bitwidth = 0; + + up(&state->sem); + + DPRINTK("snd_ep93xx_pcm_open - exit\n"); + return 0; +} + +/* + *free the HW dma channel + *free the HW dma buffer + *free the Hw dma decrotion using function :kfree + */ +static int snd_ep93xx_pcm_close(struct snd_pcm_substream *substream) +{ + audio_state_t *state = (audio_state_t *)(substream->private_data); + + DPRINTK("snd_ep93xx_pcm_close - enter\n"); + + snd_ep93xx_release(substream); + + if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + state->codec_set_by_playback = 0; + else + state->codec_set_by_capture = 0; + + DPRINTK("snd_ep93xx_pcm_close - exit\n"); + return 0; +} + +static int snd_ep93xx_pcm_copy_playback(struct snd_pcm_substream * substream,int channel, + snd_pcm_uframes_t pos,void __user *src, snd_pcm_uframes_t count) +{ + + audio_state_t *state = (audio_state_t *)substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + audio_stream_t *stream = state->output_stream ; + audio_channel_t *dma_channel; + int i; + int tocount = frames_to_bytes(runtime,count); + + for( i = 0; i < stream->dma_num_channels; i++ ){ + + dma_channel = &stream->dma_channels[i]; + stream->hwbuf[i] = dma_channel->area + ( frames_to_bytes(runtime,pos) * stream->dma2usr_ratio / stream->dma_num_channels ); + + } + + if(copy_from_user_with_conversion(stream ,(const char*)src,(tocount * stream->dma2usr_ratio),state->bCompactMode) <=0 ){ + DPRINTK(KERN_ERR "copy_from_user_with_conversion() failed\n"); + return -EFAULT; + } + + DPRINTK("snd_ep93xx_pcm_copy_playback - exit\n"); + return 0; +} + + +static int snd_ep93xx_pcm_copy_capture(struct snd_pcm_substream * substream,int channel, + snd_pcm_uframes_t pos,void __user *src, snd_pcm_uframes_t count) +{ + audio_state_t *state = (audio_state_t *)substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + audio_stream_t *stream = state->input_stream ; + audio_channel_t *dma_channel; + int i; + + int tocount = frames_to_bytes(runtime,count); + + for( i = 0; i < stream->dma_num_channels; i++ ){ + + dma_channel = &stream->dma_channels[i]; + stream->hwbuf[i] = dma_channel->area + ( frames_to_bytes(runtime,pos) * stream->dma2usr_ratio / stream->dma_num_channels ); + + } + + if(copy_to_user_with_conversion(stream,(const char*)src,tocount,state->bCompactMode) <=0 ){ + + DPRINTK(KERN_ERR "copy_to_user_with_conversion() failed\n"); + return -EFAULT; + } + + DPRINTK("snd_ep93xx_pcm_copy_capture - exit\n"); + return 0; +} + +/*----------------------------------------------------------------------------------*/ +static unsigned short ep93xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg) +{ + int val = -1; + /*volatile u32 *reg_addr;*/ + + DPRINTK(" number of codec:%x reg=%x\n",ac97->num,reg); + val=peek(reg); + if(val==-1){ + printk(KERN_ERR "%s: read error (ac97_reg=%d )val=%x\n", + __FUNCTION__, reg, val); + return 0; + } + + return val; +} + +static void ep93xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) +{ + /*volatile u32 *reg_addr;*/ + int ret; + + DPRINTK(" number of codec:%x rge=%x val=%x\n",ac97->num,reg,val); + ret=poke(reg, val); + if(ret!=0){ + printk(KERN_ERR "%s: write error (ac97_reg=%d val=%x)\n", + __FUNCTION__, reg, val); + } + +} + +static void ep93xx_ac97_reset(struct snd_ac97 *ac97) +{ + + DPRINTK(" ep93xx_ac97_reset\n"); + ep93xx_audio_init(); + +} + +static struct snd_ac97_bus_ops ep93xx_ac97_ops = { + .read = ep93xx_ac97_read, + .write = ep93xx_ac97_write, + .reset = ep93xx_ac97_reset, +}; + +static struct snd_pcm *ep93xx_ac97_pcm; +static struct snd_ac97 *ep93xx_ac97_ac97; + +static struct snd_pcm_ops snd_ep93xx_pcm_playback_ops = { + .open = snd_ep93xx_pcm_open, + .close = snd_ep93xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ep93xx_pcm_hw_params, + .hw_free = snd_ep93xx_pcm_hw_free, + .prepare = snd_ep93xx_pcm_prepare_playback, + .trigger = snd_ep93xx_pcm_trigger, + .pointer = snd_ep93xx_pcm_pointer_playback, + .copy = snd_ep93xx_pcm_copy_playback, + +}; + +static struct snd_pcm_ops snd_ep93xx_pcm_capture_ops = { + .open = snd_ep93xx_pcm_open, + .close = snd_ep93xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ep93xx_pcm_hw_params, + .hw_free = snd_ep93xx_pcm_hw_free, + .prepare = snd_ep93xx_pcm_prepare_capture, + .trigger = snd_ep93xx_pcm_trigger, + .pointer = snd_ep93xx_pcm_pointer_capture, + .copy = snd_ep93xx_pcm_copy_capture, +}; + +/*--------------------------------------------------------------------------*/ + + +static int snd_ep93xx_pcm_new(struct snd_card *card, audio_state_t *state, struct snd_pcm **rpcm) +{ + struct snd_pcm *pcm; + int play = state->output_stream? 1 : 0;/*SNDRV_PCM_STREAM_PLAYBACK*/ + int capt = state->input_stream ? 1 : 0;/*SNDRV_PCM_STREAM_CAPTURE*/ + int ret = 0; + + DPRINTK("snd_ep93xx_pcm_new - enter\n"); + + /* Register the new pcm device interface */ + ret = snd_pcm_new(card, "EP93xx-AC97-PCM", 0, play, capt, &pcm); + + if (ret){ + DPRINTK("%s--%x:card=%x,play=%x,capt=%x,&pcm=%x\n",__FUNCTION__,ret,(int)card,play,capt,(int)pcm); + goto out; + } + + /* allocate the pcm(DMA) memory */ + ret = snd_pcm_lib_preallocate_pages_for_all(pcm, /*SNDRV_DMA_TYPE_DEV,0,*/SNDRV_DMA_TYPE_CONTINUOUS,snd_dma_continuous_data(GFP_KERNEL),128*1024,128*1024); + + DPRINTK("The substream item : \n"); + DPRINTK("pcm->streams[0].substream->dma_buffer.addr = 0x%x\n", pcm->streams[0].substream->dma_buffer.addr); + DPRINTK("pcm->streams[0].substream->dma_buffer.area = 0x%x\n", pcm->streams[0].substream->dma_buffer.area); + DPRINTK("pcm->streams[0].substream->dma_buffer.bytes = 0x%x\n", pcm->streams[0].substream->dma_buffer.bytes); + DPRINTK("pcm->streams[1].substream->dma_buffer.addr = 0x%x\n", pcm->streams[1].substream->dma_buffer.addr); + DPRINTK("pcm->streams[1].substream->dma_buffer.area = 0x%x\n", pcm->streams[1].substream->dma_buffer.area); + DPRINTK("pcm->streams[1].substream->dma_buffer.bytes = 0x%x\n", pcm->streams[1].substream->dma_buffer.bytes); + + pcm->private_data = state; + + /* seem to free the pcm data struct-->self dma buffer */ + pcm->private_free = (void*) snd_pcm_lib_preallocate_free_for_all; + + /* alsa pcm ops setting for SNDRV_PCM_STREAM_PLAYBACK */ + if (play) { + int stream = SNDRV_PCM_STREAM_PLAYBACK; + snd_pcm_set_ops(pcm, stream, &snd_ep93xx_pcm_playback_ops); + } + + /* alsa pcm ops setting for SNDRV_PCM_STREAM_CAPTURE */ + if (capt) { + int stream = SNDRV_PCM_STREAM_CAPTURE; + snd_pcm_set_ops(pcm, stream, &snd_ep93xx_pcm_capture_ops); + } + + if (rpcm) + *rpcm = pcm; + DPRINTK("snd_ep93xx_pcm_new - exit\n"); +out: + return ret; +} + +#ifdef CONFIG_PM + +int ep93xx_ac97_do_suspend(struct snd_card *card, unsigned int state) +{ + if (card->power_state != SNDRV_CTL_POWER_D3cold) { + snd_pcm_suspend_all(ep93xx_ac97_pcm); + snd_ac97_suspend(ep93xx_ac97_ac97); + snd_power_change_state(card, SNDRV_CTL_POWER_D3cold); + } + + return 0; +} + +int ep93xx_ac97_do_resume(struct snd_card *card, unsigned int state) +{ + if (card->power_state != SNDRV_CTL_POWER_D0) { + + snd_ac97_resume(ep93xx_ac97_ac97); + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + } + + return 0; +} + +int ep93xx_ac97_suspend(struct platform_device *_dev, u32 state, u32 level) +{ + struct snd_card *card = platform_get_drvdata(_dev); + int ret = 0; + + if (card && level == SUSPEND_DISABLE) + ret = ep93xx_ac97_do_suspend(card, SNDRV_CTL_POWER_D3cold); + + return ret; +} + +int ep93xx_ac97_resume(struct platform_device *_dev, u32 level) +{ + struct snd_card *card = platform_get_drvdata(_dev); + int ret = 0; + + if (card && level == RESUME_ENABLE) + ret = ep93xx_ac97_do_resume(card, SNDRV_CTL_POWER_D0); + + return ret; +} + +#else +/* +#define ep93xx_ac97_do_suspend NULL +#define ep93xx_ac97_do_resume NULL +#define ep93xx_ac97_suspend NULL +#define ep93xx_ac97_resume NULL +*/ + +int ep93xx_ac97_do_suspend(struct snd_card *card, unsigned int state) +{ + return 0; +} + +int ep93xx_ac97_do_resume(struct snd_card *card, unsigned int state) +{ + return 0; +} + +int ep93xx_ac97_resume(struct platform_device *_dev, u32 level) +{ + struct snd_card *card = platform_get_drvdata(_dev); + int ret = 0; + + //if (card && level == RESUME_ENABLE) + ret = ep93xx_ac97_do_resume(card, SNDRV_CTL_POWER_D0); + + return ret; +} + +int ep93xx_ac97_suspend(struct platform_device *_dev, u32 state, u32 level) +{ + struct snd_card *card = platform_get_drvdata(_dev); + int ret = 0; + + //if (card && level == SUSPEND_DISABLE) + ret = ep93xx_ac97_do_suspend(card, SNDRV_CTL_POWER_D3cold); + + return ret; +} + +#endif + + + +/* module init & exit */ +static int __devinit ep93xx_ac97_probe(struct platform_device *dev) +{ + struct snd_card *card; + struct snd_ac97_bus *ac97_bus; + struct snd_ac97_template ac97_template; + int err = -ENOMEM; + struct resource *res = NULL; + + DPRINTK("snd_ep93xx_probe - enter\n"); + + /* Enable audio early on, give the DAC time to come up. */ + res = platform_get_resource( dev, IORESOURCE_MEM, 0); + + if(!res) { + printk("error : platform_get_resource \n"); + return -ENODEV; + } + + if (!request_mem_region(res->start,res->end - res->start + 1, "snd-ac97-cs4202" )){ + printk("error : request_mem_region\n"); + return -EBUSY; + } + + /*enable ac97 codec*/ + ep93xx_audio_init(); + + /* register the soundcard */ + card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0); + if (!card){ + printk("AC97: snd_card_new error\n"); + goto error; + } + + card->dev = &dev->dev; + /*regist the new pcm device*/ + err = snd_ep93xx_pcm_new(card, &audio_state, &ep93xx_ac97_pcm); + if (err){ + printk("AC97: ep93xx_ac97_pcm_new error\n"); + goto error; + } + if (card == NULL) { + DPRINTK(KERN_ERR "snd_card_new() failed\n"); + goto error; + } + + /*driver name*/ + strcpy(card->driver, "CS4202A"); + strcpy(card->shortname, "Cirrus Logic AC97 Audio "); + strcpy(card->longname, "Cirrus Logic AC97 Audio with CS4202A"); + + /*regist the new ac97 device*/ + err = snd_ac97_bus(card, 0, &ep93xx_ac97_ops, NULL, &ac97_bus); + if (err){ + printk("AC97: snd_ac97_bus error\n"); + goto error; + } + + memset(&ac97_template, 0, sizeof(ac97_template)); + err = snd_ac97_mixer(ac97_bus, &ac97_template, &ep93xx_ac97_ac97); + if (err){ + printk("AC97: snd_ac97_mixer error\n"); + goto error; + } + + /**/ + ep93xx_audio_init(); + /*setting the card device callback*/ + //err = snd_card_set_pm_callback(card, ep93xx_ac97_do_suspend,ep93xx_ac97_do_resume, (void*)NULL); + //if(err != 0){ + // printk("snd_card_set_pm_callback error\n"); + //} + + /*regist the new CARD device*/ + err = snd_card_register(card); + if (err == 0) { + printk( KERN_INFO "Cirrus Logic ep93xx ac97 audio initialized\n" ); + platform_set_drvdata(dev,card); + DPRINTK("snd_ep93xx_probe - exit\n"); + return 0; + } + +error: + snd_card_free(card); + printk("snd_ep93xx_probe - error\n"); + return err; + +return 0; +} + +static int __devexit ep93xx_ac97_remove(struct platform_device *dev) +{ + struct resource *res; + struct snd_card *card = platform_get_drvdata(dev); + + res = platform_get_resource( dev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res->end - res->start + 1); + + DPRINTK("snd_ep93xx_ac97_remove - enter\n"); + + if (card) { + snd_card_free(card); + platform_set_drvdata(dev, NULL); + } + DPRINTK("snd_ep93xx_remove - exit\n"); + +return 0; +} + + +static struct platform_driver ep93xx_ac97_driver = { + .probe = ep93xx_ac97_probe, + .remove = __devexit_p (ep93xx_ac97_remove), + .suspend = ep93xx_ac97_suspend, + .resume = ep93xx_ac97_resume, + .driver = { + .name = "ep93xx-ac97", + }, +}; + + +static int __init ep93xx_ac97_init(void) +{ + int ret; + + DPRINTK(KERN_INFO "%s: version %s\n", DRIVER_DESC, DRIVER_VERSION); + DPRINTK("snd_ep93xx_AC97_init - enter\n"); + ret = platform_driver_register(&ep93xx_ac97_driver); + DPRINTK("snd_ep93xx_AC97_init - exit\n"); + return ret; +} + +static void __exit ep93xx_ac97_exit(void) +{ + DPRINTK("ep93xx_ac97_exit - enter\n"); + return platform_driver_unregister(&ep93xx_ac97_driver); +} + +module_init(ep93xx_ac97_init); +module_exit(ep93xx_ac97_exit); + +MODULE_DESCRIPTION("Cirrus Logic audio module"); +MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.6.31/sound/arm/ep93xx-ac97.h @@ -0,0 +1,89 @@ +/* + * linux/sound/arm/ep93xx-ac97.h -- ALSA PCM interface for the edb93xx ac97 audio + * + * Author: Fred Wei + * Created: July 19, 2005 + * Copyright: Cirrus Logic, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define EP93XX_DEFAULT_NUM_CHANNELS 2 +#define EP93XX_DEFAULT_FORMAT SNDRV_PCM_FORMAT_S16_LE +#define EP93XX_DEFAULT_BIT_WIDTH 16 +#define MAX_DEVICE_NAME 20 + +/* + * Buffer Management + */ + +typedef struct { + + unsigned char *area; /* virtual pointer */ + dma_addr_t dma_addr; /* physical address */ + size_t bytes; + size_t reportedbytes; /* buffer size */ + int sent; /* indicates that dma has the buf */ + char *start; /* points to actual buffer */ + +} audio_buf_t; + + +typedef struct { + + unsigned char *area; /* virtual pointer */ + dma_addr_t addr; /* physical address */ + size_t bytes; /* buffer size in bytes */ + unsigned char *buff_pos; /* virtual pointer */ + audio_buf_t *audio_buffers; /* array of audio buffer structures */ + int audio_buff_count; + + +} audio_channel_t; + +typedef struct audio_stream_s { + + /* dma stuff */ + int dmahandles[3]; /* handles for dma driver instances */ + char devicename[MAX_DEVICE_NAME]; /* string - name of device */ + int dma_num_channels; /* 1, 2, or 3 DMA channels */ + audio_channel_t *dma_channels; + u_int nbfrags; /* nbr of fragments i.e. buffers */ + u_int fragsize; /* fragment i.e. buffer size */ + u_int dmasize; + int bytecount; /* nbr of processed bytes */ + int externedbytecount; /* nbr of processed bytes */ + volatile int active; /* actually in progress */ + volatile int stopped; /* might be active but stopped */ + char *hwbuf[3]; + long audio_rate; + long audio_num_channels; /* Range: 1 to 6 */ + int audio_channels_flag; + long audio_format; + long audio_stream_bitwidth; /* Range: 8, 16, 24 */ + int dma2usr_ratio; + +} audio_stream_t; + + +/* + * State structure for one instance + */ +typedef struct { + + audio_stream_t *output_stream; + audio_stream_t *input_stream; + ep93xx_dma_dev_t output_dma[3]; + ep93xx_dma_dev_t input_dma[3]; + char *output_id[3]; + char *input_id[3]; + struct semaphore sem; /* to protect against races in attach() */ + int codec_set_by_playback; + int codec_set_by_capture; + int DAC_bit_width; /* 16, 20, 24 bits */ + int bCompactMode; /* set if 32bits = a stereo sample */ + +} audio_state_t; +