Subject: port-sparc/32481: audiocs(4) mixer does not label inputs correctly, does not control output ports
To: None <port-sparc-maintainer@netbsd.org, gnats-admin@netbsd.org,>
From: None <svs+pr@grep.ru>
List: netbsd-bugs
Date: 01/08/2006 20:05:00
>Number: 32481
>Category: port-sparc
>Synopsis: audiocs(4) mixer does not label inputs correctly, does not control output ports
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: port-sparc-maintainer
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sun Jan 08 20:05:00 +0000 2006
>Originator: Sergey Svishchev
>Release: 3.0
>Organization:
>Environment:
>Description:
audiocs(4) labels some mixer inputs incorrectly: CD input (which is wired to AUX1) is labeled inputs.dac, Mono input (meant for PC Speaker) is labeled inputs.mic, and AUX2 input is labeled inputs.cd. AUX2 and Mono inputs may not be connected to any audio source at all.
Also, there are usually three output ports -- internal speaker, line out and headphone out; current code can mute the speaker, but not the other outputs.
>How-To-Repeat:
>Fix:
Tested on a SparcStation 5.
--- dev/ic/ad1848.c 15 Jan 2005 15:19:52 -0000 1.20
+++ dev/ic/ad1848.c 8 Jan 2006 16:55:52 -0000
@@ -440,7 +440,6 @@
{ CS_LEFT_LINE_CONTROL, CS_RIGHT_LINE_CONTROL, LINE_INPUT_ATTEN_BITS,
LINE_INPUT_ATTEN_MASK },
{ CS_MONO_IO_CONTROL, 0, MONO_INPUT_ATTEN_BITS, MONO_INPUT_ATTEN_MASK },
- { CS_MONO_IO_CONTROL, 0, 0, 0 },
{ SP_DIGITAL_MIX, 0, OUTPUT_ATTEN_BITS, MIX_ATTEN_MASK }
};
@@ -464,10 +463,7 @@
ad1848_mute_wave_output(sc, WAVE_UNMUTE1, 0);
ad_write(sc, mixer_channel_info[device].left_reg,
reg & ~DIGITAL_MIX1_ENABLE);
- } else if (device == AD1848_OUT_CHANNEL)
- ad_write(sc, mixer_channel_info[device].left_reg,
- reg | MONO_OUTPUT_MUTE);
- else
+ } else
ad_write(sc, mixer_channel_info[device].left_reg,
reg | 0x80);
} else if (!(sc->mute[device] & MUTE_LEFT)) {
@@ -476,10 +472,7 @@
reg | DIGITAL_MIX1_ENABLE);
if (sc->open_mode & FREAD)
ad1848_mute_wave_output(sc, WAVE_UNMUTE1, 1);
- } else if (device == AD1848_OUT_CHANNEL)
- ad_write(sc, mixer_channel_info[device].left_reg,
- reg & ~MONO_OUTPUT_MUTE);
- else
+ } else
ad_write(sc, mixer_channel_info[device].left_reg,
reg & ~0x80);
}
@@ -702,6 +695,12 @@
error = 0;
break;
+ case AD1848_KIND_OUTPUTPORT:
+ if (cp->type != AUDIO_MIXER_SET) break;
+ cp->un.mask = cs4231_get_play_port(ac);
+ error = 0;
+ break;
+
default:
printf ("Invalid kind\n");
break;
@@ -770,6 +769,12 @@
error = ad1848_set_rec_port(ac, cp->un.ord);
break;
+ case AD1848_KIND_OUTPUTPORT:
+ if (cp->type != AUDIO_MIXER_SET) break;
+
+ error = cs4231_set_play_port(ac, cp->un.mask);
+ break;
+
default:
printf ("Invalid kind\n");
break;
@@ -1015,6 +1020,45 @@
return sc->rec_port;
}
+/*
+ * audiocs(4)-specific code
+ */
+
+int
+cs4231_set_play_port(struct ad1848_softc *sc, int port)
+{
+ u_char xctl = XCTL0_ENABLE | XCTL1_ENABLE, reg;
+
+ DPRINTF(("cs4231_set_play_port: 0x%x\n", port));
+
+ if (port > 7 || port < 0)
+ return(EINVAL);
+ if (port & 2)
+ xctl &= ~XCTL1_ENABLE;
+ if (port & 4)
+ xctl &= ~XCTL0_ENABLE;
+
+ reg = ad_read(sc, SP_PIN_CONTROL);
+ reg &= XCTL_MASK;
+ ad_write(sc, SP_PIN_CONTROL, reg | xctl);
+
+ reg = ad_read(sc, CS_MONO_IO_CONTROL);
+ if (port & 1)
+ ad_write(sc, CS_MONO_IO_CONTROL, reg & ~MONO_OUTPUT_MUTE);
+ else
+ ad_write(sc, CS_MONO_IO_CONTROL, reg | MONO_OUTPUT_MUTE);
+
+ sc->play_port = port;
+
+ return (0);
+}
+
+int
+cs4231_get_play_port(struct ad1848_softc *sc)
+{
+ return (sc->play_port);
+}
+
int
ad1848_round_blocksize(void *addr, int blk,
int mode, const audio_params_t *param)
--- dev/ic/ad1848reg.h 6 Apr 2003 18:20:12 -0000 1.10
+++ dev/ic/ad1848reg.h 8 Jan 2006 16:55:52 -0000
@@ -154,6 +154,7 @@
#define INTERRUPT_ENABLE 0x02
#define XCTL0_ENABLE 0x40
#define XCTL1_ENABLE 0x80
+#define XCTL_MASK 0x3F
/* Test and init reg bits - register I11 (read-only) */
#define OVERRANGE_LEFT_MASK 0xfc
diff -u -r1.12 ad1848var.h
--- dev/ic/ad1848var.h 15 Jan 2005 15:19:52 -0000 1.12
+++ dev/ic/ad1848var.h 8 Jan 2006 16:55:52 -0000
@@ -118,6 +118,7 @@
struct ad1848_volume rec_gain;
int rec_port; /* recording port */
+ int play_port; /* playback port */
/* ad1848 */
u_char MCE_bit;
@@ -140,7 +141,7 @@
};
/*
- * Ad1848 registers.
+ * AD1848 registers.
*/
#define MIC_IN_PORT 0
#define LINE_IN_PORT 1
@@ -152,6 +153,7 @@
#define AD1848_KIND_RECORDGAIN 2
#define AD1848_KIND_MICGAIN 3
#define AD1848_KIND_RECORDSOURCE 4
+#define AD1848_KIND_OUTPUTPORT 5
typedef struct ad1848_devmap {
int id;
@@ -185,6 +187,8 @@
int ad1848_commit_settings(void *);
int ad1848_set_rec_port(struct ad1848_softc *, int);
int ad1848_get_rec_port(struct ad1848_softc *);
+int cs4231_set_play_port(struct ad1848_softc *, int);
+int cs4231_get_play_port(struct ad1848_softc *);
int ad1848_set_channel_gain(struct ad1848_softc *, int,
struct ad1848_volume *);
int ad1848_get_device_gain(struct ad1848_softc *, int,
--- dev/ic/cs4231.c 15 Jan 2005 15:19:52 -0000 1.17
+++ dev/ic/cs4231.c 8 Jan 2006 17:06:26 -0000
@@ -60,24 +60,25 @@
#include <dev/ic/cs4231var.h>
/*---*/
-#define CSAUDIO_DAC_LVL 0
+#define CSAUDIO_CD_LVL 0
#define CSAUDIO_LINE_IN_LVL 1
#define CSAUDIO_MONO_LVL 2
-#define CSAUDIO_CD_LVL 3
-#define CSAUDIO_OUTPUT_LVL 4
+#define CSAUDIO_AUX_LVL 3
+#define CSAUDIO_LOOPBACK_LVL 4
#define CSAUDIO_OUT_LVL 5
#define CSAUDIO_LINE_IN_MUTE 6
-#define CSAUDIO_DAC_MUTE 7
-#define CSAUDIO_CD_MUTE 8
+#define CSAUDIO_CD_MUTE 7
+#define CSAUDIO_AUX_MUTE 8
#define CSAUDIO_MONO_MUTE 9
-#define CSAUDIO_OUTPUT_MUTE 10
-#define CSAUDIO_OUT_MUTE 11
-#define CSAUDIO_REC_LVL 12
-#define CSAUDIO_RECORD_SOURCE 13
+#define CSAUDIO_LOOPBACK_MUTE 10
+#define CSAUDIO_REC_LVL 11
+#define CSAUDIO_RECORD_SOURCE 12
+#define CSAUDIO_OUTPUT_PORT 13
#define CSAUDIO_INPUT_CLASS 14
-#define CSAUDIO_MONITOR_CLASS 15
-#define CSAUDIO_RECORD_CLASS 16
+#define CSAUDIO_OUTPUT_CLASS 15
+#define CSAUDIO_MONITOR_CLASS 16
+#define CSAUDIO_RECORD_CLASS 17
#ifdef AUDIO_DEBUG
int cs4231_debug = 0;
@@ -383,20 +384,20 @@
}
static ad1848_devmap_t csmapping[] = {
- { CSAUDIO_DAC_LVL, AD1848_KIND_LVL, AD1848_AUX1_CHANNEL },
+ { CSAUDIO_CD_LVL, AD1848_KIND_LVL, AD1848_AUX1_CHANNEL },
{ CSAUDIO_LINE_IN_LVL, AD1848_KIND_LVL, AD1848_LINE_CHANNEL },
{ CSAUDIO_MONO_LVL, AD1848_KIND_LVL, AD1848_MONO_CHANNEL },
- { CSAUDIO_CD_LVL, AD1848_KIND_LVL, AD1848_AUX2_CHANNEL },
- { CSAUDIO_OUTPUT_LVL, AD1848_KIND_LVL, AD1848_MONITOR_CHANNEL },
+ { CSAUDIO_AUX_LVL, AD1848_KIND_LVL, AD1848_AUX2_CHANNEL },
+ { CSAUDIO_LOOPBACK_LVL, AD1848_KIND_LVL, AD1848_MONITOR_CHANNEL },
{ CSAUDIO_OUT_LVL, AD1848_KIND_LVL, AD1848_DAC_CHANNEL },
- { CSAUDIO_DAC_MUTE, AD1848_KIND_MUTE, AD1848_AUX1_CHANNEL },
+ { CSAUDIO_CD_MUTE, AD1848_KIND_MUTE, AD1848_AUX1_CHANNEL },
{ CSAUDIO_LINE_IN_MUTE, AD1848_KIND_MUTE, AD1848_LINE_CHANNEL },
{ CSAUDIO_MONO_MUTE, AD1848_KIND_MUTE, AD1848_MONO_CHANNEL },
- { CSAUDIO_CD_MUTE, AD1848_KIND_MUTE, AD1848_AUX2_CHANNEL },
- { CSAUDIO_OUTPUT_MUTE, AD1848_KIND_MUTE, AD1848_MONITOR_CHANNEL },
- { CSAUDIO_OUT_MUTE, AD1848_KIND_MUTE, AD1848_OUT_CHANNEL },
+ { CSAUDIO_AUX_MUTE, AD1848_KIND_MUTE, AD1848_AUX2_CHANNEL },
+ { CSAUDIO_LOOPBACK_MUTE, AD1848_KIND_MUTE, AD1848_MONITOR_CHANNEL },
{ CSAUDIO_REC_LVL, AD1848_KIND_RECORDGAIN, -1 },
- { CSAUDIO_RECORD_SOURCE, AD1848_KIND_RECORDSOURCE, -1 }
+ { CSAUDIO_RECORD_SOURCE, AD1848_KIND_RECORDSOURCE, -1 },
+ { CSAUDIO_OUTPUT_PORT, AD1848_KIND_OUTPUTPORT, -1},
};
static int nummap = sizeof(csmapping) / sizeof(csmapping[0]);
@@ -435,17 +436,17 @@
switch(dip->index) {
- case CSAUDIO_DAC_LVL: /* dacout */
+ case CSAUDIO_CD_LVL: /* aux1 (cd) input */
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = CSAUDIO_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
- dip->next = CSAUDIO_DAC_MUTE;
- strcpy(dip->label.name, AudioNdac);
+ dip->next = CSAUDIO_CD_MUTE;
+ strcpy(dip->label.name, AudioNcd);
dip->un.v.num_channels = 2;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
- case CSAUDIO_LINE_IN_LVL: /* line */
+ case CSAUDIO_LINE_IN_LVL: /* line input */
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = CSAUDIO_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
@@ -455,92 +456,78 @@
strcpy(dip->un.v.units.name, AudioNvolume);
break;
- case CSAUDIO_MONO_LVL: /* mono/microphone mixer */
+ /* is it even present on Sun workstations? */
+ case CSAUDIO_MONO_LVL: /* mono (system beeper) input -- mixed into mixerout and passed to mono out */
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = CSAUDIO_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = CSAUDIO_MONO_MUTE;
- strcpy(dip->label.name, AudioNmicrophone);
+ strcpy(dip->label.name, AudioNmono);
dip->un.v.num_channels = 1;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
- case CSAUDIO_CD_LVL: /* cd */
+ case CSAUDIO_AUX_LVL: /* aux2 input -- mixed into mixerout */
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = CSAUDIO_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
- dip->next = CSAUDIO_CD_MUTE;
- strcpy(dip->label.name, AudioNcd);
+ dip->next = CSAUDIO_AUX_MUTE;
+ strcpy(dip->label.name, AudioNaux);
dip->un.v.num_channels = 2;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
-
- case CSAUDIO_OUTPUT_LVL: /* monitor level */
+ case CSAUDIO_LOOPBACK_LVL: /* digital loopback */
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = CSAUDIO_MONITOR_CLASS;
- dip->next = CSAUDIO_OUTPUT_MUTE;
+ dip->next = CSAUDIO_LOOPBACK_MUTE;
dip->prev = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioNmonitor);
dip->un.v.num_channels = 1;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
- case CSAUDIO_OUT_LVL: /* cs4231 output volume */
+ case CSAUDIO_OUT_LVL: /* DAC output (host audio data + digital loopback, if enabled) */
dip->type = AUDIO_MIXER_VALUE;
- dip->mixer_class = CSAUDIO_MONITOR_CLASS;
+ dip->mixer_class = CSAUDIO_OUTPUT_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioNmaster);
dip->un.v.num_channels = 2;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
- case CSAUDIO_OUT_MUTE: /* mute built-in speaker */
- dip->mixer_class = CSAUDIO_MONITOR_CLASS;
- dip->type = AUDIO_MIXER_ENUM;
- dip->prev = CSAUDIO_MONITOR_CLASS;
- dip->next = AUDIO_MIXER_LAST;
- strcpy(dip->label.name, AudioNmono);
- /* names reversed, this is a "mute" value used as "mono enabled" */
- dip->un.e.num_mem = 2;
- strcpy(dip->un.e.member[0].label.name, AudioNon);
- dip->un.e.member[0].ord = 0;
- strcpy(dip->un.e.member[1].label.name, AudioNoff);
- dip->un.e.member[1].ord = 1;
- break;
-
- case CSAUDIO_LINE_IN_MUTE:
+ case CSAUDIO_LINE_IN_MUTE: /* line input */
dip->mixer_class = CSAUDIO_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = CSAUDIO_LINE_IN_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
- case CSAUDIO_DAC_MUTE:
+ case CSAUDIO_CD_MUTE: /* aux1 (cd) input */
dip->mixer_class = CSAUDIO_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
- dip->prev = CSAUDIO_DAC_LVL;
+ dip->prev = CSAUDIO_CD_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
- case CSAUDIO_CD_MUTE:
+ case CSAUDIO_AUX_MUTE: /* aux2 input */
dip->mixer_class = CSAUDIO_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
- dip->prev = CSAUDIO_CD_LVL;
+ dip->prev = CSAUDIO_AUX_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
- case CSAUDIO_MONO_MUTE:
+ case CSAUDIO_MONO_MUTE: /* mono output, muted separately from stereo outputs */
dip->mixer_class = CSAUDIO_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = CSAUDIO_MONO_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
- case CSAUDIO_OUTPUT_MUTE:
+ case CSAUDIO_LOOPBACK_MUTE: /* digital loopback -- use only to mix mic input into output */
dip->mixer_class = CSAUDIO_MONITOR_CLASS;
dip->type = AUDIO_MIXER_ENUM;
- dip->prev = CSAUDIO_OUTPUT_LVL;
+ dip->prev = CSAUDIO_LOOPBACK_LVL;
dip->next = AUDIO_MIXER_LAST;
mute:
strcpy(dip->label.name, AudioNmute);
@@ -551,12 +538,26 @@
dip->un.e.member[1].ord = 1;
break;
+ case CSAUDIO_OUTPUT_PORT:
+ dip->mixer_class = CSAUDIO_OUTPUT_CLASS;
+ dip->type = AUDIO_MIXER_SET;
+ dip->next = dip->prev = AUDIO_MIXER_LAST;
+ strcpy(dip->label.name, AudioNselect);
+ dip->un.s.num_mem = 3;
+ strcpy(dip->un.s.member[0].label.name, AudioNspeaker);
+ dip->un.s.member[0].mask = 1;
+ strcpy(dip->un.s.member[1].label.name, AudioNheadphone);
+ dip->un.s.member[1].mask = 2;
+ strcpy(dip->un.s.member[2].label.name, AudioNline);
+ dip->un.s.member[2].mask = 4;
+ break;
+
case CSAUDIO_REC_LVL: /* record level */
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = CSAUDIO_RECORD_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = CSAUDIO_RECORD_SOURCE;
- strcpy(dip->label.name, AudioNrecord);
+ strcpy(dip->label.name, AudioNmaster);
dip->un.v.num_channels = 2;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
@@ -568,11 +569,11 @@
dip->next = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioNsource);
dip->un.e.num_mem = 4;
- strcpy(dip->un.e.member[0].label.name, AudioNoutput);
+ strcpy(dip->un.e.member[0].label.name, AudioNmixerout);
dip->un.e.member[0].ord = DAC_IN_PORT;
strcpy(dip->un.e.member[1].label.name, AudioNmicrophone);
dip->un.e.member[1].ord = MIC_IN_PORT;
- strcpy(dip->un.e.member[2].label.name, AudioNdac);
+ strcpy(dip->un.e.member[2].label.name, AudioNcd);
dip->un.e.member[2].ord = AUX1_IN_PORT;
strcpy(dip->un.e.member[3].label.name, AudioNline);
dip->un.e.member[3].ord = LINE_IN_PORT;
@@ -592,6 +593,13 @@
strcpy(dip->label.name, AudioCmonitor);
break;
+ case CSAUDIO_OUTPUT_CLASS: /* output ... class */
+ dip->type = AUDIO_MIXER_CLASS;
+ dip->mixer_class = CSAUDIO_OUTPUT_CLASS;
+ dip->next = dip->prev = AUDIO_MIXER_LAST;
+ strcpy(dip->label.name, AudioCoutputs);
+ break;
+
case CSAUDIO_RECORD_CLASS: /* record source class */
dip->type = AUDIO_MIXER_CLASS;
dip->mixer_class = CSAUDIO_RECORD_CLASS;