Subject: Initializing bits on obio & a mixer question
To: None <port-macppc@netbsd.org, tech-kern@netbsd.org>
From: Thomas Klausner <wiz@danbala.ifoer.tuwien.ac.at>
List: tech-kern
Date: 02/17/2001 22:08:16
--w3uUfsyyY1Pqa/ej
Content-Type: text/plain; charset=us-ascii

Hi!

While browsing the linux kernel source, I found the following piece of
code:
                /* Powerbooks have odd ways of enabling inputs such as
                   an expansion-bay CD or sound from an internal modem
                   or a PC-card modem. */
                if (machine_is_compatible("AAPL,3400/2400")
                        || machine_is_compatible("AAPL,3500")) {
                        is_pbook_3400 = 1;
                        /*
                         * Enable CD and PC-card sound inputs.
                         * This is done by reading from address
                         * f301a000, + 0x10 to enable the expansion-bay
                         * CD sound input, + 0x80 to enable the PC-card
                         * sound input.  The 0x100 enables the SCSI bus
                         * terminator power.
                         */
                        in_8((unsigned char *)0xf301a190);
                } else if (machine_is_compatible("PowerBook1,1")
                           || machine_is_compatible("AAPL,PowerBook1998")) {
                        struct device_node* mio;
                        macio_base = 0;
                        is_pbook_G3 = 1;
                        for (mio = np->parent; mio; mio = mio->parent) {
                                if (strcmp(mio->name, "mac-io") == 0
                                    && mio->n_addrs > 0) {
                                        macio_base = (unsigned char *) ioremap
                                                (mio->addrs[0].address, 0x40);
                                        break;
                                }
                        }
                        /* enable CD sound input */
                        if (macio_base)
                                out_8(macio_base + 0x37, 3);
                }

Since I have a Powerbook G3, and wanted to see if the code is correct,
I hacked together the attached diff to sys/arch/macppc/dev/obio.c, and
lo-and-behold, I could hear sound when my media-bay CD drive was
playing audio CDs (which doesn't work in -current).  How I did it is
obviously not the best method -- what's the correct way and place to
do this? There doesn't seem to be an unmapiodev, so what is one
supposed to with the mapped space after one doesn't need it anymore?
And how does one do the above model checks on NetBSD (I can't test a
3400 anyway, so that's not so important to me)?

Also attached is a diff for sys/arch/macppc/dev/awacs.c.
I can't test my line-in now, but otherwise it seems to work for me(TM).

But it gave me some questions:
Why is it necessary to set mc->un.value.num_channels to 2 for the
speakers and for the CD in, but not for the others (if I don't do
this, they are treated as mono devices by mixerctl, while the others
are treated as stereo)? The device properties for all of them say they
are stereo -- just luck for the other ones?

Also, as there is only one input volume (for the MUX controlling cd,
mic and line in) -- what's the correct way to handle this? Three
"independant" volume knobs like I did doesn't seem correct.

And last, there seems to be a knob to turn off mic pre-amplification
-- what's the correct mixer thingy for that?

Thanks,
 Thomas

-- 
Thomas Klausner - wiz@danbala.tuwien.ac.at
To live for some future goal is shallow. It's the sides of the mountain
that sustain life, not the top. -- Robert M. Pirsig

--w3uUfsyyY1Pqa/ej
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="obio.c.diff"

$NetBSD$

--- obio.c.orig	Sun Nov  5 20:12:55 2000
+++ obio.c
@@ -92,6 +92,7 @@
 	struct confargs ca;
 	int node, child, namelen;
 	u_int reg[20];
+	char *wizbase;
 	int intr[6];
 	char name[32];
 
@@ -126,6 +127,12 @@
 	ca.ca_baseaddr = reg[2];
 
 	printf(": addr 0x%x\n", ca.ca_baseaddr);
+
+	printf("WIZHACK at obio");
+	wizbase = (char *)mapiodev(ca.ca_baseaddr, 0x40);
+	printf(": wizbase: 0x%x", wizbase);
+	out8(wizbase+0x37, 3);
+	printf(": wrote 3 at 0x%x\n", wizbase+0x37);
 
 	for (child = OF_child(node); child; child = OF_peer(child)) {
 		namelen = OF_getprop(child, "name", name, sizeof(name));

--w3uUfsyyY1Pqa/ej
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="awacs.c.diff"

$NetBSD$

--- awacs.c.orig	Thu Jan 25 14:39:22 2001
+++ awacs.c
@@ -186,7 +186,8 @@
 /* cc0 */
 #define AWACS_DEFAULT_CD_GAIN	0x000000bb
 #define AWACS_INPUT_CD		0x00000200
-#define AWACS_INPUT_MIC		0x00000400
+#define AWACS_INPUT_LINE	0x00000400
+#define AWACS_INPUT_MICROPHONE	0x00000800
 #define AWACS_INPUT_MASK	0x00000e00
 
 /* cc1 */
@@ -258,15 +259,22 @@
 	awacs_set_speaker_volume(sc, 80, 80);
 
 	/* Set loopback (for CD?) */
-	sc->sc_codecctl1 |= 0x440;
+	/* sc->sc_codecctl1 |= 0x440; */
+	sc->sc_codecctl1 |= 0x40;
 	awacs_write_codec(sc, sc->sc_codecctl1);
 
+	/* default output to speakers */
 	sc->sc_output_mask = 1 << 0;
-
 	sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER;
 	sc->sc_codecctl1 |= AWACS_MUTE_HEADPHONE;
 	awacs_write_codec(sc, sc->sc_codecctl1);
 
+	/* default input from CD */
+	sc->sc_record_source = 1 << 0;
+	sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
+	sc->sc_codecctl0 |= AWACS_INPUT_CD;
+	awacs_write_codec(sc, sc->sc_codecctl0);
+
 	/* Enable interrupts and looping mode. */
 	/* XXX ... */
 
@@ -550,6 +558,12 @@
 	AWACS_VOL_HEADPHONE,
 	AWACS_OUTPUT_CLASS,
 	AWACS_MONITOR_CLASS,
+	AWACS_INPUT_SELECT,
+	AWACS_VOL_CD,
+	AWACS_VOL_MICROPHONE,
+	AWACS_VOL_LINE_IN,
+	AWACS_INPUT_CLASS,
+	AWACS_RECORD_CLASS,
 	AWACS_ENUM_LAST
 };
 
@@ -582,7 +596,7 @@
 			sc->sc_codecctl1 &= ~AWACS_MUTE_HEADPHONE;
 			awacs_write_codec(sc, sc->sc_codecctl1);
 			break;
-		default: /* XXX */
+		default: /* invalid argument */
 			return -1;
 		}
 		sc->sc_output_mask = mc->un.mask;
@@ -595,6 +609,40 @@
 	case AWACS_VOL_HEADPHONE:
 		awacs_set_ext_volume(sc, l, r);
 		return 0;
+
+	case AWACS_VOL_CD:
+	case AWACS_VOL_MICROPHONE:
+	case AWACS_VOL_LINE_IN:
+		sc->sc_codecctl0 &= ~0xff;
+		sc->sc_codecctl0 |= (l & 0xf0) | (r >> 4);
+		awacs_write_codec(sc, sc->sc_codecctl0);
+		return 0;
+
+	case AWACS_INPUT_SELECT:
+		/* no change necessary? */
+		if (mc->un.mask == sc->sc_record_source)
+			return 0;
+		switch(mc->un.mask) {
+		case 1<<0: /* CD */
+			sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
+			sc->sc_codecctl0 |= AWACS_INPUT_CD;
+			awacs_write_codec(sc, sc->sc_codecctl0);
+			break;
+		case 1<<1: /* microphone */
+			sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
+			sc->sc_codecctl0 |= AWACS_INPUT_MICROPHONE;
+			awacs_write_codec(sc, sc->sc_codecctl0);
+			break;
+		case 1<<2: /* line in */
+			sc->sc_codecctl0 &= ~AWACS_INPUT_MASK;
+			sc->sc_codecctl0 |= AWACS_INPUT_LINE;
+			awacs_write_codec(sc, sc->sc_codecctl0);
+			break;
+		default: /* invalid argument */
+			return -1;
+		}
+		sc->sc_record_source = mc->un.mask;
+		return 0;
 	}
 
 	return ENXIO;
@@ -620,6 +668,7 @@
 		l = (15 - ((vol & 0x3c0) >> 6)) * 16;
 		r = (15 - (vol & 0x0f)) * 16;
 		mc->un.mask = 1 << 0;
+		mc->un.value.num_channels = 2;
 		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
 		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
 		return 0;
@@ -629,6 +678,41 @@
 		l = (15 - ((vol & 0x3c0) >> 6)) * 16;
 		r = (15 - (vol & 0x0f)) * 16;
 		mc->un.mask = 1 << 1;
+		mc->un.value.num_channels = 2;
+		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
+		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
+		return 0;
+
+	case AWACS_INPUT_SELECT:
+		mc->un.mask = sc->sc_record_source;
+		return 0;
+
+	case AWACS_VOL_CD:
+		vol = sc->sc_codecctl0 & 0xff;
+		l = (vol & 0xf0);
+		r = (vol & 0x0f) << 4;
+		mc->un.mask = 1 << 0;
+		mc->un.value.num_channels = 2;
+		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
+		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
+		return 0;
+
+	case AWACS_VOL_MICROPHONE:
+		vol = sc->sc_codecctl0 & 0xff;
+		l = (vol & 0xf0);
+		r = (vol & 0x0f) << 4;
+		mc->un.mask = 1 << 1;
+		mc->un.value.num_channels = 2;
+		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
+		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
+		return 0;
+
+	case AWACS_VOL_LINE_IN:
+		vol = sc->sc_codecctl0 & 0xff;
+		l = (vol & 0xf0);
+		r = (vol & 0x0f) << 4;
+		mc->un.mask = 1 << 2;
+		mc->un.value.num_channels = 2;
 		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
 		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
 		return 0;
@@ -680,6 +764,47 @@
 		strcpy(dip->un.v.units.name, AudioNvolume);
 		return 0;
 
+	case AWACS_INPUT_SELECT:
+		dip->mixer_class = AWACS_MONITOR_CLASS;
+		strcpy(dip->label.name, AudioNinput);
+		dip->type = AUDIO_MIXER_SET;
+		dip->prev = dip->next = AUDIO_MIXER_LAST;
+		dip->un.s.num_mem = 3;
+		strcpy(dip->un.s.member[0].label.name, AudioNcd);
+		dip->un.s.member[0].mask = 1 << 0;
+		strcpy(dip->un.s.member[1].label.name, AudioNmicrophone);
+		dip->un.s.member[1].mask = 1 << 1;
+		strcpy(dip->un.s.member[2].label.name, AudioNline);
+		dip->un.s.member[2].mask = 1 << 2;
+		return 0;
+
+	case AWACS_VOL_CD:
+		dip->mixer_class = AWACS_INPUT_CLASS;
+		strcpy(dip->label.name, AudioNcd);
+		dip->type = AUDIO_MIXER_VALUE;
+		dip->prev = dip->next = AUDIO_MIXER_LAST;
+		dip->un.v.num_channels = 2;
+		strcpy(dip->un.v.units.name, AudioNvolume);
+		return 0;
+
+	case AWACS_VOL_MICROPHONE:
+		dip->mixer_class = AWACS_INPUT_CLASS;
+		strcpy(dip->label.name, AudioNmicrophone);
+		dip->type = AUDIO_MIXER_VALUE;
+		dip->prev = dip->next = AUDIO_MIXER_LAST;
+		dip->un.v.num_channels = 2; /* XXX: 1? */
+		strcpy(dip->un.v.units.name, AudioNvolume);
+		return 0;
+
+	case AWACS_VOL_LINE_IN:
+		dip->mixer_class = AWACS_INPUT_CLASS;
+		strcpy(dip->label.name, AudioNline);
+		dip->type = AUDIO_MIXER_VALUE;
+		dip->prev = dip->next = AUDIO_MIXER_LAST;
+		dip->un.v.num_channels = 2;
+		strcpy(dip->un.v.units.name, AudioNvolume);
+		return 0;
+
 	case AWACS_MONITOR_CLASS:
 		dip->mixer_class = AWACS_MONITOR_CLASS;
 		strcpy(dip->label.name, AudioCmonitor);
@@ -690,6 +815,20 @@
 	case AWACS_OUTPUT_CLASS:
 		dip->mixer_class = AWACS_OUTPUT_CLASS;
 		strcpy(dip->label.name, AudioCoutputs);
+		dip->type = AUDIO_MIXER_CLASS;
+		dip->next = dip->prev = AUDIO_MIXER_LAST;
+		return 0;
+
+	case AWACS_RECORD_CLASS:
+		dip->mixer_class = AWACS_MONITOR_CLASS;
+		strcpy(dip->label.name, AudioCrecord);
+		dip->type = AUDIO_MIXER_CLASS;
+		dip->next = dip->prev = AUDIO_MIXER_LAST;
+		return 0;
+
+	case AWACS_INPUT_CLASS:
+		dip->mixer_class = AWACS_INPUT_CLASS;
+		strcpy(dip->label.name, AudioCinputs);
 		dip->type = AUDIO_MIXER_CLASS;
 		dip->next = dip->prev = AUDIO_MIXER_LAST;
 		return 0;

--w3uUfsyyY1Pqa/ej--