I have managed to get capture stereo sound from right channels of both codecs. I have just redefined AC slots and made sure second codec is configured.
The attached patch is very experimental, proof of concept. But some parts of it could be commited (especially replacing numeric constants like 0,1, 10, 11 with things like chip->src_left_play_slot). Also there are constants like SRCSLOT_LEFT_PCM_PLAYBACK. The translation table ss2slot and change of definition for BA0_AC*SV_SLV is probably also to be merged in. The real development should go the following way IMHO: 1. add second PCM subdevice for use with second codec (interleaved 4 channel mode will be rather not possible). It will be 2 subdevices with 1-2 channels each. 2. Reserve 4 available SRC to be used with the primary codec (same as in the current code). 3. Use the second codec without CS4281 rate convertes and set the closest rates in AC97_PCM_FRONT_DAC_RATE and AC97_PCM_LR_ADC_RATE (rates supported by CS4299: 8000, 11025, 16000, 22050, 32000, 44100, 48000). B0 in AC97_EXTENDED_STATUS has to be set before. My plans at the moment are to backport the changes to ALSA5 (card-cs4281.c hacked by Pekka Pessi), and then if time allows (or porting problems arise) implement above points. Best regards, -- Tomasz Motylewski
diff -ur alsa-driver-0.9.0rc1-orig-takashi2/alsa-kernel/include/ac97_codec.h alsa-driver-0.9.0rc1/alsa-kernel/include/ac97_codec.h --- alsa-driver-0.9.0rc1-orig-takashi2/alsa-kernel/include/ac97_codec.h Thu Apr 4 09:37:53 2002 +++ alsa-driver-0.9.0rc1/alsa-kernel/include/ac97_codec.h Wed May 29 17:21:13 +2002 @@ -118,6 +118,16 @@ #define AC97_AD_SERIAL_CFG 0x74 /* Serial Configuration */ #define AC97_AD_MISC 0x76 /* Misc Control Bits */ +/* specific - Cirrus Logic (at least CS4299) */ +#define AC97_CS_MODE 0x5e /* mainly AC slot mapping */ +#define AC97_CS_MISC 0x60 /* Misc. Crystal Control */ +#define AC97_CS_SPDIF_REG 0x68 /* S/PDIF Control */ +/* CS flags */ +#define AC97_CS_MODE_AMAP (1<<7) /* 1 - slot mapping by codec ID, 0 - by SM */ +#define AC97_CS_MODE_DDM (1<<8) +#define AC97_CS_MODE_SM(x) (((x)&3)<<4) /* Slot Map No. */ +#define AC97_CS_MISC_LOSM (1<<0) /* Loss of SYNC Mute Enable. Defaults to 1 */ + /* ac97->scaps */ #define AC97_SCAP_SURROUND_DAC (1<<0) /* surround L&R DACs are present */ #define AC97_SCAP_CENTER_LFE_DAC (1<<1) /* center and LFE DACs are present */ diff -ur alsa-driver-0.9.0rc1-orig-takashi2/alsa-kernel/pci/cs4281.c alsa-driver-0.9.0rc1/alsa-kernel/pci/cs4281.c --- alsa-driver-0.9.0rc1-orig-takashi2/alsa-kernel/pci/cs4281.c Sat May 25 23:04:35 2002 +++ alsa-driver-0.9.0rc1/alsa-kernel/pci/cs4281.c Wed May 29 17:23:57 2002 @@ -51,6 +51,8 @@ static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ static int snd_dual_codec[SNDRV_CARDS]; /* dual codec */ +static mangle_channels =1; + MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); MODULE_PARM_DESC(snd_index, "Index value for CS4281 soundcard."); MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); @@ -63,6 +65,8 @@ MODULE_PARM(snd_dual_codec, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); MODULE_PARM_DESC(snd_dual_codec, "Enable Dual Codec. (put ID(1-3) here)"); MODULE_PARM_SYNTAX(snd_dual_codec, SNDRV_BOOLEAN_FALSE_DESC); +MODULE_PARM(mangle_channels, "i"); +MODULE_PARM_DESC(mangle_channels, "set to 0 to get default L/R"); /* * @@ -283,13 +287,13 @@ #define BA0_ACSTS_CRDY (1<<0) /* Codec Ready */ #define BA0_ACOSV 0x0468 /* AC'97 Output Slot Valid */ -#define BA0_ACOSV_SLV(x) (1<<((x)-3)) +#define BA0_ACOSV_SLV(x) (1<<((ss2slot[x])-3)) #define BA0_ACCAD 0x046c /* AC'97 Command Address */ #define BA0_ACCDA 0x0470 /* AC'97 Command Data */ #define BA0_ACISV 0x0474 /* AC'97 Input Slot Valid */ -#define BA0_ACISV_SLV(x) (1<<((x)-3)) +#define BA0_ACISV_SLV(x) (1<<((ss2slot[x])-3)) #define BA0_ACSAD 0x0478 /* AC'97 Status Address */ #define BA0_ACSDA 0x047c /* AC'97 Status Data */ @@ -488,12 +492,16 @@ snd_rawmidi_substream_t *midi_input; snd_rawmidi_substream_t *midi_output; +/// DMA(FIFOs) 0 and 1 play, 2 and 3 record +#define DMA_PLAY_INDX 0 +/// was 1, but we need 2 dma for dual codec +#define DMA_REC_INDX 2 cs4281_dma_t dma[4]; - unsigned char src_left_play_slot; - unsigned char src_right_play_slot; - unsigned char src_left_rec_slot; - unsigned char src_right_rec_slot; + unsigned char src_left_play_slot[2]; + unsigned char src_right_play_slot[2]; + unsigned char src_left_rec_slot[2]; + unsigned char src_right_rec_slot[2]; unsigned int spurious_dhtc_irq; unsigned int spurious_dtc_irq; @@ -516,6 +524,14 @@ { 0, } }; +#define SS_SIZE 32 +/// Table 29 and Table 30 CS4281 Programming Manual +unsigned char ss2slot[SS_SIZE] = { +// 0 1 2 3 4 5 6 7 8 9 a b c d e f + 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, 0 +}; + MODULE_DEVICE_TABLE(pci, snd_cs4281_ids); /* @@ -757,11 +773,21 @@ static void snd_cs4281_mode(cs4281_t *chip, cs4281_dma_t *dma, snd_pcm_runtime_t *runtime, int capture, int src) { int rec_mono; + int codec=0; dma->valDMR = BA0_DMR_TYPE_SINGLE | BA0_DMR_AUTO | (capture ? BA0_DMR_TR_WRITE : BA0_DMR_TR_READ); - if (runtime->channels == 1) + switch(runtime->channels == 1) { + case 1: dma->valDMR |= BA0_DMR_MONO; + case 2: + break; + case 4: + // FXDUAL activate second codec + break; + case 3: + default: + } if (snd_pcm_format_unsigned(runtime->format) > 0) dma->valDMR |= BA0_DMR_USIGN; if (snd_pcm_format_big_endian(runtime->format) > 0) @@ -780,23 +806,23 @@ /* Initialize DMA */ snd_cs4281_pokeBA0(chip, dma->regDBA, runtime->dma_addr); snd_cs4281_pokeBA0(chip, dma->regDBC, runtime->buffer_size - 1); - rec_mono = (chip->dma[1].valDMR & BA0_DMR_MONO) == BA0_DMR_MONO; - snd_cs4281_pokeBA0(chip, BA0_SRCSA, (chip->src_left_play_slot << 0) | - (chip->src_right_play_slot << 8) | - (chip->src_left_rec_slot << 16) | - ((rec_mono ? 31 : chip->src_right_rec_slot) << 24)); + rec_mono = (chip->dma[DMA_REC_INDX].valDMR & BA0_DMR_MONO) == BA0_DMR_MONO; + snd_cs4281_pokeBA0(chip, BA0_SRCSA, (chip->src_left_play_slot[codec] << 0) | + (chip->src_right_play_slot[codec] << 8) | + (chip->src_left_rec_slot[codec] << 16) | + ((rec_mono ? 31 : +chip->src_right_rec_slot[codec]) << 24)); if (!src) goto __skip_src; if (!capture) { - if (dma->left_slot == chip->src_left_play_slot) { + if (dma->left_slot == chip->src_left_play_slot[codec]) { unsigned int val = snd_cs4281_rate(runtime->rate, NULL); - snd_assert(dma->right_slot == chip->src_right_play_slot, ); + snd_assert(dma->right_slot == +chip->src_right_play_slot[codec], ); snd_cs4281_pokeBA0(chip, BA0_DACSR, val); } } else { - if (dma->left_slot == chip->src_left_rec_slot) { + if (dma->left_slot == chip->src_left_rec_slot[codec]) { unsigned int val = snd_cs4281_rate(runtime->rate, NULL); - snd_assert(dma->right_slot == chip->src_right_rec_slot, ); + snd_assert(dma->right_slot == chip->src_right_rec_slot[codec], +); snd_cs4281_pokeBA0(chip, BA0_ADCSR, val); } } @@ -915,10 +941,10 @@ snd_pcm_runtime_t *runtime = substream->runtime; cs4281_dma_t *dma; - dma = &chip->dma[0]; + dma = &chip->dma[DMA_PLAY_INDX]; dma->substream = substream; - dma->left_slot = 0; - dma->right_slot = 1; + dma->left_slot = chip->src_left_play_slot[0]; + dma->right_slot = chip->src_right_play_slot[0]; runtime->private_data = dma; runtime->hw = snd_cs4281_playback; snd_pcm_set_sync(substream); @@ -935,10 +961,10 @@ snd_pcm_runtime_t *runtime = substream->runtime; cs4281_dma_t *dma; - dma = &chip->dma[1]; + dma = &chip->dma[DMA_REC_INDX]; dma->substream = substream; - dma->left_slot = 10; - dma->right_slot = 11; + dma->left_slot = chip->src_left_rec_slot[0]; + dma->right_slot = chip->src_right_rec_slot[0]; runtime->private_data = dma; runtime->hw = snd_cs4281_capture; snd_pcm_set_sync(substream); @@ -1532,12 +1558,37 @@ */ end_time = (jiffies + (5 * HZ) / 4) + 1; + chip->src_left_play_slot[0] = SRCSLOT_LEFT_PCM_PLAYBACK; + chip->src_right_play_slot[0] = SRCSLOT_RIGHT_PCM_PLAYBACK; + chip->src_left_rec_slot[0] = SRCSLOT_LEFT_PCM_RECORD; + chip->src_right_rec_slot[0] = SRCSLOT_RIGHT_PCM_RECORD; + +// FXDUAL - this will depend on setting AC Mode Control Register (0x5e) in secondary +codec + // AC97 channels are 6,9 by default for the codec TCID=3 + // may be changed by writing to AC97_CS_MODE + // and by default it maps to these values: + chip->src_left_play_slot[1] = SRCSLOT_CENTER_PCM_PLAYBACK; + chip->src_right_play_slot[1] = SRCSLOT_LFE_PCM_PLAYBACK; + chip->src_left_rec_slot[1] = SRCSLOT_SECONDARY_MIC_ADC; + chip->src_right_rec_slot[1] = 26; + + if(mangle_channels) { + chip->src_right_play_slot[0] = chip->src_left_play_slot[1]; + chip->src_left_rec_slot[0] = chip->src_right_rec_slot[1]; + } + do { /* * Read the input slot valid register and see if input slots 3 * 4 are valid yet. */ - if ((snd_cs4281_peekBA0(chip, BA0_ACISV) & (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4))) == (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4))) + if ( mangle_channels && + ((snd_cs4281_peekBA0(chip, BA0_ACISV) & +BA0_ACISV_SLV(chip->src_right_rec_slot[0])) == +BA0_ACISV_SLV(chip->src_right_rec_slot[0])) + && + ((snd_cs4281_peekBA0(chip, BA0_ACISV2) & +BA0_ACISV_SLV(chip->src_left_rec_slot[0])) == +BA0_ACISV_SLV(chip->src_left_rec_slot[0])) + ) { + goto __ok2; + } else if ((snd_cs4281_peekBA0(chip, BA0_ACISV) & +(BA0_ACISV_SLV(chip->src_left_rec_slot[0]) | +BA0_ACISV_SLV(chip->src_right_rec_slot[0]))) == +(BA0_ACISV_SLV(chip->src_left_rec_slot[0]) | +BA0_ACISV_SLV(chip->src_right_rec_slot[0]))) goto __ok2; set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); @@ -1553,7 +1604,7 @@ * Now, assert valid frame and the slot 3 and 4 valid bits. This will * commense the transfer of digital audio data to the AC97 codec. */ - snd_cs4281_pokeBA0(chip, BA0_ACOSV, BA0_ACOSV_SLV(3) | BA0_ACOSV_SLV(4)); + snd_cs4281_pokeBA0(chip, BA0_ACOSV, BA0_ACOSV_SLV(chip->src_left_play_slot[0]) +| BA0_ACOSV_SLV(chip->src_right_play_slot[0])); /* * Initialize DMA structures @@ -1577,10 +1628,6 @@ BA0_FCR_OF(dma->fifo_offset)); } - chip->src_left_play_slot = 0; /* AC'97 left PCM playback (3) */ - chip->src_right_play_slot = 1; /* AC'97 right PCM playback (4) */ - chip->src_left_rec_slot = 10; /* AC'97 left PCM record (3) */ - chip->src_right_rec_slot = 11; /* AC'97 right PCM record (4) */ /* Initialize digital volume */ snd_cs4281_pokeBA0(chip, BA0_PPLVC, 0); diff -ur alsa-driver-0.9.0rc1-orig-takashi2/include/sound/ac97_codec.h alsa-driver-0.9.0rc1/include/sound/ac97_codec.h --- alsa-driver-0.9.0rc1-orig-takashi2/include/sound/ac97_codec.h Thu Apr 4 09:37:53 2002 +++ alsa-driver-0.9.0rc1/include/sound/ac97_codec.h Wed May 29 17:21:13 2002 @@ -118,6 +118,16 @@ #define AC97_AD_SERIAL_CFG 0x74 /* Serial Configuration */ #define AC97_AD_MISC 0x76 /* Misc Control Bits */ +/* specific - Cirrus Logic (at least CS4299) */ +#define AC97_CS_MODE 0x5e /* mainly AC slot mapping */ +#define AC97_CS_MISC 0x60 /* Misc. Crystal Control */ +#define AC97_CS_SPDIF_REG 0x68 /* S/PDIF Control */ +/* CS flags */ +#define AC97_CS_MODE_AMAP (1<<7) /* 1 - slot mapping by codec ID, 0 - by SM */ +#define AC97_CS_MODE_DDM (1<<8) +#define AC97_CS_MODE_SM(x) (((x)&3)<<4) /* Slot Map No. */ +#define AC97_CS_MISC_LOSM (1<<0) /* Loss of SYNC Mute Enable. Defaults to 1 */ + /* ac97->scaps */ #define AC97_SCAP_SURROUND_DAC (1<<0) /* surround L&R DACs are present */ #define AC97_SCAP_CENTER_LFE_DAC (1<<1) /* center and LFE DACs are present */ Binary files alsa-driver-0.9.0rc1-orig-takashi2/modules/snd-cs4281.o and alsa-driver-0.9.0rc1/modules/snd-cs4281.o differ Binary files alsa-driver-0.9.0rc1-orig-takashi2/pci/cs4281.o and alsa-driver-0.9.0rc1/pci/cs4281.o differ Binary files alsa-driver-0.9.0rc1-orig-takashi2/pci/snd-cs4281.o and alsa-driver-0.9.0rc1/pci/snd-cs4281.o differ