CS4237B sound chip now works perfectly

CS4237B sound chip now works perfectly

Post by dtco.. » Sun, 31 Dec 1899 09:00:00



Here's an account of my saga to make work the CS4237B sound chip on my
Dell Latitude CPi D266XT. It should be useful to anyone having that
sound chip on their Linux-powered notebooks.

1) My distribution is Red Hat 6.1, which comes with OSS/Free 3.8s. If
   you use sndconfig and specify "Crystal CS423x sound chip", it will
   correctly set up your CS4237B by writing the following in
   /etc/conf.modules:

      alias sound cs4232
      pre-install sound /sbin/insmod sound dmabuf=1
      alias midi opl3
      options opl3 io=0x388
      options cs4232 io=0x530 irq=5 dma=1 dma2=0 mpuio=0x330

   HOWEVER, you will notice three problems:

   (a) Enabling PCMCIA services (if you do use them) messes up the
   sound driver. The symptom is that whatever sound you play gets
   repeated a few times in quick succession. For instance if you play
   a file with "hello world", you get something like "hello - hello -
   hello - world - world - world".

   (b) Whenever you play a sound, there is a loud "click" or "pop"
   sound at the beginning of playback.

   (c) The master volume, synthesizer, microphone and input mixer
   gains don't work (as you can verify if you try to use aumix, kmix,
   or any other mixer).

2) Problem (a) is really not the sound driver's fault; it is caused by
   the i/o port probe of the PCMCIA service. A solution people have
   posted is to remove the sound modules and install them again after
   PCMCIA has been started. That workround does work, but the real
   solution is to edit your /etc/pcmcia/config.opts and comment out
   line
        "include port 0x100-0x4ff, port 0x1000-0x17ff"

   That will solve the problem and allow you to build the sound
   driver into the kernel, if you so wish, since the unload/reload
   operation will no longer be needed. If you have any PCMCIA cards
   that use i/o address in those ranges, then, instead of just
   commenting out that line, change the ranges to just that interval
   you know you will be needing.

3) Now for problem (b), the annoying click. I tried installing the
   ALSA driver, v. 0.4.1e, but its support for the CS4237B is not that
   great either, and it too produces a click. The only difference is
   the click comes at the end of every playback, instead of at the
   beginning. Didn't bother trying the commercial OSS, since some
   posts to Usenet suggested it too has problems with the CS4237B.

4) Luckly, Cirrus Logic, who makes the CS4237B, has made available its
   datasheet at http://www.cirrus.com/ftp/pubs/4237b.pdf. Reading it
   and experimenting with the values used for register initialization,
   I found out that problem (b) is caused by a calibration procedure
   that the OSS driver enables by default, and problem (c) is caused
   by the OSS driver not using the CS4237B's extended registers,
   available in its "Mode 3".

5) To fix problems (b) and (c) I edited files ad1848.c and
   ad1848_mixer.h in drivers/sound. The diff is given at the end of
   this message. These changes got rid of the click and made the mixer
   work perfectly. Please note that they are just a quick fix. In
   order to properly support the CS4237B, the files should be further
   modified to identify the chip and perhaps do a whole bunch of other
   things that I am not aware of.

6) Here are the relevant lines of the sound section in my .config (all
   other options are not set). Note that, in order to have MIDI, you
   need both "Generic OPL2/OPL3 FM synthesizer support"
   (CONFIG_SOUND_ADLIB) and "FM synthesizer (YM3812/OPL3) support"
   (CONFIG_SOUND_YM3812). If you build sound support into the kernel
   like this, you don't need anything in /etc/conf.modules.

      #
      # Sound
      #
      CONFIG_SOUND=y
      CONFIG_SOUND_OSS=y
      CONFIG_SOUND_ADLIB=y
      CONFIG_SOUND_CS4232=y
      CONFIG_CS4232_BASE=530
      CONFIG_CS4232_IRQ=5
      CONFIG_CS4232_DMA=1
      CONFIG_CS4232_DMA2=0
      CONFIG_CS4232_MPU_BASE=330
      CONFIG_CS4232_MPU_IRQ=7
      CONFIG_SOUND_YM3812=y

And that's it! I now get perfect 16-bit, full duplex sound plus MIDI
out of the CS4237B. Hope these hints will be useful to you.

And now the patch. I tried posting it uuencoded to avoid long lines
being accidentally split in two during posting, but my post got
rejected because these groups do not allow binaries, so you will have
to fix any split lines by hand. To make your job easier, I added a "@"
sign at the end of every line in the file, so if you see a line
that doesn't end with a "@", it should be joined with the next
line. After fixing the lines, just delete all "@"s. Then, to apply the
patch, go to /usr/src/linux/drivers/sound, copy ad1848.c to
ad1848.c.orig and ad1848_mixer.h to ad1848_mixer.h.orig, and do
"patch < sound.diff".

>>>>>>>>>>>> Begin included file "sound.diff" <<<<<<<<<<<<

diff -C 2 -r ad1848.c.orig ad1848.c@
*** ad1848.c.orig       Thu Nov 25 01:02:15 1999@
--- ad1848.c    Thu Dec  9 12:57:40 1999@
***************@
*** 53,57 ****@
        int             dual_dma;       /* 1, when two DMA channels allocated */@
        unsigned char   MCE_bit;@
!       unsigned char   saved_regs[32];@
        int             debug_flag;@
  @
--- 53,57 ----@
        int             dual_dma;       /* 1, when two DMA channels allocated */@
        unsigned char   MCE_bit;@
!       unsigned char   saved_regs[58]; /* Include extended registers */@
        int             debug_flag;@
  @
***************@
*** 181,187 ****@
        save_flags(flags);@
        cli();@
!       outb(((unsigned char) (reg & 0xff) | devc->MCE_bit),
io_Index_Addr(devc));@
!       x = inb(io_Indexed_Data(devc));@
! /* printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */@
        restore_flags(flags);@
  @
--- 181,198 ----@
        save_flags(flags);@
        cli();@
!       if (reg <= 31) {@
!         outb(((unsigned char) (reg & 0xff) | devc->MCE_bit),
io_Index_Addr(devc));@
!         x = inb(io_Indexed_Data(devc));@
!         /* printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */@
!       }@
!       else {          /* Extended registers. */@
!         int xreg, xra;@
! @
!         xreg = (reg & 0xff) - 32;@
!         xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2);@
!         outb(((unsigned char) (23 & 0xff) | devc->MCE_bit),
io_Index_Addr(devc));@
!         outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc));@
!         x = inb(io_Indexed_Data(devc));@
!       }@
        restore_flags(flags);@
  @
***************@
*** 199,205 ****@
        save_flags(flags);@
        cli();@
!       outb(((unsigned char) (reg & 0xff) | devc->MCE_bit),
io_Index_Addr(devc));@
!       outb(((unsigned char) (data & 0xff)), io_Indexed_Data(devc));@
!       /* printk("(%02x->%02x) ", reg|devc->MCE_bit, data); */@
        restore_flags(flags);@
  }@
--- 210,228 ----@
        save_flags(flags);@
        cli();@
! @
!       if (reg <= 31) {@
!         outb(((unsigned char) (reg & 0xff) | devc->MCE_bit),
io_Index_Addr(devc));@
!         outb(((unsigned char) (data & 0xff)), io_Indexed_Data(devc));@
!         /* printk("(%02x->%02x) ", reg|devc->MCE_bit, data); */@
!         }@
!       else {          /* Extended registers. */@
!         int xreg, xra;@
! @
!         xreg = (reg & 0xff) - 32;@
!         xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2);@
!         outb(((unsigned char) (23 & 0xff) | devc->MCE_bit),
io_Index_Addr(devc));@
!         outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc));@
!         outb((unsigned char) (data & 0xff), io_Indexed_Data(devc));@
!       }@
        restore_flags(flags);@
  }@
***************@
*** 1388,1393 ****@
        static int      init_values[] =@
        {@
                0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,@
!               0x00, 0x0c, 0x02, 0x00, 0x8a, 0x01, 0x00, 0x00,@
  @
        /* Positions 16 to 31 just for CS4231/2 and ad1845 */@
--- 1411,1428 ----@
        static int      init_values[] =@
        {@
+ @
                0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,@
! @
!         /* Some of the register initialization values were changed. In@
!            order to get rid of the click that preceded PCM playback,@
!            calibration was disabled on the 10th byte. On that same byte,@
!            dual DMA was enabled; on the 11th byte, ADC dithering was@
!            enabled, since that is theoretically desirable; on the 13th@
!            byte, Mode 3 was selected, to enable access to extended@
!            registers.@
! @
!               0x00, 0x0c, 0x02, 0x00, 0x8a, 0x01, 0x00, 0x00, */@
! @
!               0x00, 0x00, 0x06, 0x00, 0xe0, 0x01, 0x00, 0x00,@
  @
        /* Positions 16 to 31 just for CS4231/2 and ad1845 */@
***************@
*** 1395,1399 ****@
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00@
        };@
- @
  @
        for (i = 0; i < 16; i++)@
--- 1430,1433 ----@
diff -C 2 -r ad1848_mixer.h.orig ad1848_mixer.h@
*** ad1848_mixer.h.orig Mon Nov 29 23:22:17 1999@
--- ad1848_mixer.h      Thu Dec  9 10:39:37 1999@
***************@
*** 58,69 ****@
  @
  struct mixer_def {@
!       unsigned int regno:5;           /* register number for volume */@
        unsigned int polarity:1;        /* volume polarity: 0=normal, 1=reversed */@
        unsigned int bitpos:3;          /* position of bits in register for volume
*/@
        unsigned int nbits:3;           /* number of bits in register for volume */@
!       unsigned int mutereg:5;         /* register number for mute bit */@
        unsigned int mutepol:1;         /* mute polarity: 0=normal, 1=reversed */@
        unsigned int mutepos:4;         /* position of mute bit in register */@
!       unsigned int recreg:5;          /* register number for recording bit */@
        unsigned int recpol:1;          /* recording polarity: 0=normal, 1=reversed
*/@
        unsigned int recpos:4;          /* position of recording bit in register */@
--- 58,69 ----@
  @
  struct mixer_def {@
!       unsigned int regno:6;           /* register number for volume */@
        unsigned int polarity:1;        /* volume polarity: 0=normal, 1=reversed */@
        unsigned int bitpos:3;          /* position of bits in register for volume
*/@
        unsigned int nbits:3;           /* number of bits in register for volume */@
!       unsigned int mutereg:6;         /* register number for mute bit */@
        unsigned int mutepol:1;         /* mute polarity: 0=normal, 1=reversed */@
        unsigned int mutepos:4;         /* position of mute bit in register */@
!       unsigned int recreg:6;          /* register number for recording bit */@
        unsigned int recpol:1;          /* recording polarity: 0=normal, 1=reversed
*/@
        unsigned int recpos:4;          /* position of recording bit in register */@
***************@
*** 105,109 ****@
  @
  static mixer_ents ad1848_mix_devices[32] = {@
! MIX_ENT(SOUND_MIXER_VOLUME,   27, 1, 0, 4,    29, 1, 0, 4,  8),@
  MIX_ENT(SOUND_MIXER_BASS,      0, 0, 0, 0,     0, 0, 0, 0,  8),@
  MIX_ENT(SOUND_MIXER_TREBLE,    0, 0, 0, 0,     0, 0, 0, 0,  8),@
--- 105,111 ----@
  @
  static mixer_ents ad1848_mix_devices[32] = {@
!   /* Digital master volume actually has seven bits, but we only use@
!   six to avoid the discontinuity when the analog gain kicks in. */@
! MIX_ENT(SOUND_MIXER_VOLUME,   46, 1, 0, 6,    47, 1, 0, 6,  7),@
  MIX_ENT(SOUND_MIXER_BASS,      0, 0, 0, 0,     0, 0, 0, 0,  8),@
  MIX_ENT(SOUND_MIXER_TREBLE,    0, 0, 0, 0,     0, 0, 0, 0,  8),@
***************@
*** 112,118 ****@
  MIX_ENT(SOUND_MIXER_SPEAKER,  26, 1, 0, 4,     0, 0, 0, 0,  8),@
  MIX_ENT(SOUND_MIXER_LINE,     18, 1, 0, 5,    19, 1, 0, 5,  7),@
! MIX_ENT(SOUND_MIXER_MIC,       0, 0, 5, 1,     1, 0, 5, 1,  8),@
  MIX_ENT(SOUND_MIXER_CD,                2, 1, 0, 5,     3, 1, 0, 5,  7),@
! MIX_ENT(SOUND_MIXER_IMIX,     13, 1, 2, 6,     0, 0, 0, 0,  8),@
  MIX_ENT(SOUND_MIXER_ALTPCM,    0, 0, 0, 0,     0, 0, 0, 0,  8),@
  MIX_ENT(SOUND_MIXER_RECLEV,    0, 0, 0, 0,     0, 0, 0, 0,  8),@
--- 114,124 ----@
  MIX_ENT(SOUND_MIXER_SPEAKER,  26, 1, 0, 4,     0, 0, 0, 0,  8),@
  MIX_ENT(SOUND_MIXER_LINE,     18, 1, 0, 5,    19, 1, 0, 5,  7),@
! MIX_ENT(SOUND_MIXER_MIC,      34, 1, 0, 5,    35, 1, 0, 5,  7),@
  MIX_ENT(SOUND_MIXER_CD,                2, 1, 0, 5,     3, 1, 0, 5,  7),@
! /* For the IMIX entry, it was not possible to use the MIX_ENT macro@
!    because the mute bit is in different positions for the two@
!    channels and requires reverse polarity. */@
! [SOUND_MIXER_IMIX] = {{13, 1, 2, 6, 13, 1, 0, 0, 0, 8},@
!                     {42, 1, 0, 6, 42, 1, 7, 0, 0, 8}},@
  MIX_ENT(SOUND_MIXER_ALTPCM,    0, 0, 0, 0,     0, 0, 0, 0,  8),@
  MIX_ENT(SOUND_MIXER_RECLEV,    0, 0, 0, 0,     0, 0, 0, 0,  8),@
***************@
*** 121,125 ****@
  MIX_ENT(SOUND_MIXER_LINE1,     2, 1, 0, 5,     3, 1, 0, 5,  7),@
  MIX_ENT(SOUND_MIXER_LINE2,     4, 1, 0, 5,     5, 1, 0, 5,  7),@
! MIX_ENT(SOUND_MIXER_LINE3,    18, 1, 0, 5,    19, 1, 0, 5,  7)@
  };@
  @
--- 127,131 ----@
  MIX_ENT(SOUND_MIXER_LINE1,     2, 1, 0, 5,     3, 1, 0, 5,  7),@
  MIX_ENT(SOUND_MIXER_LINE2,     4, 1, 0, 5,     5, 1, 0, 5,  7),@
! MIX_ENT(SOUND_MIXER_LINE3,    38, 1, 0, 6,    39, 1, 0, 6,  7)@
  };@
  @

>>>>>>>>>>>> End included file "sound.diff" <<<<<<<<<<<<

Sent via Deja.com http://www.deja.com/
Before you buy.