diff options
-rw-r--r-- | sys/arch/hppa/gsc/harmony.c | 882 |
1 files changed, 882 insertions, 0 deletions
diff --git a/sys/arch/hppa/gsc/harmony.c b/sys/arch/hppa/gsc/harmony.c new file mode 100644 index 00000000000..8762b075365 --- /dev/null +++ b/sys/arch/hppa/gsc/harmony.c @@ -0,0 +1,882 @@ +/* $OpenBSD: harmony.c,v 1.1 2003/01/26 07:21:40 jason Exp $ */ + +/* + * Copyright (c) 2003 Jason L. Wright (jason@thought.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jason L. Wright + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/device.h> +#include <sys/proc.h> +#include <sys/malloc.h> + +#include <sys/audioio.h> +#include <dev/audio_if.h> +#include <dev/auconv.h> + +#include <machine/cpu.h> +#include <machine/intr.h> +#include <machine/iomod.h> +#include <machine/autoconf.h> +#include <machine/bus.h> + +#include <hppa/dev/cpudevs.h> +#include <hppa/gsc/gscbusvar.h> + +#define HARMONY_NREGS 0x40 + +#define HARMONY_ID 0x00 +#define HARMONY_RESET 0x04 +#define HARMONY_CNTL 0x08 +#define HARMONY_GAINCTL 0x0c /* gain control */ +#define HARMONY_PLAYNXT 0x10 /* play next address */ +#define HARMONY_PLAYCUR 0x14 /* play current address */ +#define HARMONY_CAPTNXT 0x18 /* capture next address */ +#define HARMONY_CAPTCUR 0x1c /* capture current address */ +#define HARMONY_DSTATUS 0x20 /* device status */ +#define HARMONY_OV 0x24 +#define HARMONY_PIO 0x28 +#define HARMONY_DIAG 0x3c + +#define CNTL_INCNTL 0x80000000 +#define CNTL_FORMAT_MASK 0x000000c0 +#define CNTL_FORMAT_SLINEAR16BE 0x00000000 +#define CNTL_FORMAT_ULAW 0x00000040 +#define CNTL_FORMAT_ALAW 0x00000080 +#define CNTL_CHANS_MASK 0x00000020 +#define CNTL_CHANS_MONO 0x00000000 +#define CNTL_CHANS_STEREO 0x00000020 +#define CNTL_RATE_MASK 0x0000001f +#define CNTL_RATE_5125 0x00000010 +#define CNTL_RATE_6615 0x00000017 +#define CNTL_RATE_8000 0x00000008 +#define CNTL_RATE_9600 0x0000000f +#define CNTL_RATE_11025 0x00000011 +#define CNTL_RATE_16000 0x00000009 +#define CNTL_RATE_18900 0x00000012 +#define CNTL_RATE_22050 0x00000013 +#define CNTL_RATE_27428 0x0000000a +#define CNTL_RATE_32000 0x0000000b +#define CNTL_RATE_33075 0x00000016 +#define CNTL_RATE_37800 0x00000014 +#define CNTL_RATE_44100 0x00000015 +#define CNTL_RATE_48000 0x0000000e + +#define GAINCTL_INPUT_LEFT_M 0x0000f000 +#define GAINCTL_INPUT_LEFT_S 12 +#define GAINCTL_INPUT_RIGHT_M 0x000f0000 +#define GAINCTL_INPUT_RIGHT_S 16 +#define GAINCTL_MONITOR_M 0x00f00000 +#define GAINCTL_MONITOR_S 20 +#define GAINCTL_OUTPUT_LEFT_M 0x00000fc0 +#define GAINCTL_OUTPUT_LEFT_S 6 +#define GAINCTL_OUTPUT_RIGHT_M 0x0000003f +#define GAINCTL_OUTPUT_RIGHT_S 0 + +#define DSTATUS_INTRENA 0x80000000 +#define DSTATUS_PLAYNXT 0x00000200 +#define DSTATUS_CAPTNXT 0x00000002 + +#define HARMONY_PORT_INPUT_LVL 0 +#define HARMONY_PORT_OUTPUT_LVL 1 +#define HARMONY_PORT_MONITOR_LVL 2 +#define HARMONY_PORT_INPUT_CLASS 3 +#define HARMONY_PORT_OUTPUT_CLASS 4 +#define HARMONY_PORT_MONITOR_CLASS 5 + +#define PLAYBACK_EMPTYS 3 /* playback empty buffers */ +#define CAPTURE_EMPTYS 3 /* capture empty buffers */ +#define HARMONY_BUFSIZE 4096 + +struct harmony_empty { + u_int8_t playback[PLAYBACK_EMPTYS][HARMONY_BUFSIZE]; + u_int8_t capture[CAPTURE_EMPTYS][HARMONY_BUFSIZE]; +}; + +struct harmony_softc { + struct device sc_dv; + bus_dma_tag_t sc_dmat; + bus_space_tag_t sc_bt; + bus_space_handle_t sc_bh; + int sc_open; + u_int32_t sc_gainctl; + u_int32_t sc_cntlbits; + int sc_need_commit; + int sc_playback_empty; + bus_addr_t sc_playback_paddrs[PLAYBACK_EMPTYS]; + int sc_capture_empty; + bus_addr_t sc_capture_paddrs[CAPTURE_EMPTYS]; + bus_dmamap_t sc_empty_map; + bus_dma_segment_t sc_empty_seg; + int sc_empty_rseg; + struct harmony_empty *sc_empty_kva; +}; + +int harmony_open(void *, int); +void harmony_close(void *); +int harmony_query_encoding(void *, struct audio_encoding *); +int harmony_set_params(void *, int, int, struct audio_params *, + struct audio_params *); +int harmony_round_blocksize(void *, int); +int harmony_commit_settings(void *); +int harmony_halt_output(void *); +int harmony_halt_input(void *); +int harmony_getdev(void *, struct audio_device *); +int harmony_set_port(void *, mixer_ctrl_t *); +int harmony_get_port(void *, mixer_ctrl_t *); +int harmony_query_devinfo(void *addr, mixer_devinfo_t *); +void * harmony_alloc(void *, int, size_t, int, int); +void harmony_free(void *, void *, int); +size_t harmony_round_buffersize(void *, int, size_t); +int harmony_get_props(void *); +int harmony_trigger_output(void *, void *, void *, int, + void (*intr)(void *), void *arg, struct audio_params *); +int harmony_trigger_input(void *, void *, void *, int, + void (*intr)(void *), void *arg, struct audio_params *); +u_int32_t harmony_speed_bits(struct harmony_softc *, u_long *); + +struct audio_hw_if harmony_sa_hw_if = { + harmony_open, + harmony_close, + NULL, + harmony_query_encoding, + harmony_set_params, + harmony_round_blocksize, + harmony_commit_settings, + NULL, + NULL, + NULL, + NULL, + harmony_halt_output, + harmony_halt_input, + NULL, + harmony_getdev, + NULL, + harmony_set_port, + harmony_get_port, + harmony_query_devinfo, + NULL, + NULL, + harmony_round_buffersize, + NULL, + harmony_get_props, + harmony_trigger_output, + harmony_trigger_input, +}; + +struct audio_device harmony_device = { + "harmony", + "gsc", + "lasi", +}; + +int harmony_match(struct device *, void *, void *); +void harmony_attach(struct device *, struct device *, void *); +int harmony_intr(void *); +void harmony_intr_enable(struct harmony_softc *); +void harmony_intr_disable(struct harmony_softc *); +void harmony_wait(struct harmony_softc *); +void harmony_set_gainctl(struct harmony_softc *, u_int32_t); + +int +harmony_match(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct gsc_attach_args *ga = aux; + + if (ga->ga_type.iodc_type == HPPA_TYPE_FIO) { + if (ga->ga_type.iodc_sv_model == HPPA_FIO_A1 || + ga->ga_type.iodc_sv_model == HPPA_FIO_A2NB || + ga->ga_type.iodc_sv_model == HPPA_FIO_A1NB || + ga->ga_type.iodc_sv_model == HPPA_FIO_A2) + return (1); + } + return (0); +} + +void +harmony_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct harmony_softc *sc = (struct harmony_softc *)self; + struct gsc_attach_args *ga = aux; + int i; + + sc->sc_bt = ga->ga_iot; + sc->sc_dmat = ga->ga_dmatag; + + if (bus_space_map(sc->sc_bt, ga->ga_hpa, HARMONY_NREGS, 0, + &sc->sc_bh) != 0) { + printf(": couldn't map registers\n"); + return; + } + + if (bus_dmamem_alloc(sc->sc_dmat, sizeof(struct harmony_empty), + PAGE_SIZE, 0, &sc->sc_empty_seg, 1, &sc->sc_empty_rseg, + BUS_DMA_NOWAIT) != 0) { + printf(": couldn't alloc empty memory\n"); + return; + } + if (bus_dmamem_map(sc->sc_dmat, &sc->sc_empty_seg, 1, + sizeof(struct harmony_empty), (caddr_t *)&sc->sc_empty_kva, + BUS_DMA_NOWAIT) != 0) { + printf(": couldn't map empty memory\n"); + bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg, + sc->sc_empty_rseg); + return; + } + if (bus_dmamap_create(sc->sc_dmat, sizeof(struct harmony_empty), 1, + sizeof(struct harmony_empty), 0, BUS_DMA_NOWAIT, + &sc->sc_empty_map) != 0) { + printf(": can't create empty dmamap\n"); + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_empty_kva, + sizeof(struct harmony_empty)); + bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg, + sc->sc_empty_rseg); + return; + } + if (bus_dmamap_load(sc->sc_dmat, sc->sc_empty_map, sc->sc_empty_kva, + sizeof(struct harmony_empty), NULL, BUS_DMA_NOWAIT) != 0) { + printf(": can't load empty dmamap\n"); + bus_dmamap_destroy(sc->sc_dmat, sc->sc_empty_map); + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_empty_kva, + sizeof(struct harmony_empty)); + bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg, + sc->sc_empty_rseg); + return; + } + + sc->sc_playback_empty = 0; + for (i = 0; i < PLAYBACK_EMPTYS; i++) + sc->sc_playback_paddrs[i] = + sc->sc_empty_map->dm_segs[0].ds_addr + + offsetof(struct harmony_empty, playback[i][0]); + + sc->sc_capture_empty = 0; + for (i = 0; i < CAPTURE_EMPTYS; i++) + sc->sc_capture_paddrs[i] = + sc->sc_empty_map->dm_segs[0].ds_addr + + offsetof(struct harmony_empty, playback[i][0]); + + bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map, + offsetof(struct harmony_empty, playback[0][0]), + PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_PREWRITE); + + (void)gsc_intr_establish((struct gsc_softc *)parent, + IPL_AUDIO, ga->ga_irq, harmony_intr, sc, &sc->sc_dv); + + /* set default gains */ + sc->sc_gainctl = + ((0x2 << GAINCTL_OUTPUT_LEFT_S) & GAINCTL_OUTPUT_LEFT_M) | + ((0x2 << GAINCTL_OUTPUT_RIGHT_S) & GAINCTL_OUTPUT_RIGHT_M) | + ((0xf << GAINCTL_INPUT_LEFT_S) & GAINCTL_INPUT_LEFT_M) | + ((0xf << GAINCTL_INPUT_RIGHT_S) & GAINCTL_INPUT_RIGHT_M) | + ((0x2 << GAINCTL_MONITOR_S) & GAINCTL_MONITOR_M) | + 0x0f000000; + + printf("\n"); + + audio_attach_mi(&harmony_sa_hw_if, sc, &sc->sc_dv); +} + +/* + * interrupt handler + */ +int +harmony_intr(vsc) + void *vsc; +{ + struct harmony_softc *sc = vsc; + u_int32_t dstatus; + + harmony_intr_disable(sc); + harmony_wait(sc); + + dstatus = bus_space_read_4(sc->sc_bt, sc->sc_bh, HARMONY_DSTATUS) & + (DSTATUS_PLAYNXT | DSTATUS_CAPTNXT); + if (dstatus == 0) + return (0); + + printf("%s: intr %x\n", sc->sc_dv.dv_xname, dstatus); + + if (dstatus & DSTATUS_PLAYNXT) { + bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_PLAYNXT, + sc->sc_playback_paddrs[sc->sc_playback_empty]); + if (++sc->sc_playback_empty == PLAYBACK_EMPTYS) + sc->sc_playback_empty = 0; + } + + if (dstatus & DSTATUS_CAPTNXT) { + bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_CAPTNXT, + sc->sc_capture_paddrs[sc->sc_capture_empty]); + if (++sc->sc_capture_empty == CAPTURE_EMPTYS) + sc->sc_capture_empty = 0; + } + + harmony_intr_enable(sc); + + return (1); +} + +void +harmony_intr_enable(struct harmony_softc *sc) +{ + harmony_wait(sc); + bus_space_write_4(sc->sc_bt, sc->sc_bh, + HARMONY_DSTATUS, DSTATUS_INTRENA); +} + +void +harmony_intr_disable(struct harmony_softc *sc) +{ + harmony_wait(sc); + bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_DSTATUS, 0); +} + +void +harmony_wait(struct harmony_softc *sc) +{ + int i = 5000; + + for (i = 5000; i > 0; i++) + if (((bus_space_read_4(sc->sc_bt, sc->sc_bh, HARMONY_CNTL) + & CNTL_INCNTL)) == 0) + return; + printf("%s: wait timeout\n", sc->sc_dv.dv_xname); +} + +void +harmony_set_gainctl(struct harmony_softc *sc, u_int32_t gain) +{ + harmony_wait(sc); + bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_GAINCTL, gain); +} + +int +harmony_open(void *vsc, int flags) +{ + struct harmony_softc *sc = vsc; + + if (sc->sc_open) + return (EBUSY); + sc->sc_open = 1; + + /* silence */ + harmony_set_gainctl(sc, GAINCTL_OUTPUT_LEFT_M | + GAINCTL_OUTPUT_RIGHT_M | GAINCTL_MONITOR_M); + + /* reset codec */ + harmony_wait(sc); + bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_RESET, 1); + DELAY(50000); + bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_RESET, 0); + + harmony_set_gainctl(sc, sc->sc_gainctl); + + return (0); +} + +void +harmony_close(void *vsc) +{ + struct harmony_softc *sc = vsc; + + harmony_halt_input(sc); + harmony_halt_output(sc); + sc->sc_open = 0; +} + +int +harmony_query_encoding(void *vsc, struct audio_encoding *fp) +{ + int err = 0; + + switch (fp->index) { + case 0: + strcpy(fp->name, AudioEmulaw); + fp->encoding = AUDIO_ENCODING_ULAW; + fp->precision = 8; + fp->flags = 0; + break; + case 1: + strcpy(fp->name, AudioEalaw); + fp->encoding = AUDIO_ENCODING_ALAW; + fp->precision = 8; + fp->flags = 0; + break; + case 2: + strcpy(fp->name, AudioEslinear_be); + fp->encoding = AUDIO_ENCODING_SLINEAR_BE; + fp->precision = 16; + fp->flags = 0; + break; + case 3: + strcpy(fp->name, AudioEslinear_le); + fp->encoding = AUDIO_ENCODING_SLINEAR_LE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + break; + case 4: + strcpy(fp->name, AudioEulinear_be); + fp->encoding = AUDIO_ENCODING_ULINEAR_BE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + break; + default: + err = EINVAL; + } + return (err); +} + +int +harmony_set_params(void *vsc, int setmode, int usemode, + struct audio_params *p, struct audio_params *r) +{ + struct harmony_softc *sc = vsc; + u_int32_t bits; + void (*pswcode)(void *, u_char *, int cnt) = NULL; + void (*rswcode)(void *, u_char *, int cnt) = NULL; + + switch (p->encoding) { + case AUDIO_ENCODING_ULAW: + if (p->precision != 8) + return (EINVAL); + bits = CNTL_FORMAT_ULAW; + break; + case AUDIO_ENCODING_ALAW: + if (p->precision != 8) + return (EINVAL); + bits = CNTL_FORMAT_ALAW; + break; + case AUDIO_ENCODING_SLINEAR_BE: + if (p->precision != 16) + return (EINVAL); + bits = CNTL_FORMAT_SLINEAR16BE; + break; + + /* emulated formats */ + case AUDIO_ENCODING_SLINEAR_LE: + if (p->precision != 16) + return (EINVAL); + bits = CNTL_FORMAT_SLINEAR16BE; + rswcode = pswcode = swap_bytes; + break; + case AUDIO_ENCODING_ULINEAR_BE: + if (p->precision != 16) + return (EINVAL); + bits = CNTL_FORMAT_SLINEAR16BE; + rswcode = pswcode = change_sign16_be; + break; + default: + return (EINVAL); + } + + if (p->channels == 1) + bits |= CNTL_CHANS_MONO; + else if (p->channels == 2) + bits |= CNTL_CHANS_STEREO; + else + return (EINVAL); + + bits |= harmony_speed_bits(sc, &p->sample_rate); + p->sw_code = pswcode; + r->sw_code = rswcode; + sc->sc_cntlbits = bits; + sc->sc_need_commit = 1; + + return (0); +} + +int +harmony_round_blocksize(void *vsc, int blk) +{ + return (blk & (-4)); +} + +int +harmony_commit_settings(void *vsc) +{ + struct harmony_softc *sc = vsc; + u_int8_t quietchar; + int i; + + if (sc->sc_need_commit == 0) + return (0); + + harmony_wait(sc); + bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_CNTL, sc->sc_cntlbits); + + /* set the silence character based on the encoding type */ + bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map, + offsetof(struct harmony_empty, playback[0][0]), + PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_POSTWRITE); + switch (sc->sc_cntlbits & CNTL_FORMAT_MASK) { + case CNTL_FORMAT_ULAW: + quietchar = 0x7f; + break; + case CNTL_FORMAT_ALAW: + quietchar = 0x55; + break; + case CNTL_FORMAT_SLINEAR16BE: + default: + quietchar = 0; + break; + } + for (i = 0; i < PLAYBACK_EMPTYS; i++) + memset(&sc->sc_empty_kva->playback[i][0], + quietchar, HARMONY_BUFSIZE); + bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map, + offsetof(struct harmony_empty, playback[0][0]), + PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_PREWRITE); + sc->sc_need_commit = 0; + + return (0); +} + +int +harmony_halt_output(void *vsc) +{ + struct harmony_softc *sc = vsc; + + harmony_intr_disable(sc); + return (0); +} + +int +harmony_halt_input(void *vsc) +{ + struct harmony_softc *sc = vsc; + + harmony_intr_disable(sc); + return (0); +} + +int +harmony_getdev(void *vsc, struct audio_device *retp) +{ + *retp = harmony_device; + return (0); +} + +int +harmony_set_port(void *vsc, mixer_ctrl_t *cp) +{ + struct harmony_softc *sc = vsc; + int err = EINVAL; + + switch (cp->dev) { + case HARMONY_PORT_INPUT_LVL: + if (cp->type != AUDIO_MIXER_VALUE) + break; + if (cp->un.value.num_channels == 1) { + sc->sc_gainctl &= + ~(GAINCTL_INPUT_LEFT_M | GAINCTL_INPUT_RIGHT_M); + sc->sc_gainctl |= + (cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] << + GAINCTL_INPUT_LEFT_S) & GAINCTL_INPUT_LEFT_M; + sc->sc_gainctl |= + (cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] << + GAINCTL_INPUT_RIGHT_S) & GAINCTL_INPUT_RIGHT_M; + } else if (cp->un.value.num_channels == 2) { + sc->sc_gainctl &= + ~(GAINCTL_INPUT_LEFT_M | GAINCTL_INPUT_RIGHT_M); + sc->sc_gainctl |= + (cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] << + GAINCTL_INPUT_RIGHT_S) & GAINCTL_INPUT_RIGHT_M; + sc->sc_gainctl |= + (cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] << + GAINCTL_INPUT_RIGHT_S) & GAINCTL_INPUT_RIGHT_M; + } else + break; + harmony_set_gainctl(sc, sc->sc_gainctl); + err = 0; + break; + case HARMONY_PORT_OUTPUT_LVL: + if (cp->type != AUDIO_MIXER_VALUE) + break; + if (cp->un.value.num_channels == 1) { + sc->sc_gainctl &= + ~(GAINCTL_OUTPUT_LEFT_M | GAINCTL_OUTPUT_RIGHT_M); + sc->sc_gainctl |= + (cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] << + GAINCTL_OUTPUT_LEFT_S) & GAINCTL_OUTPUT_LEFT_M; + sc->sc_gainctl |= + (cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] << + GAINCTL_OUTPUT_RIGHT_S) & GAINCTL_OUTPUT_RIGHT_M; + } else if (cp->un.value.num_channels == 2) { + sc->sc_gainctl &= + ~(GAINCTL_OUTPUT_LEFT_M | GAINCTL_OUTPUT_RIGHT_M); + sc->sc_gainctl |= + (cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] << + GAINCTL_OUTPUT_RIGHT_S) & GAINCTL_OUTPUT_RIGHT_M; + sc->sc_gainctl |= + (cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] << + GAINCTL_OUTPUT_RIGHT_S) & GAINCTL_OUTPUT_RIGHT_M; + } else + break; + harmony_set_gainctl(sc, sc->sc_gainctl); + err = 0; + break; + case HARMONY_PORT_MONITOR_LVL: + if (cp->type != AUDIO_MIXER_VALUE) + break; + if (cp->un.value.num_channels != 1) + break; + sc->sc_gainctl &= ~GAINCTL_MONITOR_M; + sc->sc_gainctl |= + (cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] << + GAINCTL_MONITOR_S) & GAINCTL_MONITOR_M; + harmony_set_gainctl(sc, sc->sc_gainctl); + err = 0; + break; + } + + return (err); +} + +int +harmony_get_port(void *vsc, mixer_ctrl_t *cp) +{ + struct harmony_softc *sc = vsc; + int err = EINVAL; + + switch (cp->dev) { + case HARMONY_PORT_INPUT_LVL: + if (cp->type != AUDIO_MIXER_VALUE) + break; + if (cp->un.value.num_channels == 1) { + cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = + (sc->sc_gainctl & GAINCTL_INPUT_LEFT_M) >> + GAINCTL_INPUT_LEFT_S; + } else if (cp->un.value.num_channels == 2) { + cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = + (sc->sc_gainctl & GAINCTL_INPUT_LEFT_M) >> + GAINCTL_INPUT_LEFT_S; + cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = + (sc->sc_gainctl & GAINCTL_INPUT_RIGHT_M) >> + GAINCTL_INPUT_RIGHT_S; + } else + break; + err = 0; + break; + case HARMONY_PORT_OUTPUT_LVL: + if (cp->type != AUDIO_MIXER_VALUE) + break; + if (cp->un.value.num_channels == 1) { + cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = + (sc->sc_gainctl & GAINCTL_OUTPUT_LEFT_M) >> + GAINCTL_OUTPUT_LEFT_S; + } else if (cp->un.value.num_channels == 2) { + cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = + (sc->sc_gainctl & GAINCTL_OUTPUT_LEFT_M) >> + GAINCTL_OUTPUT_LEFT_S; + cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = + (sc->sc_gainctl & GAINCTL_OUTPUT_RIGHT_M) >> + GAINCTL_OUTPUT_RIGHT_S; + } else + break; + err = 0; + break; + case HARMONY_PORT_MONITOR_LVL: + if (cp->type != AUDIO_MIXER_VALUE) + break; + if (cp->un.value.num_channels != 1) + break; + cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = + (sc->sc_gainctl & GAINCTL_MONITOR_M) >> + GAINCTL_MONITOR_S; + err = 0; + break; + } + return (0); +} + +int +harmony_query_devinfo(void *vsc, mixer_devinfo_t *dip) +{ + int err = 0; + + switch (dip->index) { + case HARMONY_PORT_INPUT_LVL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = HARMONY_PORT_INPUT_CLASS; + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNinput); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + break; + case HARMONY_PORT_OUTPUT_LVL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = HARMONY_PORT_OUTPUT_CLASS; + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNoutput); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + break; + case HARMONY_PORT_MONITOR_LVL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = HARMONY_PORT_MONITOR_CLASS; + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNoutput); + dip->un.v.num_channels = 1; + strcpy(dip->un.v.units.name, AudioNvolume); + break; + case HARMONY_PORT_INPUT_CLASS: + dip->type = AUDIO_MIXER_CLASS; + dip->mixer_class = HARMONY_PORT_INPUT_CLASS; + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioCinputs); + break; + case HARMONY_PORT_OUTPUT_CLASS: + dip->type = AUDIO_MIXER_CLASS; + dip->mixer_class = HARMONY_PORT_INPUT_CLASS; + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioCoutputs); + break; + case HARMONY_PORT_MONITOR_CLASS: + dip->type = AUDIO_MIXER_CLASS; + dip->mixer_class = HARMONY_PORT_INPUT_CLASS; + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioCmonitor); + break; + default: + err = ENXIO; + break; + } + + return (err); +} + +size_t +harmony_round_buffersize(void *vsc, int direction, size_t size) +{ + return (size); +} + +int +harmony_get_props(void *vsc) +{ + return (AUDIO_PROP_FULLDUPLEX); +} + +int +harmony_trigger_output(void *vsc, void *start, void *end, int blksize, + void (*intr)(void *), void *arg, struct audio_params *param) +{ + struct harmony_softc *sc = vsc; + + bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_PLAYNXT, + sc->sc_playback_paddrs[sc->sc_playback_empty]); + if (++sc->sc_playback_empty == PLAYBACK_EMPTYS) + sc->sc_playback_empty = 0; + harmony_intr_enable(sc); + return (0); +} + +int +harmony_trigger_input(void *vsc, void *start, void *end, int blksize, + void (*intr)(void *), void *arg, struct audio_params *param) +{ + struct harmony_softc *sc = vsc; + + bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_CAPTNXT, + sc->sc_capture_paddrs[sc->sc_capture_empty]); + if (++sc->sc_capture_empty == CAPTURE_EMPTYS) + sc->sc_capture_empty = 0; + harmony_intr_enable(sc); + return (0); +} + +static const struct speed_struct { + u_int32_t speed; + u_int32_t bits; +} harmony_speeds[] = { + { 5125, CNTL_RATE_5125 }, + { 6615, CNTL_RATE_6615 }, + { 8000, CNTL_RATE_8000 }, + { 9600, CNTL_RATE_9600 }, + { 11025, CNTL_RATE_11025 }, + { 16000, CNTL_RATE_16000 }, + { 18900, CNTL_RATE_18900 }, + { 22050, CNTL_RATE_22050 }, + { 27428, CNTL_RATE_27428 }, + { 32000, CNTL_RATE_32000 }, + { 33075, CNTL_RATE_33075 }, + { 37800, CNTL_RATE_37800 }, + { 44100, CNTL_RATE_44100 }, + { 48000, CNTL_RATE_48000 }, +}; + +u_int32_t +harmony_speed_bits(struct harmony_softc *sc, u_long *speedp) +{ + int i, n, selected = -1; + + n = sizeof(harmony_speeds) / sizeof(harmony_speeds[0]); + + if ((*speedp) <= harmony_speeds[0].speed) + selected = 0; + else if ((*speedp) >= harmony_speeds[n - 1].speed) + selected = n - 1; + else { + for (i = 1; selected == -1 && i < n; i++) { + if ((*speedp) == harmony_speeds[i].speed) + selected = i; + else if ((*speedp) < harmony_speeds[i].speed) { + int diff1, diff2; + + diff1 = (*speedp) - harmony_speeds[i - 1].speed; + diff2 = harmony_speeds[i].speed - (*speedp); + if (diff1 < diff2) + selected = i - 1; + else + selected = i; + } + } + } + + if (selected == -1) + selected = 2; + + *speedp = harmony_speeds[selected].speed; + return (harmony_speeds[selected].bits); +} + +struct cfdriver harmony_cd = { + NULL, "harmony", DV_DULL +}; + +struct cfattach harmony_ca = { + sizeof(struct harmony_softc), harmony_match, harmony_attach +}; |