1*cc5bdd41Skn /* $OpenBSD: cs4231.c,v 1.44 2022/10/26 20:19:09 kn Exp $ */
27f529f01Sjason
37f529f01Sjason /*
47f529f01Sjason * Copyright (c) 1999 Jason L. Wright (jason@thought.net)
57f529f01Sjason * All rights reserved.
67f529f01Sjason *
77f529f01Sjason * Redistribution and use in source and binary forms, with or without
87f529f01Sjason * modification, are permitted provided that the following conditions
97f529f01Sjason * are met:
107f529f01Sjason * 1. Redistributions of source code must retain the above copyright
117f529f01Sjason * notice, this list of conditions and the following disclaimer.
127f529f01Sjason * 2. Redistributions in binary form must reproduce the above copyright
137f529f01Sjason * notice, this list of conditions and the following disclaimer in the
147f529f01Sjason * documentation and/or other materials provided with the distribution.
157f529f01Sjason *
167f529f01Sjason * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
177f529f01Sjason * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
187f529f01Sjason * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
197f529f01Sjason * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
207f529f01Sjason * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
217f529f01Sjason * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
227f529f01Sjason * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237f529f01Sjason * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
247f529f01Sjason * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
257f529f01Sjason * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
267f529f01Sjason * POSSIBILITY OF SUCH DAMAGE.
275248d82bSjason *
285248d82bSjason * Effort sponsored in part by the Defense Advanced Research Projects
295248d82bSjason * Agency (DARPA) and Air Force Research Laboratory, Air Force
305248d82bSjason * Materiel Command, USAF, under agreement number F30602-01-2-0537.
315248d82bSjason *
327f529f01Sjason */
337f529f01Sjason
347f529f01Sjason /*
357f529f01Sjason * Driver for CS4231 based audio found in some sun4m systems (cs4231)
367f529f01Sjason * based on ideas from the S/Linux project and the NetBSD project.
377f529f01Sjason */
387f529f01Sjason
397f529f01Sjason #include <sys/param.h>
407f529f01Sjason #include <sys/systm.h>
417f529f01Sjason #include <sys/errno.h>
427f529f01Sjason #include <sys/ioctl.h>
437f529f01Sjason #include <sys/device.h>
447f529f01Sjason #include <sys/proc.h>
457f529f01Sjason #include <sys/malloc.h>
467f529f01Sjason
477f529f01Sjason #include <machine/bus.h>
487f529f01Sjason #include <machine/intr.h>
497f529f01Sjason #include <machine/autoconf.h>
507f529f01Sjason
517f529f01Sjason #include <sys/audioio.h>
527f529f01Sjason #include <dev/audio_if.h>
537f529f01Sjason
547e0aa3e6Sjason #include <dev/ic/ad1848reg.h>
557e0aa3e6Sjason #include <dev/ic/cs4231reg.h>
56a1f7da73Sjason #include <dev/ic/apcdmareg.h>
577f529f01Sjason #include <dev/sbus/sbusvar.h>
587f529f01Sjason #include <dev/sbus/cs4231var.h>
597f529f01Sjason
607f529f01Sjason #define CSAUDIO_DAC_LVL 0
617f529f01Sjason #define CSAUDIO_LINE_IN_LVL 1
627f529f01Sjason #define CSAUDIO_MIC_LVL 2
637f529f01Sjason #define CSAUDIO_CD_LVL 3
647f529f01Sjason #define CSAUDIO_MONITOR_LVL 4
657f529f01Sjason #define CSAUDIO_OUTPUT_LVL 5
667f529f01Sjason #define CSAUDIO_LINE_IN_MUTE 6
677f529f01Sjason #define CSAUDIO_DAC_MUTE 7
687f529f01Sjason #define CSAUDIO_CD_MUTE 8
697f529f01Sjason #define CSAUDIO_MIC_MUTE 9
707f529f01Sjason #define CSAUDIO_MONITOR_MUTE 10
717f529f01Sjason #define CSAUDIO_OUTPUT_MUTE 11
727f529f01Sjason #define CSAUDIO_REC_LVL 12
737f529f01Sjason #define CSAUDIO_RECORD_SOURCE 13
747f529f01Sjason #define CSAUDIO_OUTPUT 14
757f529f01Sjason #define CSAUDIO_INPUT_CLASS 15
767f529f01Sjason #define CSAUDIO_OUTPUT_CLASS 16
777f529f01Sjason #define CSAUDIO_RECORD_CLASS 17
787f529f01Sjason #define CSAUDIO_MONITOR_CLASS 18
797f529f01Sjason
807f529f01Sjason #define CSPORT_AUX2 0
817f529f01Sjason #define CSPORT_AUX1 1
827f529f01Sjason #define CSPORT_DAC 2
837f529f01Sjason #define CSPORT_LINEIN 3
847f529f01Sjason #define CSPORT_MONO 4
857f529f01Sjason #define CSPORT_MONITOR 5
867f529f01Sjason #define CSPORT_SPEAKER 6
877f529f01Sjason #define CSPORT_LINEOUT 7
887f529f01Sjason #define CSPORT_HEADPHONE 8
899820c8cbSjason #define CSPORT_MICROPHONE 9
907f529f01Sjason
917f529f01Sjason #define MIC_IN_PORT 0
927f529f01Sjason #define LINE_IN_PORT 1
937f529f01Sjason #define AUX1_IN_PORT 2
947f529f01Sjason #define DAC_IN_PORT 3
957f529f01Sjason
967f529f01Sjason #ifdef AUDIO_DEBUG
977f529f01Sjason #define DPRINTF(x) printf x
987f529f01Sjason #else
997f529f01Sjason #define DPRINTF(x)
1007f529f01Sjason #endif
1017f529f01Sjason
102a1f7da73Sjason #define CS_TIMEOUT 90000
103a1f7da73Sjason
104a1f7da73Sjason #define CS_PC_LINEMUTE XCTL0_ENABLE
105a1f7da73Sjason #define CS_PC_HDPHMUTE XCTL1_ENABLE
1069820c8cbSjason #define CS_AFS_TI 0x40 /* timer interrupt */
1079820c8cbSjason #define CS_AFS_CI 0x20 /* capture interrupt */
1089820c8cbSjason #define CS_AFS_PI 0x10 /* playback interrupt */
1099820c8cbSjason #define CS_AFS_CU 0x08 /* capture underrun */
1109820c8cbSjason #define CS_AFS_CO 0x04 /* capture overrun */
1119820c8cbSjason #define CS_AFS_PO 0x02 /* playback overrun */
1129820c8cbSjason #define CS_AFS_PU 0x01 /* playback underrun */
113a1f7da73Sjason
1147f529f01Sjason #define CS_WRITE(sc,r,v) \
115a1f7da73Sjason bus_space_write_1((sc)->sc_bustag, (sc)->sc_regs, (r) << 2, (v))
1167f529f01Sjason #define CS_READ(sc,r) \
117a1f7da73Sjason bus_space_read_1((sc)->sc_bustag, (sc)->sc_regs, (r) << 2)
1187f529f01Sjason
1197f529f01Sjason #define APC_WRITE(sc,r,v) \
1207f529f01Sjason bus_space_write_4(sc->sc_bustag, sc->sc_regs, r, v)
1217f529f01Sjason #define APC_READ(sc,r) \
1227f529f01Sjason bus_space_read_4(sc->sc_bustag, sc->sc_regs, r)
1237f529f01Sjason
124c4071fd1Smillert int cs4231_match(struct device *, void *, void *);
125c4071fd1Smillert void cs4231_attach(struct device *, struct device *, void *);
126c4071fd1Smillert int cs4231_intr(void *);
1277f529f01Sjason
128c4071fd1Smillert int cs4231_set_speed(struct cs4231_softc *, u_long *);
129c4071fd1Smillert void cs4231_setup_output(struct cs4231_softc *sc);
1307f529f01Sjason
131c4071fd1Smillert void cs4231_write(struct cs4231_softc *, u_int8_t, u_int8_t);
132c4071fd1Smillert u_int8_t cs4231_read(struct cs4231_softc *, u_int8_t);
1337f529f01Sjason
1347f529f01Sjason /* Audio interface */
135c4071fd1Smillert int cs4231_open(void *, int);
136c4071fd1Smillert void cs4231_close(void *);
137c4071fd1Smillert int cs4231_set_params(void *, int, int, struct audio_params *,
138c4071fd1Smillert struct audio_params *);
139c4071fd1Smillert int cs4231_round_blocksize(void *, int);
140c4071fd1Smillert int cs4231_commit_settings(void *);
141c4071fd1Smillert int cs4231_halt_output(void *);
142c4071fd1Smillert int cs4231_halt_input(void *);
143c4071fd1Smillert int cs4231_set_port(void *, mixer_ctrl_t *);
144c4071fd1Smillert int cs4231_get_port(void *, mixer_ctrl_t *);
1456e4fde28Sjason int cs4231_query_devinfo(void *, mixer_devinfo_t *);
146c4071fd1Smillert void * cs4231_alloc(void *, int, size_t, int, int);
147c4071fd1Smillert void cs4231_free(void *, void *, int);
1484f9e30d0Smillert int cs4231_trigger_output(void *, void *, void *, int,
1496e4fde28Sjason void (*)(void *), void *, struct audio_params *);
1504f9e30d0Smillert int cs4231_trigger_input(void *, void *, void *, int,
1516e4fde28Sjason void (*)(void *), void *, struct audio_params *);
1527f529f01Sjason
1530d6a2fdeSmiod const struct audio_hw_if cs4231_sa_hw_if = {
1545a8d990aSkn .open = cs4231_open,
1555a8d990aSkn .close = cs4231_close,
1565a8d990aSkn .set_params = cs4231_set_params,
1575a8d990aSkn .round_blocksize = cs4231_round_blocksize,
1585a8d990aSkn .commit_settings = cs4231_commit_settings,
1595a8d990aSkn .halt_output = cs4231_halt_output,
1605a8d990aSkn .halt_input = cs4231_halt_input,
1615a8d990aSkn .set_port = cs4231_set_port,
1625a8d990aSkn .get_port = cs4231_get_port,
1635a8d990aSkn .query_devinfo = cs4231_query_devinfo,
1645a8d990aSkn .allocm = cs4231_alloc,
1655a8d990aSkn .freem = cs4231_free,
1665a8d990aSkn .trigger_output = cs4231_trigger_output,
1675a8d990aSkn .trigger_input = cs4231_trigger_input,
1687f529f01Sjason };
1697f529f01Sjason
17022e452dfSmpi const struct cfattach audiocs_ca = {
1717f529f01Sjason sizeof (struct cs4231_softc), cs4231_match, cs4231_attach
1727f529f01Sjason };
1737f529f01Sjason
1747f529f01Sjason struct cfdriver audiocs_cd = {
1757f529f01Sjason NULL, "audiocs", DV_DULL
1767f529f01Sjason };
1777f529f01Sjason
1787f529f01Sjason int
cs4231_match(struct device * parent,void * vcf,void * aux)1796e4fde28Sjason cs4231_match(struct device *parent, void *vcf, void *aux)
1807f529f01Sjason {
1817f529f01Sjason struct sbus_attach_args *sa = aux;
1827f529f01Sjason
1837f529f01Sjason return (strcmp("SUNW,CS4231", sa->sa_name) == 0);
1847f529f01Sjason }
1857f529f01Sjason
1867f529f01Sjason void
cs4231_attach(struct device * parent,struct device * self,void * aux)1876e4fde28Sjason cs4231_attach(struct device *parent, struct device *self, void *aux)
1887f529f01Sjason {
1897f529f01Sjason struct sbus_attach_args *sa = aux;
1907f529f01Sjason struct cs4231_softc *sc = (struct cs4231_softc *)self;
1917f529f01Sjason int node;
1927f529f01Sjason u_int32_t sbusburst, burst;
1937f529f01Sjason
1947f529f01Sjason node = sa->sa_node;
1957f529f01Sjason
1967f529f01Sjason /* Pass on the bus tags */
1977f529f01Sjason sc->sc_bustag = sa->sa_bustag;
1987f529f01Sjason sc->sc_dmatag = sa->sa_dmatag;
1997f529f01Sjason
2007f529f01Sjason /* Make sure things are sane. */
2017f529f01Sjason if (sa->sa_nintr != 1) {
2027f529f01Sjason printf(": expected 1 interrupt, got %d\n", sa->sa_nintr);
2037f529f01Sjason return;
2047f529f01Sjason }
2057f529f01Sjason if (sa->sa_nreg != 1) {
2067f529f01Sjason printf(": expected 1 register set, got %d\n",
2077f529f01Sjason sa->sa_nreg);
2087f529f01Sjason return;
2097f529f01Sjason }
2107f529f01Sjason
2117f529f01Sjason if (bus_intr_establish(sa->sa_bustag, sa->sa_pri, IPL_AUDIO, 0,
21285729938Shenric cs4231_intr, sc, self->dv_xname) == NULL) {
21349f9bd99Smiod printf(": couldn't establish interrupt, pri %d\n",
21449f9bd99Smiod INTLEV(sa->sa_pri));
2157f529f01Sjason return;
2167f529f01Sjason }
2177f529f01Sjason
2187f529f01Sjason if (sbus_bus_map(sa->sa_bustag,
219eb79e960Shenric sa->sa_reg[0].sbr_slot,
2207f529f01Sjason (bus_addr_t)sa->sa_reg[0].sbr_offset,
2217f529f01Sjason (bus_size_t)sa->sa_reg[0].sbr_size,
2227f529f01Sjason BUS_SPACE_MAP_LINEAR, 0, &sc->sc_regs) != 0) {
223eb79e960Shenric printf(": couldn't map registers\n");
2247f529f01Sjason return;
2257f529f01Sjason }
2267f529f01Sjason
2277f529f01Sjason sbusburst = ((struct sbus_softc *)parent)->sc_burst;
2287f529f01Sjason if (sbusburst == 0)
2297f529f01Sjason sbusburst = SBUS_BURST_32 - 1; /* 1->16 */
2307f529f01Sjason burst = getpropint(node, "burst-sizes", -1);
2317f529f01Sjason if (burst == -1)
2327f529f01Sjason burst = sbusburst;
2337f529f01Sjason sc->sc_burst = burst & sbusburst;
2347f529f01Sjason
235145b857fSjason printf("\n");
2367f529f01Sjason
2372baa08e2Santon audio_attach_mi(&cs4231_sa_hw_if, sc, NULL, &sc->sc_dev);
2387f529f01Sjason
2397f529f01Sjason /* Default to speaker, unmuted, reasonable volume */
2407f529f01Sjason sc->sc_out_port = CSPORT_SPEAKER;
2419820c8cbSjason sc->sc_in_port = CSPORT_MICROPHONE;
2427f529f01Sjason sc->sc_mute[CSPORT_SPEAKER] = 1;
2433562e709Sjason sc->sc_mute[CSPORT_MONITOR] = 1;
2447f529f01Sjason sc->sc_volume[CSPORT_SPEAKER].left = 192;
2457f529f01Sjason sc->sc_volume[CSPORT_SPEAKER].right = 192;
2467f529f01Sjason }
2477f529f01Sjason
2487f529f01Sjason /*
2493f51034aSjason * Write to one of the indexed registers of cs4231.
2507f529f01Sjason */
2517f529f01Sjason void
cs4231_write(struct cs4231_softc * sc,u_int8_t r,u_int8_t v)2526e4fde28Sjason cs4231_write(struct cs4231_softc *sc, u_int8_t r, u_int8_t v)
2537f529f01Sjason {
254a1f7da73Sjason CS_WRITE(sc, AD1848_IADDR, r);
255a1f7da73Sjason CS_WRITE(sc, AD1848_IDATA, v);
2567f529f01Sjason }
2577f529f01Sjason
2587f529f01Sjason /*
2593f51034aSjason * Read from one of the indexed registers of cs4231.
2607f529f01Sjason */
2617f529f01Sjason u_int8_t
cs4231_read(struct cs4231_softc * sc,u_int8_t r)2626e4fde28Sjason cs4231_read(struct cs4231_softc *sc, u_int8_t r)
2637f529f01Sjason {
264a1f7da73Sjason CS_WRITE(sc, AD1848_IADDR, r);
265a1f7da73Sjason return (CS_READ(sc, AD1848_IDATA));
2667f529f01Sjason }
2677f529f01Sjason
2687f529f01Sjason int
cs4231_set_speed(struct cs4231_softc * sc,u_long * argp)2696e4fde28Sjason cs4231_set_speed(struct cs4231_softc *sc, u_long *argp)
2707f529f01Sjason {
2717f529f01Sjason /*
2727f529f01Sjason * The available speeds are in the following table. Keep the speeds in
2737f529f01Sjason * the increasing order.
2747f529f01Sjason */
2757f529f01Sjason typedef struct {
2767f529f01Sjason int speed;
2777f529f01Sjason u_char bits;
2787f529f01Sjason } speed_struct;
2797f529f01Sjason u_long arg = *argp;
2807f529f01Sjason
2818f47c760Sjsg static const speed_struct speed_table[] = {
282a1f7da73Sjason {5510, (0 << 1) | CLOCK_XTAL2},
283a1f7da73Sjason {5510, (0 << 1) | CLOCK_XTAL2},
284a1f7da73Sjason {6620, (7 << 1) | CLOCK_XTAL2},
285a1f7da73Sjason {8000, (0 << 1) | CLOCK_XTAL1},
286a1f7da73Sjason {9600, (7 << 1) | CLOCK_XTAL1},
287a1f7da73Sjason {11025, (1 << 1) | CLOCK_XTAL2},
288a1f7da73Sjason {16000, (1 << 1) | CLOCK_XTAL1},
289a1f7da73Sjason {18900, (2 << 1) | CLOCK_XTAL2},
290a1f7da73Sjason {22050, (3 << 1) | CLOCK_XTAL2},
291a1f7da73Sjason {27420, (2 << 1) | CLOCK_XTAL1},
292a1f7da73Sjason {32000, (3 << 1) | CLOCK_XTAL1},
293a1f7da73Sjason {33075, (6 << 1) | CLOCK_XTAL2},
294a1f7da73Sjason {33075, (4 << 1) | CLOCK_XTAL2},
295a1f7da73Sjason {44100, (5 << 1) | CLOCK_XTAL2},
296a1f7da73Sjason {48000, (6 << 1) | CLOCK_XTAL1},
2977f529f01Sjason };
2987f529f01Sjason
2997f529f01Sjason int i, n, selected = -1;
3007f529f01Sjason
3017f529f01Sjason n = sizeof(speed_table) / sizeof(speed_struct);
3027f529f01Sjason
3037f529f01Sjason if (arg < speed_table[0].speed)
3047f529f01Sjason selected = 0;
3057f529f01Sjason if (arg > speed_table[n - 1].speed)
3067f529f01Sjason selected = n - 1;
3077f529f01Sjason
3087f529f01Sjason for (i = 1; selected == -1 && i < n; i++) {
3097f529f01Sjason if (speed_table[i].speed == arg)
3107f529f01Sjason selected = i;
3117f529f01Sjason else if (speed_table[i].speed > arg) {
3127f529f01Sjason int diff1, diff2;
3137f529f01Sjason
3147f529f01Sjason diff1 = arg - speed_table[i - 1].speed;
3157f529f01Sjason diff2 = speed_table[i].speed - arg;
3167f529f01Sjason if (diff1 < diff2)
3177f529f01Sjason selected = i - 1;
3187f529f01Sjason else
3197f529f01Sjason selected = i;
3207f529f01Sjason }
3217f529f01Sjason }
3227f529f01Sjason
3239820c8cbSjason if (selected == -1)
3247f529f01Sjason selected = 3;
3257f529f01Sjason
3267f529f01Sjason sc->sc_speed_bits = speed_table[selected].bits;
3277f529f01Sjason sc->sc_need_commit = 1;
3287f529f01Sjason *argp = speed_table[selected].speed;
3297f529f01Sjason
3307f529f01Sjason return (0);
3317f529f01Sjason }
3327f529f01Sjason
3337f529f01Sjason /*
3347f529f01Sjason * Audio interface functions
3357f529f01Sjason */
3367f529f01Sjason int
cs4231_open(void * vsc,int flags)3376e4fde28Sjason cs4231_open(void *vsc, int flags)
3387f529f01Sjason {
3396e4fde28Sjason struct cs4231_softc *sc = vsc;
3403f51034aSjason int tries;
3417f529f01Sjason
3427f529f01Sjason if (sc->sc_open)
3437f529f01Sjason return (EBUSY);
3447f529f01Sjason sc->sc_open = 1;
3459820c8cbSjason
3469820c8cbSjason sc->sc_capture.cs_intr = NULL;
3479820c8cbSjason sc->sc_capture.cs_arg = NULL;
3489820c8cbSjason sc->sc_capture.cs_locked = 0;
3499820c8cbSjason
3509820c8cbSjason sc->sc_playback.cs_intr = NULL;
3519820c8cbSjason sc->sc_playback.cs_arg = NULL;
3529820c8cbSjason sc->sc_playback.cs_locked = 0;
3537f529f01Sjason
3547f529f01Sjason APC_WRITE(sc, APC_CSR, APC_CSR_RESET);
3557f529f01Sjason DELAY(10);
3567f529f01Sjason APC_WRITE(sc, APC_CSR, 0);
3577f529f01Sjason DELAY(10);
3587f529f01Sjason APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) | APC_CSR_CODEC_RESET);
3597f529f01Sjason
3607f529f01Sjason DELAY(20);
3617f529f01Sjason
3627f529f01Sjason APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) & (~APC_CSR_CODEC_RESET));
3637f529f01Sjason
3643f51034aSjason for (tries = CS_TIMEOUT;
365a1f7da73Sjason tries && CS_READ(sc, AD1848_IADDR) == SP_IN_INIT; tries--)
3663f51034aSjason DELAY(10);
3673f51034aSjason if (tries == 0)
3683f51034aSjason printf("%s: timeout waiting for reset\n", sc->sc_dev.dv_xname);
3697f529f01Sjason
3703f51034aSjason /* Turn on cs4231 mode */
3717e0aa3e6Sjason cs4231_write(sc, SP_MISC_INFO,
372a1f7da73Sjason cs4231_read(sc, SP_MISC_INFO) | MODE2);
3737f529f01Sjason
3747f529f01Sjason cs4231_setup_output(sc);
375bf383a97Sjason
376bf383a97Sjason cs4231_write(sc, SP_PIN_CONTROL,
377bf383a97Sjason cs4231_read(sc, SP_PIN_CONTROL) | INTERRUPT_ENABLE);
378bf383a97Sjason
3797f529f01Sjason return (0);
3807f529f01Sjason }
3817f529f01Sjason
3827f529f01Sjason void
cs4231_setup_output(struct cs4231_softc * sc)3836e4fde28Sjason cs4231_setup_output(struct cs4231_softc *sc)
3847f529f01Sjason {
3853562e709Sjason u_int8_t pc, mi, rm, lm;
3867f529f01Sjason
3873562e709Sjason pc = cs4231_read(sc, SP_PIN_CONTROL) | CS_PC_HDPHMUTE | CS_PC_LINEMUTE;
3887f529f01Sjason
3893562e709Sjason mi = cs4231_read(sc, CS_MONO_IO_CONTROL) | MONO_OUTPUT_MUTE;
3903562e709Sjason
3913562e709Sjason lm = cs4231_read(sc, SP_LEFT_OUTPUT_CONTROL);
3923562e709Sjason lm &= ~OUTPUT_ATTEN_BITS;
3933562e709Sjason lm |= ((~(sc->sc_volume[CSPORT_SPEAKER].left >> 2)) &
3943562e709Sjason OUTPUT_ATTEN_BITS) | OUTPUT_MUTE;
3953562e709Sjason
3963562e709Sjason rm = cs4231_read(sc, SP_RIGHT_OUTPUT_CONTROL);
3973562e709Sjason rm &= ~OUTPUT_ATTEN_BITS;
3983562e709Sjason rm |= ((~(sc->sc_volume[CSPORT_SPEAKER].right >> 2)) &
3993562e709Sjason OUTPUT_ATTEN_BITS) | OUTPUT_MUTE;
4003562e709Sjason
4013562e709Sjason if (sc->sc_mute[CSPORT_MONITOR]) {
4023562e709Sjason lm &= ~OUTPUT_MUTE;
4033562e709Sjason rm &= ~OUTPUT_MUTE;
4043562e709Sjason }
4057f529f01Sjason
4067f529f01Sjason switch (sc->sc_out_port) {
4077f529f01Sjason case CSPORT_HEADPHONE:
4083562e709Sjason if (sc->sc_mute[CSPORT_SPEAKER])
4093562e709Sjason pc &= ~CS_PC_HDPHMUTE;
4107f529f01Sjason break;
4117f529f01Sjason case CSPORT_SPEAKER:
4123562e709Sjason if (sc->sc_mute[CSPORT_SPEAKER])
4133562e709Sjason mi &= ~MONO_OUTPUT_MUTE;
4147f529f01Sjason break;
4157f529f01Sjason case CSPORT_LINEOUT:
4163562e709Sjason if (sc->sc_mute[CSPORT_SPEAKER])
4173562e709Sjason pc &= ~CS_PC_LINEMUTE;
4187f529f01Sjason break;
4197f529f01Sjason }
4207f529f01Sjason
4213562e709Sjason cs4231_write(sc, SP_LEFT_OUTPUT_CONTROL, lm);
4223562e709Sjason cs4231_write(sc, SP_RIGHT_OUTPUT_CONTROL, rm);
4233562e709Sjason cs4231_write(sc, SP_PIN_CONTROL, pc);
4243562e709Sjason cs4231_write(sc, CS_MONO_IO_CONTROL, mi);
4259820c8cbSjason
4269820c8cbSjason /* XXX doesn't really belong here... */
4279820c8cbSjason switch (sc->sc_in_port) {
4289820c8cbSjason case CSPORT_LINEIN:
4299820c8cbSjason pc = LINE_INPUT;
4309820c8cbSjason break;
4319820c8cbSjason case CSPORT_AUX1:
4329820c8cbSjason pc = AUX_INPUT;
4339820c8cbSjason break;
4349820c8cbSjason case CSPORT_DAC:
4359820c8cbSjason pc = MIXED_DAC_INPUT;
4369820c8cbSjason break;
4379820c8cbSjason case CSPORT_MICROPHONE:
4389820c8cbSjason default:
4399820c8cbSjason pc = MIC_INPUT;
4409820c8cbSjason break;
4419820c8cbSjason }
4429820c8cbSjason lm = cs4231_read(sc, SP_LEFT_INPUT_CONTROL);
4439820c8cbSjason rm = cs4231_read(sc, SP_RIGHT_INPUT_CONTROL);
4449820c8cbSjason lm &= ~(MIXED_DAC_INPUT | ATTEN_22_5);
4459820c8cbSjason rm &= ~(MIXED_DAC_INPUT | ATTEN_22_5);
4469820c8cbSjason lm |= pc | (sc->sc_adc.left >> 4);
4479820c8cbSjason rm |= pc | (sc->sc_adc.right >> 4);
4489820c8cbSjason cs4231_write(sc, SP_LEFT_INPUT_CONTROL, lm);
4499820c8cbSjason cs4231_write(sc, SP_RIGHT_INPUT_CONTROL, rm);
4507f529f01Sjason }
4517f529f01Sjason
4527f529f01Sjason void
cs4231_close(void * vsc)4536e4fde28Sjason cs4231_close(void *vsc)
4547f529f01Sjason {
4556e4fde28Sjason struct cs4231_softc *sc = vsc;
4567f529f01Sjason
4577f529f01Sjason cs4231_halt_input(sc);
4587f529f01Sjason cs4231_halt_output(sc);
459bf383a97Sjason cs4231_write(sc, SP_PIN_CONTROL,
460bf383a97Sjason cs4231_read(sc, SP_PIN_CONTROL) & (~INTERRUPT_ENABLE));
4617f529f01Sjason sc->sc_open = 0;
4627f529f01Sjason }
4637f529f01Sjason
4647f529f01Sjason int
cs4231_set_params(void * vsc,int setmode,int usemode,struct audio_params * p,struct audio_params * r)4656e4fde28Sjason cs4231_set_params(void *vsc, int setmode, int usemode,
4666e4fde28Sjason struct audio_params *p, struct audio_params *r)
4677f529f01Sjason {
4686e4fde28Sjason struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
4699820c8cbSjason int err, bits, enc = p->encoding;
4707f529f01Sjason
4717f529f01Sjason switch (enc) {
4727f529f01Sjason case AUDIO_ENCODING_ULAW:
4739820c8cbSjason if (p->precision != 8)
4749820c8cbSjason return (EINVAL);
475a1f7da73Sjason bits = FMT_ULAW >> 5;
4767f529f01Sjason break;
4777f529f01Sjason case AUDIO_ENCODING_ALAW:
4789820c8cbSjason if (p->precision != 8)
4799820c8cbSjason return (EINVAL);
480a1f7da73Sjason bits = FMT_ALAW >> 5;
4817f529f01Sjason break;
4827f529f01Sjason case AUDIO_ENCODING_SLINEAR_LE:
483ac2bb4f3Sratchov if (p->precision == 16)
4849820c8cbSjason bits = FMT_TWOS_COMP >> 5;
485b1765456Sjason else
486b1765456Sjason return (EINVAL);
4879820c8cbSjason break;
4887f529f01Sjason case AUDIO_ENCODING_SLINEAR_BE:
489ac2bb4f3Sratchov if (p->precision == 16)
4909820c8cbSjason bits = FMT_TWOS_COMP_BE >> 5;
491b1765456Sjason else
492b1765456Sjason return (EINVAL);
4939820c8cbSjason break;
4947f529f01Sjason case AUDIO_ENCODING_ULINEAR_LE:
4959820c8cbSjason case AUDIO_ENCODING_ULINEAR_BE:
496b1765456Sjason if (p->precision == 8)
497b1765456Sjason bits = FMT_PCM8 >> 5;
498ac2bb4f3Sratchov else
499b1765456Sjason return (EINVAL);
5009820c8cbSjason break;
5017f529f01Sjason default:
5027f529f01Sjason return (EINVAL);
5037f529f01Sjason }
5047f529f01Sjason
5057f529f01Sjason if (p->channels != 1 && p->channels != 2)
5067f529f01Sjason return (EINVAL);
5077f529f01Sjason
5087f529f01Sjason err = cs4231_set_speed(sc, &p->sample_rate);
5097f529f01Sjason if (err)
5107f529f01Sjason return (err);
5117f529f01Sjason
51216579317Sjakemsr p->bps = AUDIO_BPS(p->precision);
51316579317Sjakemsr r->bps = AUDIO_BPS(r->precision);
51416579317Sjakemsr p->msb = r->msb = 1;
5157f529f01Sjason
5167f529f01Sjason sc->sc_format_bits = bits;
5177f529f01Sjason sc->sc_channels = p->channels;
5187f529f01Sjason sc->sc_precision = p->precision;
5197f529f01Sjason sc->sc_need_commit = 1;
5207f529f01Sjason return (0);
5217f529f01Sjason }
5227f529f01Sjason
5237f529f01Sjason int
cs4231_round_blocksize(void * vsc,int blk)5246e4fde28Sjason cs4231_round_blocksize(void *vsc, int blk)
5257f529f01Sjason {
526e7e79399Smickey return ((blk + 3) & (-4));
5277f529f01Sjason }
5287f529f01Sjason
5297f529f01Sjason int
cs4231_commit_settings(void * vsc)5306e4fde28Sjason cs4231_commit_settings(void *vsc)
5317f529f01Sjason {
5326e4fde28Sjason struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
533886882aaSratchov int tries;
5343562e709Sjason u_int8_t r, fs;
5357f529f01Sjason
5367f529f01Sjason if (sc->sc_need_commit == 0)
5377f529f01Sjason return (0);
5387f529f01Sjason
5393562e709Sjason fs = sc->sc_speed_bits | (sc->sc_format_bits << 5);
5403562e709Sjason if (sc->sc_channels == 2)
5413562e709Sjason fs |= FMT_STEREO;
5427f529f01Sjason
543886882aaSratchov /* XXX: this is called before DMA is setup, useful ? */
544886882aaSratchov mtx_enter(&audio_lock);
5457f529f01Sjason
546a1f7da73Sjason r = cs4231_read(sc, SP_INTERFACE_CONFIG) | AUTO_CAL_ENABLE;
547a1f7da73Sjason CS_WRITE(sc, AD1848_IADDR, MODE_CHANGE_ENABLE);
548a1f7da73Sjason CS_WRITE(sc, AD1848_IADDR, MODE_CHANGE_ENABLE | SP_INTERFACE_CONFIG);
549a1f7da73Sjason CS_WRITE(sc, AD1848_IDATA, r);
5503f51034aSjason
551a1f7da73Sjason CS_WRITE(sc, AD1848_IADDR, MODE_CHANGE_ENABLE | SP_CLOCK_DATA_FORMAT);
5523562e709Sjason CS_WRITE(sc, AD1848_IDATA, fs);
553a1f7da73Sjason CS_READ(sc, AD1848_IDATA);
554a1f7da73Sjason CS_READ(sc, AD1848_IDATA);
5553f51034aSjason tries = CS_TIMEOUT;
5563f51034aSjason for (tries = CS_TIMEOUT;
557a1f7da73Sjason tries && CS_READ(sc, AD1848_IADDR) == SP_IN_INIT; tries--)
5587f529f01Sjason DELAY(10);
5593f51034aSjason if (tries == 0)
5607f529f01Sjason printf("%s: timeout committing fspb\n", sc->sc_dev.dv_xname);
5617f529f01Sjason
562a1f7da73Sjason CS_WRITE(sc, AD1848_IADDR, MODE_CHANGE_ENABLE | CS_REC_FORMAT);
5633562e709Sjason CS_WRITE(sc, AD1848_IDATA, fs);
564a1f7da73Sjason CS_READ(sc, AD1848_IDATA);
565a1f7da73Sjason CS_READ(sc, AD1848_IDATA);
5663f51034aSjason for (tries = CS_TIMEOUT;
567a1f7da73Sjason tries && CS_READ(sc, AD1848_IADDR) == SP_IN_INIT; tries--)
5687f529f01Sjason DELAY(10);
5693f51034aSjason if (tries == 0)
5707f529f01Sjason printf("%s: timeout committing cdf\n", sc->sc_dev.dv_xname);
5717f529f01Sjason
572a1f7da73Sjason CS_WRITE(sc, AD1848_IADDR, 0);
5733f51034aSjason for (tries = CS_TIMEOUT;
574a1f7da73Sjason tries && CS_READ(sc, AD1848_IADDR) == SP_IN_INIT; tries--)
5753f51034aSjason DELAY(10);
5763f51034aSjason if (tries == 0)
5773f51034aSjason printf("%s: timeout waiting for !mce\n", sc->sc_dev.dv_xname);
5783f51034aSjason
579a1f7da73Sjason CS_WRITE(sc, AD1848_IADDR, SP_TEST_AND_INIT);
5803f51034aSjason for (tries = CS_TIMEOUT;
581a1f7da73Sjason tries && CS_READ(sc, AD1848_IDATA) & AUTO_CAL_IN_PROG; tries--)
5823f51034aSjason DELAY(10);
5833f51034aSjason if (tries == 0)
5843f51034aSjason printf("%s: timeout waiting for autocalibration\n",
5853f51034aSjason sc->sc_dev.dv_xname);
5867f529f01Sjason
587886882aaSratchov mtx_leave(&audio_lock);
5887f529f01Sjason
5897f529f01Sjason sc->sc_need_commit = 0;
5907f529f01Sjason return (0);
5917f529f01Sjason }
5927f529f01Sjason
5937f529f01Sjason int
cs4231_halt_output(void * vsc)5946e4fde28Sjason cs4231_halt_output(void *vsc)
5957f529f01Sjason {
5966e4fde28Sjason struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
5977f529f01Sjason
5989820c8cbSjason /* XXX Kills some capture bits */
599886882aaSratchov mtx_enter(&audio_lock);
6007f529f01Sjason APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) &
6017f529f01Sjason ~(APC_CSR_EI | APC_CSR_GIE | APC_CSR_PIE |
6027f529f01Sjason APC_CSR_EIE | APC_CSR_PDMA_GO | APC_CSR_PMIE));
6037e0aa3e6Sjason cs4231_write(sc, SP_INTERFACE_CONFIG,
604a1f7da73Sjason cs4231_read(sc, SP_INTERFACE_CONFIG) & (~PLAYBACK_ENABLE));
6059820c8cbSjason sc->sc_playback.cs_locked = 0;
606886882aaSratchov mtx_leave(&audio_lock);
6077f529f01Sjason return (0);
6087f529f01Sjason }
6097f529f01Sjason
6107f529f01Sjason int
cs4231_halt_input(void * vsc)6116e4fde28Sjason cs4231_halt_input(void *vsc)
6127f529f01Sjason {
6136e4fde28Sjason struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
6147f529f01Sjason
6159820c8cbSjason /* XXX Kills some playback bits */
616886882aaSratchov mtx_enter(&audio_lock);
6177f529f01Sjason APC_WRITE(sc, APC_CSR, APC_CSR_CAPTURE_PAUSE);
6187e0aa3e6Sjason cs4231_write(sc, SP_INTERFACE_CONFIG,
619a1f7da73Sjason cs4231_read(sc, SP_INTERFACE_CONFIG) & (~CAPTURE_ENABLE));
6209820c8cbSjason sc->sc_capture.cs_locked = 0;
621886882aaSratchov mtx_leave(&audio_lock);
6227f529f01Sjason return (0);
6237f529f01Sjason }
6247f529f01Sjason
6257f529f01Sjason int
cs4231_set_port(void * vsc,mixer_ctrl_t * cp)6266e4fde28Sjason cs4231_set_port(void *vsc, mixer_ctrl_t *cp)
6277f529f01Sjason {
6286e4fde28Sjason struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
6297f529f01Sjason int error = EINVAL;
6307f529f01Sjason
6317f529f01Sjason DPRINTF(("cs4231_set_port: port=%d type=%d\n", cp->dev, cp->type));
6327f529f01Sjason
6337f529f01Sjason switch (cp->dev) {
6347f529f01Sjason case CSAUDIO_DAC_LVL:
6357f529f01Sjason if (cp->type != AUDIO_MIXER_VALUE)
6367f529f01Sjason break;
6377f529f01Sjason if (cp->un.value.num_channels == 1)
6387e0aa3e6Sjason cs4231_write(sc, SP_LEFT_AUX1_CONTROL,
6397f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] &
640a1f7da73Sjason LINE_INPUT_ATTEN_BITS);
6417f529f01Sjason else if (cp->un.value.num_channels == 2) {
6427e0aa3e6Sjason cs4231_write(sc, SP_LEFT_AUX1_CONTROL,
6437f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] &
644a1f7da73Sjason LINE_INPUT_ATTEN_BITS);
6457e0aa3e6Sjason cs4231_write(sc, SP_RIGHT_AUX1_CONTROL,
6467f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] &
647a1f7da73Sjason LINE_INPUT_ATTEN_BITS);
6487f529f01Sjason } else
6497f529f01Sjason break;
6507f529f01Sjason error = 0;
6517f529f01Sjason break;
6527f529f01Sjason case CSAUDIO_LINE_IN_LVL:
6537f529f01Sjason if (cp->type != AUDIO_MIXER_VALUE)
6547f529f01Sjason break;
6557f529f01Sjason if (cp->un.value.num_channels == 1)
6567e0aa3e6Sjason cs4231_write(sc, CS_LEFT_LINE_CONTROL,
6577f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] &
658a1f7da73Sjason AUX_INPUT_ATTEN_BITS);
6597f529f01Sjason else if (cp->un.value.num_channels == 2) {
6607e0aa3e6Sjason cs4231_write(sc, CS_LEFT_LINE_CONTROL,
6617f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] &
662a1f7da73Sjason AUX_INPUT_ATTEN_BITS);
6637e0aa3e6Sjason cs4231_write(sc, CS_RIGHT_LINE_CONTROL,
6647f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] &
665a1f7da73Sjason AUX_INPUT_ATTEN_BITS);
6667f529f01Sjason } else
6677f529f01Sjason break;
6687f529f01Sjason error = 0;
6697f529f01Sjason break;
6707f529f01Sjason case CSAUDIO_MIC_LVL:
6717f529f01Sjason if (cp->type != AUDIO_MIXER_VALUE)
6727f529f01Sjason break;
6737f529f01Sjason if (cp->un.value.num_channels == 1) {
6747f529f01Sjason #if 0
6757e0aa3e6Sjason cs4231_write(sc, CS_MONO_IO_CONTROL,
6767f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] &
677a1f7da73Sjason MONO_INPUT_ATTEN_BITS);
6787f529f01Sjason #endif
6797f529f01Sjason } else
6807f529f01Sjason break;
6817f529f01Sjason error = 0;
6827f529f01Sjason break;
6837f529f01Sjason case CSAUDIO_CD_LVL:
6847f529f01Sjason if (cp->type != AUDIO_MIXER_VALUE)
6857f529f01Sjason break;
6867f529f01Sjason if (cp->un.value.num_channels == 1) {
6877e0aa3e6Sjason cs4231_write(sc, SP_LEFT_AUX2_CONTROL,
6887f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] &
689a1f7da73Sjason LINE_INPUT_ATTEN_BITS);
6907f529f01Sjason } else if (cp->un.value.num_channels == 2) {
6917e0aa3e6Sjason cs4231_write(sc, SP_LEFT_AUX2_CONTROL,
6927f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] &
693a1f7da73Sjason LINE_INPUT_ATTEN_BITS);
6947e0aa3e6Sjason cs4231_write(sc, SP_RIGHT_AUX2_CONTROL,
6957f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] &
696a1f7da73Sjason LINE_INPUT_ATTEN_BITS);
6977f529f01Sjason } else
6987f529f01Sjason break;
6997f529f01Sjason error = 0;
7007f529f01Sjason break;
7017f529f01Sjason case CSAUDIO_MONITOR_LVL:
7027f529f01Sjason if (cp->type != AUDIO_MIXER_VALUE)
7037f529f01Sjason break;
7047f529f01Sjason if (cp->un.value.num_channels == 1)
7057e0aa3e6Sjason cs4231_write(sc, SP_DIGITAL_MIX,
7067f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] << 2);
7077f529f01Sjason else
7087f529f01Sjason break;
7097f529f01Sjason error = 0;
7107f529f01Sjason break;
7117f529f01Sjason case CSAUDIO_OUTPUT_LVL:
7127f529f01Sjason if (cp->type != AUDIO_MIXER_VALUE)
7137f529f01Sjason break;
7147f529f01Sjason if (cp->un.value.num_channels == 1) {
7157f529f01Sjason sc->sc_volume[CSPORT_SPEAKER].left =
7167f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
7177f529f01Sjason sc->sc_volume[CSPORT_SPEAKER].right =
7187f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
7197f529f01Sjason }
7207f529f01Sjason else if (cp->un.value.num_channels == 2) {
7217f529f01Sjason sc->sc_volume[CSPORT_SPEAKER].left =
7227f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
7237f529f01Sjason sc->sc_volume[CSPORT_SPEAKER].right =
7247f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
7257f529f01Sjason }
7267f529f01Sjason else
7277f529f01Sjason break;
7287f529f01Sjason
7297f529f01Sjason cs4231_setup_output(sc);
7307f529f01Sjason error = 0;
7317f529f01Sjason break;
7327f529f01Sjason case CSAUDIO_OUTPUT:
7339820c8cbSjason if (cp->type != AUDIO_MIXER_ENUM)
7349820c8cbSjason break;
7357f529f01Sjason if (cp->un.ord != CSPORT_LINEOUT &&
7367f529f01Sjason cp->un.ord != CSPORT_SPEAKER &&
7377f529f01Sjason cp->un.ord != CSPORT_HEADPHONE)
7387f529f01Sjason return (EINVAL);
7397f529f01Sjason sc->sc_out_port = cp->un.ord;
7407f529f01Sjason cs4231_setup_output(sc);
7417f529f01Sjason error = 0;
7427f529f01Sjason break;
7437f529f01Sjason case CSAUDIO_LINE_IN_MUTE:
7447f529f01Sjason if (cp->type != AUDIO_MIXER_ENUM)
7457f529f01Sjason break;
7467f529f01Sjason sc->sc_mute[CSPORT_LINEIN] = cp->un.ord ? 1 : 0;
7477f529f01Sjason error = 0;
7487f529f01Sjason break;
7497f529f01Sjason case CSAUDIO_DAC_MUTE:
7507f529f01Sjason if (cp->type != AUDIO_MIXER_ENUM)
7517f529f01Sjason break;
7527f529f01Sjason sc->sc_mute[CSPORT_AUX1] = cp->un.ord ? 1 : 0;
7537f529f01Sjason error = 0;
7547f529f01Sjason break;
7557f529f01Sjason case CSAUDIO_CD_MUTE:
7567f529f01Sjason if (cp->type != AUDIO_MIXER_ENUM)
7577f529f01Sjason break;
7587f529f01Sjason sc->sc_mute[CSPORT_AUX2] = cp->un.ord ? 1 : 0;
7597f529f01Sjason error = 0;
7607f529f01Sjason break;
7617f529f01Sjason case CSAUDIO_MIC_MUTE:
7627f529f01Sjason if (cp->type != AUDIO_MIXER_ENUM)
7637f529f01Sjason break;
7647f529f01Sjason sc->sc_mute[CSPORT_MONO] = cp->un.ord ? 1 : 0;
7657f529f01Sjason error = 0;
7667f529f01Sjason break;
7677f529f01Sjason case CSAUDIO_MONITOR_MUTE:
7687f529f01Sjason if (cp->type != AUDIO_MIXER_ENUM)
7697f529f01Sjason break;
7707f529f01Sjason sc->sc_mute[CSPORT_MONITOR] = cp->un.ord ? 1 : 0;
7717f529f01Sjason error = 0;
7727f529f01Sjason break;
7737f529f01Sjason case CSAUDIO_OUTPUT_MUTE:
7747f529f01Sjason if (cp->type != AUDIO_MIXER_ENUM)
7757f529f01Sjason break;
7767f529f01Sjason sc->sc_mute[CSPORT_SPEAKER] = cp->un.ord ? 1 : 0;
7777f529f01Sjason cs4231_setup_output(sc);
7787f529f01Sjason error = 0;
7797f529f01Sjason break;
7807f529f01Sjason case CSAUDIO_REC_LVL:
7817f529f01Sjason if (cp->type != AUDIO_MIXER_VALUE)
7827f529f01Sjason break;
7839820c8cbSjason if (cp->un.value.num_channels == 1) {
7849820c8cbSjason sc->sc_adc.left =
7859820c8cbSjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
7869820c8cbSjason sc->sc_adc.right =
7879820c8cbSjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
7889820c8cbSjason } else if (cp->un.value.num_channels == 2) {
7899820c8cbSjason sc->sc_adc.left =
7909820c8cbSjason cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
7919820c8cbSjason sc->sc_adc.right =
7929820c8cbSjason cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
7939820c8cbSjason } else
7949820c8cbSjason break;
7959820c8cbSjason cs4231_setup_output(sc);
7969820c8cbSjason error = 0;
7977f529f01Sjason break;
7987f529f01Sjason case CSAUDIO_RECORD_SOURCE:
7997f529f01Sjason if (cp->type != AUDIO_MIXER_ENUM)
8007f529f01Sjason break;
8019820c8cbSjason if (cp->un.ord == CSPORT_MICROPHONE ||
8029820c8cbSjason cp->un.ord == CSPORT_LINEIN ||
8039820c8cbSjason cp->un.ord == CSPORT_AUX1 ||
8049820c8cbSjason cp->un.ord == CSPORT_DAC) {
8059820c8cbSjason sc->sc_in_port = cp->un.ord;
8069820c8cbSjason error = 0;
8079820c8cbSjason cs4231_setup_output(sc);
8089820c8cbSjason }
8097f529f01Sjason break;
8107f529f01Sjason }
8117f529f01Sjason
8127f529f01Sjason return (error);
8137f529f01Sjason }
8147f529f01Sjason
8157f529f01Sjason int
cs4231_get_port(void * vsc,mixer_ctrl_t * cp)8166e4fde28Sjason cs4231_get_port(void *vsc, mixer_ctrl_t *cp)
8177f529f01Sjason {
8186e4fde28Sjason struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
8197f529f01Sjason int error = EINVAL;
8207f529f01Sjason
8217f529f01Sjason DPRINTF(("cs4231_get_port: port=%d type=%d\n", cp->dev, cp->type));
8227f529f01Sjason
8237f529f01Sjason switch (cp->dev) {
8247f529f01Sjason case CSAUDIO_DAC_LVL:
8257f529f01Sjason if (cp->type != AUDIO_MIXER_VALUE)
8267f529f01Sjason break;
8277f529f01Sjason if (cp->un.value.num_channels == 1)
8287f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]=
8297e0aa3e6Sjason cs4231_read(sc, SP_LEFT_AUX1_CONTROL) &
830a1f7da73Sjason LINE_INPUT_ATTEN_BITS;
8317f529f01Sjason else if (cp->un.value.num_channels == 2) {
8327f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
8337e0aa3e6Sjason cs4231_read(sc, SP_LEFT_AUX1_CONTROL) &
834a1f7da73Sjason LINE_INPUT_ATTEN_BITS;
8357f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
8367e0aa3e6Sjason cs4231_read(sc, SP_RIGHT_AUX1_CONTROL) &
837a1f7da73Sjason LINE_INPUT_ATTEN_BITS;
8387f529f01Sjason } else
8397f529f01Sjason break;
8407f529f01Sjason error = 0;
8417f529f01Sjason break;
8427f529f01Sjason case CSAUDIO_LINE_IN_LVL:
8437f529f01Sjason if (cp->type != AUDIO_MIXER_VALUE)
8447f529f01Sjason break;
8457f529f01Sjason if (cp->un.value.num_channels == 1)
8467f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
847a1f7da73Sjason cs4231_read(sc, CS_LEFT_LINE_CONTROL) & AUX_INPUT_ATTEN_BITS;
8487f529f01Sjason else if (cp->un.value.num_channels == 2) {
8497f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
850a1f7da73Sjason cs4231_read(sc, CS_LEFT_LINE_CONTROL) & AUX_INPUT_ATTEN_BITS;
8517f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
852a1f7da73Sjason cs4231_read(sc, CS_RIGHT_LINE_CONTROL) & AUX_INPUT_ATTEN_BITS;
8537f529f01Sjason } else
8547f529f01Sjason break;
8557f529f01Sjason error = 0;
8567f529f01Sjason break;
8577f529f01Sjason case CSAUDIO_MIC_LVL:
8587f529f01Sjason if (cp->type != AUDIO_MIXER_VALUE)
8597f529f01Sjason break;
8607f529f01Sjason if (cp->un.value.num_channels == 1) {
8617f529f01Sjason #if 0
8627f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
8637e0aa3e6Sjason cs4231_read(sc, CS_MONO_IO_CONTROL) &
864a1f7da73Sjason MONO_INPUT_ATTEN_BITS;
8657f529f01Sjason #endif
8667f529f01Sjason } else
8677f529f01Sjason break;
8687f529f01Sjason error = 0;
8697f529f01Sjason break;
8707f529f01Sjason case CSAUDIO_CD_LVL:
8717f529f01Sjason if (cp->type != AUDIO_MIXER_VALUE)
8727f529f01Sjason break;
8737f529f01Sjason if (cp->un.value.num_channels == 1)
8747f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
8757e0aa3e6Sjason cs4231_read(sc, SP_LEFT_AUX2_CONTROL) &
876a1f7da73Sjason LINE_INPUT_ATTEN_BITS;
8777f529f01Sjason else if (cp->un.value.num_channels == 2) {
8787f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
8797e0aa3e6Sjason cs4231_read(sc, SP_LEFT_AUX2_CONTROL) &
880a1f7da73Sjason LINE_INPUT_ATTEN_BITS;
8817f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
8827e0aa3e6Sjason cs4231_read(sc, SP_RIGHT_AUX2_CONTROL) &
883a1f7da73Sjason LINE_INPUT_ATTEN_BITS;
8847f529f01Sjason }
8857f529f01Sjason else
8867f529f01Sjason break;
8877f529f01Sjason error = 0;
8887f529f01Sjason break;
8897f529f01Sjason case CSAUDIO_MONITOR_LVL:
8907f529f01Sjason if (cp->type != AUDIO_MIXER_VALUE)
8917f529f01Sjason break;
8927f529f01Sjason if (cp->un.value.num_channels != 1)
8937f529f01Sjason break;
8947f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
8957e0aa3e6Sjason cs4231_read(sc, SP_DIGITAL_MIX) >> 2;
8967f529f01Sjason error = 0;
8977f529f01Sjason break;
8987f529f01Sjason case CSAUDIO_OUTPUT_LVL:
8997f529f01Sjason if (cp->type != AUDIO_MIXER_VALUE)
9007f529f01Sjason break;
9017f529f01Sjason if (cp->un.value.num_channels == 1)
9027f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
9037f529f01Sjason sc->sc_volume[CSPORT_SPEAKER].left;
9047f529f01Sjason else if (cp->un.value.num_channels == 2) {
9057f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
9067f529f01Sjason sc->sc_volume[CSPORT_SPEAKER].left;
9077f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
9087f529f01Sjason sc->sc_volume[CSPORT_SPEAKER].right;
9097f529f01Sjason }
9107f529f01Sjason else
9117f529f01Sjason break;
9127f529f01Sjason error = 0;
9137f529f01Sjason break;
9147f529f01Sjason case CSAUDIO_LINE_IN_MUTE:
9157f529f01Sjason if (cp->type != AUDIO_MIXER_ENUM)
9167f529f01Sjason break;
9177f529f01Sjason cp->un.ord = sc->sc_mute[CSPORT_LINEIN] ? 1 : 0;
9187f529f01Sjason error = 0;
9197f529f01Sjason break;
9207f529f01Sjason case CSAUDIO_DAC_MUTE:
9217f529f01Sjason if (cp->type != AUDIO_MIXER_ENUM)
9227f529f01Sjason break;
9237f529f01Sjason cp->un.ord = sc->sc_mute[CSPORT_AUX1] ? 1 : 0;
9247f529f01Sjason error = 0;
9257f529f01Sjason break;
9267f529f01Sjason case CSAUDIO_CD_MUTE:
9277f529f01Sjason if (cp->type != AUDIO_MIXER_ENUM)
9287f529f01Sjason break;
9297f529f01Sjason cp->un.ord = sc->sc_mute[CSPORT_AUX2] ? 1 : 0;
9307f529f01Sjason error = 0;
9317f529f01Sjason break;
9327f529f01Sjason case CSAUDIO_MIC_MUTE:
9337f529f01Sjason if (cp->type != AUDIO_MIXER_ENUM)
9347f529f01Sjason break;
9357f529f01Sjason cp->un.ord = sc->sc_mute[CSPORT_MONO] ? 1 : 0;
9367f529f01Sjason error = 0;
9377f529f01Sjason break;
9387f529f01Sjason case CSAUDIO_MONITOR_MUTE:
9397f529f01Sjason if (cp->type != AUDIO_MIXER_ENUM)
9407f529f01Sjason break;
9417f529f01Sjason cp->un.ord = sc->sc_mute[CSPORT_MONITOR] ? 1 : 0;
9427f529f01Sjason error = 0;
9437f529f01Sjason break;
9447f529f01Sjason case CSAUDIO_OUTPUT_MUTE:
9457f529f01Sjason if (cp->type != AUDIO_MIXER_ENUM)
9467f529f01Sjason break;
9477f529f01Sjason cp->un.ord = sc->sc_mute[CSPORT_SPEAKER] ? 1 : 0;
9487f529f01Sjason error = 0;
9497f529f01Sjason break;
9507f529f01Sjason case CSAUDIO_REC_LVL:
9517f529f01Sjason if (cp->type != AUDIO_MIXER_VALUE)
9527f529f01Sjason break;
9537f529f01Sjason if (cp->un.value.num_channels == 1) {
9547f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
9559820c8cbSjason sc->sc_adc.left;
9567f529f01Sjason } else if (cp->un.value.num_channels == 2) {
9577f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
9589820c8cbSjason sc->sc_adc.left;
9597f529f01Sjason cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
9609820c8cbSjason sc->sc_adc.right;
9617f529f01Sjason } else
9627f529f01Sjason break;
9637f529f01Sjason error = 0;
9647f529f01Sjason break;
9657f529f01Sjason case CSAUDIO_RECORD_SOURCE:
9669820c8cbSjason if (cp->type != AUDIO_MIXER_ENUM)
9679820c8cbSjason break;
9689820c8cbSjason cp->un.ord = sc->sc_in_port;
9697f529f01Sjason error = 0;
9707f529f01Sjason break;
9717f529f01Sjason case CSAUDIO_OUTPUT:
9729820c8cbSjason if (cp->type != AUDIO_MIXER_ENUM)
9739820c8cbSjason break;
9747f529f01Sjason cp->un.ord = sc->sc_out_port;
9757f529f01Sjason error = 0;
9767f529f01Sjason break;
9777f529f01Sjason }
9787f529f01Sjason return (error);
9797f529f01Sjason }
9807f529f01Sjason
9817f529f01Sjason int
cs4231_query_devinfo(void * vsc,mixer_devinfo_t * dip)9826e4fde28Sjason cs4231_query_devinfo(void *vsc, mixer_devinfo_t *dip)
9837f529f01Sjason {
9847f529f01Sjason int err = 0;
9857f529f01Sjason
9867f529f01Sjason switch (dip->index) {
9877f529f01Sjason case CSAUDIO_MIC_LVL: /* mono/microphone mixer */
9887f529f01Sjason dip->type = AUDIO_MIXER_VALUE;
9897f529f01Sjason dip->mixer_class = CSAUDIO_INPUT_CLASS;
9907f529f01Sjason dip->prev = AUDIO_MIXER_LAST;
9917f529f01Sjason dip->next = CSAUDIO_MIC_MUTE;
9922627362dSho strlcpy(dip->label.name, AudioNmicrophone,
9932627362dSho sizeof dip->label.name);
9947f529f01Sjason dip->un.v.num_channels = 1;
9952627362dSho strlcpy(dip->un.v.units.name, AudioNvolume,
9962627362dSho sizeof dip->un.v.units.name);
9977f529f01Sjason break;
9987f529f01Sjason case CSAUDIO_DAC_LVL: /* dacout */
9997f529f01Sjason dip->type = AUDIO_MIXER_VALUE;
10007f529f01Sjason dip->mixer_class = CSAUDIO_INPUT_CLASS;
10017f529f01Sjason dip->prev = AUDIO_MIXER_LAST;
10027f529f01Sjason dip->next = CSAUDIO_DAC_MUTE;
10032627362dSho strlcpy(dip->label.name, AudioNdac,
10042627362dSho sizeof dip->label.name);
10057f529f01Sjason dip->un.v.num_channels = 2;
10062627362dSho strlcpy(dip->un.v.units.name, AudioNvolume,
10072627362dSho sizeof dip->un.v.units.name);
10087f529f01Sjason break;
10097f529f01Sjason case CSAUDIO_LINE_IN_LVL: /* line */
10107f529f01Sjason dip->type = AUDIO_MIXER_VALUE;
10117f529f01Sjason dip->mixer_class = CSAUDIO_INPUT_CLASS;
10127f529f01Sjason dip->prev = AUDIO_MIXER_LAST;
10137f529f01Sjason dip->next = CSAUDIO_LINE_IN_MUTE;
10142627362dSho strlcpy(dip->label.name, AudioNline, sizeof dip->label.name);
10157f529f01Sjason dip->un.v.num_channels = 2;
10162627362dSho strlcpy(dip->un.v.units.name, AudioNvolume,
10172627362dSho sizeof dip->un.v.units.name);
10187f529f01Sjason break;
10197f529f01Sjason case CSAUDIO_CD_LVL: /* cd */
10207f529f01Sjason dip->type = AUDIO_MIXER_VALUE;
10217f529f01Sjason dip->mixer_class = CSAUDIO_INPUT_CLASS;
10227f529f01Sjason dip->prev = AUDIO_MIXER_LAST;
10237f529f01Sjason dip->next = CSAUDIO_CD_MUTE;
10242627362dSho strlcpy(dip->label.name, AudioNcd, sizeof dip->label.name);
10257f529f01Sjason dip->un.v.num_channels = 2;
10262627362dSho strlcpy(dip->un.v.units.name, AudioNvolume,
10272627362dSho sizeof dip->un.v.units.name);
10287f529f01Sjason break;
10297f529f01Sjason case CSAUDIO_MONITOR_LVL: /* monitor level */
10307f529f01Sjason dip->type = AUDIO_MIXER_VALUE;
10317f529f01Sjason dip->mixer_class = CSAUDIO_MONITOR_CLASS;
10327f529f01Sjason dip->prev = AUDIO_MIXER_LAST;
10337f529f01Sjason dip->next = CSAUDIO_MONITOR_MUTE;
10342627362dSho strlcpy(dip->label.name, AudioNmonitor,
10352627362dSho sizeof dip->label.name);
10367f529f01Sjason dip->un.v.num_channels = 1;
10372627362dSho strlcpy(dip->un.v.units.name, AudioNvolume,
10382627362dSho sizeof dip->un.v.units.name);
10397f529f01Sjason break;
10407f529f01Sjason case CSAUDIO_OUTPUT_LVL:
10417f529f01Sjason dip->type = AUDIO_MIXER_VALUE;
10427f529f01Sjason dip->mixer_class = CSAUDIO_OUTPUT_CLASS;
10437f529f01Sjason dip->prev = AUDIO_MIXER_LAST;
10447f529f01Sjason dip->next = CSAUDIO_OUTPUT_MUTE;
10452627362dSho strlcpy(dip->label.name, AudioNoutput, sizeof dip->label.name);
10467f529f01Sjason dip->un.v.num_channels = 2;
10472627362dSho strlcpy(dip->un.v.units.name, AudioNvolume,
10482627362dSho sizeof dip->un.v.units.name);
10497f529f01Sjason break;
10507f529f01Sjason case CSAUDIO_LINE_IN_MUTE:
10517f529f01Sjason dip->type = AUDIO_MIXER_ENUM;
10527f529f01Sjason dip->mixer_class = CSAUDIO_INPUT_CLASS;
10537f529f01Sjason dip->prev = CSAUDIO_LINE_IN_LVL;
10547f529f01Sjason dip->next = AUDIO_MIXER_LAST;
10557f529f01Sjason goto mute;
10567f529f01Sjason case CSAUDIO_DAC_MUTE:
10577f529f01Sjason dip->type = AUDIO_MIXER_ENUM;
10587f529f01Sjason dip->mixer_class = CSAUDIO_INPUT_CLASS;
10597f529f01Sjason dip->prev = CSAUDIO_DAC_LVL;
10607f529f01Sjason dip->next = AUDIO_MIXER_LAST;
10617f529f01Sjason goto mute;
10627f529f01Sjason case CSAUDIO_CD_MUTE:
10637f529f01Sjason dip->type = AUDIO_MIXER_ENUM;
10647f529f01Sjason dip->mixer_class = CSAUDIO_INPUT_CLASS;
10657f529f01Sjason dip->prev = CSAUDIO_CD_LVL;
10667f529f01Sjason dip->next = AUDIO_MIXER_LAST;
10677f529f01Sjason goto mute;
10687f529f01Sjason case CSAUDIO_MIC_MUTE:
10697f529f01Sjason dip->type = AUDIO_MIXER_ENUM;
10707f529f01Sjason dip->mixer_class = CSAUDIO_INPUT_CLASS;
10717f529f01Sjason dip->prev = CSAUDIO_MIC_LVL;
10727f529f01Sjason dip->next = AUDIO_MIXER_LAST;
10737f529f01Sjason goto mute;
10747f529f01Sjason case CSAUDIO_MONITOR_MUTE:
10757f529f01Sjason dip->type = AUDIO_MIXER_ENUM;
10767f529f01Sjason dip->mixer_class = CSAUDIO_OUTPUT_CLASS;
10777f529f01Sjason dip->prev = CSAUDIO_MONITOR_LVL;
10787f529f01Sjason dip->next = AUDIO_MIXER_LAST;
10797f529f01Sjason goto mute;
10807f529f01Sjason case CSAUDIO_OUTPUT_MUTE:
10817f529f01Sjason dip->type = AUDIO_MIXER_ENUM;
10827f529f01Sjason dip->mixer_class = CSAUDIO_OUTPUT_CLASS;
10837f529f01Sjason dip->prev = CSAUDIO_OUTPUT_LVL;
10847f529f01Sjason dip->next = AUDIO_MIXER_LAST;
10857f529f01Sjason goto mute;
10867f529f01Sjason
10877f529f01Sjason mute:
10882627362dSho strlcpy(dip->label.name, AudioNmute, sizeof dip->label.name);
10897f529f01Sjason dip->un.e.num_mem = 2;
10902627362dSho strlcpy(dip->un.e.member[0].label.name, AudioNon,
10912627362dSho sizeof dip->un.e.member[0].label.name);
10927f529f01Sjason dip->un.e.member[0].ord = 0;
10932627362dSho strlcpy(dip->un.e.member[1].label.name, AudioNoff,
10942627362dSho sizeof dip->un.e.member[1].label.name);
10957f529f01Sjason dip->un.e.member[1].ord = 1;
10967f529f01Sjason break;
10977f529f01Sjason case CSAUDIO_REC_LVL: /* record level */
10987f529f01Sjason dip->type = AUDIO_MIXER_VALUE;
10997f529f01Sjason dip->mixer_class = CSAUDIO_RECORD_CLASS;
11007f529f01Sjason dip->prev = AUDIO_MIXER_LAST;
11017f529f01Sjason dip->next = CSAUDIO_RECORD_SOURCE;
11022627362dSho strlcpy(dip->label.name, AudioNrecord, sizeof dip->label.name);
11037f529f01Sjason dip->un.v.num_channels = 2;
11042627362dSho strlcpy(dip->un.v.units.name, AudioNvolume,
11052627362dSho sizeof dip->un.v.units.name);
11067f529f01Sjason break;
11077f529f01Sjason case CSAUDIO_RECORD_SOURCE:
11087f529f01Sjason dip->type = AUDIO_MIXER_ENUM;
11097f529f01Sjason dip->mixer_class = CSAUDIO_RECORD_CLASS;
11107f529f01Sjason dip->prev = CSAUDIO_REC_LVL;
11117f529f01Sjason dip->next = AUDIO_MIXER_LAST;
11122627362dSho strlcpy(dip->label.name, AudioNsource, sizeof dip->label.name);
11139820c8cbSjason dip->un.e.num_mem = 4;
11142627362dSho strlcpy(dip->un.e.member[0].label.name, AudioNmicrophone,
11152627362dSho sizeof dip->un.e.member[0].label.name);
11169820c8cbSjason dip->un.e.member[0].ord = CSPORT_MICROPHONE;
11172627362dSho strlcpy(dip->un.e.member[1].label.name, AudioNline,
11182627362dSho sizeof dip->un.e.member[1].label.name);
11199820c8cbSjason dip->un.e.member[1].ord = CSPORT_LINEIN;
11202627362dSho strlcpy(dip->un.e.member[2].label.name, AudioNcd,
11212627362dSho sizeof dip->un.e.member[2].label.name);
11229820c8cbSjason dip->un.e.member[2].ord = CSPORT_AUX1;
11232627362dSho strlcpy(dip->un.e.member[3].label.name, AudioNdac,
11242627362dSho sizeof dip->un.e.member[3].label.name);
11259820c8cbSjason dip->un.e.member[3].ord = CSPORT_DAC;
11267f529f01Sjason break;
11277f529f01Sjason case CSAUDIO_OUTPUT:
11287f529f01Sjason dip->type = AUDIO_MIXER_ENUM;
11297f529f01Sjason dip->mixer_class = CSAUDIO_MONITOR_CLASS;
11307f529f01Sjason dip->prev = dip->next = AUDIO_MIXER_LAST;
11312627362dSho strlcpy(dip->label.name, AudioNoutput, sizeof dip->label.name);
11327f529f01Sjason dip->un.e.num_mem = 3;
11332627362dSho strlcpy(dip->un.e.member[0].label.name, AudioNspeaker,
11342627362dSho sizeof dip->un.e.member[0].label.name);
11357f529f01Sjason dip->un.e.member[0].ord = CSPORT_SPEAKER;
11362627362dSho strlcpy(dip->un.e.member[1].label.name, AudioNline,
11372627362dSho sizeof dip->un.e.member[1].label.name);
11387f529f01Sjason dip->un.e.member[1].ord = CSPORT_LINEOUT;
11392627362dSho strlcpy(dip->un.e.member[2].label.name, AudioNheadphone,
11402627362dSho sizeof dip->un.e.member[2].label.name);
11417f529f01Sjason dip->un.e.member[2].ord = CSPORT_HEADPHONE;
11427f529f01Sjason break;
11437f529f01Sjason case CSAUDIO_INPUT_CLASS: /* input class descriptor */
11447f529f01Sjason dip->type = AUDIO_MIXER_CLASS;
11457f529f01Sjason dip->mixer_class = CSAUDIO_INPUT_CLASS;
11467f529f01Sjason dip->prev = AUDIO_MIXER_LAST;
11477f529f01Sjason dip->next = AUDIO_MIXER_LAST;
11482627362dSho strlcpy(dip->label.name, AudioCinputs, sizeof dip->label.name);
11497f529f01Sjason break;
11507f529f01Sjason case CSAUDIO_OUTPUT_CLASS: /* output class descriptor */
11517f529f01Sjason dip->type = AUDIO_MIXER_CLASS;
11527f529f01Sjason dip->mixer_class = CSAUDIO_OUTPUT_CLASS;
11537f529f01Sjason dip->prev = AUDIO_MIXER_LAST;
11547f529f01Sjason dip->next = AUDIO_MIXER_LAST;
11552627362dSho strlcpy(dip->label.name, AudioCoutputs,
11562627362dSho sizeof dip->label.name);
11577f529f01Sjason break;
11587f529f01Sjason case CSAUDIO_MONITOR_CLASS: /* monitor class descriptor */
11597f529f01Sjason dip->type = AUDIO_MIXER_CLASS;
11607f529f01Sjason dip->mixer_class = CSAUDIO_MONITOR_CLASS;
11617f529f01Sjason dip->prev = AUDIO_MIXER_LAST;
11627f529f01Sjason dip->next = AUDIO_MIXER_LAST;
11632627362dSho strlcpy(dip->label.name, AudioCmonitor,
11642627362dSho sizeof dip->label.name);
11657f529f01Sjason break;
11667f529f01Sjason case CSAUDIO_RECORD_CLASS: /* record class descriptor */
11677f529f01Sjason dip->type = AUDIO_MIXER_CLASS;
11687f529f01Sjason dip->mixer_class = CSAUDIO_RECORD_CLASS;
11697f529f01Sjason dip->prev = AUDIO_MIXER_LAST;
11707f529f01Sjason dip->next = AUDIO_MIXER_LAST;
11712627362dSho strlcpy(dip->label.name, AudioCrecord, sizeof dip->label.name);
11727f529f01Sjason break;
11737f529f01Sjason default:
11747f529f01Sjason err = ENXIO;
11757f529f01Sjason }
11767f529f01Sjason
11777f529f01Sjason return (err);
11787f529f01Sjason }
11797f529f01Sjason
11807f529f01Sjason /*
11817f529f01Sjason * Hardware interrupt handler
11827f529f01Sjason */
11837f529f01Sjason int
cs4231_intr(void * vsc)11846e4fde28Sjason cs4231_intr(void *vsc)
11857f529f01Sjason {
11866e4fde28Sjason struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
11877f529f01Sjason u_int32_t csr;
11887f529f01Sjason u_int8_t reg, status;
11897f529f01Sjason struct cs_dma *p;
11907f529f01Sjason int r = 0;
11917f529f01Sjason
1192886882aaSratchov mtx_enter(&audio_lock);
11937f529f01Sjason csr = APC_READ(sc, APC_CSR);
1194bf383a97Sjason APC_WRITE(sc, APC_CSR, csr);
1195bf383a97Sjason
1196bf383a97Sjason if ((csr & APC_CSR_EIE) && (csr & APC_CSR_EI)) {
1197bf383a97Sjason printf("%s: error interrupt\n", sc->sc_dev.dv_xname);
1198bf383a97Sjason r = 1;
1199bf383a97Sjason }
1200bf383a97Sjason
1201bf383a97Sjason if ((csr & APC_CSR_PIE) && (csr & APC_CSR_PI)) {
1202bf383a97Sjason /* playback interrupt */
1203bf383a97Sjason r = 1;
1204bf383a97Sjason }
1205bf383a97Sjason
1206bf383a97Sjason if ((csr & APC_CSR_GIE) && (csr & APC_CSR_GI)) {
1207bf383a97Sjason /* general interrupt */
1208a1f7da73Sjason status = CS_READ(sc, AD1848_STATUS);
1209a1f7da73Sjason if (status & (INTERRUPT_STATUS | SAMPLE_ERROR)) {
12107e0aa3e6Sjason reg = cs4231_read(sc, CS_IRQ_STATUS);
12117f529f01Sjason if (reg & CS_AFS_PI) {
12127e0aa3e6Sjason cs4231_write(sc, SP_LOWER_BASE_COUNT, 0xff);
12137e0aa3e6Sjason cs4231_write(sc, SP_UPPER_BASE_COUNT, 0xff);
12147f529f01Sjason }
12159820c8cbSjason if (reg & CS_AFS_CI) {
12169820c8cbSjason cs4231_write(sc, CS_LOWER_REC_CNT, 0xff);
12179820c8cbSjason cs4231_write(sc, CS_UPPER_REC_CNT, 0xff);
12189820c8cbSjason }
1219a1f7da73Sjason CS_WRITE(sc, AD1848_STATUS, 0);
12207f529f01Sjason }
1221bf383a97Sjason r = 1;
1222bf383a97Sjason }
12237f529f01Sjason
12247f529f01Sjason
12257f529f01Sjason if (csr & (APC_CSR_PI|APC_CSR_PMI|APC_CSR_PIE|APC_CSR_PD))
12267f529f01Sjason r = 1;
12277f529f01Sjason
1228bf383a97Sjason if ((csr & APC_CSR_PMIE) && (csr & APC_CSR_PMI)) {
12299820c8cbSjason struct cs_channel *chan = &sc->sc_playback;
12307f529f01Sjason u_long nextaddr, togo;
12317f529f01Sjason
12329820c8cbSjason p = chan->cs_curdma;
12339820c8cbSjason togo = chan->cs_segsz - chan->cs_cnt;
12347f529f01Sjason if (togo == 0) {
12357f529f01Sjason nextaddr = (u_int32_t)p->dmamap->dm_segs[0].ds_addr;
12369820c8cbSjason chan->cs_cnt = togo = chan->cs_blksz;
12377f529f01Sjason } else {
12389820c8cbSjason nextaddr = APC_READ(sc, APC_PNVA) + chan->cs_blksz;
12399820c8cbSjason if (togo > chan->cs_blksz)
12409820c8cbSjason togo = chan->cs_blksz;
12419820c8cbSjason chan->cs_cnt += togo;
12427f529f01Sjason }
12437f529f01Sjason
12447f529f01Sjason APC_WRITE(sc, APC_PNVA, nextaddr);
12457f529f01Sjason APC_WRITE(sc, APC_PNC, togo);
12467f529f01Sjason
12479820c8cbSjason if (chan->cs_intr != NULL)
12489820c8cbSjason (*chan->cs_intr)(chan->cs_arg);
12497f529f01Sjason r = 1;
12507f529f01Sjason }
12517f529f01Sjason
12529820c8cbSjason if ((csr & APC_CSR_CIE) && (csr & APC_CSR_CI)) {
12539820c8cbSjason if (csr & APC_CSR_CD) {
12549820c8cbSjason struct cs_channel *chan = &sc->sc_capture;
12559820c8cbSjason u_long nextaddr, togo;
12569820c8cbSjason
12579820c8cbSjason p = chan->cs_curdma;
12589820c8cbSjason togo = chan->cs_segsz - chan->cs_cnt;
12599820c8cbSjason if (togo == 0) {
12609820c8cbSjason nextaddr =
12619820c8cbSjason (u_int32_t)p->dmamap->dm_segs[0].ds_addr;
12629820c8cbSjason chan->cs_cnt = togo = chan->cs_blksz;
12639820c8cbSjason } else {
12649820c8cbSjason nextaddr = APC_READ(sc, APC_CNVA) +
12659820c8cbSjason chan->cs_blksz;
12669820c8cbSjason if (togo > chan->cs_blksz)
12679820c8cbSjason togo = chan->cs_blksz;
12689820c8cbSjason chan->cs_cnt += togo;
12699820c8cbSjason }
12709820c8cbSjason
12719820c8cbSjason APC_WRITE(sc, APC_CNVA, nextaddr);
12729820c8cbSjason APC_WRITE(sc, APC_CNC, togo);
12739820c8cbSjason
12749820c8cbSjason if (chan->cs_intr != NULL)
12759820c8cbSjason (*chan->cs_intr)(chan->cs_arg);
12769820c8cbSjason }
12777f529f01Sjason r = 1;
12787f529f01Sjason }
12799820c8cbSjason
12809820c8cbSjason if ((csr & APC_CSR_CMIE) && (csr & APC_CSR_CMI)) {
12819820c8cbSjason /* capture empty */
12829820c8cbSjason r = 1;
12837f529f01Sjason }
12847f529f01Sjason
1285886882aaSratchov mtx_leave(&audio_lock);
12867f529f01Sjason return (r);
12877f529f01Sjason }
12887f529f01Sjason
12897f529f01Sjason void *
cs4231_alloc(void * vsc,int direction,size_t size,int pool,int flags)12906e4fde28Sjason cs4231_alloc(void *vsc, int direction, size_t size, int pool, int flags)
12917f529f01Sjason {
12926e4fde28Sjason struct cs4231_softc *sc = (struct cs4231_softc *)vsc;
12937f529f01Sjason bus_dma_tag_t dmat = sc->sc_dmatag;
12947f529f01Sjason struct cs_dma *p;
12957f529f01Sjason
12967f529f01Sjason p = (struct cs_dma *)malloc(sizeof(struct cs_dma), pool, flags);
12977f529f01Sjason if (p == NULL)
12984223a637Smiod return (NULL);
12997f529f01Sjason
13007f529f01Sjason if (bus_dmamap_create(dmat, size, 1, size, 0,
13017f529f01Sjason BUS_DMA_NOWAIT, &p->dmamap) != 0)
13027f529f01Sjason goto fail;
13037f529f01Sjason
13047f529f01Sjason p->size = size;
13057f529f01Sjason
13067f529f01Sjason if (bus_dmamem_alloc(dmat, size, 64*1024, 0, p->segs,
1307d9c3e9c1Sjasper nitems(p->segs), &p->nsegs,
13087f529f01Sjason BUS_DMA_NOWAIT) != 0)
13097f529f01Sjason goto fail1;
13107f529f01Sjason
13117f529f01Sjason if (bus_dmamem_map(dmat, p->segs, p->nsegs, p->size,
13127f529f01Sjason &p->addr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT) != 0)
13137f529f01Sjason goto fail2;
13147f529f01Sjason
13157f529f01Sjason if (bus_dmamap_load(dmat, p->dmamap, p->addr, size, NULL,
13167f529f01Sjason BUS_DMA_NOWAIT) != 0)
13177f529f01Sjason goto fail3;
13187f529f01Sjason
13197f529f01Sjason p->next = sc->sc_dmas;
13207f529f01Sjason sc->sc_dmas = p;
13217f529f01Sjason return (p->addr);
13227f529f01Sjason
13237f529f01Sjason fail3:
13247f529f01Sjason bus_dmamem_unmap(dmat, p->addr, p->size);
13257f529f01Sjason fail2:
13267f529f01Sjason bus_dmamem_free(dmat, p->segs, p->nsegs);
13277f529f01Sjason fail1:
13287f529f01Sjason bus_dmamap_destroy(dmat, p->dmamap);
13297f529f01Sjason fail:
1330df2ac69fStedu free(p, pool, 0);
13317f529f01Sjason return (NULL);
13327f529f01Sjason }
13337f529f01Sjason
13347f529f01Sjason void
cs4231_free(void * vsc,void * ptr,int pool)13356e4fde28Sjason cs4231_free(void *vsc, void *ptr, int pool)
13367f529f01Sjason {
13376e4fde28Sjason struct cs4231_softc *sc = vsc;
13387f529f01Sjason bus_dma_tag_t dmat = sc->sc_dmatag;
13397f529f01Sjason struct cs_dma *p, **pp;
13407f529f01Sjason
13417f529f01Sjason for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &(*pp)->next) {
13427f529f01Sjason if (p->addr != ptr)
13437f529f01Sjason continue;
13447f529f01Sjason bus_dmamap_unload(dmat, p->dmamap);
13457f529f01Sjason bus_dmamem_unmap(dmat, p->addr, p->size);
13467f529f01Sjason bus_dmamem_free(dmat, p->segs, p->nsegs);
13477f529f01Sjason bus_dmamap_destroy(dmat, p->dmamap);
13487f529f01Sjason *pp = p->next;
1349df2ac69fStedu free(p, pool, 0);
13507f529f01Sjason return;
13517f529f01Sjason }
13527f529f01Sjason printf("%s: attempt to free rogue pointer\n", sc->sc_dev.dv_xname);
13537f529f01Sjason }
13547f529f01Sjason
13557f529f01Sjason int
cs4231_trigger_output(void * vsc,void * start,void * end,int blksize,void (* intr)(void *),void * arg,struct audio_params * param)13566e4fde28Sjason cs4231_trigger_output(void *vsc, void *start, void *end, int blksize,
13576e4fde28Sjason void (*intr)(void *), void *arg, struct audio_params *param)
13587f529f01Sjason {
13596e4fde28Sjason struct cs4231_softc *sc = vsc;
13609820c8cbSjason struct cs_channel *chan = &sc->sc_playback;
13617f529f01Sjason struct cs_dma *p;
13627f529f01Sjason u_int32_t csr;
13639820c8cbSjason u_long n;
13647f529f01Sjason
13659820c8cbSjason if (chan->cs_locked != 0) {
13667f529f01Sjason printf("%s: trigger_output: already running\n",
13677f529f01Sjason sc->sc_dev.dv_xname);
13687f529f01Sjason return (EINVAL);
13697f529f01Sjason }
13707f529f01Sjason
13719820c8cbSjason chan->cs_locked = 1;
13729820c8cbSjason chan->cs_intr = intr;
13739820c8cbSjason chan->cs_arg = arg;
13747f529f01Sjason
13757f529f01Sjason for (p = sc->sc_dmas; p->addr != start; p = p->next)
13767f529f01Sjason /*EMPTY*/;
13777f529f01Sjason if (p == NULL) {
1378eb79e960Shenric printf("%s: trigger_output: bad addr: %p\n",
13797f529f01Sjason sc->sc_dev.dv_xname, start);
13807f529f01Sjason return (EINVAL);
13817f529f01Sjason }
13827f529f01Sjason
13837f529f01Sjason n = (char *)end - (char *)start;
13847f529f01Sjason
13857f529f01Sjason /*
13867f529f01Sjason * Do only `blksize' at a time, so audio_pint() is kept
13877f529f01Sjason * synchronous with us...
13887f529f01Sjason */
13899820c8cbSjason chan->cs_blksz = blksize;
13909820c8cbSjason chan->cs_curdma = p;
13919820c8cbSjason chan->cs_segsz = n;
13927f529f01Sjason
13939820c8cbSjason if (n > chan->cs_blksz)
13949820c8cbSjason n = chan->cs_blksz;
13957f529f01Sjason
13969820c8cbSjason chan->cs_cnt = n;
13977f529f01Sjason
1398886882aaSratchov mtx_enter(&audio_lock);
13997f529f01Sjason csr = APC_READ(sc, APC_CSR);
14007f529f01Sjason
14017f529f01Sjason APC_WRITE(sc, APC_PNVA, (u_long)p->dmamap->dm_segs[0].ds_addr);
14027f529f01Sjason APC_WRITE(sc, APC_PNC, (u_long)n);
14037f529f01Sjason
14047f529f01Sjason if ((csr & APC_CSR_PDMA_GO) == 0 || (csr & APC_CSR_PPAUSE) != 0) {
14057f529f01Sjason APC_WRITE(sc, APC_CSR,
14067f529f01Sjason APC_READ(sc, APC_CSR) & ~(APC_CSR_PIE | APC_CSR_PPAUSE));
14077f529f01Sjason APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) |
14087f529f01Sjason APC_CSR_EI | APC_CSR_GIE | APC_CSR_PIE | APC_CSR_EIE |
14097f529f01Sjason APC_CSR_PMIE | APC_CSR_PDMA_GO);
14107e0aa3e6Sjason cs4231_write(sc, SP_LOWER_BASE_COUNT, 0xff);
14117e0aa3e6Sjason cs4231_write(sc, SP_UPPER_BASE_COUNT, 0xff);
14127e0aa3e6Sjason cs4231_write(sc, SP_INTERFACE_CONFIG,
1413a1f7da73Sjason cs4231_read(sc, SP_INTERFACE_CONFIG) | PLAYBACK_ENABLE);
14147f529f01Sjason }
1415886882aaSratchov mtx_leave(&audio_lock);
14167f529f01Sjason return (0);
14177f529f01Sjason }
14187f529f01Sjason
14197f529f01Sjason int
cs4231_trigger_input(void * vsc,void * start,void * end,int blksize,void (* intr)(void *),void * arg,struct audio_params * param)14206e4fde28Sjason cs4231_trigger_input(void *vsc, void *start, void *end, int blksize,
14216e4fde28Sjason void (*intr)(void *), void *arg, struct audio_params *param)
14227f529f01Sjason {
14236e4fde28Sjason struct cs4231_softc *sc = vsc;
14249820c8cbSjason struct cs_channel *chan = &sc->sc_capture;
14259820c8cbSjason struct cs_dma *p;
14269820c8cbSjason u_int32_t csr;
14279820c8cbSjason u_long n;
14289820c8cbSjason
14299820c8cbSjason if (chan->cs_locked != 0) {
14309820c8cbSjason printf("%s: trigger_input: already running\n",
14319820c8cbSjason sc->sc_dev.dv_xname);
14329820c8cbSjason return (EINVAL);
14339820c8cbSjason }
14349820c8cbSjason chan->cs_locked = 1;
14359820c8cbSjason chan->cs_intr = intr;
14369820c8cbSjason chan->cs_arg = arg;
14379820c8cbSjason
14389820c8cbSjason for (p = sc->sc_dmas; p->addr != start; p = p->next)
14399820c8cbSjason /*EMPTY*/;
14409820c8cbSjason if (p == NULL) {
1441eb79e960Shenric printf("%s: trigger_input: bad addr: %p\n",
14429820c8cbSjason sc->sc_dev.dv_xname, start);
14439820c8cbSjason return (EINVAL);
14449820c8cbSjason }
14459820c8cbSjason
14469820c8cbSjason n = (char *)end - (char *)start;
14479820c8cbSjason
14489820c8cbSjason /*
14499820c8cbSjason * Do only `blksize' at a time, so audio_cint() is kept
14509820c8cbSjason * synchronous with us...
14519820c8cbSjason */
14529820c8cbSjason chan->cs_blksz = blksize;
14539820c8cbSjason chan->cs_curdma = p;
14549820c8cbSjason chan->cs_segsz = n;
14559820c8cbSjason
14569820c8cbSjason if (n > chan->cs_blksz)
14579820c8cbSjason n = chan->cs_blksz;
14589820c8cbSjason chan->cs_cnt = n;
14599820c8cbSjason
1460886882aaSratchov mtx_enter(&audio_lock);
14619820c8cbSjason APC_WRITE(sc, APC_CNVA, p->dmamap->dm_segs[0].ds_addr);
14629820c8cbSjason APC_WRITE(sc, APC_CNC, (u_long)n);
14639820c8cbSjason
14649820c8cbSjason csr = APC_READ(sc, APC_CSR);
14659820c8cbSjason if ((csr & APC_CSR_CDMA_GO) == 0 || (csr & APC_CSR_CPAUSE) != 0) {
14669820c8cbSjason csr &= APC_CSR_CPAUSE;
14679820c8cbSjason csr |= APC_CSR_GIE | APC_CSR_CMIE | APC_CSR_CIE | APC_CSR_EI |
14689820c8cbSjason APC_CSR_CDMA_GO;
14699820c8cbSjason APC_WRITE(sc, APC_CSR, csr);
14709820c8cbSjason cs4231_write(sc, CS_LOWER_REC_CNT, 0xff);
14719820c8cbSjason cs4231_write(sc, CS_UPPER_REC_CNT, 0xff);
14729820c8cbSjason cs4231_write(sc, SP_INTERFACE_CONFIG,
14739820c8cbSjason cs4231_read(sc, SP_INTERFACE_CONFIG) | CAPTURE_ENABLE);
14749820c8cbSjason }
14759820c8cbSjason
14769820c8cbSjason if (APC_READ(sc, APC_CSR) & APC_CSR_CD) {
14779820c8cbSjason u_long nextaddr, togo;
14789820c8cbSjason
14799820c8cbSjason p = chan->cs_curdma;
14809820c8cbSjason togo = chan->cs_segsz - chan->cs_cnt;
14819820c8cbSjason if (togo == 0) {
14829820c8cbSjason nextaddr = (u_int32_t)p->dmamap->dm_segs[0].ds_addr;
14839820c8cbSjason chan->cs_cnt = togo = chan->cs_blksz;
14849820c8cbSjason } else {
14859820c8cbSjason nextaddr = APC_READ(sc, APC_CNVA) + chan->cs_blksz;
14869820c8cbSjason if (togo > chan->cs_blksz)
14879820c8cbSjason togo = chan->cs_blksz;
14889820c8cbSjason chan->cs_cnt += togo;
14899820c8cbSjason }
14909820c8cbSjason
14919820c8cbSjason APC_WRITE(sc, APC_CNVA, nextaddr);
14929820c8cbSjason APC_WRITE(sc, APC_CNC, togo);
14939820c8cbSjason }
14949820c8cbSjason
1495886882aaSratchov mtx_leave(&audio_lock);
14969820c8cbSjason return (0);
14977f529f01Sjason }
1498