Attached is a patch against ac97_codec.c (0.9.0beta12) that adds sp/dif
support for the CS4205 AC97 codec.

CS was on some good crack when they designed this chip, as it appears to
be otherwise fully AC97 compliant.  They used vendor-specific crap to 
handle anything SP/DIF related despite there being a RightWay to do it.

Known bugs -- Well, it's never been tested with "real" data.  Not only
do I not have the spdif dongle for my laptop, I don't have anything
which will accept a spdif signal.  (Plus I don't know how to generate
one anyway.. documentation is very sparse in that regard..)

I'm looking for feedback and someone [else] who can help me test this
thing.  And of course, the critical eye of "gaah!  you mangled the
otherwise beautiful ac97_codec.c file." is also welcome; I'll gladly
rearrange stuff if there is a RightWay.

Other notes about the ac97_codec:

* The spdif sample rate bits are set incorrectly in the
  IEC958_AES0_PROFESSIONALcase.

I can submit a seperate patch for this bug if so desired; it's fixed in
the attached diff in any case.

Oh yeah, despite the CS4205 claiming to have tone controls (and ALSA
exposing them) they appear to have no effect on anything.  Anyone else
have one of these chips?

 - Pizza
-- 
Solomon Peachy                                    pizzaATfucktheusers.org
I ain't broke, but I'm badly bent.                           ICQ# 1318344
Patience comes to those who wait.
    ...It's not "Beanbag Love", it's a "Transanimate Relationship"...
--- /usr/src/alsa-driver-0.9.0beta12/alsa-kernel/pci/ac97/ac97_codec.c  Sun Feb 24 
15:45:11 2002
+++ /tmp/ac97_codec.c   Mon Apr  1 16:33:20 2002
@@ -35,7 +35,6 @@
 MODULE_AUTHOR("Jaroslav Kysela <[EMAIL PROTECTED]>");
 MODULE_DESCRIPTION("Universal interface for Audio Codec '97");
 MODULE_LICENSE("GPL");
-
 static int enable_loopback = 0;
 
 MODULE_PARM(enable_loopback, "i");
@@ -59,6 +58,7 @@
 static int patch_sigmatel_stac9744(ac97_t * ac97);
 static int patch_sigmatel_stac9756(ac97_t * ac97);
 static int patch_cirrus_cs4299(ac97_t * ac97);
+static int patch_cirrus_cs4205(ac97_t * ac97);
 static int patch_ad1819(ac97_t * ac97);
 static int patch_ad1881(ac97_t * ac97);
 
@@ -111,7 +111,7 @@
 { 0x42525928, 0xfffffff8, "CS4294",            NULL },
 { 0x43525930, 0xfffffff8, "CS4299",            patch_cirrus_cs4299 },
 { 0x43525948, 0xfffffff8, "CS4201",            NULL },
-{ 0x43525958, 0xfffffff8, "CS4205",            NULL },
+{ 0x43525958, 0xfffffff8, "CS4205",            patch_cirrus_cs4205 },
 { 0x43525960, 0xfffffff8, "CS4291",            NULL },
 { 0x48525300, 0xffffff00, "HMP9701",           NULL },
 { 0x49434501, 0xffffffff, "ICE1230",           NULL },
@@ -156,6 +156,12 @@
 #define AC97_ID_STAC9744       0x83847644
 #define AC97_ID_STAC9756       0x83847656
 
+#define AC97_ID_CS4205         0x43525958
+#define AC97_MASK_CS4205        0xfffffff8
+
+#define SPDIF_CS4205            0x68
+#define MODE_CS4205            0x5e
+
 static const char *snd_ac97_stereo_enhancements[] =
 {
   /*   0 */ "No 3D Stereo Enhancement",
@@ -722,8 +728,8 @@
                new |= ucontrol->value.iec958.status[0] & 
(IEC958_AES0_PRO_FS|IEC958_AES0_PRO_EMPHASIS_5015);
                switch (new & IEC958_AES0_PRO_FS) {
                case IEC958_AES0_PRO_FS_44100: val |= 0<<12; break;
-               case IEC958_AES0_PRO_FS_32000: val |= 2<<12; break;
-               case IEC958_AES0_PRO_FS_48000: val |= 1<<12; break;
+               case IEC958_AES0_PRO_FS_48000: val |= 2<<12; break;
+               case IEC958_AES0_PRO_FS_32000: val |= 3<<12; break;
                default:                       val |= 1<<12; break;
                }
                if ((new & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015)
@@ -744,7 +750,21 @@
                default:                       val |= 1<<12; break;
                }
        }
-       change = snd_ac97_update_bits(ac97, AC97_SPDIF, 0x3fff, val);
+
+       if ((ac97->id & AC97_MASK_CS4205) == AC97_ID_CS4205) {
+         int x = (val >> 12) & 0x03;
+         switch (x) {
+         case 0: x = 1; break;  // 44.1
+         case 2: x = 0; break;  // 48.0
+         default: x = 0; break; // illegal.
+         }
+         val &= 0xcfff;
+         val |= x <<12;
+         change = snd_ac97_update_bits(ac97, SPDIF_CS4205, 0x3fff, val);
+       } else {
+         change = snd_ac97_update_bits(ac97, AC97_SPDIF, 0x3fff, val);
+       }
+
        change |= ac97->spdif_status != new;
        ac97->spdif_status = new;
        spin_unlock(&ac97->reg_lock);
@@ -773,10 +793,16 @@
                get: snd_ac97_spdif_default_get,
                put: snd_ac97_spdif_default_put,
        },
+
        AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),AC97_EXTENDED_STATUS, 2, 
1, 0),
        AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) 
"AC97-SPSA",AC97_EXTENDED_STATUS, 4, 3, 0)
 };
 
+static const snd_kcontrol_new_t snd_ac97_cs4205_controls_spdif[2] = {
+    AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),SPDIF_CS4205, 15, 1, 0),
+    AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA",MODE_CS4205, 0, 
+3, 0)
+};
+
 #define AD18XX_PCM_BITS(xname, codec, shift, mask) \
 { iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: 
snd_ac97_ad18xx_pcm_info_bits, \
   get: snd_ac97_ad18xx_pcm_get_bits, put: snd_ac97_ad18xx_pcm_put_bits, \
@@ -1270,12 +1296,27 @@
        }
        
        /* build S/PDIF controls */
-       if (ac97->ext_id & 0x0004) {
-               for (idx = 0; idx < 5; idx++)
+       if (ac97->ext_id & AC97_EA_SPDIF) {
+         if ((ac97->id & AC97_MASK_CS4205) == AC97_ID_CS4205) {
+               for (idx = 0; idx < 3; idx++)
                        if ((err = snd_ctl_add(card, 
snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0)
                                return err;
-               /* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original */
-               snd_ac97_write_cache(ac97, AC97_SPDIF, 0x2a20);
+               for (idx = 0; idx < 2; idx++)
+                       if ((err = snd_ctl_add(card, 
+snd_ac97_cnew(&snd_ac97_cs4205_controls_spdif[idx], ac97))) < 0)
+                               return err;
+
+         } else {
+               for (idx = 0; idx < 5; idx++)
+                       if ((err = snd_ctl_add(card, 
+snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0)
+                               return err;
+               /* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original, 
+48000 Hz */
+         }
+
+               if ((ac97->id & AC97_MASK_CS4205) == AC97_ID_CS4205)
+                 snd_ac97_write_cache(ac97, SPDIF_CS4205, 0x0a20);
+               else 
+                 snd_ac97_write_cache(ac97, AC97_SPDIF, 0x2a20);
+
                ac97->spdif_status = SNDRV_PCM_DEFAULT_CON_SPDIF;
        }
        
@@ -1511,6 +1552,7 @@
        unsigned short val, tmp, ext;
        static const char *spdif_slots[4] = { " SPDIF=3/4", " SPDIF=7/8", " 
SPDIF=6/9", " SPDIF=res" };
        static const char *spdif_rates[4] = { " Rate=44.1kHz", " Rate=res", " 
Rate=48kHz", " Rate=32kHz" };
+       static const char *spdif_rates_cs4205[4] = { " Rate=48kHz", " Rate=44.1kHz", " 
+Rate=res", " Rate=res" };
 
        id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16;
        id |= snd_ac97_read(ac97, AC97_VENDOR_ID2);
@@ -1557,6 +1599,10 @@
                    val & 0x0100 ? "Mic2" : "Mic1",
                    val & 0x0080 ? "on" : "off");
        ext = snd_ac97_read(ac97, AC97_EXTENDED_ID);
+
+       if ((id & AC97_MASK_CS4205) == AC97_ID_CS4205)
+         ext |= AC97_EA_SPDIF;
+
        if (ext == 0)
                return;
        snd_iprintf(buffer, "Extended ID      : codec=%i rev=%i%s%s%s%s 
DSA=%i%s%s%s%s\n",
@@ -1606,7 +1652,11 @@
                snd_iprintf(buffer, "PCM MIC ADC      : %iHz\n", val);
        }
        if (ext & 0x0004) {
-               val = snd_ac97_read(ac97, AC97_SPDIF);
+              if ((id & AC97_MASK_CS4205) == AC97_ID_CS4205)
+                 val = snd_ac97_read(ac97, SPDIF_CS4205);
+               else
+                 val = snd_ac97_read(ac97, AC97_SPDIF);
+
                snd_iprintf(buffer, "SPDIF Control    :%s%s%s%s Category=0x%x 
Generation=%i%s%s%s\n",
                        val & 0x0001 ? " PRO" : " Consumer",
                        val & 0x0002 ? " Non-audio" : " PCM",
@@ -1614,7 +1664,9 @@
                        val & 0x0008 ? " Preemph50/15" : "",
                        (val & 0x07f0) >> 4,
                        (val & 0x0800) >> 11,
-                       spdif_rates[(val & 0x3000) >> 12],
+                           ((id & AC97_MASK_CS4205) == AC97_ID_CS4205) ?
+                           spdif_rates_cs4205[(val & 0x3000) >> 12] :
+                           spdif_rates[(val & 0x3000) >> 12],
                        val & 0x4000 ? " DRS" : "",
                        val & 0x8000 ? " Validity" : "");
        }
@@ -1668,6 +1720,7 @@
        ac97_t *ac97 = snd_magic_cast(ac97_t, entry->private_data, return);
 
        if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) {        // Analog Devices 
AD1881/85/86
+
                int idx;
                down(&ac97->spec.ad18xx.mutex);
                for (idx = 0; idx < 3; idx++)
@@ -1819,6 +1872,24 @@
 static int patch_cirrus_cs4299(ac97_t * ac97)
 {
        ac97->flags |= AC97_HAS_PC_BEEP;        /* force the detection of PC Beep */
+       
+       return 0;
+}
+
+static int patch_cirrus_cs4205(ac97_t * ac97)
+{
+  /* Basically, the cs4205 has non-standard sp/dif registers.
+     WHY CAN'T ANYONE FOLLOW THE BLOODY SPEC?  *sigh*
+      - sp/dif EA ID is not set, but sp/dif is always present.
+        - enable/disable is spdif register bit 15.
+      - sp/dif control register is 0x68.  differs from AC97:
+        - valid is bit 14 (vs 15)
+       - no DRS
+       - only 44.1/48k [00 = 48, 01=44,1] (AC97 is 00=44.1, 10=48)
+      - sp/dif ssource select is in 0x5e bits 0,1.
+  */
+        ac97->ext_id |= AC97_EA_SPDIF; /* force the detection of spdif */
+       snd_ac97_write_cache(ac97, MODE_CS4205, 0x0080);
        return 0;
 }
 

Attachment: msg02449/pgp00000.pgp
Description: PGP signature

Reply via email to