From d810895d6ad59222c4b220d602fc307d80ee97a2 Mon Sep 17 00:00:00 2001 From: Oliver Schinagl Date: Fri, 18 Mar 2011 23:26:20 +0000 Subject: w90n745 ac97 audio driver, seems to be a W83972 based audio chip. --- uClinux-2.4.20-uc1/drivers/sound/Config.in | 7 + uClinux-2.4.20-uc1/drivers/sound/Makefile | 2 + uClinux-2.4.20-uc1/drivers/sound/w90n745_ac97.c | 837 ++++++++++++++++ uClinux-2.4.20-uc1/drivers/sound/w90n745_ac97.h | 60 ++ uClinux-2.4.20-uc1/drivers/sound/w90n745_audio.c | 1049 ++++++++++++++++++++ uClinux-2.4.20-uc1/drivers/sound/w90n745_audio.h | 145 +++ .../drivers/sound/w90n745_audio_regs.h | 199 ++++ uClinux-2.4.20-uc1/drivers/sound/w90n745_i2s.c | 677 +++++++++++++ uClinux-2.4.20-uc1/drivers/sound/w90n745_i2s.h | 69 ++ 9 files changed, 3045 insertions(+) create mode 100644 uClinux-2.4.20-uc1/drivers/sound/w90n745_ac97.c create mode 100644 uClinux-2.4.20-uc1/drivers/sound/w90n745_ac97.h create mode 100644 uClinux-2.4.20-uc1/drivers/sound/w90n745_audio.c create mode 100644 uClinux-2.4.20-uc1/drivers/sound/w90n745_audio.h create mode 100644 uClinux-2.4.20-uc1/drivers/sound/w90n745_audio_regs.h create mode 100644 uClinux-2.4.20-uc1/drivers/sound/w90n745_i2s.c create mode 100644 uClinux-2.4.20-uc1/drivers/sound/w90n745_i2s.h diff --git a/uClinux-2.4.20-uc1/drivers/sound/Config.in b/uClinux-2.4.20-uc1/drivers/sound/Config.in index 5fd0ed4..253c1ba 100644 --- a/uClinux-2.4.20-uc1/drivers/sound/Config.in +++ b/uClinux-2.4.20-uc1/drivers/sound/Config.in @@ -6,6 +6,13 @@ # Prompt user for primary drivers. +# dep_tristate ' Winbond W90N745 audio support' CONFIG_SOUND_W90N745 $CONFIG_SOUND +if [ "$CONFIG_SOUND" = "y" ]; then + if [ "$CONFIG_W90N7453_SERIAL" != "y" ]; then + bool ' Winbond W90N745 audio support' CONFIG_SOUND_W90N745 + fi +fi + dep_tristate ' ALi5455 audio support' CONFIG_SOUND_ALI5455 $CONFIG_SOUND $CONFIG_PCI dep_tristate ' BT878 audio dma' CONFIG_SOUND_BT878 $CONFIG_SOUND $CONFIG_PCI dep_tristate ' C-Media PCI (CMI8338/8738)' CONFIG_SOUND_CMPCI $CONFIG_SOUND $CONFIG_PCI diff --git a/uClinux-2.4.20-uc1/drivers/sound/Makefile b/uClinux-2.4.20-uc1/drivers/sound/Makefile index 3be4b96..c692c21 100644 --- a/uClinux-2.4.20-uc1/drivers/sound/Makefile +++ b/uClinux-2.4.20-uc1/drivers/sound/Makefile @@ -20,6 +20,8 @@ obj-$(CONFIG_SOUND_CS4232) += cs4232.o ad1848.o # Please leave it as is, cause the link order is significant ! +obj-$(CONFIG_SOUND_W90N745) += w90n745_audio.o w90n745_ac97.o w90n745_i2s.o + obj-$(CONFIG_SOUND_HAL2) += hal2.o obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o obj-$(CONFIG_SOUND_ALI5455) += ali5455.o ac97_codec.o diff --git a/uClinux-2.4.20-uc1/drivers/sound/w90n745_ac97.c b/uClinux-2.4.20-uc1/drivers/sound/w90n745_ac97.c new file mode 100644 index 0000000..fb58349 --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/sound/w90n745_ac97.c @@ -0,0 +1,837 @@ +/**************************************************************************** + * + * Copyright (c) 2004 - 2007 Winbond Electronics Corp. All rights reserved. + * + * + * FILENAME + * w90n745_AC97.c + * + * VERSION + * 1.2 + * + * DESCRIPTION + * AC97 interface for W83972D codec + * + * DATA STRUCTURES + * None + * + * FUNCTIONS + * ... + * + * HISTORY + * 2004.06.01 Created by Yung-Chang Huang + * 2004.09.02 Ver 1.0 Modify for w90n745 coding standard + * 2005.11.24 Ver 1.1 Modified for uCLinux Sound Driver by PC34 QFu + * 2006.01.17 Ver 1.2 Modified for w90n745 Version B + * + * REMARK + * None + * + **************************************************************************/ +#include +#include +#include +#include + +#include "w90n745_audio_regs.h" +#include "w90n745_audio.h" +#include "w90n745_ac97.h" + +//#define AC97_DEBUG +//#define AC97_DEBUG_ENTER_LEAVE +//#define AC97_DEBUG_MSG +//#define AC97_DEBUG_MSG2 + +#ifdef AC97_DEBUG +#define DBG(fmt, arg...) printk(fmt, ##arg) +#else +#define DBG(fmt, arg...) +#endif + +#ifdef AC97_DEBUG_ENTER_LEAVE +#define ENTER() DBG("[%-10s] : Enter\n", __FUNCTION__) +#define LEAVE() DBG("[%-10s] : Leave\n", __FUNCTION__) +#else +#define ENTER() +#define LEAVE() +#endif + +#ifdef AC97_DEBUG_MSG +#define MSG(fmt) DBG("[%-10s] : "fmt, __FUNCTION__) +#else +#define MSG(fmt) +#endif + +#ifdef AC97_DEBUG_MSG2 +#define MSG2(fmt, arg...) DBG("[%-10s] : "fmt, __FUNCTION__, ##arg) +#else +#define MSG2(fmt, arg...) +#endif + +#define AUDIO_WRITE(addr, val) writel(val, addr) +#define AUDIO_READ(addr) readl(addr) + +static AUDIO_T _tAC97; + +static int _bAC97Active = 0; +static volatile int _bPlayDmaToggle, _bRecDmaToggle; + +static void ac97StopPlay(void); + +struct semaphore ac97_sem; + +static unsigned int ac97_read_register(INT nIdx) +{ + volatile INT nWait; + unsigned int i; + + down(&ac97_sem); + + /* set the R_WB bit and write register index */ + AUDIO_WRITE(REG_ACTL_ACOS1, 0x80 | nIdx); + + /* set the valid frame bit and valid slots */ + AUDIO_WRITE(REG_ACTL_ACOS0, AUDIO_READ(REG_ACTL_ACOS0) |0x11); + + udelay(100); + + /* polling the AC_R_FINISH */ + for (nWait = 0; nWait < 0x10000; nWait++) + { + if (AUDIO_READ(REG_ACTL_ACCON) & AC_R_FINISH) + break; + } + if (nWait == 0x10000) + MSG("ac97_read_register time out!\n"); + + AUDIO_WRITE(REG_ACTL_ACOS0, AUDIO_READ(REG_ACTL_ACOS0) & ~1); + + if (AUDIO_READ(REG_ACTL_ACIS1) >> 2 != nIdx) + { MSG2("ac97_read_register - R_INDEX of REG_ACTL_ACIS1 not match!, 0x%x\n", AUDIO_READ(REG_ACTL_ACIS1)); } + + udelay(100); + i = AUDIO_READ(REG_ACTL_ACIS2) & 0xFFFF; + + up(&ac97_sem); + return (i); +} + + +static int ac97_write_register(INT nIdx, UINT16 sValue) +{ + volatile INT nWait; + + down(&ac97_sem); + + /* clear the R_WB bit and write register index */ + AUDIO_WRITE(REG_ACTL_ACOS1, nIdx); + + /* write register value */ + AUDIO_WRITE(REG_ACTL_ACOS2, (UINT32)sValue); + + /* set the valid frame bit and valid slots */ + AUDIO_WRITE(REG_ACTL_ACOS0, AUDIO_READ(REG_ACTL_ACOS0) | 0x13); + + udelay(100); + + /* polling the AC_W_FINISH */ + for (nWait = 0; nWait < 0x10000; nWait++) + { + if (!(AUDIO_READ(REG_ACTL_ACCON) & AC_W_FINISH)) + break; + } + + AUDIO_WRITE(REG_ACTL_ACOS0, AUDIO_READ(REG_ACTL_ACOS0) & ~3); + + up(&ac97_sem); + + + // paraniod? + //if (ac97_read_register(nIdx) != sValue) + // MSG2("ac97_write_register, nIdx=0x%x, mismatch, 0x%x must be 0x%x\n", nIdx, ac97_read_register(nIdx), sValue); + + return 0; +} + +#if 0 +static void ac97_read_all_registers() +{ + MSG("AC97_RESET = 0x%04x\n", ac97_read_register(AC97_RESET)); + MSG("AC97_MASTER_VOLUME = 0x%04x\n", ac97_read_register(AC97_MASTER_VOLUME)); + MSG("AC97_AUX_OUT_VOLUME = 0x%04x\n", ac97_read_register(AC97_AUX_OUT_VOLUME)); + MSG("AC97_MONO_VOLUME = 0x%04x\n", ac97_read_register(AC97_MONO_VOLUME)); + MSG("AC97_MASTER_TONE = 0x%04x\n", ac97_read_register(AC97_MASTER_TONE)); + MSG("AC97_PC_BEEP_VOLUME = 0x%04x\n", ac97_read_register(AC97_PC_BEEP_VOLUME)); + MSG("AC97_PHONE_VOLUME = 0x%04x\n", ac97_read_register(AC97_PHONE_VOLUME)); + MSG("AC97_MIC_VOLUME = 0x%04x\n", ac97_read_register(AC97_MIC_VOLUME)); + MSG("AC97_LINE_IN_VOLUME = 0x%04x\n", ac97_read_register(AC97_LINE_IN_VOLUME)); + MSG("AC97_CD_VOLUME = 0x%04x\n", ac97_read_register(AC97_CD_VOLUME)); + MSG("AC97_VIDEO_VOLUME = 0x%04x\n", ac97_read_register(AC97_VIDEO_VOLUME)); + MSG("AC97_AUX_IN_VOLUME = 0x%04x\n", ac97_read_register(AC97_AUX_IN_VOLUME)); + MSG("AC97_PCM_OUT_VOLUME = 0x%04x\n", ac97_read_register(AC97_PCM_OUT_VOLUME)); + MSG("AC97_RECORD_SELECT = 0x%04x\n", ac97_read_register(AC97_RECORD_SELECT)); + MSG("AC97_RECORD_GAIN = 0x%04x\n", ac97_read_register(AC97_RECORD_GAIN)); + MSG("AC97_RECORD_GAIN_MIC = 0x%04x\n", ac97_read_register(AC97_RECORD_GAIN_MIC)); + MSG("AC97_GENERAL_PURPOSE = 0x%04x\n", ac97_read_register(AC97_GENERAL_PURPOSE)); + MSG("AC97_3D_CONTROL = 0x%04x\n", ac97_read_register(AC97_3D_CONTROL)); + MSG("AC97_AUDIO_INT_PAGING = 0x%04x\n", ac97_read_register(AC97_AUDIO_INT_PAGING)); + MSG("AC97_POWERDOWN_CTRL = 0x%04x\n", ac97_read_register(AC97_POWERDOWN_CTRL)); + MSG("AC97_FRONT_DAC_RATE = 0x%04x\n", ac97_read_register(AC97_FRONT_DAC_RATE)); +} +#endif + +static void ac97_play_isr(void) +{ + int bPlayLastBlock; + + ENTER(); + + if(!(AUDIO_READ(REG_ACTL_CON) & T_DMA_IRQ)) + return; + + AUDIO_WRITE(REG_ACTL_CON, AUDIO_READ(REG_ACTL_CON) | T_DMA_IRQ); + + if (_bPlayDmaToggle == 0) + { + if (AUDIO_READ(REG_ACTL_PSR) & P_DMA_MIDDLE_IRQ) + AUDIO_WRITE(REG_ACTL_PSR, P_DMA_MIDDLE_IRQ); + else + MSG("ac97_play_isr - miss middle!\n"); + + _bPlayDmaToggle = 1; + + bPlayLastBlock = _tAC97.fnPlayCallBack(_tAC97.uPlayBufferAddr, + _tAC97.uPlayBufferLength/2); + } + else + { + if (AUDIO_READ(REG_ACTL_PSR) & P_DMA_END_IRQ) + AUDIO_WRITE(REG_ACTL_PSR, P_DMA_END_IRQ); + else + MSG("ac97_play_isr - miss end!\n"); + + _bPlayDmaToggle = 0; + + bPlayLastBlock = _tAC97.fnPlayCallBack(_tAC97.uPlayBufferAddr+ _tAC97.uPlayBufferLength/ 2, + _tAC97.uPlayBufferLength/2); + } + + + /* here, we will check whether the next buffer is ready. If not, stop play. */ + if (bPlayLastBlock) + AUDIO_WRITE(REG_ACTL_PSR, P_DMA_MIDDLE_IRQ | P_DMA_END_IRQ); + + + LEAVE(); + +} + + +static void ac97_rec_isr(void) +{ + int bPlayLastBlock; + + ENTER(); + + if(!(AUDIO_READ(REG_ACTL_CON) & R_DMA_IRQ)) + return; + + AUDIO_WRITE(REG_ACTL_CON, AUDIO_READ(REG_ACTL_CON) | R_DMA_IRQ); + + if (_bRecDmaToggle == 0) + { + if (AUDIO_READ(REG_ACTL_RSR) & R_DMA_MIDDLE_IRQ) + AUDIO_WRITE(REG_ACTL_RSR, R_DMA_MIDDLE_IRQ); + else + MSG("ac97_rec_isr - miss middle!\n"); + + _bRecDmaToggle = 1; + bPlayLastBlock = _tAC97.fnRecCallBack(_tAC97.uRecordBufferAddr, + _tAC97.uRecordBufferLength/2); + } + else + { + if (AUDIO_READ(REG_ACTL_RSR) & R_DMA_END_IRQ) + AUDIO_WRITE(REG_ACTL_RSR, R_DMA_END_IRQ); + else + MSG("ac97_rec_isr - miss end!\n"); + + _bRecDmaToggle = 0; + bPlayLastBlock = _tAC97.fnRecCallBack(_tAC97.uRecordBufferAddr + _tAC97.uRecordBufferLength / 2, + _tAC97.uRecordBufferLength /2); + } + + if (bPlayLastBlock) + AUDIO_WRITE(REG_ACTL_RSR, R_DMA_MIDDLE_IRQ | R_DMA_END_IRQ); + + LEAVE(); +} + +static int ac97_reset(void) +{ + ENTER(); + + AUDIO_WRITE(REG_CLKSEL, AUDIO_READ(REG_CLKSEL) | 0x10000); + + AUDIO_WRITE(0xFFF83000,0x155);//GPIO_CFG0 PT0CFG0~4 + AUDIO_WRITE(0xFFF83004,0xd);//GPIO4,1:In GPIO0,2,3:Out + + //AUDIO_WRITE(REG_GPIOA_OE, (AUDIO_READ(REG_GPIOA_OE) & 0xFFFFC0FF) | 0x2700); + + /* disable pull-high/low */ + //AUDIO_WRITE(REG_GPIOA_PE, 0x3F00); + + udelay(1000); + + _tAC97.sPlayVolume = 0x0000; + _tAC97.sRecVolume = 0x0000; + + /* enable audio controller and AC-link interface */ + AUDIO_WRITE(REG_ACTL_CON, IIS_AC_PIN_SEL | AUDIO_EN | ACLINK_EN | PFIFO_EN | RFIFO_EN | T_DMA_IRQ | R_DMA_IRQ | DMA_EN); + udelay(1000); + + /* reset Audio Controller */ + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) | ACTL_RESET_BIT); + udelay(1000); + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) & ~ACTL_RESET_BIT); + udelay(1000); + + /* reset AC-link interface */ + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) | AC_RESET); + udelay(1000); + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) & ~AC_RESET); + udelay(1000); + + + /* cold reset AC 97 */ + AUDIO_WRITE(REG_ACTL_ACCON, AUDIO_READ(REG_ACTL_ACCON) | AC_C_RES); + udelay(1000); + AUDIO_WRITE(REG_ACTL_ACCON, AUDIO_READ(REG_ACTL_ACCON) & ~AC_C_RES); + udelay(1000); + + + if (!(AUDIO_READ(REG_ACTL_ACIS0) & 0x10)) + { + MSG("Error - AC97 codec ready was not set, cold reset failed!\n"); + return ERR_AC97_CODEC_RESET; + } + + udelay(100); + //ac97_read_all_registers(); + + /* set volumes */ + + ac97_write_register(AC97_MASTER_VOLUME, _tAC97.sPlayVolume); + + ac97_write_register(AC97_MONO_VOLUME, 0x000f); + //ac97_write_register(AC97_MASTER_TONE, 0x0303); + ac97_write_register(AC97_MIC_VOLUME, 0x8000); + //ac97_write_register(AC97_LINE_IN_VOLUME, 0x0707); + //ac97_write_register(AC97_AUX_IN_VOLUME, 0x0707); + ac97_write_register(AC97_PCM_OUT_VOLUME, _tAC97.sPlayVolume); + + ac97_write_register(AC97_RECORD_SELECT, 0); /* record select MIC in */ + ac97_write_register(AC97_RECORD_GAIN, 0x0404); + ac97_write_register(AC97_RECORD_GAIN_MIC, 0x0004); + ac97_write_register(AC97_GENERAL_PURPOSE, 0); + + LEAVE(); + + return 0; +} + +static VOID ac97SetPlaySampleRate(INT nSamplingRate) +{ + ENTER(); + + /* set play sampling rate */ + if (nSamplingRate != 48000) + { + /* enable VRA and set sampling frequency */ + ac97_write_register(AC97_EXT_AUDIO_CTRL, ac97_read_register(AC97_EXT_AUDIO_CTRL)|0x1); + ac97_write_register(AC97_FRONT_DAC_RATE, nSamplingRate); + } + + _tAC97.nPlaySamplingRate = nSamplingRate; + + LEAVE(); +} + +static VOID ac97SetRecordSampleRate(INT nSamplingRate) +{ + ENTER(); + + /* set record sampling rate */ + if (nSamplingRate != 48000) + { + /* enable VRA and set sampling frequency */ + ac97_write_register(AC97_EXT_AUDIO_CTRL, ac97_read_register(AC97_EXT_AUDIO_CTRL)|0x1); + ac97_write_register(AC97_LR_ADC_RATE, nSamplingRate); + } + + _tAC97.nRecSamplingRate = nSamplingRate; + + LEAVE(); +} + +static VOID ac97SetPlayCallBackFunction(AU_CB_FUN_T fnCallBack) +{ + ENTER(); + + _tAC97.fnPlayCallBack = fnCallBack; + + LEAVE(); +} + +static VOID ac97SetRecordCallBackFunction(AU_CB_FUN_T fnCallBack) +{ + ENTER(); + + _tAC97.fnRecCallBack = fnCallBack; + + LEAVE(); +} + + + +/*************************************************************************/ +/* */ +/* FUNCTION */ +/* ac97SetPlayVolume */ +/* */ +/* DESCRIPTION */ +/* Set AC97 left and right channel play volume. */ +/* */ +/* INPUTS */ +/* ucLeftVol play volume of left channel */ +/* ucRightVol play volume of left channel */ +/* 0: mute */ +/* 1: minimal volume */ +/* 31: maxmum volume */ +/* */ +/* OUTPUTS */ +/* None */ +/* */ +/* RETURN */ +/* 0 Success */ +/* Otherwise error */ +/* */ +/*************************************************************************/ +static int ac97SetPlayVolume(UINT32 ucLeftVol, UINT32 ucRightVol) +{ + int nLData, nRData; + + ENTER(); + + //MSG2("Set AC97 volume to : %d-%d\n", ucLeftVol, ucRightVol); + + if (ucLeftVol == 0) + nLData = 0x80; + else + nLData = 31 - (ucLeftVol & 0x1f); + + if (ucRightVol == 0) + nRData = 0x80; + else + nRData = 31 - (ucRightVol & 0x1f); + + _tAC97.sPlayVolume = (nLData << 8) | nRData; + ac97_write_register(AC97_PCM_OUT_VOLUME, (nLData << 8) | nRData); + //ac97_write_register(AC97_PCM_OUT_VOLUME, 0x101); + + if (ucLeftVol == 0) + nLData = 0x80; + else + nLData = 62 - ucLeftVol*2; + + if (ucRightVol == 0) + nRData = 0x80; + else + nRData = 62 - ucRightVol*2; + + ac97_write_register(AC97_AUX_OUT_VOLUME, (nLData << 8) | nRData); + ac97_write_register(AC97_MASTER_VOLUME, (nLData << 8) | nRData); + + MSG2("AC97_MASTER_VOLUME = 0x%04x\r\n", ac97_read_register(AC97_MASTER_VOLUME)); + MSG2("AC97_AUX_OUT_VOLUME = 0x%04x\r\n", ac97_read_register(AC97_AUX_OUT_VOLUME)); + MSG2("AC97_PCM_OUT_VOLUME = 0x%04x\r\n", ac97_read_register(AC97_PCM_OUT_VOLUME)); +#if 0 + down(&ac97_sem); + if (_bAC97Active & AC97_PLAY_ACTIVE) + AUDIO_WRITE(REG_ACTL_ACOS0, 0x1f); + else + AUDIO_WRITE(REG_ACTL_ACOS0, 0); + up(&ac97_sem); +#endif + LEAVE(); + + return 0; +} + + +/*************************************************************************/ +/* */ +/* FUNCTION */ +/* ac97SetRecordVolume */ +/* */ +/* DESCRIPTION */ +/* Set AC97 left and right channel record volume. */ +/* */ +/* INPUTS */ +/* ucLeftVol record volume of left channel */ +/* ucRightVol record volume of left channel */ +/* 0: mute */ +/* 1: minimal volume */ +/* 31: maxmum volume */ +/* */ +/* OUTPUTS */ +/* None */ +/* */ +/* RETURN */ +/* 0 Success */ +/* Otherwise error */ +/* */ +/*************************************************************************/ +static int ac97SetRecordVolume(UINT32 ucLeftVol, UINT32 ucRightVol) +{ + //INT nStatus; + + ENTER(); + + if (ucLeftVol == 0) + ucLeftVol = 0x80; + else + ucLeftVol = 32 - (ucLeftVol & 0x1f); + + if (ucRightVol == 0) + ucRightVol = 0x80; + else + ucRightVol = 32 - (ucRightVol & 0x1f); + + _tAC97.sRecVolume = (ucLeftVol << 8) | ucRightVol; + + ac97_write_register(AC97_MIC_VOLUME, ucRightVol ); + + //ac97_read_all_registers(); + LEAVE(); + + return 0; +} + + +/*************************************************************************/ +/* */ +/* FUNCTION */ +/* ac97StartPlay */ +/* */ +/* DESCRIPTION */ +/* Start AC97 playback. */ +/* */ +/* INPUTS */ +/* fnCallBack client program provided callback function. The audio */ +/* driver will call back to get next block of PCM data */ +/* nSamplingRate the playback sampling rate. Supported sampling */ +/* rate are 48000, 44100, 32000, 24000, 22050, 16000, */ +/* 11025, and 8000 Hz */ +/* nChannels number of playback nChannels */ +/* 1: single channel, otherwise: double nChannels */ +/* */ +/* OUTPUTS */ +/* None */ +/* */ +/* RETURN */ +/* 0 Success */ +/* Otherwise error */ +/* */ +/*************************************************************************/ +static int ac97StartPlay(AU_CB_FUN_T fnCallBack, INT nSamplingRate, INT nChannels) +{ + INT nStatus; + + ENTER(); + + if (_bAC97Active & AC97_PLAY_ACTIVE) + return ERR_AC97_PLAY_ACTIVE; /* AC97 was playing */ + + + if (_bAC97Active == 0) + { + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) &~PLAY_LEFT_CHNNEL &~PLAY_RIGHT_CHNNEL); + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) | PLAY_RIGHT_CHNNEL); + if (nChannels != 1) + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) | PLAY_LEFT_CHNNEL); + + nStatus = ac97_reset(); + if (nStatus < 0) + return nStatus; + } + + /* disable by Qfu , for the installation of irq has been done in wb_audio.c + the only thing to do is to enable irq by this routine */ + Enable_Int(AU_PLAY_INT_NUM); + + /* set play sampling rate */ + ac97SetPlaySampleRate(nSamplingRate); + ac97SetPlayCallBackFunction(fnCallBack); + + /* set DMA play destination base address */ + AUDIO_WRITE(REG_ACTL_PDSTB, _tAC97.uPlayBufferAddr); + + /* set DMA play buffer length */ + AUDIO_WRITE(REG_ACTL_PDST_LENGTH, _tAC97.uPlayBufferLength); + + /* start playing */ + MSG("AC97 start playing...\n"); + _bPlayDmaToggle = 0; + down(&ac97_sem); + AUDIO_WRITE(REG_ACTL_ACOS0, AUDIO_READ(REG_ACTL_ACOS0) | 0x1C); + up(&ac97_sem); + AUDIO_WRITE(REG_ACTL_PSR, 0x3); + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) | AC_PLAY); + _bAC97Active |= AC97_PLAY_ACTIVE; + + LEAVE(); + + return 0; +} + + +/*************************************************************************/ +/* */ +/* FUNCTION */ +/* ac97StopPlay */ +/* */ +/* DESCRIPTION */ +/* Stop AC97 playback immdediately. */ +/* */ +/* INPUTS */ +/* None */ +/* */ +/* OUTPUTS */ +/* None */ +/* */ +/* RETURN */ +/* 0 Success */ +/* Otherwise error */ +/* */ +/*************************************************************************/ +static void ac97StopPlay(void) +{ + ENTER(); + + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) & ~(PLAY_RIGHT_CHNNEL | PLAY_LEFT_CHNNEL)); + + if (!(_bAC97Active & AC97_PLAY_ACTIVE)) + return; + + + //ac97_read_all_registers (); + + /* stop playing */ + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) & ~AC_PLAY); + + down(&ac97_sem); + AUDIO_WRITE(REG_ACTL_ACOS0, AUDIO_READ(REG_ACTL_ACOS0) & ~0xC); + up(&ac97_sem); + + + _bAC97Active &= ~AC97_PLAY_ACTIVE; + /* disable audio play interrupt */ + if (!_bAC97Active) + Disable_Int(AU_PLAY_INT_NUM); + + + LEAVE(); + + return; +} + + +/*************************************************************************/ +/* */ +/* FUNCTION */ +/* ac97StartRecord */ +/* */ +/* DESCRIPTION */ +/* Start AC97 record. */ +/* */ +/* INPUTS */ +/* fnCallBack client program provided callback function. The audio */ +/* driver will call back to deliver the newly recorded */ +/* block of PCM data */ +/* nSamplingRate the record sampling rate. Supported sampling */ +/* rate are 48000, 44100, 32000, 24000, 22050, 16000, */ +/* 11025, and 8000 Hz */ +/* nChannels number of record nChannels */ +/* 1: single channel, otherwise: double nChannels */ +/* */ +/* OUTPUTS */ +/* None */ +/* */ +/* RETURN */ +/* 0 Success */ +/* Otherwise error */ +/* */ +/*************************************************************************/ +static int ac97StartRecord(AU_CB_FUN_T fnCallBack, INT nSamplingRate, INT nChannels) +{ + INT nStatus; + unsigned int i; + + ENTER(); + + if (_bAC97Active & AC97_REC_ACTIVE) + return ERR_AC97_REC_ACTIVE; /* AC97 was recording */ + + if (_bAC97Active == 0) + { + + nStatus = ac97_reset(); + if (nStatus < 0) + return nStatus; + } + + + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) &~RECORD_RIGHT_CHNNEL &~RECORD_LEFT_CHNNEL); + + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) | RECORD_RIGHT_CHNNEL); + if (nChannels != 1) { + + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) | RECORD_LEFT_CHNNEL); + } + + + + + /* enable AC97 record interrupt */ + Enable_Int(AU_REC_INT_NUM); + + + + /* set record sampling rate */ + ac97SetRecordSampleRate(nSamplingRate); + ac97SetRecordCallBackFunction(fnCallBack); + + + /* set DMA record destination base address */ + AUDIO_WRITE(REG_ACTL_RDSTB, _tAC97.uRecordBufferAddr); + + /* set DMA record buffer length */ + AUDIO_WRITE(REG_ACTL_RDST_LENGTH, _tAC97.uRecordBufferLength); + + /* start recording */ + MSG("AC97 start recording...\n"); + _bRecDmaToggle = 0; + AUDIO_WRITE(REG_ACTL_RSR, 0x3); + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) | AC_RECORD); + _bAC97Active |= AC97_REC_ACTIVE; + + LEAVE(); + + return 0; +} + + +/*************************************************************************/ +/* */ +/* FUNCTION */ +/* ac97StopRecord */ +/* */ +/* DESCRIPTION */ +/* Stop AC97 record immediately. */ +/* */ +/* INPUTS */ +/* None */ +/* */ +/* OUTPUTS */ +/* None */ +/* */ +/* RETURN */ +/* 0 Success */ +/* Otherwise error */ +/* */ +/*************************************************************************/ +static void ac97StopRecord(void) +{ + ENTER(); + + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) & ~(RECORD_RIGHT_CHNNEL | RECORD_LEFT_CHNNEL)); + + if (!(_bAC97Active & AC97_REC_ACTIVE)) + return; + + //ac97_read_all_registers (); + /* stop recording */ + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) & ~AC_RECORD); + + _bAC97Active &= ~AC97_REC_ACTIVE; + /* disable audio record interrupt */ + if (!_bAC97Active) + Disable_Int(AU_PLAY_INT_NUM); + + + LEAVE(); + + return; +} + +static void ac97SetPlayBuffer(UINT32 uDMABufferAddr, UINT32 uDMABufferLength) +{ + ENTER(); + + _tAC97.uPlayBufferAddr = uDMABufferAddr; + _tAC97.uPlayBufferLength = uDMABufferLength; + + LEAVE(); +} + +static void ac97SetRecordBuffer(UINT32 uDMABufferAddr, UINT32 uDMABufferLength) +{ + ENTER(); + + _tAC97.uRecordBufferAddr = uDMABufferAddr; + _tAC97.uRecordBufferLength = uDMABufferLength; + + LEAVE(); +} + + +static INT ac97Init(VOID) +{ + int nStatus = 0; + + ENTER(); + + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) &~PLAY_LEFT_CHNNEL &~PLAY_RIGHT_CHNNEL); + + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) &~RECORD_LEFT_CHNNEL &~RECORD_RIGHT_CHNNEL); + + nStatus = ac97_reset(); + if (nStatus < 0) + return nStatus; + + LEAVE(); + + return 0; +} + +static INT ac97GetCapacity(VOID) +{ + return DSP_CAP_DUPLEX; /* support full duplex */ +} + +WB_AUDIO_CODEC_T wb_ac97_codec = { + dev: AU_DEV_AC97, + get_capacity: ac97GetCapacity, + set_play_buffer: ac97SetPlayBuffer, + set_record_buffer: ac97SetRecordBuffer, + reset: ac97Init, + start_play: ac97StartPlay, + stop_play: ac97StopPlay, + start_record: ac97StartRecord, + stop_record: ac97StopRecord, + set_play_volume: ac97SetPlayVolume, + set_record_volume: ac97SetRecordVolume, + play_interrupt: ac97_play_isr, + record_interrupt: ac97_rec_isr, +}; diff --git a/uClinux-2.4.20-uc1/drivers/sound/w90n745_ac97.h b/uClinux-2.4.20-uc1/drivers/sound/w90n745_ac97.h new file mode 100644 index 0000000..96c4dc4 --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/sound/w90n745_ac97.h @@ -0,0 +1,60 @@ +/************************************************************************************************** + * + * Copyright (c) 2004 - 2007 Winbond Electronics Corp. All rights reserved. + * + * FILENAME + * w90n745_AC97.h + * + * VERSION + * 1.0 + * + * DESCRIPTION + * This file contains the register map of AC97 audio codec + * + * HISTORY + * 02/09/2004 Ver 1.0 Created by PC30 YCHuang + * + * REMARK + * None + * + *************************************************************************************************/ +#ifndef _W90N745_AC97_H_ +#define _W90N745_AC97_H_ + +#define AC97_RESET 0x00 +#define AC97_MASTER_VOLUME 0x02 +#define AC97_AUX_OUT_VOLUME 0x04 +#define AC97_MONO_VOLUME 0x06 +#define AC97_MASTER_TONE 0x08 +#define AC97_PC_BEEP_VOLUME 0x0A +#define AC97_PHONE_VOLUME 0x0C +#define AC97_MIC_VOLUME 0x0E +#define AC97_LINE_IN_VOLUME 0x10 +#define AC97_CD_VOLUME 0x12 +#define AC97_VIDEO_VOLUME 0x14 +#define AC97_AUX_IN_VOLUME 0x16 +#define AC97_PCM_OUT_VOLUME 0x18 +#define AC97_RECORD_SELECT 0x1A +#define AC97_RECORD_GAIN 0x1C +#define AC97_RECORD_GAIN_MIC 0x1E +#define AC97_GENERAL_PURPOSE 0x20 +#define AC97_3D_CONTROL 0x22 +#define AC97_AUDIO_INT_PAGING 0x24 +#define AC97_POWERDOWN_CTRL 0x26 +#define AC97_EXT_AUDIO_ID 0x28 +#define AC97_EXT_AUDIO_CTRL 0x2A +#define AC97_FRONT_DAC_RATE 0x2C +#define AC97_LR_ADC_RATE 0x32 +#define AC97_MIC_ADC_RATE 0x34 + +/* bit definition of ATCL_CON register */ +#define AUDCLK_EN 0x8000 +#define PFIFO_EN 0x4000 +#define RFIFO_EN 0x2000 + +#define AC97_ACTIVE 0x1 +#define AC97_PLAY_ACTIVE 0x2 +#define AC97_REC_ACTIVE 0x4 + + +#endif /* _W90N745_AC97_H_ */ diff --git a/uClinux-2.4.20-uc1/drivers/sound/w90n745_audio.c b/uClinux-2.4.20-uc1/drivers/sound/w90n745_audio.c new file mode 100644 index 0000000..48067c7 --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/sound/w90n745_audio.c @@ -0,0 +1,1049 @@ +/**************************************************************************** + * + * Copyright (c) 2004 - 2007 Winbond Electronics Corp. All rights reserved. + * + * + * FILENAME + * w90n745_Audio.c + * + * VERSION + * 1.0 + * + * DESCRIPTION + * Winbond w90n745 Audio Driver for Linux 2.4.x with OSS frame + * + * DATA STRUCTURES + * None + * + * FUNCTIONS + * + * + * HISTORY + * 2005.11.24 Created by PC34 QFu + * + * REMARK + * None + * + **************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "w90n745_audio_regs.h" +#include "w90n745_audio.h" + +#define AUDIO_BUFFER_ORDER 4 /* 64K x 2 (play + record) */ + +#define FRAG_SIZE (( PAGE_SIZE << AUDIO_BUFFER_ORDER ) / 2) + +#define WB_AUDIO_DEFAULT_SAMPLERATE AU_SAMPLE_RATE_44100 +#define WB_AUDIO_DEFAULT_CHANNEL 2 +#define WB_AUDIO_DEFAULT_VOLUME 30 + +//#define WB_AU_DEBUG +//#define WB_AU_DEBUG_ENTER_LEAVE +//#define WB_AU_DEBUG_MSG +//#define WB_AU_DEBUG_MSG2 + +#ifdef WB_AU_DEBUG +#define DBG(fmt, arg...) printk(fmt, ##arg) +#else +#define DBG(fmt, arg...) +#endif + +#ifdef WB_AU_DEBUG_ENTER_LEAVE +#define ENTER() DBG("[%-10s] : Enter\n", __FUNCTION__) +#define LEAVE() DBG("[%-10s] : Leave\n", __FUNCTION__) +#else +#define ENTER() +#define LEAVE() +#endif + +#ifdef WB_AU_DEBUG_MSG +#define MSG(fmt) DBG("[%-10s] : "fmt, __FUNCTION__) +#else +#define MSG(fmt) +#endif + +#ifdef WB_AU_DEBUG_MSG2 +#define MSG2(fmt, arg...) DBG("[%-10s] : "fmt, __FUNCTION__, ##arg) +#else +#define MSG2(fmt, arg...) +#endif + + +static WB_AUDIO_T audio_dev; +static WB_AUDIO_CODEC_T *all_codecs[2] = {&wb_i2s_codec, &wb_ac97_codec}; +static int dev_dsp[2] = {-1, -1}, dev_mixer[2] = {-1, -1}; +static int nSamplingRate[]={AU_SAMPLE_RATE_8000, + AU_SAMPLE_RATE_11025, + AU_SAMPLE_RATE_16000, + AU_SAMPLE_RATE_22050, + AU_SAMPLE_RATE_24000, + AU_SAMPLE_RATE_32000, + AU_SAMPLE_RATE_44100, + AU_SAMPLE_RATE_48000}; + +static void wb_audio_stop_play(void) +{ + MSG("Stop Play! \n"); + + if ( audio_dev.state & AU_STATE_PLAYING){ + audio_dev.codec->set_play_volume(0, 0); + audio_dev.codec->stop_play(); + audio_dev.state &= ~AU_STATE_PLAYING; + wake_up_interruptible(&audio_dev.write_wait_queue); + MSG2("Buf[0] : %x, Buf[1] : %x\n", + audio_dev.play_half_buf[0].ptr, + audio_dev.play_half_buf[1].ptr); + + } +} + +static void wb_audio_stop_record(void) +{ + MSG("Stop Record!\n"); + + if ( audio_dev.state & AU_STATE_RECORDING){ + audio_dev.codec->set_record_volume(0, 0); + audio_dev.codec->stop_record(); + audio_dev.state &= ~AU_STATE_RECORDING; + wake_up_interruptible(&audio_dev.read_wait_queue); + } +} + +static int play_callback(UINT32 uAddr, UINT32 uLen) +{ + int i = 1; + + ENTER(); + + if(uAddr == audio_dev.play_buf_addr){ + MSG("<"); + i = 0; + } + else + MSG(">"); + + audio_dev.play_half_buf[i].ptr = 0; /* set buffer empty flag */ + + wake_up_interruptible(&audio_dev.write_wait_queue); /* wake up all block write system call */ + + if(audio_dev.play_half_buf[i^1].ptr /*== FRAG_SIZE*/ > 0){ /* check whether next buffer is full ? */ + LEAVE(); + return 0; + } + else{ + wb_audio_stop_play(); + return 1; + } +} + +static int record_callback(UINT32 uAddr, UINT32 uLen) +{ + int i = 1; + + if(uAddr == audio_dev.record_buf_addr) + i = 0; + + audio_dev.record_half_buf[i].ptr =0; /* indicate read from buffer[0] */ + + wake_up_interruptible(&audio_dev.read_wait_queue); /* wake up all blocked read system call */ + + if ( audio_dev.record_half_buf[i ^ 1].ptr == 0) { /* last block wan't take off , user may stop record */ + wb_audio_stop_record(); + return 1; + } + else + return 0; +} + +static int wb_audio_start_play(void) +{ + int ret = 0; + + MSG("Start Playing ... \n"); + + if ( audio_dev.state & AU_STATE_PLAYING) /* playing? */ + return 0; + + if ( audio_dev.state & AU_STATE_RECORDING ) { /* recording? */ + if (!(audio_dev.codec->get_capacity() & DSP_CAP_DUPLEX)) /* not support full duplex */ + wb_audio_stop_record(); + } + + audio_dev.state |= AU_STATE_PLAYING; + + MSG2("Buf[0] : %x, Buf[1] : %x\n", + audio_dev.play_half_buf[0].ptr, + audio_dev.play_half_buf[1].ptr); + + if ( audio_dev.codec->start_play(play_callback, + audio_dev.nSamplingRate, + audio_dev.nChannels)) { + audio_dev.state &= ~AU_STATE_PLAYING; + MSG("Play error\n"); + + ret = -EIO; + } + else{ + audio_dev.codec->set_play_volume(audio_dev.nPlayVolumeLeft, audio_dev.nPlayVolumeRight); + } + + return ret; +} + +static int wb_audio_start_record(void) +{ + int ret=0; + + MSG("Start Recording ... \n"); + + if (audio_dev.state & AU_STATE_RECORDING) + return 0; + + if (audio_dev.state & AU_STATE_PLAYING) { + if (!(audio_dev.codec->get_capacity() & DSP_CAP_DUPLEX)) + wb_audio_stop_play(); + } + + audio_dev.record_half_buf[0].ptr =FRAG_SIZE; + audio_dev.record_half_buf[1].ptr = FRAG_SIZE; + audio_dev.state |= AU_STATE_RECORDING; + + if ( audio_dev.codec->start_record((AU_CB_FUN_T)record_callback, + audio_dev.nSamplingRate, + audio_dev.nChannels)) { + audio_dev.state &= ~AU_STATE_RECORDING; + ret = -EIO; + } + else{ + audio_dev.codec->set_record_volume(audio_dev.nRecordVolumeLeft, audio_dev.nRecordVolumeRight); + } + + return ret; +} + +static int wb_mixer_open(struct inode *inode, struct file *file) +{ + int retval; + + int minor = MINOR(inode->i_rdev); + + ENTER(); + + if(minor != 0 && minor != 16) + return -ENODEV; + + if(minor == 16) + minor = 1; + + down(&audio_dev.mixer_sem); + + retval = -EBUSY; + + if(audio_dev.open_flags != 0){ + if(audio_dev.mixer_openflag != 0) + goto quit; + else{ + if(audio_dev.dsp_openflag != 0 && audio_dev.dsp_dev != minor) + goto quit; + } + } + + audio_dev.open_flags = 1; + audio_dev.mixer_openflag = 1; + audio_dev.mixer_dev = minor; + + audio_dev.codec = all_codecs[minor]; + + MSG2("Mixer[%d] opened\n", minor); + + retval = 0; + + MOD_INC_USE_COUNT; + +quit: + up(&audio_dev.mixer_sem); + + LEAVE(); + + return retval; +} + +static int wb_mixer_release(struct inode *inode, struct file *file) +{ + audio_dev.mixer_openflag = 0; + if(audio_dev.dsp_openflag == 0) + audio_dev.open_flags = 0; + + MOD_DEC_USE_COUNT; + + return 0; +} + +static int wb_mixer_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0, val=0, err = 0; + int tmpVolumeLeft, tmpVolumeRight; + + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + memset(&info,0,sizeof(info)); + strncpy(info.id,"w90n745",sizeof(info.id)-1); + strncpy(info.name,"Winbond w90n745 Audio",sizeof(info.name)-1); + info.modify_counter = 0; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + memset(&info,0,sizeof(info)); + strncpy(info.id,"w90n745",sizeof(info.id)-1); + strncpy(info.name,"Winbond w90n745 Audio",sizeof(info.name)-1); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + + /* read */ + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (cmd) { + case MIXER_READ(SOUND_MIXER_CAPS): + ret = SOUND_CAP_EXCL_INPUT; /* only one input can be selected */ + break; + case MIXER_READ(SOUND_MIXER_STEREODEVS): + ret = 1; /* check whether support stereo */ + break; + + case MIXER_READ(SOUND_MIXER_RECMASK): /* get input channels mask */ + ret = SOUND_MASK_MIC; + + case MIXER_READ(SOUND_MIXER_DEVMASK): /* get all channels mask */ + ret |= SOUND_MASK_PCM + SOUND_MASK_VOLUME; + break; + + case MIXER_WRITE(SOUND_MIXER_RECSRC): + if ( val != SOUND_MASK_MIC ) + err = -EPERM; + + case MIXER_READ(SOUND_MIXER_RECSRC): + ret = SOUND_MASK_MIC; + break; + + case MIXER_WRITE(SOUND_MIXER_VOLUME): + case MIXER_WRITE(SOUND_MIXER_PCM): + case MIXER_WRITE(SOUND_MIXER_MIC): + tmpVolumeLeft = (val & 0xff) * 31 / 100; + tmpVolumeRight = ((val >> 8) & 0xff) * 31 / 100; + if (cmd == MIXER_WRITE(SOUND_MIXER_MIC)){ /* set mic volume */ + audio_dev.codec->set_record_volume(tmpVolumeLeft, tmpVolumeRight); + audio_dev.nRecordVolumeLeft = tmpVolumeLeft; + audio_dev.nRecordVolumeRight = tmpVolumeRight; + } + else{ + audio_dev.codec->set_play_volume(tmpVolumeLeft, tmpVolumeRight); /* set play volume */ + audio_dev.nPlayVolumeLeft = tmpVolumeLeft; + audio_dev.nPlayVolumeRight = tmpVolumeRight; + } + + ret = (val & 0xffff); + + break; + + case MIXER_READ(SOUND_MIXER_VOLUME): + case MIXER_READ(SOUND_MIXER_PCM): + ret = ((audio_dev.nPlayVolumeLeft * 100 / 31) | + ((audio_dev.nPlayVolumeRight * 100 / 31 ) << 8)); + break; + + case MIXER_READ(SOUND_MIXER_MIC): + ret =((audio_dev.nRecordVolumeLeft * 100 / 31) | + ((audio_dev.nRecordVolumeRight * 100 / 31 ) << 8)); + break; + + default: + return -EINVAL; + } + + if (put_user(ret, (int *)arg)) + return -EFAULT; + + return err; +} + + +static struct file_operations wb_mixer_fops = { + owner: THIS_MODULE, + ioctl: wb_mixer_ioctl, + open: wb_mixer_open, + release: wb_mixer_release, +}; + +static void wb_dsp_irq(int irq, void *dev_id, struct pt_regs * regs) +{ + ENTER(); + + if( audio_dev.state & AU_STATE_PLAYING) + audio_dev.codec->play_interrupt(); + if(audio_dev.state & AU_STATE_RECORDING) + audio_dev.codec->record_interrupt(); + + LEAVE(); +} + +static int wb_dsp_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + int retval = -EBUSY; + + ENTER(); + + if(minor != 3 && minor != 19){ + MSG2("Minor error : %d\n", minor); + return -ENODEV; + } + + if(minor == 3) + minor = 0; + else + minor = 1; + + if(audio_dev.open_flags != 0){ + if(audio_dev.dsp_openflag != 0) + goto quit; + else{ + if(audio_dev.mixer_openflag != 0 && audio_dev.mixer_dev != minor) + goto quit; + } + } + + if((retval = request_irq(INT_AC97, wb_dsp_irq, SA_INTERRUPT, "wb audio", NULL))){ + printk("wb_audio_init : Request IRQ error\n"); + goto quit; + } + + //enable_irq(INT_AC97); + + audio_dev.open_flags = 1; + audio_dev.dsp_openflag = 1; + audio_dev.dsp_dev = minor; + audio_dev.state = AU_STATE_NOP; + + audio_dev.play_buf_addr = __get_free_pages(GFP_KERNEL, AUDIO_BUFFER_ORDER); + if(audio_dev.play_buf_addr == NULL){ + free_irq(INT_AC97, NULL); + MSG("Not enough memory\n"); + return -ENOMEM; + } + + audio_dev.record_buf_addr = __get_free_pages(GFP_KERNEL, AUDIO_BUFFER_ORDER); + if(audio_dev.record_buf_addr == NULL){ + free_pages(audio_dev.play_buf_addr, AUDIO_BUFFER_ORDER); + free_irq(INT_AC97, NULL); + MSG("Not enough memory\n"); + return -ENOMEM; + } + + audio_dev.play_buf_addr |= 0x80000000; // none - cache + audio_dev.play_buf_length = ( PAGE_SIZE << AUDIO_BUFFER_ORDER ); + audio_dev.record_buf_addr |= 0x80000000; // none - cache + audio_dev.record_buf_length = ( PAGE_SIZE << AUDIO_BUFFER_ORDER ); + + MSG2("Audio_Dev.play_buf_addr : %x, Length: %x\n", + audio_dev.play_buf_addr, audio_dev.play_buf_length); + + init_waitqueue_head(&audio_dev.read_wait_queue); + init_waitqueue_head(&audio_dev.write_wait_queue); + + memset(&audio_dev.play_half_buf, 0, sizeof(audio_dev.play_half_buf)); + memset(&audio_dev.record_half_buf, 0, sizeof(audio_dev.record_half_buf)); + + audio_dev.nSamplingRate = WB_AUDIO_DEFAULT_SAMPLERATE; + audio_dev.nChannels = WB_AUDIO_DEFAULT_CHANNEL; + audio_dev.nPlayVolumeLeft = WB_AUDIO_DEFAULT_VOLUME; + audio_dev.nPlayVolumeRight = WB_AUDIO_DEFAULT_VOLUME; + audio_dev.nRecordVolumeLeft = WB_AUDIO_DEFAULT_VOLUME; + audio_dev.nRecordVolumeRight = WB_AUDIO_DEFAULT_VOLUME; + + audio_dev.codec = all_codecs[minor]; + + /* set dma buffer */ + audio_dev.codec->set_play_buffer(audio_dev.play_buf_addr, + audio_dev.play_buf_length); + audio_dev.codec->set_record_buffer(audio_dev.record_buf_addr, + audio_dev.record_buf_length); + + audio_dev.codec->reset(); + + MOD_INC_USE_COUNT; + + LEAVE(); + + return 0; + +quit: + + MSG2("Open failed : %d\n", retval); + + return retval; +} + +static int wb_dsp_release(struct inode *inode, struct file *file) +{ + ENTER(); + + if(audio_dev.state & AU_STATE_PLAYING){ + /* wait until stop playing */ + wait_event_interruptible(audio_dev.write_wait_queue, + (audio_dev.state & AU_STATE_PLAYING) == 0 || + (audio_dev.play_half_buf[0].ptr == 0 && + audio_dev.play_half_buf[1].ptr == 0)); + } + wb_audio_stop_play(); + wb_audio_stop_record(); + + free_irq(INT_AC97, NULL); + + free_pages(audio_dev.play_buf_addr & 0x7fffffff, AUDIO_BUFFER_ORDER); + free_pages(audio_dev.record_buf_addr & 0x7fffffff, AUDIO_BUFFER_ORDER); + + audio_dev.dsp_openflag = 0; + if(audio_dev.mixer_openflag == 0) + audio_dev.open_flags = 0; + + MOD_DEC_USE_COUNT; + + LEAVE(); + + return 0; +} + +static ssize_t wb_dsp_read(struct file *file, char *buffer, + size_t swcount, loff_t *ppos) +{ + int i, tmp, length, block_len, retval, bpos; + char *dma_buf = (char *)audio_dev.record_buf_addr; + + ENTER(); + + if(swcount == 0) + return 0; + + if(down_interruptible(&audio_dev.dsp_read_sem)) + return -ERESTARTSYS; + +again: + + if((audio_dev.state & AU_STATE_RECORDING) == 0) { /* if record not start, then start it */ + retval = wb_audio_start_record(); + if ( retval ) + goto quit; + } + + length = swcount; + block_len = FRAG_SIZE; + retval = -EFAULT; + + if(audio_dev.record_half_buf[0].ptr == FRAG_SIZE && + audio_dev.record_half_buf[1].ptr == FRAG_SIZE){ /* buffer empty */ + if( file->f_flags & O_NONBLOCK){ + retval = -EAGAIN; + goto quit; + } + else { + wait_event_interruptible(audio_dev.read_wait_queue, + (audio_dev.state & AU_STATE_RECORDING) == 0 || + audio_dev.record_half_buf[0].ptr == 0 || + audio_dev.record_half_buf[1].ptr == 0 ); + if ( (audio_dev.state & AU_STATE_RECORDING) == 0){ + retval = 0; + goto quit; + } + } + + } + + retval = 0; + bpos = 0; + for(i = 0; i < 2; i++){ + tmp = block_len - audio_dev.record_half_buf[i].ptr; + + if(swcount < tmp) + tmp = swcount; + + if(tmp){ + retval = -EFAULT; + if(copy_to_user(buffer + bpos, + dma_buf + i * block_len + audio_dev.record_half_buf[i].ptr , tmp)) + goto quit; + } + else + continue; + + swcount -= tmp; + audio_dev.record_half_buf[i].ptr += tmp; + bpos += tmp; + + if(swcount == 0) + break; + + } + + + retval = length - swcount; + + if(swcount != 0){ + if( file->f_flags & O_NONBLOCK + && audio_dev.play_half_buf[0].ptr == FRAG_SIZE + && audio_dev.play_half_buf[1].ptr == FRAG_SIZE) + goto quit; + + buffer += retval; + goto again; + } + +quit: + up(&audio_dev.dsp_read_sem); + + LEAVE(); + + return retval; +} + +static ssize_t wb_dsp_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + int tmp, retval, length, i; + char *dma_buf = (char *)audio_dev.play_buf_addr; + + ENTER(); + + MSG2("DSP Write : Buffer : %08x Count : %x\n", buffer, count); + + if(count == 0) + return 0; + + if(down_interruptible(&audio_dev.dsp_write_sem)) + return -ERESTARTSYS; + + +again: + + length = count; + + if(audio_dev.state & AU_STATE_PLAYING){ + if(audio_dev.play_half_buf[0].ptr == FRAG_SIZE && + audio_dev.play_half_buf[1].ptr == FRAG_SIZE){ + if( file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto quit; + } + else{ + MSG("Write block ...\n"); + wait_event_interruptible(audio_dev.write_wait_queue, + (audio_dev.state & AU_STATE_PLAYING) == 0 || + audio_dev.play_half_buf[0].ptr == 0 || + audio_dev.play_half_buf[1].ptr == 0); + } + } + } + + + retval = -EFAULT; + + + for(i = 0; i < 2; i ++){ + tmp = FRAG_SIZE - audio_dev.play_half_buf[i].ptr; + + if(tmp > count) + tmp = count; + + if(tmp){ + if(copy_from_user(dma_buf + audio_dev.play_half_buf[i].ptr + i * FRAG_SIZE, + buffer + length - count, tmp)) + goto quit; + } + + count -= tmp; + audio_dev.play_half_buf[i].ptr += tmp; + if( count == 0) + break; + } + + retval = length - count; + + if((audio_dev.state & AU_STATE_PLAYING ) == 0 + && audio_dev.play_half_buf[0].ptr == FRAG_SIZE + && audio_dev.play_half_buf[1].ptr == FRAG_SIZE){ + + if (wb_audio_start_play()){ + retval = -EIO; + goto quit; + } + } + + if(count != 0){ + if( file->f_flags & O_NONBLOCK + && audio_dev.play_half_buf[0].ptr == FRAG_SIZE + && audio_dev.play_half_buf[1].ptr == FRAG_SIZE) + goto quit; + + buffer += retval; + goto again; + } + +quit: + + up(&audio_dev.dsp_write_sem); + + DBG("W"); + + LEAVE(); + + return retval; +} + +static int wb_dsp_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int val = 0, i, err = 0; + audio_buf_info info; + + ENTER(); + + switch (cmd) { + + case OSS_GETVERSION: + val = SOUND_VERSION; + + break; + + case SNDCTL_DSP_GETCAPS: + + if (audio_dev.codec->get_capacity() & DSP_CAP_DUPLEX) + val |= DSP_CAP_DUPLEX; + + val |= DSP_CAP_TRIGGER; + + break; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int*)arg)) + return -EFAULT; + + for(i = 0; i < sizeof(nSamplingRate)/sizeof(unsigned int); i++) + if(val == nSamplingRate[i]) + break; + + if(i >= sizeof(nSamplingRate)/sizeof(unsigned int)){ /* not supported */ + val = audio_dev.nSamplingRate; + err = -EPERM; + } + else + audio_dev.nSamplingRate = val; + + break; + + + case SOUND_PCM_READ_RATE: + val = audio_dev.nSamplingRate; + break; + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int*)arg)) + return -EFAULT; + + audio_dev.nChannels = val ? 2:1; + + break; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int*)arg)) + return -EFAULT; + + if(val != 1 && val != 2){ + val = audio_dev.nChannels; + err = -EPERM; + } + + audio_dev.nChannels = val; + break; + + case SOUND_PCM_READ_CHANNELS: + val = audio_dev.nChannels; + + break; + + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int*)arg)) + return -EFAULT; + + if ( (val & (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE |AFMT_U16_BE)) == 0) + err = -EPERM; + + case SNDCTL_DSP_GETFMTS: + + val = (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE |AFMT_U16_BE); + break; + + case SOUND_PCM_READ_BITS: + val = 16; + break; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + break; + + case SNDCTL_DSP_RESET: + wb_audio_stop_play(); + wb_audio_stop_record(); + + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + val =FRAG_SIZE; + break; + + case SNDCTL_DSP_GETISPACE: + + info.fragsize = FRAG_SIZE; + info.fragstotal = 2; + info.bytes = audio_dev.record_buf_length - audio_dev.record_half_buf[0].ptr - audio_dev.record_half_buf[1].ptr; + info.fragments = info.bytes / info.fragsize; + + MSG2("SNDCTL_DSP_GETISPACE returns %d/%d/%d/%d\n", + info.fragsize, info.fragstotal, + info.bytes, info.fragments); + + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_GETOSPACE: + + info.fragsize = FRAG_SIZE; + info.fragstotal = 2; + info.bytes = audio_dev.play_buf_length - audio_dev.play_half_buf[0].ptr - audio_dev.play_half_buf[1].ptr; + info.fragments = info.bytes / info.fragsize; + + MSG2("SNDCTL_DSP_GETISPACE returns %d/%d/%d/%d\n", + info.fragsize, info.fragstotal, + info.bytes, info.fragments); + + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_SYNC: + /* no data */ + if ( audio_dev.play_half_buf[0].ptr == 0 && + audio_dev.play_half_buf[1].ptr == 0) + return 0; + + wb_audio_start_play(); + + /* wait until stop playing */ + wait_event_interruptible(audio_dev.write_wait_queue, + (audio_dev.state & AU_STATE_PLAYING) == 0 || + (audio_dev.play_half_buf[0].ptr == 0 && + audio_dev.play_half_buf[1].ptr == 0)); + wb_audio_stop_play(); + + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if ( audio_dev.state & AU_STATE_PLAYING ) + val |= PCM_ENABLE_OUTPUT; + if ( audio_dev.state & AU_STATE_RECORDING) + val |= PCM_ENABLE_INPUT; + break; + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int*)arg)) + return -EFAULT; + if ( val & PCM_ENABLE_OUTPUT){ + if ( wb_audio_start_play()){ + val &= ~PCM_ENABLE_OUTPUT; + err = -EPERM ; + } + } + else{ + wb_audio_stop_play(); + } + + if ( val & PCM_ENABLE_INPUT){ + if ( wb_audio_start_record()){ + val &= ~PCM_ENABLE_INPUT; + err = -EPERM ; + } + } + else{ + wb_audio_stop_record(); + } + + break; + + default: + return -EINVAL; + } + + LEAVE(); + + return err?-EPERM:put_user(val, (int *)arg); + +} + +static unsigned int wb_dsp_poll(struct file *file, struct poll_table_struct *wait) +{ + int mask = 0; + + ENTER(); + + if (file->f_mode & FMODE_WRITE){ + poll_wait(file, &audio_dev.write_wait_queue, wait); + + /* check if has data */ + if(audio_dev.play_half_buf[0].ptr != FRAG_SIZE || + audio_dev.play_half_buf[1].ptr != FRAG_SIZE ) + mask |= POLLOUT | POLLWRNORM; + } + + if (file->f_mode & FMODE_READ){ + poll_wait(file, &audio_dev.read_wait_queue, wait); + + /* check if can read */ + if(audio_dev.record_half_buf[0].ptr != FRAG_SIZE || + audio_dev.record_half_buf[1].ptr != FRAG_SIZE ) + mask |= POLLIN | POLLRDNORM; + } + + LEAVE(); + + return mask; +} + +static struct file_operations wb_dsp_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: wb_dsp_read, + write: wb_dsp_write, + poll: wb_dsp_poll, + ioctl: wb_dsp_ioctl, + open: wb_dsp_open, + release: wb_dsp_release, +}; + +static int __init wb_dsp_init(void) +{ + int i; + + for(i=0;i<2;i++){ + dev_dsp[i] = register_sound_dsp(&wb_dsp_fops, i); + MSG2("Dsp Device No : %d\n", dev_dsp[i]); + if(dev_dsp[i]< 0) + goto quit; + } + + return 0; + +quit: + printk("Register DSP device failed\n"); + return -1; +} + + +static int __init wb_mixer_init(void) +{ + int i; + + for(i=0;i<2;i++){ + dev_mixer[i] = register_sound_mixer(&wb_mixer_fops, i); + MSG2("Mixer Device No : %d\n", dev_mixer[i]); + if(dev_mixer[i]< 0) + goto quit; + } + + return 0; + +quit: + printk("Register Mixer device failed\n"); + return -1; +} + +static void wb_dsp_unload(void) +{ + int i; + for(i=0;i<2;i++) + unregister_sound_dsp(dev_dsp[i]); +} + +static void wb_mixer_unload(void) +{ + int i; + for(i=0;i<2;i++) + unregister_sound_mixer(dev_mixer[i]); +} + + +MODULE_AUTHOR("QFu"); +MODULE_DESCRIPTION("Winbond w90n745 Audio Driver"); +MODULE_LICENSE("GPL"); + +static void wb_audio_exit (void) +{ + wb_mixer_unload(); + wb_dsp_unload(); + free_irq(INT_AC97, NULL); +} +extern struct semaphore ac97_sem; // move here to ensure it's initialized before accesign it. +static int __init wb_audio_init(void) +{ + + Disable_Int(INT_AC97); + + memset(&audio_dev, 0, sizeof(audio_dev)); + sema_init(& audio_dev.dsp_read_sem, 1); + sema_init(&audio_dev.dsp_write_sem, 1); + sema_init(&audio_dev.mixer_sem, 1); + sema_init(&ac97_sem, 1); + + if(wb_dsp_init()) + goto quit; + + if(wb_mixer_init()) + goto quit; + + printk("Winbond Audio Driver v1.0 Initialization successfully.\n"); + + return 0; + +quit: + printk("Winbond Audio Driver Initialization failed\n"); + wb_audio_exit(); + return -1; +} + +module_init(wb_audio_init); +module_exit(wb_audio_exit); + diff --git a/uClinux-2.4.20-uc1/drivers/sound/w90n745_audio.h b/uClinux-2.4.20-uc1/drivers/sound/w90n745_audio.h new file mode 100644 index 0000000..421f3d4 --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/sound/w90n745_audio.h @@ -0,0 +1,145 @@ +/************************************************************************************************** + * + * Copyright (c) 2004 - 2007 Winbond Electronics Corp. All rights reserved. + * + * FILENAME + * W90N745_AUDIO.H + * + * VERSION + * 1.1 + * + * DESCRIPTION + * This file contains the main structure of w90n745 audio module + * + * HISTORY + * 02/09/2004 Ver 1.0 Created by PC30 YCHuang + * 11/25/2005 Ver 1.1 Modified by PC34 QFu + * + * REMARK + * None + * + *************************************************************************************************/ + +#ifndef _W90N745_AUDIO_H_ +#define _W90N745_AUDIO_H_ + +#include +#include + +typedef int (*AU_CB_FUN_T)(UINT32, UINT32); + +typedef enum au_dev_e +{ + AU_DEV_AC97, + AU_DEV_IIS +} AU_DEV_E; + +typedef struct wb_audio_codec_t{ + AU_DEV_E dev; /* codec type */ + INT (*get_capacity)(VOID); + VOID (*set_play_buffer)(UINT32, UINT32); + VOID (*set_record_buffer)(UINT32, UINT32); + + INT (*set_play_volume)(UINT32 nLeft, UINT32 nRight); + INT (*set_record_volume)(UINT32 nLeft, UINT32 nRight); + + INT (*reset)(VOID); + + INT (*start_play)(AU_CB_FUN_T fnCallBack, INT nSamplingRate, INT nChannels); + VOID (*stop_play)(VOID); + + INT (*start_record)(AU_CB_FUN_T fnCallBack, INT nSamplingRate, INT nChannels); + VOID (*stop_record)(VOID); + + VOID (*play_interrupt)(VOID); /* nonzero play stopped */ + VOID (*record_interrupt)(VOID); +}WB_AUDIO_CODEC_T; + +typedef struct audio_t +{ + AU_CB_FUN_T fnPlayCallBack; + AU_CB_FUN_T fnRecCallBack; + INT nPlaySamplingRate; + INT nRecSamplingRate; + short sPlayVolume; + short sRecVolume; + UINT32 uPlayBufferAddr; + UINT32 uPlayBufferLength; + UINT32 uRecordBufferAddr; + UINT32 uRecordBufferLength; +}AUDIO_T; + +typedef struct wb_audio_t{ + int state; + int open_flags; + int dsp_dev, dsp_openflag; + int mixer_dev, mixer_openflag; + unsigned int play_buf_addr, record_buf_addr; + unsigned int play_buf_length, record_buf_length; + int nSamplingRate; + int nChannels; + int nPlayVolumeLeft, nPlayVolumeRight, nRecordVolumeLeft, nRecordVolumeRight; + + struct{ + int ptr; + }play_half_buf[2], record_half_buf[2]; + + WB_AUDIO_CODEC_T *codec; + struct semaphore dsp_read_sem, dsp_write_sem, mixer_sem; + struct fasync_struct *fasync_ptr; + wait_queue_head_t write_wait_queue, read_wait_queue; +}WB_AUDIO_T; + + + +#define AU_SAMPLE_RATE_48000 48000 +#define AU_SAMPLE_RATE_44100 44100 +#define AU_SAMPLE_RATE_32000 32000 +#define AU_SAMPLE_RATE_24000 24000 +#define AU_SAMPLE_RATE_22050 22050 +#define AU_SAMPLE_RATE_16000 16000 +#define AU_SAMPLE_RATE_11025 11025 +#define AU_SAMPLE_RATE_8000 8000 + +#define AU_CH_MONO 1 +#define AU_CH_STEREO 2 + +/* state code */ +#define AU_STATE_NOP 0 +#define AU_STATE_PLAYING 1 +#define AU_STATE_RECORDING 2 + +/* capacity */ +#define AU_CAP_DUPLEX 1 + +/* Error Code */ +#define ERR_AU_GENERAL_ERROR -1 +#define ERR_AU_NO_MEMORY -5 /* memory allocate failure */ +#define ERR_AU_ILL_BUFF_SIZE -10 /* illegal callback buffer size */ +#define ERR_AC97_CODEC_RESET -20 /* AC97 codec reset failed */ +#define ERR_AC97_PLAY_ACTIVE -22 /* AC97 playback has been activated */ +#define ERR_AC97_REC_ACTIVE -23 /* AC97 record has been activated */ +#define ERR_AC97_NO_DEVICE -24 /* have no AC97 codec on board */ +#define ERR_MA3_PLAY_ACTIVE -50 /* MA3 playback has been activated */ +#define ERR_MA3_NO_DEVICE -51 /* have no MA3 chip on board */ +#define ERR_MA5_PLAY_ACTIVE -80 /* MA5 playback has been activated */ +#define ERR_MA5_NO_DEVICE -81 /* have no MA5 chip on board */ +#define ERR_MA5I_NO_DEVICE -90 /* have no MA5i chip on board */ +#define ERR_DAC_PLAY_ACTIVE -110 /* DAC playback has been activated */ +#define ERR_DAC_NO_DEVICE -111 /* DAC is not available */ +#define ERR_ADC_REC_ACTIVE -120 /* ADC record has been activated */ +#define ERR_ADC_NO_DEVICE -121 /* ADC is not available */ +#define ERR_IIS_PLAY_ACTIVE -140 /* IIS playback has been activated */ +#define ERR_IIS_REC_ACTIVE -141 /* IIS record has been activated */ +#define ERR_IIS_NO_DEVICE -142 /* has no IIS codec on board */ +#define ERR_WM8753_NO_DEVICE -150 /* has no wm8753 codec on board */ +#define ERR_W5691_PLAY_ACTIVE -160 /* W5691 playback has been activated */ +#define ERR_W5691_NO_DEVICE -161 /* Have no W5691 chip on board */ + +#define ERR_NO_DEVICE -201 /* audio device not available */ + +extern WB_AUDIO_CODEC_T wb_ac97_codec; +extern WB_AUDIO_CODEC_T wb_i2s_codec; + +#endif /* _W90N745_AUDIO_H_ */ + diff --git a/uClinux-2.4.20-uc1/drivers/sound/w90n745_audio_regs.h b/uClinux-2.4.20-uc1/drivers/sound/w90n745_audio_regs.h new file mode 100644 index 0000000..abcfb64 --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/sound/w90n745_audio_regs.h @@ -0,0 +1,199 @@ +/************************************************************************************************** + * + * Copyright (c) 2004 - 2007 Winbond Electronics Corp. All rights reserved. + * + * FILENAME + * w90n745_Audio_REGS.H + * + * VERSION + * 1.0 + * + * DESCRIPTION + * This file contains the register map of W90N710 audio controller. + * + * HISTORY + * 05/26/2004 Ver 1.0 Created by PC30 YCHuang + * 11/24/2005 Ver 1.1 Modified by PC34 QFu + * + * REMARK + * None + * + *************************************************************************************************/ +#ifndef _W90N745_AUDIO_REGS_H_ +#define _W90N745_AUDIO_REGS_H_ + +#define REG_CLKSEL 0xfff0000c + +#define GPIO_BA 0xFFF83000 +/********************************************************************************************************** + * + * 4. GPIO Control Registers + * + **********************************************************************************************************/ +/* GPIO Pins */ +#define REG_GPIO_CFG (GPIO_BA+0x00) /* GPIO Pins Output Enable Control Register */ +#define REG_GPIO_DIR (GPIO_BA+0x04) /* GPIO Pins Data Register */ +#define REG_GPIO_DATAOUT (GPIO_BA+0x08) /* GPIO Pins Status Register */ + + +/* GPIO-A Pins */ +#define REG_GPIOA_OE (GPIO_BA+0x20) /* GPIO-A Pins Output Enable Control Register */ +#define REG_GPIOA_DAT (GPIO_BA+0x24) /* GPIO-A Pins Data Register */ +#define REG_GPIOA_STS (GPIO_BA+0x28) /* GPIO-A Pins Status Register */ +#define REG_GPIOA_PE (GPIO_BA+0x2c) /* GPIO-A Pull-Up/Down Enable Control Register */ + +/* GPIO-B Pins */ +#define REG_GPIOB_OE (GPIO_BA+0x30) /* GPIO-B Pins Output Enable Control Register */ +#define REG_GPIOB_DAT (GPIO_BA+0x34) /* GPIO-B Pins Data Register */ +#define REG_GPIOB_STS (GPIO_BA+0x38) /* GPIO-B Pins Status Register */ +#define REG_GPIOB_PE (GPIO_BA+0x3c) /* GPIO-B Pull-Up/Down Enable Control Register */ + +/* GPIO-S Pins */ +#define REG_GPIOS_OE (GPIO_BA+0x40) /* GPIO-S Pins Output Enable Control Register */ +#define REG_GPIOS_DAT (GPIO_BA+0x44) /* GPIO-S Pins Data Register */ +#define REG_GPIOS_STS (GPIO_BA+0x48) /* GPIO-S Pins Status Register */ +#define REG_GPIOS_PE (GPIO_BA+0x4c) /* GPIO-S Pull-Up/Down Enable Control Register */ + + +/* to be modified in future */ +//#include "w90n745_reg.h" /* in SYSLIB */ +#define ADO_BA 0xfff09000 +#define ADO_RANGE 0x50 + +/********************************************************************************************************** + * + * 8. Audio Interface Control Registers + * + **********************************************************************************************************/ +#define REG_ACTL_CON (ADO_BA + 0x00) /* Audio controller control register */ +#define REG_ACTL_RESET (ADO_BA + 0x04) /* Sub block reset control register */ +#define REG_ACTL_RDSTB (ADO_BA + 0x08) /* DMA destination base address register for record */ +#define REG_ACTL_RDST_LENGTH (ADO_BA + 0x0C) /* DMA destination length register for record */ +#define REG_ACTL_RSR (ADO_BA + 0x14) /* Record status register */ +#define REG_ACTL_PDSTB (ADO_BA + 0x18) /* DMA destination base address register for play */ +#define REG_ACTL_PDST_LENGTH (ADO_BA + 0x1C) /* DMA destination length register for play */ +#define REG_ACTL_PDSTC (ADO_BA + 0x20) +#define REG_ACTL_PSR (ADO_BA + 0x24) /* Play status register */ +#define REG_ACTL_IISCON (ADO_BA + 0x28) /* IIS control register */ +#define REG_ACTL_ACCON (ADO_BA + 0x2C) /* AC-link control register */ +#define REG_ACTL_ACOS0 (ADO_BA + 0x30) /* AC-link out slot 0 */ +#define REG_ACTL_ACOS1 (ADO_BA + 0x34) /* AC-link out slot 1 */ +#define REG_ACTL_ACOS2 (ADO_BA + 0x38) /* AC-link out slot 2 */ +#define REG_ACTL_ACIS0 (ADO_BA + 0x3C) /* AC-link in slot 0 */ +#define REG_ACTL_ACIS1 (ADO_BA + 0x40) /* AC-link in slot 1 */ +#define REG_ACTL_ACIS2 (ADO_BA + 0x44) /* AC-link in slot 2 */ + + + +#define AU_PLAY_INT_NUM 6 +#define AU_REC_INT_NUM 6 +#define GPIO_INT_NUM 4 + +#define USED_GPIO_NUM 5 + +#define MISCR 0xC +#define PADMFC 0x20 +#define DCCS 0x5000 +#define DEVICE_CTRL 0x5004 + + +/* bit definition of REG_ACTL_CON register */ +#define AUDCLK_EN 0x8000 +#define PFIFO_EN 0x4000 +#define RFIFO_EN 0x2000 +#define R_DMA_IRQ 0x1000 +#define T_DMA_IRQ 0x0800 +#define IIS_AC_PIN_SEL 0x0100 +#define FIFO_TH 0x0080 +#define DMA_EN 0x0040 +#define DAC_EN 0x0020 +#define ADC_EN 0x0010 +#define M80_EN 0x0008 +#define ACLINK_EN 0x0004 +#define IIS_EN 0x0002 +#define AUDIO_EN 0x0001 + +/* bit definition of REG_ACTL_RESET register */ +#define W5691_PLAY 0x20000 +#define ACTL_RESET_BIT 0x10000 +#define RECORD_RIGHT_CHNNEL 0x8000 +#define RECORD_LEFT_CHNNEL 0x4000 +#define PLAY_RIGHT_CHNNEL 0x2000 +#define PLAY_LEFT_CHNNEL 0x1000 +#define DAC_PLAY 0x0800 +#define ADC_RECORD 0x0400 +#define M80_PLAY 0x0200 +#define AC_RECORD 0x0100 +#define AC_PLAY 0x0080 +#define IIS_RECORD 0x0040 +#define IIS_PLAY 0x0020 +#define DAC_RESET 0x0010 +#define ADC_RESET 0x0008 +#define M80_RESET 0x0004 +#define AC_RESET 0x0002 +#define IIS_RESET 0x0001 + +/* bit definition of REG_ACTL_ACCON register */ +#define AC_BCLK_PU_EN 0x20 +#define AC_R_FINISH 0x10 +#define AC_W_FINISH 0x08 +#define AC_W_RES 0x04 +#define AC_C_RES 0x02 + +/* bit definition of REG_ACTL_RSR register */ +#define R_FIFO_EMPTY 0x04 +#define R_DMA_END_IRQ 0x02 +#define R_DMA_MIDDLE_IRQ 0x01 + +/* bit definition of REG_ACTL_PSR register */ +#define P_FIFO_EMPTY 0x04 +#define P_DMA_END_IRQ 0x02 +#define P_DMA_MIDDLE_IRQ 0x01 + +/* bit definition of REG_ACTL_M80CON register */ +#define X86_PCM_TRANS 0x10000 +#define BITS16 0x400 +#define MA3_W5691 0x200 +#define W_IF13_ACT 0x100 +#define BUSY 0x80 +#define R_IF11_ACT 0x40 +#define R_IF10_ACT 0x20 +#define W_IF12_ACT 0x10 +#define W_IF11_ACT 0x08 +#define W_IF10_ACT 0x04 +#define SOFT_CON 0x02 +#define W_GFIFO 0x20000 + +#define CLK_DIV 0xF800 + +#define SOFT_CON_A0 0x0 +#define SOFT_CON_A1 0x100 +#define SOFT_CON_R 0x200 +#define SOFT_CON_W 0x400 +#define SOFT_CON_CS 0x800 +#define SOFT_CON_REQ 0x1000 +#define SOFT_CON_REL 0x2000 + +/* bit definition of REG_ACTL_ADCON register */ +#define ADC_ZCD_EN 0x8 +#define ADC_MUTE 0x2 + +/* bit definition of REG_ACTL_DACON register */ +#define DAC1_OEB 0x2000 +#define DAC0_OEB 0x1000 +#define DAC_ZCD_EN 0x8 +#define DAC_MUTE 0x2 + +#define AD_DA_48000 (0x0 << 4) +#define AD_DA_44100 (0x1 << 4) +#define AD_DA_32000 (0x2 << 4) +#define AD_DA_24000 (0x3 << 4) +#define AD_DA_22050 (0x4 << 4) +#define AD_DA_16000 (0x5 << 4) +#define AD_DA_12000 (0x6 << 4) +#define AD_DA_11025 (0x7 << 4) +#define AD_DA_8000 (0x8 << 4) + +#endif /* _W90N745_AUDIO_REGS_H_ */ + + diff --git a/uClinux-2.4.20-uc1/drivers/sound/w90n745_i2s.c b/uClinux-2.4.20-uc1/drivers/sound/w90n745_i2s.c new file mode 100644 index 0000000..87cc6a9 --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/sound/w90n745_i2s.c @@ -0,0 +1,677 @@ +/**************************************************************************** + * + * Copyright (c) 2004 - 2007 Winbond Electronics Corp. All rights reserved. + * + * + * FILENAME + * w90n745_I2S.c + * + * VERSION + * 1.0 + * + * DESCRIPTION + * IIS for PCM3003E codec + * + * DATA STRUCTURES + * None + * + * FUNCTIONS + * + * HISTORY + * 2004.07.16 Created by Shih-Jen Lu + * 2005.11.24 Modified by PC34 QFu + * + * REMARK + * None + * + **************************************************************************/ +#include +#include +#include +#include + +#include "w90n745_audio_regs.h" +#include "w90n745_audio.h" + +#include "w90n745_i2s.h" + +//#define I2S_DEBUG +//#define I2S_DEBUG_ENTER_LEAVE +//#define I2S_DEBUG_MSG +//#define I2S_DEBUG_MSG2 + + +#ifdef I2S_DEBUG +#define DBG(fmt, arg...) printk(fmt, ##arg) +#else +#define DBG(fmt, arg...) +#endif + +#ifdef I2S_DEBUG_ENTER_LEAVE +#define ENTER() DBG("[%-10s] : Enter\n", __FUNCTION__) +#define LEAVE() DBG("[%-10s] : Leave\n", __FUNCTION__) +#else +#define ENTER() +#define LEAVE() +#endif + +#ifdef I2S_DEBUG_MSG +#define MSG(fmt) DBG("[%-10s] : "fmt, __FUNCTION__) +#else +#define MSG(fmt) +#endif + +#ifdef I2S_DEBUG_MSG2 +#define MSG2(fmt, arg...) DBG("[%-10s] : "fmt, __FUNCTION__, ##arg) +#else +#define MSG2(fmt, arg...) +#endif + +static AUDIO_T _tIIS; + +#define AUDIO_WRITE(addr, val) writel(val, addr) +#define AUDIO_READ(addr) readl(addr) + +#define Delay(time) udelay(time * 10) + +#define MSB_FORMAT 1 +#define IIS_FORMAT 2 + +static int _bIISActive = 0; +static UINT32 _uIISCR = 0; + +#define WMDEVID 0 + +#define UINT8 unsigned char + +/*----- set data format -----*/ +static void IIS_Set_Data_Format(int choose_format) +{ + + ENTER(); + + switch(choose_format){ + case IIS_FORMAT: _uIISCR = _uIISCR | IIS; + break; + case MSB_FORMAT: _uIISCR = _uIISCR | MSB_Justified; + break; + default:break; + } + AUDIO_WRITE(REG_ACTL_IISCON,_uIISCR); + + LEAVE(); +} + +/*----- set sample Frequency -----*/ +static void IIS_Set_Sample_Frequency(int choose_sf) +{ + + ENTER(); + + switch (choose_sf) + { + case AU_SAMPLE_RATE_8000: //8KHz + _uIISCR = _uIISCR | FS_256 | SCALE_4 | BCLK_48; + + break; + case AU_SAMPLE_RATE_11025: //11.025KHz + _uIISCR = _uIISCR | FS_256 | SCALE_4 | BCLK_48; + + break; + case AU_SAMPLE_RATE_16000: //16KHz + _uIISCR = _uIISCR | FS_256 | SCALE_2 | BCLK_48; + + break; + case AU_SAMPLE_RATE_22050: //22.05KHz + _uIISCR = _uIISCR | FS_256 | SCALE_3 | BCLK_32; + + break; + case AU_SAMPLE_RATE_24000: //24KHz + _uIISCR = _uIISCR | FS_256 | SCALE_2 | BCLK_32; + + break; + case AU_SAMPLE_RATE_32000: //32KHz + _uIISCR = _uIISCR | SCALE_1 | FS_256 | BCLK_48; + + break; + case AU_SAMPLE_RATE_44100: //44.1KHz + _uIISCR = _uIISCR | SCALE_1 | FS_256 | BCLK_48; + + break; + case AU_SAMPLE_RATE_48000: //48KHz + _uIISCR = _uIISCR | FS_256 | SCALE_1 | BCLK_32; + + break; + default:break; + } + AUDIO_WRITE(REG_ACTL_IISCON,_uIISCR); + + if(choose_sf == AU_SAMPLE_RATE_44100 || choose_sf ==AU_SAMPLE_RATE_22050 || choose_sf ==AU_SAMPLE_RATE_11025 ){ + //outpw(REG_APLLCON, 0x642D);//16.934 + //outpw(REG_CLKDIV0, (inpw(REG_CLKDIV0) & 0xFF0FFFFF) | 0x300000); + AUDIO_WRITE(0xfff00010, 0x2601);//PLLCON 390Mhz + AUDIO_WRITE(0xfff00014, 0x116);// divide by 23 + }else + { + AUDIO_WRITE(0xfff00010, 0x2f01);//PLLCON 480Mhz + AUDIO_WRITE(0xfff00014, 0x126);// divide by 39 + } + + LEAVE(); +} + +static INT iis_reset(void) +{ + ENTER(); + + AUDIO_WRITE(REG_CLKSEL, AUDIO_READ(REG_CLKSEL) | 0x10000); + + AUDIO_WRITE(0xFFF83000,0x155);//GPIO_CFG0 PT0CFG0~4 + AUDIO_WRITE(0xFFF83004,0x1d);//GPIO1:In GPIO0,2,3,4:Out + + /* reset audio interface */ + AUDIO_WRITE(REG_ACTL_RESET,AUDIO_READ(REG_ACTL_RESET) | ACTL_RESET_BIT); + Delay(100); + AUDIO_WRITE(REG_ACTL_RESET,AUDIO_READ(REG_ACTL_RESET) & ~ACTL_RESET_BIT); + Delay(100); + + /* reset IIS interface */ + AUDIO_WRITE(REG_ACTL_RESET,AUDIO_READ(REG_ACTL_RESET) | IIS_RESET); + Delay(100); + AUDIO_WRITE(REG_ACTL_RESET,AUDIO_READ(REG_ACTL_RESET) & ~IIS_RESET); + Delay(100); + + /* enable audio controller and IIS interface */ + AUDIO_WRITE(REG_ACTL_CON, AUDIO_READ (REG_ACTL_CON) | AUDCLK_EN | PFIFO_EN | DMA_EN | IIS_EN | AUDIO_EN | RFIFO_EN | T_DMA_IRQ | R_DMA_IRQ); + +#if 0 + /* use GPIO 17 18 19 as L3 interface control pin */ + //writew(REG_GPIO_OE,readw(REG_GPIO_OE) & ~(1 << L3MODE_GPIO_NUM | 1<< L3CLOCK_GPIO_NUM | 1<< L3DATA_GPIO_NUM)); + AUDIO_WRITE(REG_GPIO_CFG,AUDIO_READ(REG_GPIO_CFG) & ~(0x3F<<14)); + AUDIO_WRITE(REG_GPIO_DIR,AUDIO_READ(REG_GPIO_DIR) | L3MODE_GPIO_NUM | L3CLOCK_GPIO_NUM | L3DATA_GPIO_NUM); + + /* set volume, dsp power up and no de-emph.no mute of external codec, from L3 */ +#endif + + _uIISCR = 0; + + LEAVE(); + + return 0; +} + + +VOID i2sSetPlaySampleRate(INT nSamplingRate) +{ + _tIIS.nPlaySamplingRate = nSamplingRate; +} + +VOID i2sSetRecordSampleRate(INT nSamplingRate) +{ + _tIIS.nRecSamplingRate = nSamplingRate; +} + + +VOID i2sSetPlayCallBackFunction(AU_CB_FUN_T fnCallBack) +{ + _tIIS.fnPlayCallBack = fnCallBack; +} + +VOID i2sSetRecordCallBackFunction(AU_CB_FUN_T fnCallBack) +{ + _tIIS.fnRecCallBack = fnCallBack; +} + +/*************************************************************************/ +/* */ +/* FUNCTION */ +/* i2sStartPlay */ +/* */ +/* DESCRIPTION */ +/* Start IIS playback. */ +/* */ +/* INPUTS */ +/* fnCallBack client program provided callback function. The audio */ +/* driver will call back to get next block of PCM data */ +/* nSamplingRate the playback sampling rate. Supported sampling */ +/* rate are 48000, 44100, 32000, 24000, 22050, 16000, */ +/* 11025, and 8000 Hz */ +/* */ +/* OUTPUTS */ +/* None */ +/* */ +/* RETURN */ +/* 0 Success */ +/* Otherwise error */ +/* */ +/*************************************************************************/ +static int i2sStartPlay(AU_CB_FUN_T fnCallBack, INT nSamplingRate, + INT nChannels) +{ + INT nStatus /* ,L3status */; + + ENTER(); + + if (_bIISActive & IIS_PLAY_ACTIVE){ + MSG("IIS already playing\n"); + return ERR_IIS_PLAY_ACTIVE; /* IIS was playing */ + } + + //printk("SamplingRate : %d Channels : %d\n", nSamplingRate, nChannels); + + if (_bIISActive == 0) + { + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) & ~ PLAY_LEFT_CHNNEL & ~ PLAY_RIGHT_CHNNEL); + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) | PLAY_RIGHT_CHNNEL); + if (nChannels == AU_CH_STEREO) + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) | PLAY_LEFT_CHNNEL); + nStatus = iis_reset(); + if (nStatus < 0) + return nStatus; + } + +// enable_irq(AU_PLAY_INT_NUM); + Enable_Int(AU_PLAY_INT_NUM); + + i2sSetPlayCallBackFunction(fnCallBack); + i2sSetPlaySampleRate(nSamplingRate); + +#if 0 + /* set play sampling rate and data format */ + //L3status = L3_Set_Sample_Frequency(nSamplingRate); + //L3status = L3status | L3_Set_Data_Format(data_format); + + //L3_Set_Data(EX_DAC_On); + //L3_Set_Status(L3status); +#endif + + + IIS_Set_Sample_Frequency(nSamplingRate); + IIS_Set_Data_Format(MSB_FORMAT); + + + /* set DMA play destination base address */ + AUDIO_WRITE(REG_ACTL_PDSTB, _tIIS.uPlayBufferAddr); + + /* set DMA play buffer length */ + AUDIO_WRITE(REG_ACTL_PDST_LENGTH, _tIIS.uPlayBufferLength); + + MSG2("DMA Buffer : %x, Length : %x\n", _tIIS.uPlayBufferAddr,_tIIS.uPlayBufferLength); + + /* call back to fill DMA play buffer */ + //_tIIS.fnPlayCallBack((UINT8 *)_tIIS.uDMABufferAddr, _tIIS.uDMABufferLength/2); + //_tIIS.fnPlayCallBack((UINT8 *)(_tIIS.uDMABufferAddr + _tIIS.uDMABufferLength/2), + // _tIIS.uDMABufferLength/2); + + /* start playing */ + MSG("IIS start playing...\n"); + AUDIO_WRITE(REG_ACTL_PSR, 0x3); + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) | IIS_PLAY); + _bIISActive |= IIS_PLAY_ACTIVE; + + LEAVE(); + + return 0; +} + + +/*************************************************************************/ +/* */ +/* FUNCTION */ +/* i2sStopPlay */ +/* */ +/* DESCRIPTION */ +/* Stop IIS playback immdediately. */ +/* */ +/* INPUTS */ +/* None */ +/* */ +/* OUTPUTS */ +/* None */ +/* */ +/* RETURN */ +/* 0 Success */ +/* Otherwise error */ +/* */ +/*************************************************************************/ +static void i2sStopPlay(void) +{ + ENTER(); + + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) & ~(PLAY_RIGHT_CHNNEL | PLAY_LEFT_CHNNEL)); + + if (!(_bIISActive & IIS_PLAY_ACTIVE)) + return; + + MSG("IIS stop playing\n"); + + /* stop playing */ + while( AUDIO_READ(REG_ACTL_RESET) & IIS_PLAY ) + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) & ~IIS_PLAY); + + _bIISActive &= ~IIS_PLAY_ACTIVE; + + /* disable audio play interrupt */ + if (!_bIISActive) + Disable_Int(AU_PLAY_INT_NUM); + + + LEAVE(); + + return; +} + + +/*************************************************************************/ +/* */ +/* FUNCTION */ +/* i2sStartRecord */ +/* */ +/* DESCRIPTION */ +/* Start IIS record. */ +/* */ +/* INPUTS */ +/* fnCallBack client program provided callback function. The audio */ +/* driver will call back to deliver the newly recorded */ +/* block of PCM data */ +/* nSamplingRate the record sampling rate. Supported sampling */ +/* rate are 48000, 44100, 32000, 24000, 22050, 16000, */ +/* 11025, and 8000 Hz */ +/* */ +/* OUTPUTS */ +/* None */ +/* */ +/* RETURN */ +/* 0 Success */ +/* Otherwise error */ +/* */ +/*************************************************************************/ +static int i2sStartRecord(AU_CB_FUN_T fnCallBack, INT nSamplingRate, + INT nChannels) +{ + INT nStatus /* ,L3status */; + + ENTER(); + + if (_bIISActive & IIS_REC_ACTIVE) + return ERR_IIS_REC_ACTIVE; /* IIS was recording */ + + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) & ~(RECORD_LEFT_CHNNEL & RECORD_RIGHT_CHNNEL)); + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) | RECORD_LEFT_CHNNEL); + if (nChannels != 1) + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) | RECORD_RIGHT_CHNNEL); + + if (_bIISActive == 0) + { + nStatus = iis_reset(); + if (nStatus < 0) + return nStatus; + } + + Enable_Int(AU_REC_INT_NUM); + + i2sSetRecordCallBackFunction(fnCallBack); + i2sSetRecordSampleRate(nSamplingRate); + +#if 0 + /* set record sampling rate and data format */ + //L3status = L3_Set_Sample_Frequency(nSamplingRate); + //L3status = L3status | L3_Set_Data_Format(MSB_FORMAT); + + //L3_Set_Data(EX_ADC_On); + //L3_Set_Status(L3status); +#endif + + IIS_Set_Sample_Frequency(nSamplingRate); + IIS_Set_Data_Format(MSB_FORMAT); + + + + /* set DMA record destination base address */ + AUDIO_WRITE(REG_ACTL_RDSTB, _tIIS.uRecordBufferAddr); + + /* set DMA record buffer length */ + AUDIO_WRITE(REG_ACTL_RDST_LENGTH, _tIIS.uRecordBufferLength); + + /* start recording */ + MSG("IIS start recording...\n"); + AUDIO_WRITE(REG_ACTL_RSR, 0x3); + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) | IIS_RECORD); + _bIISActive |= IIS_REC_ACTIVE; + + LEAVE(); + + return 0; +} + + +/*************************************************************************/ +/* */ +/* FUNCTION */ +/* i2sStopRecord */ +/* */ +/* DESCRIPTION */ +/* Stop IIS record immediately. */ +/* */ +/* INPUTS */ +/* None */ +/* */ +/* OUTPUTS */ +/* None */ +/* */ +/* RETURN */ +/* 0 Success */ +/* Otherwise error */ +/* */ +/*************************************************************************/ +static void i2sStopRecord(void) +{ + ENTER(); + + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) & ~(RECORD_RIGHT_CHNNEL | RECORD_LEFT_CHNNEL)); + + if (!(_bIISActive & IIS_REC_ACTIVE)) + return; + + MSG("IIS stop recording\n"); + + /* stop recording */ + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) & ~IIS_RECORD); + + _bIISActive &= ~IIS_REC_ACTIVE; + /* disable audio record interrupt */ + if (!_bIISActive) + Disable_Int(AU_PLAY_INT_NUM); + + + LEAVE(); + + return; +} + + +/*************************************************************************/ +/* */ +/* FUNCTION */ +/* i2sSetPlayVolume */ +/* */ +/* DESCRIPTION */ +/* Set i2S left and right channel play volume. */ +/* */ +/* INPUTS */ +/* ucLeftVol play volume of left channel */ +/* ucRightVol play volume of left channel */ +/* 0: mute */ +/* 1: minimal volume */ +/* 31: maxmum volume */ +/* */ +/* OUTPUTS */ +/* None */ +/* */ +/* RETURN */ +/* 0 Success */ +/* Otherwise error */ +/* */ +/*************************************************************************/ +static int i2sSetPlayVolume(UINT32 ucLeftVol, UINT32 ucRightVol) //0~31 +{ + ENTER(); + + if (ucLeftVol>31) + ucLeftVol=31; + if (ucRightVol>31) + ucRightVol=31; + + //printk("Set IIS Play volume to : %d-%d\n", ucLeftVol, ucRightVol); + + _tIIS.sPlayVolume = 0x3F - ucLeftVol*2; + +#if 0 + /*----- Address Mode -----*/ + L3_Address_Mode(EX_1345ADDR | EX_DATA);//set the DATA(volumn,de-emphasis, mute, and power control) + /*----- Data Transfer Mode -----*/ + L3_Data_Transfer_Mode((UINT8)_tIIS.sPlayVolume); +#endif + + LEAVE(); + + return 0; +} + +static int i2sSetRecordVolume(UINT32 ucLeftVol, UINT32 ucRightVol) +{ + ENTER(); + + LEAVE(); + + return 0; + +} + +static void i2sSetPlayBuffer(UINT32 uDMABufferAddr, UINT32 uDMABufferLength) +{ + ENTER(); + + _tIIS.uPlayBufferAddr = uDMABufferAddr; + _tIIS.uPlayBufferLength = uDMABufferLength; + + LEAVE(); +} + +static void i2sSetRecordBuffer(UINT32 uDMABufferAddr, UINT32 uDMABufferLength) +{ + ENTER(); + + _tIIS.uRecordBufferAddr = uDMABufferAddr; + _tIIS.uRecordBufferLength = uDMABufferLength; + + LEAVE(); +} + +static void iis_play_isr(void) +{ + int bPlayLastBlock = 0; + + ENTER(); + + MSG2("[DMA:S:%x,L:%x,C:%x]\n", + AUDIO_READ(REG_ACTL_PDSTB), + AUDIO_READ(REG_ACTL_PDST_LENGTH), + AUDIO_READ(REG_ACTL_PDSTC)); + + + AUDIO_WRITE(REG_ACTL_CON, AUDIO_READ(REG_ACTL_CON) | T_DMA_IRQ); + + if (AUDIO_READ(REG_ACTL_PSR) & P_DMA_MIDDLE_IRQ) + { + AUDIO_WRITE(REG_ACTL_PSR, P_DMA_MIDDLE_IRQ); + bPlayLastBlock = _tIIS.fnPlayCallBack(_tIIS.uPlayBufferAddr, + _tIIS.uPlayBufferLength/2); + } + else if (AUDIO_READ(REG_ACTL_PSR) & P_DMA_END_IRQ) + { + AUDIO_WRITE(REG_ACTL_PSR, P_DMA_END_IRQ); + bPlayLastBlock = _tIIS.fnPlayCallBack(_tIIS.uPlayBufferAddr + _tIIS.uPlayBufferLength/2, + _tIIS.uPlayBufferLength/2); + } + + + /* check whether the next block is ready. If not, stop play */ + + if (bPlayLastBlock) + { + AUDIO_WRITE(REG_ACTL_PSR, P_DMA_MIDDLE_IRQ | P_DMA_END_IRQ); +// i2sStopPlay(); + } + + + LEAVE(); + +} + + +static void iis_rec_isr(void) +{ + int bPlayLastBlock = 0; + + ENTER(); + + AUDIO_WRITE(REG_ACTL_CON, AUDIO_READ(REG_ACTL_CON) | R_DMA_IRQ); + + if (AUDIO_READ(REG_ACTL_RSR) & R_DMA_MIDDLE_IRQ) + { + AUDIO_WRITE(REG_ACTL_RSR, R_DMA_MIDDLE_IRQ); + bPlayLastBlock = _tIIS.fnRecCallBack(_tIIS.uRecordBufferAddr, _tIIS.uRecordBufferLength/2); + } + else if (AUDIO_READ(REG_ACTL_RSR) & R_DMA_END_IRQ) + { + AUDIO_WRITE(REG_ACTL_RSR, R_DMA_END_IRQ); + bPlayLastBlock = _tIIS.fnRecCallBack(_tIIS.uRecordBufferAddr + _tIIS.uRecordBufferLength/2, + _tIIS.uRecordBufferLength/2); + } + + /* check whether the next block is token away. If not, stop record */ + + if (bPlayLastBlock) + { + AUDIO_WRITE(REG_ACTL_RSR, R_DMA_MIDDLE_IRQ | R_DMA_END_IRQ); + } + + LEAVE(); +} + + +INT i2sInit(VOID) +{ + int nStatus = 0; + + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) &~PLAY_LEFT_CHNNEL &~PLAY_RIGHT_CHNNEL); + AUDIO_WRITE(REG_ACTL_RESET, AUDIO_READ(REG_ACTL_RESET) &~RECORD_LEFT_CHNNEL &~RECORD_RIGHT_CHNNEL); + + nStatus = iis_reset(); + if (nStatus < 0) + return nStatus; + + return 0; +} + +static INT i2sGetCapacity(VOID) +{ + return DSP_CAP_DUPLEX; /* support full duplex */ +} + +WB_AUDIO_CODEC_T wb_i2s_codec = { + dev: AU_DEV_IIS, + get_capacity: i2sGetCapacity, + set_play_buffer: i2sSetPlayBuffer, + set_record_buffer: i2sSetRecordBuffer, + reset: i2sInit, + start_play: i2sStartPlay, + stop_play: i2sStopPlay, + start_record: i2sStartRecord, + stop_record: i2sStopRecord, + set_play_volume: i2sSetPlayVolume, + set_record_volume: i2sSetRecordVolume, /* not supprted */ + play_interrupt: iis_play_isr, + record_interrupt: iis_rec_isr, +}; diff --git a/uClinux-2.4.20-uc1/drivers/sound/w90n745_i2s.h b/uClinux-2.4.20-uc1/drivers/sound/w90n745_i2s.h new file mode 100644 index 0000000..3b9aef5 --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/sound/w90n745_i2s.h @@ -0,0 +1,69 @@ +/************************************************************************************************** + * + * Copyright (c) 2004 - 2007 Winbond Electronics Corp. All rights reserved. + * + * FILENAME + * w90n745_i2s.h + * + * VERSION + * 1.0 + * + * DESCRIPTION + * This file contains the register map of IIS audio interface + * + * HISTORY + * 02/09/2004 Ver 1.0 Created by PC31 SJLu + * + * REMARK + * None + * + *************************************************************************************************/ +#ifndef _W90N745_I2S_H_ +#define _W90N745_I2S_H_ + +/*----- bit definition of REG_ACTL_IISCON register -----*/ +#define IIS 0x0 +#define MSB_Justified 0x0008 +#define SCALE_1 0x0 +#define SCALE_2 0x10000 +#define SCALE_3 0x20000 +#define SCALE_4 0x30000 +#define SCALE_5 0x40000 +#define SCALE_6 0x50000 +#define SCALE_7 0x60000 +#define SCALE_8 0x70000 +#define SCALE_10 0x90000 +#define SCALE_12 0xB0000 +#define SCALE_14 0xD0000 +#define SCALE_16 0xF0000 +#define FS_384 0x20 +#define FS_256 0x0 +#define BCLK_32 0x00 +#define BCLK_48 0x40 + +/* bit definition of L3DATA register */ +#define EX_256FS 0x20 /*-- system clock --*/ +#define EX_384FS 0x10 +#define EX_IIS 0x00 /*-- data input format --*/ +#define EX_MSB 0x08 +#define EX_1345ADDR 0x14 //The address of the UDA1345TS +#define EX_STATUS 0x02 //data transfer type (STATUS) +#define EX_DATA 0x00 //data transfer type (DATA) +#define EX_ADC_On 0xC2 //turn on the ADC +#define EX_DAC_On 0xC1 //turn on the DAC + +/*----- GPIO NUM -----*/ +#define L3MODE_GPIO_NUM (1<<17) +#define L3CLOCK_GPIO_NUM (1<<18) +#define L3DATA_GPIO_NUM (1<<19) + +#define MSB_FORMAT 1 +#define IIS_FORMAT 2 + +#define IIS_ACTIVE 0x1 +#define IIS_PLAY_ACTIVE 0x2 +#define IIS_REC_ACTIVE 0x4 + +#endif /* _W90N745_I2S_H_ */ + + -- cgit v0.12