📄 i2s.patch
字号:
- */- ip->psc_ctrl = PSC_CTRL_DISABLE; /* Disable PSC */- au_sync();- ip->psc_sel = (PSC_SEL_CLK_INTCLK | PSC_SEL_PS_I2SMODE);- au_sync();-- /* Enable PSC- */- ip->psc_ctrl = PSC_CTRL_ENABLE;- au_sync();-- /* Wait for PSC ready.- */- do {- val = ip->psc_i2sstat;- au_sync();- } while ((val & PSC_I2SSTAT_SR) == 0);-- /* Configure I2S controller.- * Deep FIFO, 16-bit sample, DMA, make sure DMA matches fifo size.- * Actual I2S mode (first bit delayed by one clock).- * Master mode (We provide the clock from the PSC).- */- val = PSC_I2SCFG_SET_LEN(16);-#ifdef TRY_441KHz- /* This really should be 250, but it appears that all of the- * PLLs, dividers and so on in the chain shift it. That's the- * problem with sourceing the clock instead of letting the very- * stable codec provide it. But, the PSC doesn't appear to want- * to work in slave mode, so this is what we get. It's not- * studio quality timing, but it's good enough for listening- * to mp3s.- */- val |= PSC_I2SCFG_SET_WS(252);-#else- val |= PSC_I2SCFG_SET_WS(250);-#endif- val |= PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8 | \- PSC_I2SCFG_BI | PSC_I2SCFG_XM;-- ip->psc_i2scfg = val;- au_sync();- val |= PSC_I2SCFG_DE_ENABLE;- ip->psc_i2scfg = val;- au_sync();-- /* Wait for Device ready.- */- do {- val = ip->psc_i2sstat;- au_sync();- } while ((val & PSC_I2SSTAT_DR) == 0);-- val = ip->psc_i2scfg;- au_sync();-- s->codec->init_codec(s->codec);+ /* Initial settings for sample rates */+ s->dma_dac.sample_rate = s->dma_adc.sample_rate = 8000;+ set_adc_rate(s, 8000); //Set default rate+ set_dac_rate(s, 8000); //Set default rate return 0; +#if 0+ err_dev3:+ unregister_sound_mixer(s->codec->dev_mixer);+ err_dev2:+ unregister_sound_dsp(s->dev_audio);+#endif err_dev2: unregister_sound_dsp(s->dev_audio); err_dev1:@@ -1987,7 +1974,7 @@ au1550_probe(void) err_dma2: au1xxx_dbdma_chan_free(s->dma_dac.dmanr); err_dma1:- release_region(CPHYSADDR(I2S_PSC_BASE), 0x30);+ i2s_hw_cleanup(s->i2s); return -1; }@@ -1999,16 +1986,18 @@ au1550_remove(void) if (!s) return;-#ifdef AU1550_DEBUG+#ifdef DEBUG if (s->ps) remove_proc_entry(AU1000_MODULE_NAME, NULL); #endif /* AU1000_DEBUG */ synchronize_irq(); au1xxx_dbdma_chan_free(s->dma_adc.dmanr); au1xxx_dbdma_chan_free(s->dma_dac.dmanr);- release_region(CPHYSADDR(I2S_PSC_BASE), 0x30);+ i2s_hw_cleanup(s->i2s); unregister_sound_dsp(s->dev_audio);- unregister_sound_mixer(s->dev_mixer);+#if 0+ unregister_sound_mixer(s->codec->dev_mixer);+#endif } static int __init@@ -2026,5 +2015,27 @@ cleanup_au1550(void) module_init(init_au1550); module_exit(cleanup_au1550); -MODULE_AUTHOR("Advanced Micro Devices (AMD), dan@embeddededge.com");-MODULE_DESCRIPTION("Au1550 I2S Audio Driver");+#ifndef MODULE++static int __init+au1550_setup(char *options)+{+ char *this_opt;++ if (!options || !*options)+ return 0;++ while ((this_opt = strsep(&options, ","))) {+ if (!*this_opt)+ continue;+ if (!strncmp(this_opt, "vra", 3)) {+ vra = 1;+ }+ }++ return 1;+}++__setup("au1550_audio=", au1550_setup);++#endif /* MODULE */Index: linux-2.6.11-rc5/sound/oss/au1550_i2s.h===================================================================--- /dev/null+++ linux-2.6.11-rc5/sound/oss/au1550_i2s.h@@ -0,0 +1,135 @@+/*+ * au1550_i2s_hw.c -- Sound driver support for AMD Alchemy + * Au1550/Au1200 MIPS Processor Cores+ *+ */++#ifndef _AU1550_I2S_H+#define _AU1550_I2S_H++#ifdef CONFIG_MIPS_PB1550+#include <asm/mach-pb1x00/pb1550.h>+ #define WM8731+#endif++#ifdef CONFIG_MIPS_DB1550+#include <asm/mach-db1x00/db1x00.h>+ #define WM8731+#endif++#ifdef CONFIG_MIPS_PB1200+#include <asm/mach-pb1x00/pb1200.h>+ #define WM8731+#endif++#ifdef CONFIG_MIPS_DB1200+#include <asm/mach-db1x00/db1200.h>+ #define WM8731+#endif+++#define AU1XXX_MODULE_NAME "Au1xxx i2s audio"+#define PFX AU1XXX_MODULE_NAME++#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg)+#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg)++#define supported_mixer(FOO) ((FOO >= 0) && \+ (FOO < SOUND_MIXER_NRDEVICES) && \+ (s->i2s->input_mask | s->i2s->output_mask) & (1<<FOO) )++enum i2s_power_mode {DAC_OFF=0, ADC_OFF, ALL_OFF, DAC_ON, ADC_ON};++ struct i2s_hw {+ char *name;+ int codec_is_master;+ int input_mask;+ int output_mask;+ volatile psc_i2s_t *psc;+ unsigned int sr;+ void (*codec_init)(struct i2s_hw *hw);+ void (*wrcodec)(u8 ctlreg, u16 val);+ int (*set_rates)(struct i2s_hw *hw, u32 *in, u32 *out);+ int (*get_mixer)(struct i2s_hw *hw, u16 oss_channel);+ void (*set_mixer)(struct i2s_hw *hw, u16 oss_channel, u16 val);+ void (*set_recsrc)(struct i2s_hw *hw, int src);+ int (*get_recsrc)(void);+#ifdef CONFIG_PM+ void (*power)(struct i2s_hw *hw, int type);+#endif++};++ ++/* Prototypes */+struct i2s_hw * i2s_hw_init(void);+void i2s_hw_cleanup(struct i2s_hw *hw);+extern struct i2s_hw * AMD_PbDb_setup(u32 psc, u32 clock);++/* Helper functions */+static inline void i2s_stop_xmit(struct i2s_hw *hw)+{+ volatile psc_i2s_t *ip = hw->psc;+ u32 stat;++ ip->psc_i2spcr = PSC_I2SPCR_TP;+ au_sync();++ /* Wait for Transmit Busy to show disabled.+ */+ do {+ stat = ip->psc_i2sstat;+ au_sync();+ } while ((stat & PSC_I2SSTAT_TB) != 0);+} + +static inline void i2s_stop_recv(struct i2s_hw *hw)+{+ volatile psc_i2s_t *ip = hw->psc;+ u32 stat;++ ip->psc_i2spcr = PSC_I2SPCR_RP;+ au_sync();++ /* Wait for Transmit Busy to show disabled.+ */+ do {+ stat = ip->psc_i2sstat;+ au_sync();+ } while ((stat & PSC_I2SSTAT_RB) != 0);+} ++static inline void i2s_start_xmit(struct i2s_hw *hw)+{+ volatile psc_i2s_t *ip = hw->psc;+ ip->psc_i2spcr = PSC_I2SPCR_TC;+ au_sync();+ ip->psc_i2spcr = PSC_I2SPCR_TS;+ au_sync();+}++static inline void i2s_start_recv(struct i2s_hw *hw)+{+ volatile psc_i2s_t *ip = hw->psc;+ ip->psc_i2spcr = PSC_I2SPCR_RC;+ au_sync();+ ip->psc_i2spcr = PSC_I2SPCR_RS;+ au_sync();+}++static inline unsigned i2s_status(struct i2s_hw *hw)+{+ volatile psc_i2s_t *ip = hw->psc;+ u32 i2s_stat;++ i2s_stat = ip->psc_i2sstat;+#ifdef AU1000_VERBOSE_DEBUG+ if (i2s_stat & (PSC_I2SSTAT_TF | PSC_I2SSTAT_TR | PSC_I2SSTAT_TF))+ dbg("I2S status = 0x%08x", i2s_stat);+#endif++ return(i2s_stat);+}++#endifIndex: linux-2.6.11-rc5/sound/oss/au1550_i2s_hw.c===================================================================--- /dev/null+++ linux-2.6.11-rc5/sound/oss/au1550_i2s_hw.c@@ -0,0 +1,672 @@+/*+ * au1550_i2s_hw.c -- Sound driver support for AMD Alchemy + * Au1550/Au1200 MIPS Processor Cores+ *+ * The WM8731 CODEC only has one input for ADC recording device. Below+ * is a snipet of code that illustrates how to swtich between MIC and+ * LINE for WM8731. This can be used for other CODECs as long as the+ * set_recsrc() routine is implemented properly.+ *+ #include <unistd.h>+ #include <stdlib.h>+ #include <stdio.h>+ #include <fcntl.h>+ #include <sys/soundcard.h>+ #include <sys/ioctl.h>++ int main (int argc, char **argv)+ {+ char *mixer="/dev/mixer";+ int fpmixer, insel=SOUND_MASK_LINE;+ int val;++ fpmixer = open(mixer, O_RDWR );+ if (-1 == fpmixer) {+ return -1;+ }+ if ( argc > 1 ) insel = SOUND_MASK_MIC;+ ioctl(fpmixer, SOUND_MIXER_WRITE_RECSRC, &insel);+ return 0;+ }+*/+++#include <linux/module.h>+#include <linux/version.h>+#include <linux/kernel.h>+#include <linux/ioport.h>+#include <linux/sound.h>+#include <linux/slab.h>+#include <linux/soundcard.h>+#include <linux/string.h>+#include <linux/errno.h>+#include <linux/bitops.h>+#include <linux/delay.h>+#include <linux/hardirq.h>+#include <asm/io.h>+#include <asm/uaccess.h>+#include <asm/mach-au1x00/au1000.h>+#include <asm/mach-au1x00/au1xxx_psc.h>++#include "au1550_i2s.h"+++#if !defined(CONFIG_I2C_AU1550)+ #error + #error + #error I2C Support is required for I2S Codec configuration. \+ Please enable Au1xxx I2C support and rebuild.+ #error + #error +#endif++volatile int rec_src = 0;+volatile int pres_src = 0;++static void+au1550_delay(int msec)+{+ unsigned long tmo;+ signed long tmo2;++ if (in_interrupt())+ return;++ tmo = jiffies + (msec * HZ) / 1000;+ for (;;) {+ tmo2 = tmo - jiffies;+ if (tmo2 <= 0)+ break;+ schedule_timeout(tmo2);+ }+}++#if defined(WM8731) || defined(WM8721)++#define WM_VOLUME_MIN 47+#define WM_VOLUME_SCALE 80++volatile static struct i2s_ro_mixer {+ unsigned int value;+ unsigned int scale;+ unsigned int hw_min;+} i2s_ro_mixer[SOUND_MIXER_NRDEVICES]= {+ [SOUND_MIXER_PCM] = {0x5050,80,0x2F},+#if defined(WM8731)+ [SOUND_MIXER_LINE] = {0x4343,31,0},+ [SOUND_MIXER_MIC] = {0x4343,31,0},+#endif+};++#define WM_SC_SR_96000 (0x7<<2)+#define WM_SC_SR_88200 (0xF<<2)+#define WM_SC_SR_48000 (0x0<<2)+#define WM_SC_SR_44100 (0x8<<2)+#define WM_SC_SR_32000 (0x6<<2)+#define WM_SC_SR_8018 (0x9<<2)+#define WM_SC_SR_8000 (0x1<<2)+#define WM_SC_MODE_USB 1+#define WM_SC_MODE_NORMAL 0+#define WM_SC_BOSR_250FS (0<<1)+#define WM_SC_BOSR_272FS (1<<1)+#define WM_SC_BOSR_256FS (0<<1)+#define WM_SC_BOSR_128FS (0<<1)+#define WM_SC_BOSR_384FS (1<<1)+#define WM_SC_BOSR_192FS (1<<1)++/* Power Control Register Bit values*/++#define WM_PWR_LINEINPD (1<<0)+#define WM_PWR_MICPD (1<<1)+#define WM_PWR_ADCPD (1<<2)+#define WM_PWR_DACPD (1<<3)+#define WM_PWR_OUTPD (1<<4)+#define WM_PWR_OSCPD (1<<5) +#define WM_PWR_CLKOUTPD (1<<6)+#define WM_PWR_POWEROFF (1<<7) ++enum wolfson_registers {+ L_LINE_IN=0,+ R_LINE_IN,+ L_HEADPHONE_OUT, + R_HEADPHONE_OUT, + ANALOGUE_AUDIO_PATH_CTRL,+ DIGITAL_AUDIO_PATH_CTRL, + POWER_DOWN_CTRL,+ DIGITAL_AUDIO_IF, + SAMPLING_CONTROL, + ACTIVE_CTRL,+ RESET=0xF };+++#ifdef CONFIG_PM+/*No Warm Codec Reset for I2S (different than AC'97)*/+++static void wm87x1_power(struct i2s_hw *hw, int type)+{++u16 tmp=0;++switch(type){++case DAC_OFF:{+ tmp |= WM_PWR_DACPD;+ break;}++case ADC_OFF: {+ tmp |= WM_PWR_ADCPD;+ break;}+case ALL_OFF:{+ /*No Read for Wolfson codec only write*/+ tmp |=WM_PWR_LINEINPD | WM_PWR_MICPD |WM_PWR_ADCPD |+ WM_PWR_DACPD | WM_PWR_OUTPD |WM_PWR_OSCPD |+ WM_PWR_CLKOUTPD; + break;+ }+case DAC_ON:{ + tmp &=~(WM_PWR_LINEINPD | WM_PWR_MICPD |+ WM_PWR_DACPD | WM_PWR_OUTPD |WM_PWR_OSCPD |+ WM_PWR_CLKOUTPD); + break;+ }+case ADC_ON: { + tmp &=~(WM_PWR_LINEINPD | WM_PWR_MICPD |+ WM_PWR_ADCPD | WM_PWR_OUTPD |WM_PWR_OSCPD |+ WM_PWR_CLKOUTPD); + break;+ }+}++hw->wrcodec(POWER_DOWN_CTRL, tmp);+}+#endif++++static void wm87x1_wrcodec(u8 ctlreg, u16 val)+{+ int rcnt;++ extern int pb1550_wm_codec_write(u8 addr, u8 reg, u8 val);+ /* The codec is a write only device, with a 16-bit control/data+ * word. Although it is written as two bytes on the I2C, the+ * format is actually 7 bits of register and 9 bits of data.+ * The ls bit of the first byte is the ms bit of the data.+ */+ rcnt = 0;+ while ((pb1550_wm_codec_write((0x36 >> 1), + (ctlreg << 1) | ((val >> 8) & 0x01), + (u8) (val & 0x00FF)) != 1) && + (rcnt < 50)) {+ rcnt++;+ }+ if ( rcnt >= 50 ) printk("timeout wm87x1_wrcodec: reg:%x val:%x\n", ctlreg, val);+
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -