Subject: port-i386/1179: wss driver improvements
To: None <gnats-admin@sun-lamp.pc.cs.cmu.edu>
From: Mike Long <mikel@shore.net>
List: netbsd-bugs
Date: 06/30/1995 02:20:05
>Number: 1179
>Category: port-i386
>Synopsis: slightly debugged wss.c and 1848.c drivers
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: gnats-admin (GNATS administrator)
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Fri Jun 30 02:20:02 1995
>Originator: Mike Long <mike.long@analog.com>
>Organization:
Just say Ack!
>Release: 6/29/95
>Environment:
System: NetBSD azathoth 1.0A NetBSD 1.0A (AZATHOTH) #0: Sat Jun 24 15:40:37 EDT 1995 root@azathoth:/usr/src/sys/arch/i386/compile/AZATHOTH i386
>Description:
The Windows Sound System/AD1848 driver is broken.
>How-To-Repeat:
build a GENERIC kernel and boot it on a system containing a WSS.
>Fix:
All right, here's what I did:
1) removed references to <i386/isa/icu.h>; not needed
2) fixed broken wssprobe()
3) addressing in ad1848* changed to put the index register at base+0
instead of base+4. The parent device is responsible for keeping its
own iobase.
4) ad1848 iobase check now only checks alignment.
5) started adding support for more codecs (AD1845).
...plus cleanup, comments, etc. As you can see from the timestamps, I
haven't had a lot of time to work on this recently.
--- /usr/src/sys/dev/isa/wssreg.h.orig Thu May 11 06:26:32 1995
+++ /usr/src/sys/dev/isa/wssreg.h Sat Jun 3 13:41:55 1995
@@ -42,20 +42,23 @@
/*
* Macros to detect valid hardware configuration data.
*/
-#define WSS_IRQ_VALID(mask) ((mask) & 0x00ac) /* IRQ 2,3,5,7 */
#define WSS_BASE_VALID(base) ((base) == 0x0530 || \
(base) == 0x0604 || \
(base) == 0x0e80 || \
(base) == 0x0f40)
+#define WSS_IRQ_VALID(irq) ((irq) == 7 || (irq) == 9 || \
+ (irq) == 10 || (irq) == 11)
+#define WSS_DRQ_VALID(chan) ((chan) == 0 || (chan) == 1 || (chan) == 3)
/* Default WSS base */
-/* WSS registers */
#define WSS_BASE_ADDRESS 0x0530
/* WSS registers */
-#define WSS_CONFIG 0x00
-#define WSS_STATUS 0x03
+#define WSS_CONFIG 0x00 /* write only */
+#define WSS_STATUS 0x03 /* read only */
-#define WSS_VERS 0x04
+/* WSS status register bits */
#define WSS_16SLOT 0x80
-#define WSS_DUALDMA 0x40
+#define WSS_IRQACTIVE 0x40
+#define WSS_VERS 0x04
+#define WSS_VERSMASK 0x3f
--- /usr/src/sys/dev/isa/wss.c.orig Thu May 11 06:26:31 1995
+++ /usr/src/sys/dev/isa/wss.c Wed Jun 14 03:34:17 1995
@@ -52,7 +52,6 @@
#include <dev/isa/isavar.h>
#include <dev/isa/isadmavar.h>
-#include <i386/isa/icu.h> /* XXX BROKEN; WHY? */
#include <dev/isa/ad1848var.h>
#include <dev/isa/ad1848reg.h>
@@ -91,7 +90,6 @@
void *sc_ih; /* interrupt vectoring */
struct ad1848_softc sc_ad1848;
-#define wss_iobase sc_ad1848.sc_iobase
#define wss_irq sc_ad1848.sc_irq
#define wss_drq sc_ad1848.sc_drq
@@ -186,19 +184,27 @@
-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20
};
static u_char dma_bits[4] = {1, 2, 0, 3};
- char bits;
if (!WSS_BASE_VALID(ia->ia_iobase)) {
printf("wss: configured iobase %d invalid\n", ia->ia_iobase);
return 0;
}
- sc->wss_iobase = iobase;
+ sc->sc_ad1848.sc_iobase = iobase + 4;
- /* Is there an ad1848 chip at the WSS iobase ? */
+ /* Is there an ad1848 chip at the WSS iobase + 4? */
if (ad1848_probe(&sc->sc_ad1848) == 0)
return 0;
+ ia->ia_iosize = WSS_NPORT;
+
+ /* Setup WSS interrupt and DMA */
+ if (!WSS_DRQ_VALID(ia->ia_drq)) {
+ printf("wss: configured dma chan %d invalid\n", ia->ia_drq);
+ return 0;
+ }
+ sc->wss_drq = ia->ia_drq;
+
#ifdef NEWCONFIG
/*
* If the IRQ wasn't compiled in, auto-detect it.
@@ -206,27 +212,21 @@
if (ia->ia_irq == IRQUNK) {
ia->ia_irq = isa_discoverintr(ad1848_forceintr, &sc->sc_ad1848);
if (!WSS_IRQ_VALID(ia->ia_irq)) {
- printf("wss: couldn't auto-detect interrupt");
+ printf("wss: couldn't auto-detect interrupt\n");
return 0;
}
}
else
#endif
- ia->ia_iosize = WSS_NPORT;
-
- /* Setup WSS interrupt and DMA */
- if ((bits = interrupt_bits[sc->wss_irq]) == -1) {
- printf("wss: invalid interrupt configuration (irq=%d)\n", sc->wss_irq);
- return 0;
+ if (!WSS_IRQ_VALID(ia->ia_irq)) {
+ printf("wss: configured interrupt %d invalid\n", ia->ia_irq);
+ return 0;
}
-#if 0
- /* XXX Dual-DMA */
- outb(sc->wss_iobase+WSS_CONFIG, (bits | 0x40));
- if ((inb(sc->wss_iobase+WSS_STATUS) & 0x40) == 0)
- printf("wss: IRQ?\n");
-#endif
- outb(sc->wss_iobase+WSS_CONFIG, (bits | dma_bits[sc->wss_drq]));
+ sc->wss_irq = ia->ia_irq;
+
+ outb(iobase+WSS_CONFIG,
+ (interrupt_bits[ia->ia_irq] | dma_bits[ia->ia_drq]));
return 1;
}
@@ -244,9 +244,6 @@
struct isa_attach_args *ia = (struct isa_attach_args *)aux;
register u_short iobase = ia->ia_iobase;
- sc->wss_iobase = iobase;
- sc->wss_drq = ia->ia_drq;
-
#ifdef NEWCONFIG
isa_establish(&sc->sc_id, &sc->sc_dev);
#endif
@@ -255,7 +252,7 @@
ad1848_attach(&sc->sc_ad1848);
- printf(" (vers %d)", inb(sc->wss_iobase+WSS_STATUS) & 0x1f);
+ printf(" (vers %d)", inb(iobase+WSS_STATUS) & WSS_VERSMASK);
printf("\n");
sc->sc_ad1848.parent = sc;
--- /usr/src/sys/dev/isa/ad1848reg.h.orig Thu May 11 06:26:28 1995
+++ /usr/src/sys/dev/isa/ad1848reg.h Wed Jun 14 03:03:06 1995
@@ -38,13 +38,14 @@
* Copyright (c) 1993 Analog Devices Inc. All rights reserved
*/
-#define AD1848_BASE_VALID(base) ((base) == 0x530)
+/* parent driver is primarily responsible for checking this */
+#define AD1848_BASE_VALID(base) (((base) & 0x003) == 0)
/* ad1848 direct registers */
-#define AD1848_IADDR 0x04
-#define AD1848_IDATA 0x05
-#define AD1848_STATUS 0x06
-#define AD1848_PIO 0x07
+#define AD1848_IADDR 0x00
+#define AD1848_IDATA 0x01
+#define AD1848_STATUS 0x02
+#define AD1848_PIO 0x03
/* Gain constants */
#define GAIN_0 0x00
@@ -63,7 +64,6 @@
#define GAIN_19_5 0x0d
#define GAIN_21 0x0e
#define GAIN_22_5 0x0f
-#define MUTE 0XFFFF
/* Attenuation constants */
@@ -90,7 +90,7 @@
#define MODE_CHANGE_MASK 0xbf
#define TRANSFER_DISABLE 0x20
#define TRANSFER_DISABLE_MASK 0xdf
-#define ADDRESS_MASK 0xf0
+#define ADDRESS_MASK 0xe0
/* Status bits */
#define INTERRUPT_STATUS 0x01
@@ -99,9 +99,7 @@
/* pbright is not left */
#define PLAYBACK_UPPER 0x08
/* bplower is not upper */
-
-#define SAMPLE_OVERRUN 0x10
-#define SAMPLE_UNDERRUN 0x10
+#define SAMPLE_ERROR 0x10
#define CAPTURE_READY 0x20
#define CAPTURE_LEFT 0x40
/* cpright is not left */
@@ -126,22 +124,19 @@
#define OUTPUT_ATTEN_BITS 0x3f
#define OUTPUT_ATTEN_MASK 0xc0
-/* Clock and Data format reg bits */
+/* Clock and Data format reg bits (some also Capture Data format) */
#define CLOCK_SELECT_MASK 0xfe
#define CLOCK_XTAL2 0x01
#define CLOCK_XTAL1 0x00
#define CLOCK_FREQ_MASK 0xf1
#define STEREO_MONO_MASK 0xef
#define FMT_STEREO 0x10
-#define RMT_MONO 0x00
-#define LINEAR_COMP_MASK 0xdf
-#define LINEAR 0x00
-#define COMPANDED 0x20
-#define FORMAT_MASK 0xbf
-#define PCM 0x00
-#define ULAW 0x00
-#define TWOS_COMP 0x40
-#define ALAW 0x40
+#define FMT_MONO 0x00
+#define FORMAT_MASK 0x1f
+#define FMT_PCM8 0x00
+#define FMT_ULAW 0x20
+#define FMT_PCM16 0x40
+#define FMT_ALAW 0x60
/* Interface Configuration reg bits */
#define PLAYBACK_ENABLE 0x01
@@ -170,31 +165,49 @@
#define DATA_REQUEST_STATUS 0x10
#define AUTO_CAL_IN_PROG 0x20
#define PLAYBACK_UNDERRUN 0x40
-#define CAPTURE_UNDERRUN 0x80
+#define CAPTURE_OVERRUN 0x80
/* Miscellaneous Control reg bits */
-#define ID_MASK 0xf0
+#define ID_MASK 0x70
+#define MODE2 0x40
/* Digital Mix Control reg bits */
#define DIGITAL_MIX1_MUTE_MASK 0xfe
#define DIGITAL_MIX1_ENABLE 0x01
#define MIX_ATTEN_MASK 0xfc
-/* 1848 Sound Port reg defines */
-#define SP_LEFT_INPUT_CONTROL 0x0
-#define SP_RIGHT_INPUT_CONTROL 0x1
-#define SP_LEFT_AUX1_CONTROL 0x2
-#define SP_RIGHT_AUX1_CONTROL 0x3
-#define SP_LEFT_AUX2_CONTROL 0x4
-#define SP_RIGHT_AUX2_CONTROL 0x5
-#define SP_LEFT_OUTPUT_CONTROL 0x6
-#define SP_RIGHT_OUTPUT_CONTROL 0x7
-#define SP_CLOCK_DATA_FORMAT 0x8
-#define SP_INTERFACE_CONFIG 0x9
-#define SP_PIN_CONTROL 0xA
-#define SP_TEST_AND_INIT 0xB
-#define SP_MISC_INFO 0xC
-#define SP_DIGITAL_MIX 0xD
-#define SP_UPPER_BASE_COUNT 0xE
-#define SP_LOWER_BASE_COUNT 0xF
-
+/* AD1848 Sound Port reg defines */
+#define SP_LEFT_INPUT_CONTROL 0x00
+#define SP_RIGHT_INPUT_CONTROL 0x01
+#define SP_LEFT_AUX1_CONTROL 0x02
+#define SP_RIGHT_AUX1_CONTROL 0x03
+#define SP_LEFT_AUX2_CONTROL 0x04
+#define SP_RIGHT_AUX2_CONTROL 0x05
+#define SP_LEFT_OUTPUT_CONTROL 0x06
+#define SP_RIGHT_OUTPUT_CONTROL 0x07
+#define SP_CLOCK_DATA_FORMAT 0x08
+#define SP_INTERFACE_CONFIG 0x09
+#define SP_PIN_CONTROL 0x0A
+#define SP_TEST_AND_INIT 0x0B
+#define SP_MISC_INFO 0x0C
+#define SP_DIGITAL_MIX 0x0D
+#define SP_UPPER_BASE_COUNT 0x0E
+#define SP_LOWER_BASE_COUNT 0x0F
+
+/* CS4231/AD1845 mode2 reg defines */
+#define SP_ALT1_LEFT_MIC_CONTROL 0x10
+#define SP_ALT2_RIGHT_MIC_CONTROL 0x11
+#define SP_LEFT_LINE_CONTROL 0x12
+#define SP_RIGHT_LINE_CONTROL 0x13
+#define SP_TIMER_LOWER_BASE 0x14
+#define SP_TIMER_UPPER_BASE 0x15
+#define SP_UPPER_FREQUENCY_SEL 0x16
+#define SP_LOWER_FREQUENCY_SEL 0x17
+#define SP_ALT_FEATURE_STATUS 0x18
+#define SP_VERSION_ID 0x19
+#define SP_MONO_CONTROL 0x1A
+#define SP_POWERDOWN_CONTROL 0x1B
+#define SP_CAPTURE_DATA_FORMAT 0x1C
+#define SP_XTAL_SELECT 0x1D
+#define SP_CAPTURE_UPPER_BASE 0x1E
+#define SP_CAPTURE_LOWER_BASE 0x1F
--- /usr/src/sys/dev/isa/ad1848.c.orig Thu May 11 06:26:27 1995
+++ /usr/src/sys/dev/isa/ad1848.c Wed Jun 14 03:52:07 1995
@@ -88,7 +88,6 @@
#include <dev/isa/isavar.h>
#include <dev/isa/isadmavar.h>
-#include <i386/isa/icu.h> /* XXX BROKEN; WHY? */
#include <dev/isa/ad1848reg.h>
#include <dev/isa/ad1848var.h>
@@ -117,7 +116,7 @@
0x19, /* Left DAC Control */
0x19, /* Right DAC Control */
/* Clock and Data Format */
- CLOCK_XTAL1|LINEAR|PCM,
+ CLOCK_XTAL1|FMT_PCM8,
/* Interface Config */
SINGLE_DMA,
INTERRUPT_ENABLE, /* Pin control */
@@ -268,7 +267,7 @@
int i;
if (!AD1848_BASE_VALID(iobase)) {
- printf("ad1848: configured iobase %d invalid\n", iobase);
+ printf("ad1848: configured iobase %#x invalid\n", iobase);
return 0;
}
@@ -276,8 +275,7 @@
/* Is there an ad1848 chip ? */
sc->MCE_bit = MODE_CHANGE_ENABLE;
- sc->chip_name = "ad1848";
- sc->mode = 1; /* MODE 1 = original ad1849 */
+ sc->mode = 1; /* MODE 1 = original ad1848/ad1846/cs4248 */
/*
* Check that the I/O address is in use.
@@ -321,7 +319,7 @@
* try to change them.
*/
tmp = ad_read(sc, SP_MISC_INFO);
- ad_write(sc, SP_MISC_INFO, (~tmp) & 0x0f);
+ ad_write(sc, SP_MISC_INFO, tmp ^ 0x0f);
if ((tmp & 0x0f) != ((tmp1 = ad_read(sc, SP_MISC_INFO)) & 0x0f)) {
DPRINTF(("ad_detect_D (%x)\n", tmp1));
@@ -329,23 +327,47 @@
}
/*
- * NOTE! Last 4 bits of the reg I12 tell the chip revision.
- * 0x01=RevB and 0x0A=RevC.
+ * MSB and 4 LSBs of the reg I12 tell the chip revision.
+ *
+ * A preliminary version of the AD1846 data sheet stated that it
+ * used an ID field of 0x0B. The current version, however,
+ * states that the AD1846 uses ID 0x0A, just like the AD1848K.
+ *
+ * this switch statement will need updating as newer clones arrive....
*/
- sc->rev = tmp1 & 0x0f;
+ sc->rev = tmp1 & 0x8f;
switch (sc->rev) {
- case 11:
- sc->chip_name = "ad1846";
- sc->rev = 0;
+ case 0x09:
+ sc->chip_name = "AD1848J";
+ break;
+ case 0x0A:
+ sc->chip_name = "AD1848K";
+ break;
+#if 0
+ case 0x0B:
+ sc->chip_name = "AD1846";
+ break;
+#endif
+ case 0x81:
+ sc->chip_name = "CS4248revB"; /* or CS4231 rev B; see below */
+ break;
+ case 0x89:
+ sc->chip_name = "CS4248";
+ break;
+ case 0x8A:
+ sc->chip_name = "broken"; /* CS4231/AD1845; see below */
+ break;
+ default:
+ sc->chip_name = "unknown";
+ DPRINTF(("ad1848: unknown codec version %#02X\n", sc->rev));
break;
}
-
/*
- * The original AD1848/CS4248 has just 15 indirect registers. This means
+ * The original AD1848/CS4248 has just 16 indirect registers. This means
* that I0 and I16 should return the same value (etc.).
* Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails
- * with CS4231.
+ * with CS4231, AD1845, etc.
*/
ad_write(sc, SP_MISC_INFO, 0); /* Mode2 = disabled */
@@ -357,34 +379,48 @@
/*
* Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40)
- * The bit 0x80 is always 1 in CS4248 and CS4231.
+ * The bit 0x80 is always 1 in CS4248, CS4231, and AD1845.
*/
- ad_write(sc, SP_MISC_INFO, 0x40); /* Set mode2, clear 0x80 */
+ ad_write(sc, SP_MISC_INFO, MODE2);
tmp1 = ad_read(sc, SP_MISC_INFO);
- if (tmp1 & 0x80)
- sc->chip_name = "cs4248";
- if ((tmp1 & 0xc0) == (0x80 | 0x40)) {
+ if ((tmp1 & 0xc0) == (0x80 | MODE2)) {
/*
- * CS4231 detected - is it?
+ * CS4231 or AD1845 detected - is it?
*
- * Verify that setting I0 doesn't change I16.
+ * Verify that setting I2 doesn't change I18.
*/
- ad_write(sc, 16, 0); /* Set I16 to known value */
+ ad_write(sc, 18, 0x88); /* Set I18 to known value */
- ad_write(sc, 0, 0x45);
- if ((tmp1 = ad_read(sc, 16)) != 0x45) { /* No change -> CS4231? */
- ad_write(sc, 0, 0xaa);
- if ((tmp1 = ad_read(sc, 16)) == 0xaa) { /* Rotten bits? */
- DPRINTF(("ad_detect_H(%x)\n", tmp1));
+ ad_write(sc, 2, 0x45);
+ if ((tmp2 = ad_read(sc, 18)) != 0x45) { /* No change -> CS4231? */
+ ad_write(sc, 2, 0xaa);
+ if ((tmp2 = ad_read(sc, 18)) == 0xaa) { /* Rotten bits? */
+ DPRINTF(("ad_detect_H(%x)\n", tmp2));
return 0;
}
/*
- * It's a CS4231
+ * It's a CS4231, or another clone with 32 registers.
+ * Let's find out which by checking I25.
*/
- sc->chip_name = "cs4231";
+ if ((tmp1 & 0x0f) == 0x8a) {
+ tmp1 = ad_read(sc, SP_VERSION_ID);
+ sc->rev |= (tmp1 & 0xe7);
+ switch (tmp1 & 0xe7) {
+ case 0xA0:
+ sc->chip_name = "CS4231A";
+ break;
+ case 0x80:
+ /* XXX I25 no good, AD1845 same as CS4231 */
+ sc->chip_name = "CS4231 or AD1845";
+ break;
+ case 0x82:
+ sc->chip_name = "CS4232";
+ break;
+ }
+ }
sc->mode = 2;
}
}
@@ -414,9 +450,12 @@
sc->sc_locked = 0;
+/* blows up when you hit I8, clock & data format */
+#if 0
/* Initialize the ad1848 */
for (i = 0; i < 16; i++)
ad_write(sc, i, ad1848_init_values[i]);
+#endif
ad1848_reset(sc);
@@ -452,7 +491,7 @@
/* Set default port */
(void) ad1848_set_rec_port(sc, MIC_IN_PORT);
- printf(": %s%c", sc->chip_name, (sc->rev)?'A'+sc->rev:' ');
+ printf(": %s", sc->chip_name);
}
/*
@@ -464,8 +503,8 @@
struct ad1848_volume *gp;
{
register u_char reg, gain;
-
- DPRINTF(("ad1848_set_in_gain: %d:%d\n", gp->left, gp->right));
+
+ DPRINTF(("ad1848_set_rec_gain: %d:%d\n", gp->left, gp->right));
sc->rec_gain = *gp;
--- /usr/src/sys/dev/isa/pss.c.orig Thu May 11 06:26:30 1995
+++ /usr/src/sys/dev/isa/pss.c Sat Jun 3 13:33:02 1995
@@ -802,7 +802,7 @@
u_char bits;
int i;
- sc->sc_iobase = cf->cf_iobase;
+ sc->sc_iobase = cf->cf_iobase + 4;
/* Set WSS io address */
pss_setaddr(sc->sc_iobase, pc->sc_iobase+PSS_WSS_CONFIG);
>Audit-Trail:
>Unformatted: