summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/arch/sparc/dev/cs4231.c573
-rw-r--r--sys/arch/sparc/dev/cs4231var.h23
2 files changed, 369 insertions, 227 deletions
diff --git a/sys/arch/sparc/dev/cs4231.c b/sys/arch/sparc/dev/cs4231.c
index 39124b1c4c7..47927c6003f 100644
--- a/sys/arch/sparc/dev/cs4231.c
+++ b/sys/arch/sparc/dev/cs4231.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cs4231.c,v 1.16 2002/08/16 18:55:42 jason Exp $ */
+/* $OpenBSD: cs4231.c,v 1.17 2002/09/11 03:11:22 jason Exp $ */
/*
* Copyright (c) 1999 Jason L. Wright (jason@thought.net)
@@ -91,6 +91,7 @@
#define CSPORT_SPEAKER 6
#define CSPORT_LINEOUT 7
#define CSPORT_HEADPHONE 8
+#define CSPORT_MICROPHONE 9
#define MIC_IN_PORT 0
#define LINE_IN_PORT 1
@@ -108,7 +109,13 @@
#define CS_PC_HDPHMUTE XCTL1_ENABLE /* mute headphone */
/* cs4231 playback interrupt */
-#define CS_AFS_PI 0x10 /* playback interrupt */
+#define CS_AFS_TI 0x40 /* timer interrupt */
+#define CS_AFS_CI 0x20 /* capture interrupt */
+#define CS_AFS_PI 0x10 /* playback interrupt */
+#define CS_AFS_CU 0x08 /* capture underrun */
+#define CS_AFS_CO 0x04 /* capture overrun */
+#define CS_AFS_PO 0x02 /* playback overrun */
+#define CS_AFS_PU 0x01 /* playback underrun */
#define CS_TIMEOUT 90000 /* recalibration timeout */
@@ -116,9 +123,7 @@ int cs4231_match(struct device *, void *, void *);
void cs4231_attach(struct device *, struct device *, void *);
int cs4231_intr(void *);
-void cs4231_wait(struct cs4231_softc *);
int cs4231_set_speed(struct cs4231_softc *, u_long *);
-void cs4231_mute_monitor(struct cs4231_softc *, int);
void cs4231_setup_output(struct cs4231_softc *sc);
/* Audio interface */
@@ -143,6 +148,8 @@ int cs4231_trigger_output(void *, void *, void *, int,
void (*intr)(void *), void *arg, struct audio_params *);
int cs4231_trigger_input(void *, void *, void *, int,
void (*intr)(void *), void *arg, struct audio_params *);
+void cs4231_write(struct cs4231_softc *, u_int8_t, u_int8_t);
+u_int8_t cs4231_read(struct cs4231_softc *, u_int8_t);
struct audio_hw_if cs4231_sa_hw_if = {
cs4231_open,
@@ -249,11 +256,31 @@ cs4231_attach(parent, self, aux)
/* Default to speaker, unmuted, reasonable volume */
sc->sc_out_port = CSPORT_SPEAKER;
+ sc->sc_in_port = CSPORT_MICROPHONE;
sc->sc_mute[CSPORT_SPEAKER] = 1;
+ sc->sc_mute[CSPORT_MONITOR] = 1;
sc->sc_volume[CSPORT_SPEAKER].left = 192;
sc->sc_volume[CSPORT_SPEAKER].right = 192;
}
+void
+cs4231_write(sc, r, v)
+ struct cs4231_softc *sc;
+ u_int8_t r, v;
+{
+ sc->sc_regs->iar = r;
+ sc->sc_regs->idr = v;
+}
+
+u_int8_t
+cs4231_read(sc, r)
+ struct cs4231_softc *sc;
+ u_int8_t r;
+{
+ sc->sc_regs->iar = r;
+ return (sc->sc_regs->idr);
+}
+
/*
* Hardware interrupt handler
*/
@@ -263,9 +290,10 @@ cs4231_intr(v)
{
struct cs4231_softc *sc = (struct cs4231_softc *)v;
struct cs4231_regs *regs = sc->sc_regs;
+ struct cs_channel *chan;
+ struct cs_dma *p;
u_int32_t csr;
u_int8_t reg, status;
- struct cs_dma *p;
int r = 0;
csr = regs->dma_csr;
@@ -288,10 +316,12 @@ cs4231_intr(v)
regs->iar = CS_IRQ_STATUS;
reg = regs->idr;
if (reg & CS_AFS_PI) {
- regs->iar = SP_LOWER_BASE_COUNT;
- regs->idr = 0xff;
- regs->iar = SP_UPPER_BASE_COUNT;
- regs->idr = 0xff;
+ cs4231_write(sc, SP_LOWER_BASE_COUNT, 0xff);
+ cs4231_write(sc, SP_UPPER_BASE_COUNT, 0xff);
+ }
+ if (reg & CS_AFS_CI) {
+ cs4231_write(sc, CS_LOWER_REC_CNT, 0xff);
+ cs4231_write(sc, CS_UPPER_REC_CNT, 0xff);
}
regs->status = 0;
}
@@ -301,64 +331,60 @@ cs4231_intr(v)
if ((csr & APC_CSR_PMIE) && (csr & APC_CSR_PMI)) {
u_int32_t nextaddr, togo;
- p = sc->sc_nowplaying;
- togo = sc->sc_playsegsz - sc->sc_playcnt;
+ chan = &sc->sc_playback;
+
+ p = chan->cs_curdma;
+ togo = chan->cs_segsz - chan->cs_cnt;
if (togo == 0) {
nextaddr = (u_int32_t)p->addr_dva;
- sc->sc_playcnt = togo = sc->sc_blksz;
+ chan->cs_cnt = togo = chan->cs_blksz;
} else {
- nextaddr = regs->dma_pnva + sc->sc_blksz;
- if (togo > sc->sc_blksz)
- togo = sc->sc_blksz;
- sc->sc_playcnt += togo;
+ nextaddr = regs->dma_pnva + chan->cs_blksz;
+ if (togo > chan->cs_blksz)
+ togo = chan->cs_blksz;
+ chan->cs_cnt += togo;
}
regs->dma_pnva = nextaddr;
regs->dma_pnc = togo;
- if (sc->sc_pintr != NULL)
- (*sc->sc_pintr)(sc->sc_parg);
- r = 1;
- }
-#if 0
- if (csr & APC_CSR_CI) {
- if (sc->sc_rintr != NULL) {
- r = 1;
- (*sc->sc_rintr)(sc->sc_rarg);
- }
+ if (chan->cs_intr != NULL)
+ (*chan->cs_intr)(chan->cs_arg);
+ r = 1;
}
-#endif
- return (r);
-}
+ if ((csr & APC_CSR_CIE) && (csr & APC_CSR_CI)) {
+ if (csr & APC_CSR_CD) {
+ u_int32_t nextaddr, togo;
+
+ chan = &sc->sc_capture;
+ p = chan->cs_curdma;
+ togo = chan->cs_segsz - chan->cs_cnt;
+ if (togo == 0) {
+ nextaddr = (u_int32_t)p->addr_dva;
+ chan->cs_cnt = togo = chan->cs_blksz;
+ } else {
+ nextaddr = regs->dma_cnva + chan->cs_blksz;
+ if (togo > chan->cs_blksz)
+ togo = chan->cs_blksz;
+ chan->cs_cnt += togo;
+ }
-void
-cs4231_mute_monitor(sc, mute)
- struct cs4231_softc *sc;
- int mute;
-{
- struct cs4231_regs *regs = sc->sc_regs;
+ regs->dma_cnva = nextaddr;
+ regs->dma_cnc = togo;
- if (mute) {
- regs->iar = SP_LEFT_OUTPUT_CONTROL; /* left dac */
- regs->idr |= OUTPUT_MUTE;
- regs->iar = SP_RIGHT_OUTPUT_CONTROL; /* right dac */
- regs->idr |= OUTPUT_MUTE;
-#if 0
- regs->iar = CS_MONO_IO_CONTROL; /* mono */
- regs->idr |= MONO_OUTPUT_MUTE;
-#endif
+ if (chan->cs_intr != NULL)
+ (*chan->cs_intr)(chan->cs_arg);
+ }
+ r = 1;
}
- else {
- regs->iar = SP_LEFT_OUTPUT_CONTROL; /* left dac */
- regs->idr &= ~OUTPUT_MUTE;
- regs->iar = SP_RIGHT_OUTPUT_CONTROL; /* right dac */
- regs->idr &= ~OUTPUT_MUTE;
-#if 0
- regs->iar = CS_MONO_IO_CONTROL; /* mono */
- regs->idr &= ~MONO_OUTPUT_MUTE;
-#endif
+
+ if ((csr & APC_CSR_CMIE) && (csr & APC_CSR_CMI)) {
+ /* capture empty */
+ r = 1;
}
+
+ return (r);
}
int
@@ -419,10 +445,8 @@ cs4231_set_speed(sc, argp)
}
}
- if (selected == -1) {
- printf("%s: can't find speed\n", sc->sc_dev.dv_xname);
+ if (selected == -1)
selected = 3;
- }
sc->sc_speed_bits = speed_table[selected].bits;
sc->sc_need_commit = 1;
@@ -431,34 +455,6 @@ cs4231_set_speed(sc, argp)
return (0);
}
-void
-cs4231_wait(sc)
- struct cs4231_softc *sc;
-{
- struct cs4231_regs *regs = sc->sc_regs;
- int tries;
-
- DELAY(100);
-
- regs->iar = ~(MODE_CHANGE_ENABLE);
- tries = CS_TIMEOUT;
- while (regs->iar == SP_IN_INIT && tries--) {
- DELAY(100);
- }
- if (!tries)
- printf("%s: waited too long to reset iar\n",
- sc->sc_dev.dv_xname);
-
- regs->iar = SP_TEST_AND_INIT;
- tries = CS_TIMEOUT;
- while (regs->idr == AUTO_CAL_IN_PROG && tries--) {
- DELAY(100);
- }
- if (!tries)
- printf("%s: waited too long to reset errinit\n",
- sc->sc_dev.dv_xname);
-}
-
/*
* Audio interface functions
*/
@@ -469,16 +465,19 @@ cs4231_open(addr, flags)
{
struct cs4231_softc *sc = addr;
struct cs4231_regs *regs = sc->sc_regs;
- u_int8_t reg;
+ int tries;
if (sc->sc_open)
return (EBUSY);
sc->sc_open = 1;
- sc->sc_locked = 0;
- sc->sc_rintr = 0;
- sc->sc_rarg = 0;
- sc->sc_pintr = 0;
- sc->sc_parg = 0;
+
+ sc->sc_capture.cs_intr = NULL;
+ sc->sc_capture.cs_arg = NULL;
+ sc->sc_capture.cs_locked = 0;
+
+ sc->sc_playback.cs_intr = NULL;
+ sc->sc_playback.cs_arg = NULL;
+ sc->sc_playback.cs_locked = 0;
regs->dma_csr = APC_CSR_RESET;
DELAY(10);
@@ -489,30 +488,21 @@ cs4231_open(addr, flags)
DELAY(20);
regs->dma_csr &= ~(APC_CSR_CODEC_RESET);
- regs->iar |= MODE_CHANGE_ENABLE;
-
- cs4231_wait(sc);
- regs->iar = MODE_CHANGE_ENABLE | SP_MISC_INFO;
- regs->idr = MODE2;
+ for (tries = CS_TIMEOUT; tries && regs->iar == SP_IN_INIT; tries--)
+ DELAY(10);
+ if (tries == 0)
+ printf("%s: timeout waiting for reset\n", sc->sc_dev.dv_xname);
- /* XXX TODO: setup some defaults */
+ /* Turn on cs4231 mode */
+ cs4231_write(sc, SP_MISC_INFO,
+ cs4231_read(sc, SP_MISC_INFO) | MODE2);
- regs->iar = ~(MODE_CHANGE_ENABLE);
- cs4231_wait(sc);
-
- regs->iar = SP_PIN_CONTROL;
- regs->idr |= INTERRUPT_ENABLE;
-
- regs->iar = MODE_CHANGE_ENABLE | SP_INTERFACE_CONFIG;
- reg = regs->idr;
- regs->iar = MODE_CHANGE_ENABLE | SP_INTERFACE_CONFIG;
- regs->idr = reg & ~(AUTO_CAL_ENABLE);
+ cs4231_setup_output(sc);
- regs->iar = ~(MODE_CHANGE_ENABLE);
- cs4231_wait(sc);
+ cs4231_write(sc, SP_PIN_CONTROL,
+ cs4231_read(sc, SP_PIN_CONTROL) | INTERRUPT_ENABLE);
- cs4231_setup_output(sc);
return (0);
}
@@ -520,42 +510,71 @@ void
cs4231_setup_output(sc)
struct cs4231_softc *sc;
{
- struct cs4231_regs *regs = sc->sc_regs;
+ u_int8_t pc, mi, rm, lm;
- regs->iar = SP_PIN_CONTROL;
- regs->idr |= CS_PC_HDPHMUTE | CS_PC_LINEMUTE;
- regs->iar = CS_MONO_IO_CONTROL;
- regs->idr |= MONO_OUTPUT_MUTE;
+ pc = cs4231_read(sc, SP_PIN_CONTROL) | CS_PC_HDPHMUTE | CS_PC_LINEMUTE;
+
+ mi = cs4231_read(sc, CS_MONO_IO_CONTROL) | MONO_OUTPUT_MUTE;
+
+ lm = cs4231_read(sc, SP_LEFT_OUTPUT_CONTROL);
+ lm &= ~OUTPUT_ATTEN_BITS;
+ lm |= ((~(sc->sc_volume[CSPORT_SPEAKER].left >> 2)) &
+ OUTPUT_ATTEN_BITS) | OUTPUT_MUTE;
+
+ rm = cs4231_read(sc, SP_RIGHT_OUTPUT_CONTROL);
+ rm &= ~OUTPUT_ATTEN_BITS;
+ rm |= ((~(sc->sc_volume[CSPORT_SPEAKER].right >> 2)) &
+ OUTPUT_ATTEN_BITS) | OUTPUT_MUTE;
+
+ if (sc->sc_mute[CSPORT_MONITOR]) {
+ lm &= ~OUTPUT_MUTE;
+ rm &= ~OUTPUT_MUTE;
+ }
switch (sc->sc_out_port) {
case CSPORT_HEADPHONE:
- if (sc->sc_mute[CSPORT_SPEAKER]) {
- regs->iar = SP_PIN_CONTROL;
- regs->idr &= ~CS_PC_HDPHMUTE;
- }
+ if (sc->sc_mute[CSPORT_SPEAKER])
+ pc &= ~CS_PC_HDPHMUTE;
break;
case CSPORT_SPEAKER:
- if (sc->sc_mute[CSPORT_SPEAKER]) {
- regs->iar = CS_MONO_IO_CONTROL;
- regs->idr &= ~MONO_OUTPUT_MUTE;
- }
+ if (sc->sc_mute[CSPORT_SPEAKER])
+ mi &= ~MONO_OUTPUT_MUTE;
break;
case CSPORT_LINEOUT:
- if (sc->sc_mute[CSPORT_SPEAKER]) {
- regs->iar = SP_PIN_CONTROL;
- regs->idr &= ~CS_PC_LINEMUTE;
- }
+ if (sc->sc_mute[CSPORT_SPEAKER])
+ pc &= ~CS_PC_LINEMUTE;
break;
}
- regs->iar = SP_LEFT_OUTPUT_CONTROL;
- regs->idr &= ~OUTPUT_ATTEN_BITS;
- regs->idr |= (~(sc->sc_volume[CSPORT_SPEAKER].left >> 2)) &
- OUTPUT_ATTEN_BITS;
- regs->iar = SP_RIGHT_OUTPUT_CONTROL;
- regs->idr &= ~OUTPUT_ATTEN_BITS;
- regs->idr |= (~(sc->sc_volume[CSPORT_SPEAKER].right >> 2)) &
- OUTPUT_ATTEN_BITS;
+ cs4231_write(sc, SP_LEFT_OUTPUT_CONTROL, lm);
+ cs4231_write(sc, SP_RIGHT_OUTPUT_CONTROL, rm);
+ cs4231_write(sc, SP_PIN_CONTROL, pc);
+ cs4231_write(sc, CS_MONO_IO_CONTROL, mi);
+
+ /* XXX doesn't really belong here... */
+ switch (sc->sc_in_port) {
+ case CSPORT_LINEIN:
+ pc = LINE_INPUT;
+ break;
+ case CSPORT_AUX1:
+ pc = AUX_INPUT;
+ break;
+ case CSPORT_DAC:
+ pc = MIXED_DAC_INPUT;
+ break;
+ case CSPORT_MICROPHONE:
+ default:
+ pc = MIC_INPUT;
+ break;
+ }
+ lm = cs4231_read(sc, SP_LEFT_INPUT_CONTROL);
+ rm = cs4231_read(sc, SP_RIGHT_INPUT_CONTROL);
+ lm &= ~(MIXED_DAC_INPUT | ATTEN_22_5);
+ rm &= ~(MIXED_DAC_INPUT | ATTEN_22_5);
+ lm |= pc | (sc->sc_adc.left >> 4);
+ rm |= pc | (sc->sc_adc.right >> 4);
+ cs4231_write(sc, SP_LEFT_INPUT_CONTROL, lm);
+ cs4231_write(sc, SP_RIGHT_INPUT_CONTROL, rm);
}
void
@@ -647,60 +666,72 @@ cs4231_set_params(addr, setmode, usemode, p, r)
struct audio_params *p, *r;
{
struct cs4231_softc *sc = (struct cs4231_softc *)addr;
- int err, bits, enc;
- void (*pswcode)(void *, u_char *, int cnt);
- void (*rswcode)(void *, u_char *, int cnt);
-
- enc = p->encoding;
- pswcode = rswcode = 0;
- switch (enc) {
- case AUDIO_ENCODING_SLINEAR_LE:
- if (p->precision == 8) {
- enc = AUDIO_ENCODING_ULINEAR_LE;
- pswcode = rswcode = change_sign8;
- }
- break;
- case AUDIO_ENCODING_ULINEAR_LE:
- if (p->precision == 16) {
- enc = AUDIO_ENCODING_SLINEAR_LE;
- pswcode = rswcode = change_sign16;
- }
- break;
- case AUDIO_ENCODING_ULINEAR_BE:
- if (p->precision == 16) {
- enc = AUDIO_ENCODING_SLINEAR_BE;
- pswcode = rswcode = change_sign16;
- }
- break;
- }
+ int err, bits, enc = p->encoding;
+ void (*pswcode)(void *, u_char *, int cnt) = NULL;
+ void (*rswcode)(void *, u_char *, int cnt) = NULL;
switch (enc) {
case AUDIO_ENCODING_ULAW:
+ if (p->precision != 8)
+ return (EINVAL);
bits = FMT_ULAW >> 5;
break;
case AUDIO_ENCODING_ALAW:
+ if (p->precision != 8)
+ return (EINVAL);
bits = FMT_ALAW >> 5;
break;
- case AUDIO_ENCODING_ADPCM:
- bits = FMT_ADPCM >> 5;
- break;
case AUDIO_ENCODING_SLINEAR_LE:
- if (p->precision == 16)
+ if (p->precision == 8) {
+ bits = FMT_PCM8 >> 5;
+ pswcode = rswcode = change_sign8;
+ } else if (p->precision == 16)
bits = FMT_TWOS_COMP >> 5;
else
return (EINVAL);
break;
+ case AUDIO_ENCODING_ULINEAR:
+ if (p->precision != 8)
+ return (EINVAL);
+ bits = FMT_PCM8 >> 5;
+ break;
case AUDIO_ENCODING_SLINEAR_BE:
- if (p->precision == 16)
+ if (p->precision == 8) {
+ bits = FMT_PCM8 >> 5;
+ pswcode = rswcode = change_sign8;
+ } else if (p->precision == 16)
bits = FMT_TWOS_COMP_BE >> 5;
else
return (EINVAL);
break;
+ case AUDIO_ENCODING_SLINEAR:
+ if (p->precision != 8)
+ return (EINVAL);
+ bits = FMT_PCM8 >> 5;
+ pswcode = rswcode = change_sign8;
+ break;
case AUDIO_ENCODING_ULINEAR_LE:
if (p->precision == 8)
bits = FMT_PCM8 >> 5;
- else
+ else if (p->precision == 16) {
+ bits = FMT_TWOS_COMP >> 5;
+ pswcode = rswcode = change_sign16_le;
+ } else
+ return (EINVAL);
+ break;
+ case AUDIO_ENCODING_ULINEAR_BE:
+ if (p->precision == 8)
+ bits = FMT_PCM8 >> 5;
+ else if (p->precision == 16) {
+ bits = FMT_TWOS_COMP_BE >> 5;
+ pswcode = rswcode = change_sign16_be;
+ } else
+ return (EINVAL);
+ break;
+ case AUDIO_ENCODING_ADPCM:
+ if (p->precision != 8)
return (EINVAL);
+ bits = FMT_ADPCM >> 5;
break;
default:
return (EINVAL);
@@ -738,47 +769,53 @@ cs4231_commit_settings(addr)
struct cs4231_softc *sc = (struct cs4231_softc *)addr;
struct cs4231_regs *regs = sc->sc_regs;
int s, tries;
- u_char fs;
- volatile u_int8_t x;
+ u_int8_t r, fs;
if (sc->sc_need_commit == 0)
return (0);
- s = splaudio();
-
- cs4231_mute_monitor(sc, 1);
-
fs = sc->sc_speed_bits | (sc->sc_format_bits << 5);
if (sc->sc_channels == 2)
fs |= FMT_STEREO;
+ s = splaudio();
+
+ r = cs4231_read(sc, SP_INTERFACE_CONFIG) | AUTO_CAL_ENABLE;
+ regs->iar = MODE_CHANGE_ENABLE;
+ regs->iar = MODE_CHANGE_ENABLE | SP_INTERFACE_CONFIG;
+ regs->idr = r;
+
regs->iar = MODE_CHANGE_ENABLE | SP_CLOCK_DATA_FORMAT;
regs->idr = fs;
- x = regs->idr;
- x = regs->idr;
- tries = 100000;
- while (tries-- && regs->idr == SP_IN_INIT);
- if (tries == 0) {
+ r = regs->idr;
+ r = regs->idr;
+ tries = CS_TIMEOUT;
+ for (tries = CS_TIMEOUT; tries && regs->iar == SP_IN_INIT; tries--)
+ DELAY(10);
+ if (tries == 0)
printf("%s: timeout committing fspb\n", sc->sc_dev.dv_xname);
- splx(s);
- return (0);
- }
regs->iar = MODE_CHANGE_ENABLE | CS_REC_FORMAT;
regs->idr = fs;
- x = regs->idr;
- x = regs->idr;
- tries = 100000;
- while (tries-- && regs->idr == SP_IN_INIT);
- if (tries == 0) {
+ r = regs->idr;
+ r = regs->idr;
+ for (tries = CS_TIMEOUT; tries && regs->iar == SP_IN_INIT; tries--)
+ DELAY(10);
+ if (tries == 0)
printf("%s: timeout committing cdf\n", sc->sc_dev.dv_xname);
- splx(s);
- return (0);
- }
- cs4231_wait(sc);
+ regs->iar = 0;
+ for (tries = CS_TIMEOUT; tries && regs->iar == SP_IN_INIT; tries--)
+ DELAY(10);
+ if (tries == 0)
+ printf("%s: timeout waiting for !mce\n", sc->sc_dev.dv_xname);
- cs4231_mute_monitor(sc, 0);
+ regs->iar = SP_TEST_AND_INIT;
+ for (tries = CS_TIMEOUT; tries && regs->idr & AUTO_CAL_IN_PROG; tries--)
+ DELAY(10);
+ if (tries == 0)
+ printf("%s: timeout waiting for autocalibration\n",
+ sc->sc_dev.dv_xname);
splx(s);
@@ -794,13 +831,14 @@ cs4231_halt_output(addr)
struct cs4231_regs *regs = sc->sc_regs;
u_int8_t r;
+ /* XXX Kills some capture bits */
regs->dma_csr &= ~(APC_CSR_EI | APC_CSR_GIE | APC_CSR_PIE |
APC_CSR_EIE | APC_CSR_PDMA_GO | APC_CSR_PMIE);
regs->iar = SP_INTERFACE_CONFIG;
r = regs->idr & (~PLAYBACK_ENABLE);
regs->iar = SP_INTERFACE_CONFIG;
regs->idr = r;
- sc->sc_locked = 0;
+ sc->sc_playback.cs_locked = 0;
return (0);
}
@@ -811,10 +849,11 @@ cs4231_halt_input(addr)
struct cs4231_softc *sc = (struct cs4231_softc *)addr;
struct cs4231_regs *regs = sc->sc_regs;
+ /* XXX Kills some playback bits */
regs->dma_csr = APC_CSR_CAPTURE_PAUSE;
regs->iar = SP_INTERFACE_CONFIG;
regs->idr &= ~CAPTURE_ENABLE;
- sc->sc_locked = 0;
+ sc->sc_capture.cs_locked = 0;
return (0);
}
@@ -954,6 +993,8 @@ cs4231_set_port(addr, cp)
error = 0;
break;
case CSAUDIO_OUTPUT:
+ if (cp->type != AUDIO_MIXER_ENUM)
+ break;
if (cp->un.ord != CSPORT_LINEOUT &&
cp->un.ord != CSPORT_SPEAKER &&
cp->un.ord != CSPORT_HEADPHONE)
@@ -1002,10 +1043,32 @@ cs4231_set_port(addr, cp)
case CSAUDIO_REC_LVL:
if (cp->type != AUDIO_MIXER_VALUE)
break;
+ if (cp->un.value.num_channels == 1) {
+ sc->sc_adc.left =
+ cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
+ sc->sc_adc.right =
+ cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
+ } else if (cp->un.value.num_channels == 2) {
+ sc->sc_adc.left =
+ cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
+ sc->sc_adc.right =
+ cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
+ } else
+ break;
+ cs4231_setup_output(sc);
+ error = 0;
break;
case CSAUDIO_RECORD_SOURCE:
if (cp->type != AUDIO_MIXER_ENUM)
break;
+ if (cp->un.ord == CSPORT_MICROPHONE ||
+ cp->un.ord == CSPORT_LINEIN ||
+ cp->un.ord == CSPORT_AUX1 ||
+ cp->un.ord == CSPORT_DAC) {
+ sc->sc_in_port = cp->un.ord;
+ error = 0;
+ cs4231_setup_output(sc);
+ }
break;
}
@@ -1164,23 +1227,25 @@ cs4231_get_port(addr, cp)
break;
if (cp->un.value.num_channels == 1) {
cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
- AUDIO_MIN_GAIN;
+ sc->sc_adc.left;
} else if (cp->un.value.num_channels == 2) {
cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
- AUDIO_MIN_GAIN;
+ sc->sc_adc.left;
cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
- AUDIO_MIN_GAIN;
+ sc->sc_adc.right;
} else
break;
error = 0;
break;
case CSAUDIO_RECORD_SOURCE:
- if (cp->type != AUDIO_MIXER_ENUM) break;
- cp->un.ord = MIC_IN_PORT;
+ if (cp->type != AUDIO_MIXER_ENUM)
+ break;
+ cp->un.ord = sc->sc_in_port;
error = 0;
break;
case CSAUDIO_OUTPUT:
- if (cp->type != AUDIO_MIXER_ENUM) break;
+ if (cp->type != AUDIO_MIXER_ENUM)
+ break;
cp->un.ord = sc->sc_out_port;
error = 0;
break;
@@ -1310,15 +1375,15 @@ cs4231_query_devinfo(addr, dip)
dip->prev = CSAUDIO_REC_LVL;
dip->next = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioNsource);
- dip->un.e.num_mem = 3;
- strcpy(dip->un.e.member[0].label.name, AudioNcd);
- 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);
- 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;
+ dip->un.e.num_mem = 4;
+ strcpy(dip->un.e.member[0].label.name, AudioNmicrophone);
+ dip->un.e.member[0].ord = CSPORT_MICROPHONE;
+ strcpy(dip->un.e.member[1].label.name, AudioNline);
+ dip->un.e.member[1].ord = CSPORT_LINEIN;
+ strcpy(dip->un.e.member[2].label.name, AudioNcd);
+ dip->un.e.member[2].ord = CSPORT_AUX1;
+ strcpy(dip->un.e.member[3].label.name, AudioNdac);
+ dip->un.e.member[3].ord = CSPORT_DAC;
break;
case CSAUDIO_OUTPUT:
dip->type = AUDIO_MIXER_ENUM;
@@ -1441,18 +1506,19 @@ cs4231_trigger_output(addr, start, end, blksize, intr, arg, param)
{
struct cs4231_softc *sc = addr;
struct cs4231_regs *regs = sc->sc_regs;
+ struct cs_channel *chan = &sc->sc_playback;
struct cs_dma *p;
u_int8_t reg;
u_int32_t n, csr;
- if (sc->sc_locked != 0) {
+ if (chan->cs_locked != 0) {
printf("cs4231_trigger_output: already running\n");
return (EINVAL);
}
- sc->sc_locked = 1;
- sc->sc_pintr = intr;
- sc->sc_parg = arg;
+ chan->cs_locked = 1;
+ chan->cs_intr = intr;
+ chan->cs_arg = arg;
p = sc->sc_dmas;
while (p != NULL && p->addr != start)
@@ -1468,14 +1534,14 @@ cs4231_trigger_output(addr, start, end, blksize, intr, arg, param)
* Do only `blksize' at a time, so audio_pint() is kept
* synchronous with us...
*/
- sc->sc_blksz = blksize;
- sc->sc_nowplaying = p;
- sc->sc_playsegsz = n;
+ chan->cs_blksz = blksize;
+ chan->cs_curdma = p;
+ chan->cs_segsz = n;
- if (n > sc->sc_blksz)
- n = sc->sc_blksz;
+ if (n > chan->cs_blksz)
+ n = chan->cs_blksz;
- sc->sc_playcnt = n;
+ chan->cs_cnt = n;
csr = regs->dma_csr;
regs->dma_pnva = (u_int32_t)p->addr_dva;
@@ -1506,7 +1572,78 @@ cs4231_trigger_input(addr, start, end, blksize, intr, arg, param)
void *arg;
struct audio_params *param;
{
- return (ENXIO);
+ struct cs4231_softc *sc = addr;
+ struct cs_channel *chan = &sc->sc_capture;
+ struct cs_dma *p;
+ u_int32_t csr;
+ u_long n;
+
+ if (chan->cs_locked != 0) {
+ printf("%s: trigger_input: already running\n",
+ sc->sc_dev.dv_xname);
+ return (EINVAL);
+ }
+ chan->cs_locked = 1;
+ chan->cs_intr = intr;
+ chan->cs_arg = arg;
+
+ for (p = sc->sc_dmas; p->addr != start; p = p->next)
+ /*EMPTY*/;
+ if (p == NULL) {
+ printf("%s: trigger_input: bad addr: %x\n",
+ sc->sc_dev.dv_xname, start);
+ return (EINVAL);
+ }
+
+ n = (char *)end - (char *)start;
+
+ /*
+ * Do only `blksize' at a time, so audio_cint() is kept
+ * synchronous with us...
+ */
+ chan->cs_blksz = blksize;
+ chan->cs_curdma = p;
+ chan->cs_segsz = n;
+
+ if (n > chan->cs_blksz)
+ n = chan->cs_blksz;
+ chan->cs_cnt = n;
+
+ sc->sc_regs->dma_cnva = (u_int32_t)p->addr_dva;
+ sc->sc_regs->dma_cnc = n;
+
+ csr = sc->sc_regs->dma_csr;
+ if ((csr & APC_CSR_CDMA_GO) == 0 || (csr & APC_CSR_CPAUSE) != 0) {
+ csr &= APC_CSR_CPAUSE;
+ csr |= APC_CSR_GIE | APC_CSR_CMIE | APC_CSR_CIE | APC_CSR_EI |
+ APC_CSR_CDMA_GO;
+ sc->sc_regs->dma_csr = csr;
+ cs4231_write(sc, CS_LOWER_REC_CNT, 0xff);
+ cs4231_write(sc, CS_UPPER_REC_CNT, 0xff);
+ cs4231_write(sc, SP_INTERFACE_CONFIG,
+ cs4231_read(sc, SP_INTERFACE_CONFIG) | CAPTURE_ENABLE);
+ }
+
+ if (sc->sc_regs->dma_csr & APC_CSR_CD) {
+ u_long nextaddr, togo;
+
+ p = chan->cs_curdma;
+ togo = chan->cs_segsz - chan->cs_cnt;
+ if (togo == 0) {
+ nextaddr = (u_int32_t)p->addr_dva;
+ chan->cs_cnt = togo = chan->cs_blksz;
+ } else {
+ nextaddr = sc->sc_regs->dma_cnva + chan->cs_blksz;
+ if (togo > chan->cs_blksz)
+ togo = chan->cs_blksz;
+ chan->cs_cnt += togo;
+ }
+
+ sc->sc_regs->dma_cnva = nextaddr;
+ sc->sc_regs->dma_cnc = togo;
+ }
+
+ return (0);
}
#endif /* NAUDIO > 0 */
diff --git a/sys/arch/sparc/dev/cs4231var.h b/sys/arch/sparc/dev/cs4231var.h
index b6542ca7fbd..65e13b0c1cc 100644
--- a/sys/arch/sparc/dev/cs4231var.h
+++ b/sys/arch/sparc/dev/cs4231var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cs4231var.h,v 1.6 2002/03/14 03:15:59 millert Exp $ */
+/* $OpenBSD: cs4231var.h,v 1.7 2002/09/11 03:11:22 jason Exp $ */
/*
* Copyright (c) 1999 Jason L. Wright (jason@thought.net)
@@ -50,6 +50,16 @@ struct cs_volume {
u_int8_t right;
};
+struct cs_channel {
+ void (*cs_intr)(void *); /* interrupt handler */
+ void *cs_arg; /* interrupt arg */
+ struct cs_dma *cs_curdma; /* current dma block */
+ u_int32_t cs_cnt; /* current block count */
+ u_int32_t cs_blksz; /* current block size */
+ u_int32_t cs_segsz; /* current segment size */
+ int cs_locked; /* channel locked? */
+};
+
struct cs4231_softc {
struct device sc_dev; /* base device */
struct sbusdev sc_sd; /* sbus device */
@@ -59,25 +69,20 @@ struct cs4231_softc {
int sc_node; /* which sbus node */
int sc_burst; /* XXX: DMA burst size in effect */
int sc_open; /* already open? */
- int sc_locked; /* locked? */
- void (*sc_rintr)(void *); /* input completion intr handler */
- void * sc_rarg; /* arg for sc_rintr() */
- void (*sc_pintr)(void *); /* output completion intr handler */
- void * sc_parg; /* arg for sc_pintr() */
+ struct cs_channel sc_playback, sc_capture;
char sc_mute[9]; /* which devs are muted */
u_int8_t sc_out_port; /* output port */
+ u_int8_t sc_in_port; /* input port */
struct cs_volume sc_volume[9]; /* software volume */
+ struct cs_volume sc_adc; /* adc volume */
int sc_format_bits;
int sc_speed_bits;
int sc_precision;
int sc_need_commit;
int sc_channels;
- u_int32_t sc_blksz;
- u_int32_t sc_playcnt;
- u_int32_t sc_playsegsz;
struct cs_dma *sc_dmas; /* dma list */
struct cs_dma *sc_nowplaying;
};