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

Reply via email to