summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver Schinagl <oliver@schinagl.nl>2011-03-18 23:26:20 (GMT)
committerOliver Schinagl <oliver@schinagl.nl>2011-03-18 23:26:20 (GMT)
commitd810895d6ad59222c4b220d602fc307d80ee97a2 (patch)
treef9cd9db7f4fb2cf541d1d600aafd1d986e60835f
parent82888781b664238847a2cd3b35f2dcfa24a29566 (diff)
downloadopenipcam-d810895d6ad59222c4b220d602fc307d80ee97a2.zip
openipcam-d810895d6ad59222c4b220d602fc307d80ee97a2.tar.gz
openipcam-d810895d6ad59222c4b220d602fc307d80ee97a2.tar.bz2
w90n745 ac97 audio driver, seems to be a W83972 based audio chip.
-rw-r--r--uClinux-2.4.20-uc1/drivers/sound/Config.in7
-rw-r--r--uClinux-2.4.20-uc1/drivers/sound/Makefile2
-rw-r--r--uClinux-2.4.20-uc1/drivers/sound/w90n745_ac97.c837
-rw-r--r--uClinux-2.4.20-uc1/drivers/sound/w90n745_ac97.h60
-rw-r--r--uClinux-2.4.20-uc1/drivers/sound/w90n745_audio.c1049
-rw-r--r--uClinux-2.4.20-uc1/drivers/sound/w90n745_audio.h145
-rw-r--r--uClinux-2.4.20-uc1/drivers/sound/w90n745_audio_regs.h199
-rw-r--r--uClinux-2.4.20-uc1/drivers/sound/w90n745_i2s.c677
-rw-r--r--uClinux-2.4.20-uc1/drivers/sound/w90n745_i2s.h69
9 files changed, 3045 insertions, 0 deletions
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 <linux/soundcard.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#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 <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/soundcard.h>
+#include <linux/sound.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/arch/irqs.h>
+
+#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 <asm/arch/cdefs.h>
+#include <asm/semaphore.h>
+
+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 <linux/soundcard.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#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_ */
+
+