📄 i2s.patch
字号:
+ au1550_delay(10);+}++static int wm87x1_get_recsrc(void)+{+ if (rec_src){+ return (SOUND_MASK_MIC);+ }else{+ return (SOUND_MASK_LINE);+ }++}+++static void wm87x1_set_recsrc(struct i2s_hw *hw, int src)+{ u16 val=0;+ hw->wrcodec(ACTIVE_CTRL, 0x00); /* Deactivate codec */+ +#if defined(WM8731)+ /* if its not MIC, then it must be LINEIN. The check for available mask+ should ahve been done at the ioctl level+ */+ /* Application starts sending ipput mask at each ioctl call. Its driver responsibility + to keep track of record source. Funtion given below keeps track of the present record source.+ */++ if(pres_src==0)+ {+ pres_src=1;+ rec_src = 1;+ }+ else+ {+ pres_src=0;+ rec_src = 0;+ }+ + if( (pres_src==1) && (src & SOUND_MASK_MIC))+ {+ val =5; + }++ hw->wrcodec(ANALOGUE_AUDIO_PATH_CTRL, 0x38 | val); /* DAC sel, BYPASS and INSEL(val),Sidetone */+#endif+ + hw->wrcodec(ACTIVE_CTRL, 0x01); /* Activate codec */+}++static int wm87x1_read_mixer(struct i2s_hw *hw, u16 oss_channel)+{+ int left, right;+/* int scale = i2s_ro_mixer[oss_channel].scale; */++ left = (i2s_ro_mixer[oss_channel].value>>8)&0x7f;+ right = (i2s_ro_mixer[oss_channel].value)&0x7f;++ return (left | (right << 8));+}++static void wm87x1_write_mixer(struct i2s_hw *hw, u16 oss_channel, u16 val)+{+ int scale = i2s_ro_mixer[oss_channel].scale;+ unsigned int left,right;++ /* cleanse input a little */+ right = ((val >> 8) & 0xff) ;+ left = (val & 0xff) ;++ if (right > 100) right = 100;+ if (left > 100) left = 100;++ i2s_ro_mixer[oss_channel].value = left<<8 | right;++ right = (right * scale) / 100;+ left = (left * scale) / 100;++ right += i2s_ro_mixer[oss_channel].hw_min;+ left += i2s_ro_mixer[oss_channel].hw_min;++ switch( oss_channel ) {+ case SOUND_MIXER_PCM:+ hw->wrcodec(L_HEADPHONE_OUT, left);+ hw->wrcodec(R_HEADPHONE_OUT, right);+ break;+ case SOUND_MIXER_LINE:+ hw->wrcodec(L_LINE_IN, left);+ hw->wrcodec(R_LINE_IN, right);+ break;+ }+}++#endif++#if defined(WM8731) || defined(WM8721)+static void wm87x1_codec_init(struct i2s_hw *hw)+{+ hw->wrcodec(RESET, 0x00); /* Reset */+ hw->wrcodec(POWER_DOWN_CTRL, 0x00); /* Power up everything */+ hw->wrcodec(ACTIVE_CTRL, 0x00); /* Deactivate codec */++ /* + * Setup some default mixer settings :)+ */+ hw->set_mixer(hw, SOUND_MIXER_PCM, (54<<8)|54);+#if !defined(WM8721)+ hw->set_mixer(hw, SOUND_MIXER_MIC, (74<<8)|74);+ hw->set_mixer(hw, SOUND_MIXER_LINE, (74<<8)|74);++ hw->wrcodec(ANALOGUE_AUDIO_PATH_CTRL, 0x38); /* DAC sel, BYPASS and LINEIN */+#else+ hw->wrcodec(ANALOGUE_AUDIO_PATH_CTRL, 0x10); /* DAC sel */+#endif++ hw->wrcodec(DIGITAL_AUDIO_PATH_CTRL, 0x00); /* Disable output mute */++ /* Set slave/master mode in codec */+ if(hw->codec_is_master)+ hw->wrcodec(DIGITAL_AUDIO_IF, 0x42);/* Set MASTER, 16-bit, I2S modes */+ else+ hw->wrcodec(DIGITAL_AUDIO_IF, 0x02);/* Set SLAVE, 16-bit, I2S modes */+ + hw->wrcodec(SAMPLING_CONTROL, 0x23); /* Default SR */+ hw->wrcodec(ACTIVE_CTRL, 0x01); /* Activate codec */+}+#endif++#if defined(WM8731)++/*+ * The WM8731 I2S Codec cannot use completly different sample+ * rate for ADC and DAC. There is small table in a Wolfson+ * whitepaper "Master clocks and audio sample rates supported+ * by Wolfson's Portable Audio IC's". + * + * ASSUMES 12MHZ and USB mode settings for CODEC!!+ * ASSUMES 12MHZ and USB mode settings for CODEC!!+ *+ * The function below implements the combinations specified in + * the whitepaper.+ *+ */+static int+wm8731_set_rates( struct i2s_hw *hw, unsigned int *in, unsigned int *out )+{+ volatile psc_i2s_t* ip = (volatile psc_i2s_t *)I2S_PSC_BASE;+ u32 cfg=0;+ u16 codec_cfg=0;+ unsigned int adc=*in, dac=*out;+ + /* Deactivate codec */+ hw->wrcodec(ACTIVE_CTRL, 0x00); /* Deactivate codec */++ /*+ * If the PSC is master, then we need to disable PSC before+ * updating registers.+ */+ if ( !(hw->codec_is_master) ) {+ /* Disable I2S controller */+ ip->psc_i2scfg &= ~PSC_I2SCFG_DE_ENABLE;+ au_sync();+ /* Wait for device disabled */+ while ((ip->psc_i2sstat & PSC_I2SSTAT_DR) == 1);+ cfg = ip->psc_i2scfg;+ /* Clear WS and DIVIDER values */+ cfg &= ~(PSC_I2SCFG_WS_MASK | PSC_I2SCFG_DIV_MASK); + }++ if ( adc == 8000 && dac == 8000 ) { + // for 8000 USB=1 BOSR=1 fs=1500+ cfg |= PSC_I2SCFG_SET_WS(94);+ cfg |= PSC_I2SCFG_DIV16;+ codec_cfg = (0x03<<2) | WM_SC_MODE_USB;+ }+ /* We cannot get 11025 with codec as master --- that I know of */+ else if ( !hw->codec_is_master && adc == 11025 && dac == 11025 ) { + // for 11025 USB=1 BOSR=1 fs=272+ cfg |= PSC_I2SCFG_SET_WS(68);+ cfg |= PSC_I2SCFG_DIV16;+ codec_cfg = WM_SC_SR_44100 | WM_SC_BOSR_272FS | WM_SC_MODE_USB;+ }+ /* We cannot get 22050 with codec as master --- that I know of */+ else if ( !hw->codec_is_master && adc == 22050 && dac == 22050 ) { + // for 22050 USB=1 BOSR=1 fs=544.22+ cfg |= PSC_I2SCFG_SET_WS(68);+ cfg |= PSC_I2SCFG_DIV8;+ codec_cfg = WM_SC_SR_44100 | WM_SC_BOSR_272FS | WM_SC_MODE_USB;+ }+ else if ( adc == 32000 && dac == 32000 ) { + // for 32000 USB=1 BOSR=1 fs=375+ cfg |= PSC_I2SCFG_SET_WS(94);+ cfg |= PSC_I2SCFG_DIV4;+ codec_cfg = WM_SC_SR_32000 | WM_SC_BOSR_272FS | WM_SC_MODE_USB;+ }+ else if ( adc == 44100 && dac == 44100 ) { + // for 44100 USB=1 BOSR=1 fs=272+ cfg |= PSC_I2SCFG_SET_WS(68);+ cfg |= PSC_I2SCFG_DIV4;+ codec_cfg = WM_SC_SR_44100 | WM_SC_BOSR_272FS | WM_SC_MODE_USB;+ }+ else if ( adc == 48000 && dac == 48000 ) { + // for 48 USB=1 BOSR=0 fs=250+ cfg |= PSC_I2SCFG_SET_WS(125);+ cfg |= PSC_I2SCFG_DIV2;+ codec_cfg = WM_SC_SR_48000 | WM_SC_MODE_USB;+ }+ else if ( adc == 88200 && dac == 88200 ) { + // for 88200 USB=1 BOSR=1 fs=136+ cfg |= PSC_I2SCFG_SET_WS(68);+ cfg |= PSC_I2SCFG_DIV2;+ codec_cfg = WM_SC_SR_88200 | WM_SC_BOSR_272FS | WM_SC_MODE_USB;+ }+ else if ( adc == 96000 && dac == 96000 ) { + // for 96000 USB=1 BOSR=0 fs=125+ cfg |= PSC_I2SCFG_SET_WS(63);+ cfg |= PSC_I2SCFG_DIV2;+ codec_cfg = WM_SC_SR_96000 | WM_SC_MODE_USB;+ }+ /*+ * This is the default sample rate for this platform. + * There is a little bit of code duplication here.... but wanted to+ * make it easy to replace defaults. Im sure there is a better way+ * but I'm in a hurry :)+ */+ else {+ // for 48 USB=1 BOSR=0 fs=250+ cfg |= PSC_I2SCFG_SET_WS(125);+ cfg |= PSC_I2SCFG_DIV2;+ codec_cfg = WM_SC_SR_48000 | WM_SC_MODE_USB;+ hw->sr = adc = dac = 48000;+ }++ if (!(hw->codec_is_master)){+ /* Reconfigure and enable */+ ip->psc_i2scfg = cfg;+ au_sync();+ ip->psc_i2scfg = cfg | PSC_I2SCFG_DE_ENABLE;+ au_sync();+ /* Wait for device enabled */+ while ((ip->psc_i2sstat & PSC_I2SSTAT_DR) == 0);+ }++ hw->wrcodec(SAMPLING_CONTROL, codec_cfg);++ /* Activate codec */+ hw->wrcodec(ACTIVE_CTRL, 0x01);++ *in = adc;+ *out = dac;+ return 0;+}+#endif++#if defined(WM8721)+/*+ * The WM8721 I2S Codec does not support ADC.+ * + * *** ASSUMES we are operating in NORMAL -- variable clocks+ * ASSUMES CODEC is always the master+ *+ */+static int +wm8721_set_rates( struct i2s_hw *hw, unsigned int *in, unsigned int *out )+{+ u16 codec_cfg=0;+ unsigned int dac=*out;++ /* Deactivate codec */+ hw->wrcodec(ACTIVE_CTRL, 0x00); /* Deactivate codec */++ switch( dac ) {+ case 8000:+ codec_cfg = WM_SC_SR_8000;+ break;+ case 8018: + codec_cfg = WM_SC_SR_8018;+ break;+ case 32000:+ codec_cfg = WM_SC_SR_32000;+ break;+ case 44100:+ codec_cfg = WM_SC_SR_44100;+ break;+ case 48000:+ codec_cfg = WM_SC_SR_48000;+ break;+ case 88200:+ codec_cfg = WM_SC_SR_88200;+ break;+ case 96000:+ codec_cfg = WM_SC_SR_96000;+ break;++ }+ hw->wrcodec(SAMPLING_CONTROL, codec_cfg);++ /* Activate codec */+ hw->wrcodec(ACTIVE_CTRL, 0x01);++ *in = 0;+ *out = dac;+ return 0;+}+#endif++++/*+ * The meat of the HW init.+ */+#if defined(CONFIG_MIPS_PB1200) || defined(CONFIG_MIPS_DB1200) \+ || defined(CONFIG_MIPS_PB1550) || defined(CONFIG_MIPS_DB1550)++static struct i2s_hw _i2s = { + .name = "Wolfson WM8731 i2s", + .input_mask = SOUND_MASK_LINE | SOUND_MASK_MIC,+ .output_mask = SOUND_MASK_PCM,+ .codec_init = wm87x1_codec_init, + .wrcodec = wm87x1_wrcodec, + .set_rates = wm8731_set_rates, + .get_mixer = wm87x1_read_mixer, + .set_mixer = wm87x1_write_mixer,+ .set_recsrc = wm87x1_set_recsrc,+ .get_recsrc = wm87x1_get_recsrc+#ifdef CONFIG_PM+, + .power = wm87x1_power+#endif+};++struct i2s_hw * AMD_PbDb_setup(u32 psc, u32 clock) +{+ volatile psc_i2s_t *ip = (volatile psc_i2s_t *)psc;+ u32 div, clk,val;+ struct i2s_hw *i2s=&_i2s;++ i2s->psc = ip;+ ip->psc_ctrl = PSC_CTRL_DISABLE; /* Disable PSC */+ au_sync();+ +#if defined(CONFIG_MIPS_PB1200) || defined(CONFIG_MIPS_DB1200)+ /* CPLD Mux for I2s */+ bcsr->resets |= BCSR_RESETS_PCS1MUX;+ au_sync();+#endif++ /*+ Clocking for a PSC in I2S mode can be complicated due to the+ numerous schemes available.+ + The Pb/Db1200 and Pb/Db1550 have a 12Mhz clock off the codec+ with the codec outputing the clock on CLK_OUT pin. THe PSC uses+ this clock as its internal clock.++ Pb1200 Option :+ Another option would be to disconnect the 12Mhz clock,X4, hanging+ off the Wolfson CODEC and populate resistors R153 and R155+ and unpopulate R167.+ The PSC would need to be configured to output its clock on GPIO2.+ Please refer to the databook for more detailed information.+ The resistor locations are different on the Pb/Db1550 but the+ same concept applies.+ */+ switch (clock) {+ case PSC_SEL_CLK_INTCLK:+ i2s->codec_is_master = 0;++ /* The FRDIV in the frequency control is (FRDIV + 1) * 2+ Our goal is to have intclk==12MHz+ */+ clk = au_readl(SYS_FREQCTRL1);+ div = au_readl(SYS_AUXPLL);+ au_sync();+ clk &= ~(SYS_FC_FRDIV4_MASK | SYS_FC_FS4);;+ clk |= (div << SYS_FC_FRDIV4_BIT);+ clk |= SYS_FC_FE4;+ au_writel(clk, SYS_FREQCTRL1);+ au_sync();++ /* Set up the clock source routing to get Freq4 to PSC1_intclk.+ */+ clk = au_readl(SYS_CLKSRC);+ au_sync();+ clk &= ~0x3e000000;+ clk |= (6 << 27); /* 6 == Freq4, refer to databook pg 451 */+ au_writel(clk, SYS_CLKSRC);+ au_sync();+ break;++ case PSC_SEL_CLK_EXTCLK:+ i2s->codec_is_master = 0;+ break;++ case PSC_SEL_CLK_SERCLK:+ i2s->codec_is_master = 1;+ break;++ default:+ i2s->codec_is_master = 1;+ clock = PSC_SEL_CLK_SERCLK;+ break;++ }++ /* Codec must be initialized here, after codec_is_master is determined. */+ i2s->codec_init(i2s);++ ip->psc_sel = (clock | 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).+ */+ val = PSC_I2SCFG_SET_LEN(16) | + PSC_I2SCFG_RT_FIFO8 | + PSC_I2SCFG_TT_FIFO8 |+ PSC_I2SCFG_BI | + PSC_I2SCFG_XM;++ if (i2s->codec_is_master) val |= PSC_I2SCFG_MS;++ ip->psc_i2scfg = val | PSC_I2SCFG_DE_ENABLE;+ au_sync();++ /* Wait for device enabled */+ while ((ip->psc_i2sstat & PSC_I2SSTAT_DR) == 0);+ return i2s;+}+#endif++struct i2s_hw * i2s_hw_init(void)+{+ /*+ * Already initialized?+ */+ if (!request_region(CPHYSADDR(I2S_PSC_BASE),+ 0x30, AU1XXX_MODULE_NAME)) {+ err("I2S Audio ports in use");+ return NULL;+ }++#if defined(CONFIG_MIPS_PB1200) || defined(CONFIG_MIPS_DB1200) \+ || defined(CONFIG_MIPS_PB1550) || defined(CONFIG_MIPS_DB1550)+ /* The header file defines the I2S_PSC_BASE + Run the PSC in Slave Mode + */+ return(AMD_PbDb_setup(I2S_PSC_BASE, PSC_SEL_CLK_SERCLK));+#endif++}++void i2s_hw_cleanup(struct i2s_hw *hw)+{+ release_region(CPHYSADDR(I2S_PSC_BASE), 0x30);+}Index: linux-2.6.11-rc5/sound/oss/Makefile===================================================================--- linux-2.6.11-rc5.orig/sound/oss/Makefile+++ linux-2.6.11-rc5/sound/oss/Makefile@@ -66,7 +66,7 @@ obj-$(CONFIG_SOUND_ES1371) += es1371.o a obj-$(CONFIG_SOUND_VRC5477) += nec_vrc5477.o ac97_codec.o obj-$(CONFIG_SOUND_AU1000) += au1000.o ac97_codec.o obj-$(CONFIG_SOUND_AU1550_AC97) += au1550_ac97.o ac97_codec.o -obj-$(CONFIG_SOUND_AU1550_I2S) += au1550_i2s.o +obj-$(CONFIG_SOUND_AU1550_I2S) += au1550_i2s.o au1550_i2s_hw.o obj-$(CONFIG_SOUND_ESSSOLO1) += esssolo1.o obj-$(CONFIG_SOUND_FUSION) += cs46xx.o ac97_codec.o obj-$(CONFIG_SOUND_MAESTRO) += maestro.o
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -