/* $NetBSD: mavb.c,v 1.10 2015/02/17 11:25:43 macallan Exp $ */ /* $OpenBSD: mavb.c,v 1.6 2005/04/15 13:05:14 mickey Exp $ */ /* * Copyright (c) 2005 Mark Kettenis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef MAVB_DEBUG #ifdef MAVB_DEBUG #define DPRINTF(l,x) do { if (mavb_debug & (l)) printf x; } while (0) #define MAVB_DEBUG_INTR 0x0100 int mavb_debug = ~MAVB_DEBUG_INTR; #else #define DPRINTF(l,x) /* nothing */ #endif /* Repeat delays for volume buttons. */ #define MAVB_VOLUME_BUTTON_REPEAT_DEL1 400 /* 400ms to start repeating */ #define MAVB_VOLUME_BUTTON_REPEAT_DELN 100 /* 100ms between repeats */ /* XXX We need access to some of the MACE ISA registers. */ #define MAVB_ISA_NREGS 0x20 /* * AD1843 Mixer. */ enum { AD1843_RECORD_CLASS, AD1843_ADC_SOURCE, /* ADC Source Select */ AD1843_ADC_GAIN, /* ADC Input Gain */ AD1843_INPUT_CLASS, AD1843_DAC1_GAIN, /* DAC1 Analog/Digital Gain/Attenuation */ AD1843_DAC1_MUTE, /* DAC1 Analog Mute */ AD1843_DAC2_GAIN, /* DAC2 Mix Gain */ AD1843_AUX1_GAIN, /* Auxilliary 1 Mix Gain */ AD1843_AUX2_GAIN, /* Auxilliary 2 Mix Gain */ AD1843_AUX3_GAIN, /* Auxilliary 3 Mix Gain */ AD1843_MIC_GAIN, /* Microphone Mix Gain */ AD1843_MONO_GAIN, /* Mono Mix Gain */ AD1843_DAC2_MUTE, /* DAC2 Mix Mute */ AD1843_AUX1_MUTE, /* Auxilliary 1 Mix Mute */ AD1843_AUX2_MUTE, /* Auxilliary 2 Mix Mute */ AD1843_AUX3_MUTE, /* Auxilliary 3 Mix Mute */ AD1843_MIC_MUTE, /* Microphone Mix Mute */ AD1843_MONO_MUTE, /* Mono Mix Mute */ AD1843_SUM_MUTE, /* Sum Mute */ AD1843_OUTPUT_CLASS, AD1843_MNO_MUTE, /* Mono Output Mute */ AD1843_HPO_MUTE /* Headphone Output Mute */ }; /* ADC Source Select. The order matches the hardware bits. */ const char *ad1843_source[] = { AudioNline, AudioNmicrophone, AudioNaux "1", AudioNaux "2", AudioNaux "3", AudioNmono, AudioNdac "1", AudioNdac "2" }; /* Mix Control. The order matches the hardware register numbering. */ const char *ad1843_input[] = { AudioNdac "2", /* AD1843_DAC2__TO_MIXER */ AudioNaux "1", AudioNaux "2", AudioNaux "3", AudioNmicrophone, AudioNmono /* AD1843_MISC_SETTINGS */ }; #define MAVB_NFORMATS 2 static const struct audio_format mavb_formats[MAVB_NFORMATS] = { { NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_BE, 16, 16, 1, AUFMT_MONAURAL, 0, { 8000, 48000 } }, { NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_BE, 16, 16, 2, AUFMT_STEREO, 0, { 8000, 48000 } }, }; struct mavb_softc { device_t sc_dev; kmutex_t sc_lock; kmutex_t sc_intr_lock; bus_space_tag_t sc_st; bus_space_handle_t sc_sh; bus_dma_tag_t sc_dmat; bus_dmamap_t sc_dmamap; /* XXX We need access to some of the MACE ISA registers. */ bus_space_handle_t sc_isash; #define MAVB_ISA_RING_SIZE 0x1000 uint8_t *sc_ring; uint8_t *sc_start, *sc_end; int sc_blksize; void (*sc_intr)(void *); void *sc_intrarg; void *sc_get; int sc_count; u_long sc_play_rate; u_int sc_play_format; struct callout sc_volume_button_ch; struct audio_format sc_formats[MAVB_NFORMATS]; struct audio_encoding_set *sc_encodings; }; struct mavb_codecvar { stream_filter_t base; }; static stream_filter_t *mavb_factory (struct audio_softc *, int (*)(struct audio_softc *, stream_fetcher_t *, audio_stream_t *, int)); static void mavb_dtor(stream_filter_t *); /* XXX I'm going to complain every time I have to copy this macro */ #define DEFINE_FILTER(name) \ static int \ name##_fetch_to(struct audio_softc *, stream_fetcher_t *, \ audio_stream_t *, int); \ stream_filter_t *name(struct audio_softc *, \ const audio_params_t *, const audio_params_t *); \ stream_filter_t * \ name(struct audio_softc *sc, const audio_params_t *from, \ const audio_params_t *to) \ { \ return mavb_factory(sc, name##_fetch_to); \ } \ static int \ name##_fetch_to(struct audio_softc *asc, stream_fetcher_t *self, \ audio_stream_t *dst, int max_used) DEFINE_FILTER(mavb_16to24) { stream_filter_t *this; int m, err; this = (stream_filter_t *)self; max_used = (max_used + 1) & ~1; if ((err = this->prev->fetch_to(asc, this->prev, this->src, max_used))) return err; m = (dst->end - dst->start) & ~1; m = min(m, max_used); FILTER_LOOP_PROLOGUE(this->src, 2, dst, 4, m) { d[3] = 0; d[2] = s[1]; d[1] = s[0]; d[0] = (s[0] & 0x80) ? 0xff : 0; } FILTER_LOOP_EPILOGUE(this->src, dst); return 0; } DEFINE_FILTER(mavb_mts) { stream_filter_t *this; int m, err; this = (stream_filter_t *)self; max_used = (max_used + 1) & ~1; if ((err = this->prev->fetch_to(asc, this->prev, this->src, max_used))) return err; m = (dst->end - dst->start) & ~1; m = min(m, max_used); FILTER_LOOP_PROLOGUE(this->src, 4, dst, 8, m) { d[3] = d[7] = s[3]; d[2] = d[6] = s[2]; d[1] = d[5] = s[1]; d[0] = d[4] = s[0]; } FILTER_LOOP_EPILOGUE(this->src, dst); return 0; } static stream_filter_t * mavb_factory(struct audio_softc *asc, int (*fetch_to)(struct audio_softc *, stream_fetcher_t *, audio_stream_t *, int)) { struct mavb_codecvar *this; this = kmem_zalloc(sizeof(*this), KM_SLEEP); this->base.base.fetch_to = fetch_to; this->base.dtor = mavb_dtor; this->base.set_fetcher = stream_filter_set_fetcher; this->base.set_inputbuffer = stream_filter_set_inputbuffer; return &this->base; } static void mavb_dtor(stream_filter_t *this) { if (this != NULL) kmem_free(this, sizeof(struct mavb_codecvar)); } typedef uint64_t ad1843_addr_t; uint16_t ad1843_reg_read(struct mavb_softc *, ad1843_addr_t); uint16_t ad1843_reg_write(struct mavb_softc *, ad1843_addr_t, uint16_t); void ad1843_dump_regs(struct mavb_softc *); int mavb_match(device_t, cfdata_t, void *); void mavb_attach(device_t, device_t, void *); CFATTACH_DECL_NEW(mavb, sizeof(struct mavb_softc), mavb_match, mavb_attach, NULL, NULL); int mavb_open(void *, int); void mavb_close(void *); int mavb_query_encoding(void *, struct audio_encoding *); int mavb_set_params(void *, int, int, struct audio_params *, struct audio_params *, stream_filter_list_t *, stream_filter_list_t *); int mavb_round_blocksize(void *hdl, int, int, const audio_params_t *); int mavb_halt_output(void *); int mavb_halt_input(void *); int mavb_getdev(void *, struct audio_device *); int mavb_set_port(void *, struct mixer_ctrl *); int mavb_get_port(void *, struct mixer_ctrl *); int mavb_query_devinfo(void *, struct mixer_devinfo *); size_t mavb_round_buffersize(void *, int, size_t); int mavb_get_props(void *); int mavb_trigger_output(void *, void *, void *, int, void (*)(void *), void *, const audio_params_t *); int mavb_trigger_input(void *, void *, void *, int, void (*)(void *), void *, const audio_params_t *); void mavb_get_locks(void *, kmutex_t **, kmutex_t **); struct audio_hw_if mavb_sa_hw_if = { mavb_open, mavb_close, 0, mavb_query_encoding, mavb_set_params, mavb_round_blocksize, 0, 0, 0, 0, 0, mavb_halt_output, mavb_halt_input, 0, mavb_getdev, 0, mavb_set_port, mavb_get_port, mavb_query_devinfo, 0, 0, mavb_round_buffersize, 0, mavb_get_props, mavb_trigger_output, mavb_trigger_input, NULL, mavb_get_locks, }; struct audio_device mavb_device = { "A3", "", "mavb" }; int mavb_open(void *hdl, int flags) { return 0; } void mavb_close(void *hdl) { } int mavb_query_encoding(void *hdl, struct audio_encoding *ae) { struct mavb_softc *sc = (struct mavb_softc *)hdl; return auconv_query_encoding(sc->sc_encodings, ae); } static int mavb_set_play_rate(struct mavb_softc *sc, u_long sample_rate) { if (sample_rate < 4000 || sample_rate > 48000) return EINVAL; if (sc->sc_play_rate != sample_rate) { ad1843_reg_write(sc, AD1843_CLOCK2_SAMPLE_RATE, sample_rate); sc->sc_play_rate = sample_rate; } return 0; } static int mavb_set_play_format(struct mavb_softc *sc, u_int encoding) { uint16_t value; u_int format; switch(encoding) { case AUDIO_ENCODING_ULINEAR_BE: format = AD1843_PCM8; break; case AUDIO_ENCODING_SLINEAR_BE: format = AD1843_PCM16; break; case AUDIO_ENCODING_ULAW: format = AD1843_ULAW; break; case AUDIO_ENCODING_ALAW: format = AD1843_ALAW; break; default: return EINVAL; } if (sc->sc_play_format != format) { value = ad1843_reg_read(sc, AD1843_SERIAL_INTERFACE); value &= ~AD1843_DA1F_MASK; value |= (format << AD1843_DA1F_SHIFT); ad1843_reg_write(sc, AD1843_SERIAL_INTERFACE, value); sc->sc_play_format = format; } return 0; } int mavb_set_params(void *hdl, int setmode, int usemode, struct audio_params *play, struct audio_params *rec, stream_filter_list_t *pfil, stream_filter_list_t *rfil) { struct mavb_softc *sc = (struct mavb_softc *)hdl; struct audio_params *p; stream_filter_list_t *fil; int error; DPRINTF(1, ("%s: mavb_set_params: sample=%u precision=%d " "channels=%d\n", device_xname(sc->sc_dev), play->sample_rate, play->precision, play->channels)); if (setmode & AUMODE_PLAY) { if (play->sample_rate < 4000 || play->sample_rate > 48000) return EINVAL; p = play; fil = pfil; if (auconv_set_converter(sc->sc_formats, MAVB_NFORMATS, AUMODE_PLAY, p, TRUE, fil) < 0) return EINVAL; fil->append(fil, mavb_16to24, p); if (p->channels == 1) fil->append(fil, mavb_mts, p); if (fil->req_size > 0) p = &fil->filters[0].param; error = mavb_set_play_rate(sc, p->sample_rate); if (error) return error; error = mavb_set_play_format(sc, p->encoding); if (error) return error; } #if 0 if (setmode & AUMODE_RECORD) { if (rec->sample_rate < 4000 || rec->sample_rate > 48000) return EINVAL; } #endif return 0; } int mavb_round_blocksize(void *hdl, int bs, int mode, const audio_params_t *p) { /* Block size should be a multiple of 32. */ return (bs + 0x1f) & ~0x1f; } int mavb_halt_output(void *hdl) { struct mavb_softc *sc = (struct mavb_softc *)hdl; DPRINTF(1, ("%s: mavb_halt_output called\n", device_xname(sc->sc_dev))); bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CHANNEL2_CONTROL, 0); return 0; } int mavb_halt_input(void *hdl) { return 0; } int mavb_getdev(void *hdl, struct audio_device *ret) { *ret = mavb_device; return 0; } int mavb_set_port(void *hdl, struct mixer_ctrl *mc) { struct mavb_softc *sc = (struct mavb_softc *)hdl; u_char left, right; ad1843_addr_t reg; uint16_t value; DPRINTF(1, ("%s: mavb_set_port: dev=%d\n", device_xname(sc->sc_dev), mc->dev)); switch (mc->dev) { case AD1843_ADC_SOURCE: value = ad1843_reg_read(sc, AD1843_ADC_SOURCE_GAIN); value &= ~(AD1843_LSS_MASK | AD1843_RSS_MASK); value |= ((mc->un.ord << AD1843_LSS_SHIFT) & AD1843_LSS_MASK); value |= ((mc->un.ord << AD1843_RSS_SHIFT) & AD1843_RSS_MASK); ad1843_reg_write(sc, AD1843_ADC_SOURCE_GAIN, value); break; case AD1843_ADC_GAIN: left = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; right = mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; value = ad1843_reg_read(sc, AD1843_ADC_SOURCE_GAIN); value &= ~(AD1843_LIG_MASK | AD1843_RIG_MASK); value |= ((left >> 4) << AD1843_LIG_SHIFT); value |= ((right >> 4) << AD1843_RIG_SHIFT); ad1843_reg_write(sc, AD1843_ADC_SOURCE_GAIN, value); break; case AD1843_DAC1_GAIN: left = AUDIO_MAX_GAIN - mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; right = AUDIO_MAX_GAIN - mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; value = ad1843_reg_read(sc, AD1843_DAC1_ANALOG_GAIN); value &= ~(AD1843_LDA1G_MASK | AD1843_RDA1G_MASK); value |= ((left >> 2) << AD1843_LDA1G_SHIFT); value |= ((right >> 2) << AD1843_RDA1G_SHIFT); ad1843_reg_write(sc, AD1843_DAC1_ANALOG_GAIN, value); break; case AD1843_DAC1_MUTE: value = ad1843_reg_read(sc, AD1843_DAC1_ANALOG_GAIN); if (mc->un.ord == 0) value &= ~(AD1843_LDA1GM | AD1843_RDA1GM); else value |= (AD1843_LDA1GM | AD1843_RDA1GM); ad1843_reg_write(sc, AD1843_DAC1_ANALOG_GAIN, value); break; case AD1843_DAC2_GAIN: case AD1843_AUX1_GAIN: case AD1843_AUX2_GAIN: case AD1843_AUX3_GAIN: case AD1843_MIC_GAIN: left = AUDIO_MAX_GAIN - mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; right = AUDIO_MAX_GAIN - mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; reg = AD1843_DAC2_TO_MIXER + mc->dev - AD1843_DAC2_GAIN; value = ad1843_reg_read(sc, reg); value &= ~(AD1843_LD2M_MASK | AD1843_RD2M_MASK); value |= ((left >> 3) << AD1843_LD2M_SHIFT); value |= ((right >> 3) << AD1843_RD2M_SHIFT); ad1843_reg_write(sc, reg, value); break; case AD1843_MONO_GAIN: left = AUDIO_MAX_GAIN - mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]; value = ad1843_reg_read(sc, AD1843_MISC_SETTINGS); value &= ~AD1843_MNM_MASK; value |= ((left >> 3) << AD1843_MNM_SHIFT); ad1843_reg_write(sc, AD1843_MISC_SETTINGS, value); break; case AD1843_DAC2_MUTE: case AD1843_AUX1_MUTE: case AD1843_AUX2_MUTE: case AD1843_AUX3_MUTE: case AD1843_MIC_MUTE: case AD1843_MONO_MUTE: /* matches left channel */ reg = AD1843_DAC2_TO_MIXER + mc->dev - AD1843_DAC2_MUTE; value = ad1843_reg_read(sc, reg); if (mc->un.ord == 0) value &= ~(AD1843_LD2MM | AD1843_RD2MM); else value |= (AD1843_LD2MM | AD1843_RD2MM); ad1843_reg_write(sc, reg, value); break; case AD1843_SUM_MUTE: value = ad1843_reg_read(sc, AD1843_MISC_SETTINGS); if (mc->un.ord == 0) value &= ~AD1843_SUMM; else value |= AD1843_SUMM; ad1843_reg_write(sc, AD1843_MISC_SETTINGS, value); break; case AD1843_MNO_MUTE: value = ad1843_reg_read(sc, AD1843_MISC_SETTINGS); if (mc->un.ord == 0) value &= ~AD1843_MNOM; else value |= AD1843_MNOM; ad1843_reg_write(sc, AD1843_MISC_SETTINGS, value); break; case AD1843_HPO_MUTE: value = ad1843_reg_read(sc, AD1843_MISC_SETTINGS); if (mc->un.ord == 0) value &= ~AD1843_HPOM; else value |= AD1843_HPOM; ad1843_reg_write(sc, AD1843_MISC_SETTINGS, value); value = ad1843_reg_read(sc, AD1843_MISC_SETTINGS); break; default: return EINVAL; } return 0; } int mavb_get_port(void *hdl, struct mixer_ctrl *mc) { struct mavb_softc *sc = (struct mavb_softc *)hdl; u_char left, right; ad1843_addr_t reg; uint16_t value; DPRINTF(1, ("%s: mavb_get_port: dev=%d\n", device_xname(sc->sc_dev), mc->dev)); switch (mc->dev) { case AD1843_ADC_SOURCE: value = ad1843_reg_read(sc, AD1843_ADC_SOURCE_GAIN); mc->un.ord = (value & AD1843_LSS_MASK) >> AD1843_LSS_SHIFT; break; case AD1843_ADC_GAIN: value = ad1843_reg_read(sc, AD1843_ADC_SOURCE_GAIN); left = (value & AD1843_LIG_MASK) >> AD1843_LIG_SHIFT; right = (value & AD1843_RIG_MASK) >> AD1843_RIG_SHIFT; mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = (left << 4) | left; mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = (right << 2) | right; break; case AD1843_DAC1_GAIN: value = ad1843_reg_read(sc, AD1843_DAC1_ANALOG_GAIN); left = (value & AD1843_LDA1G_MASK) >> AD1843_LDA1G_SHIFT; right = (value & AD1843_RDA1G_MASK) >> AD1843_RDA1G_SHIFT; mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = AUDIO_MAX_GAIN - (left << 2); mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = AUDIO_MAX_GAIN - (right << 2); break; case AD1843_DAC1_MUTE: value = ad1843_reg_read(sc, AD1843_DAC1_ANALOG_GAIN); mc->un.ord = (value & AD1843_LDA1GM) ? 1 : 0; break; case AD1843_DAC2_GAIN: case AD1843_AUX1_GAIN: case AD1843_AUX2_GAIN: case AD1843_AUX3_GAIN: case AD1843_MIC_GAIN: reg = AD1843_DAC2_TO_MIXER + mc->dev - AD1843_DAC2_GAIN; value = ad1843_reg_read(sc, reg); left = (value & AD1843_LD2M_MASK) >> AD1843_LD2M_SHIFT; right = (value & AD1843_RD2M_MASK) >> AD1843_RD2M_SHIFT; mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = AUDIO_MAX_GAIN - (left << 3); mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = AUDIO_MAX_GAIN - (right << 3); break; case AD1843_MONO_GAIN: if (mc->un.value.num_channels != 1) return EINVAL; value = ad1843_reg_read(sc, AD1843_MISC_SETTINGS); left = (value & AD1843_MNM_MASK) >> AD1843_MNM_SHIFT; mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = AUDIO_MAX_GAIN - (left << 3); break; case AD1843_DAC2_MUTE: case AD1843_AUX1_MUTE: case AD1843_AUX2_MUTE: case AD1843_AUX3_MUTE: case AD1843_MIC_MUTE: case AD1843_MONO_MUTE: /* matches left channel */ reg = AD1843_DAC2_TO_MIXER + mc->dev - AD1843_DAC2_MUTE; value = ad1843_reg_read(sc, reg); mc->un.ord = (value & AD1843_LD2MM) ? 1 : 0; break; case AD1843_SUM_MUTE: value = ad1843_reg_read(sc, AD1843_MISC_SETTINGS); mc->un.ord = (value & AD1843_SUMM) ? 1 : 0; break; case AD1843_MNO_MUTE: value = ad1843_reg_read(sc, AD1843_MISC_SETTINGS); mc->un.ord = (value & AD1843_MNOM) ? 1 : 0; break; case AD1843_HPO_MUTE: value = ad1843_reg_read(sc, AD1843_MISC_SETTINGS); mc->un.ord = (value & AD1843_HPOM) ? 1 : 0; break; default: return EINVAL; } return 0; } int mavb_query_devinfo(void *hdl, struct mixer_devinfo *di) { int i; di->prev = di->next = AUDIO_MIXER_LAST; switch (di->index) { case AD1843_RECORD_CLASS: di->type = AUDIO_MIXER_CLASS; di->mixer_class = AD1843_RECORD_CLASS; strlcpy(di->label.name, AudioCrecord, sizeof di->label.name); break; case AD1843_ADC_SOURCE: di->type = AUDIO_MIXER_ENUM; di->mixer_class = AD1843_RECORD_CLASS; di->next = AD1843_ADC_GAIN; strlcpy(di->label.name, AudioNsource, sizeof di->label.name); di->un.e.num_mem = sizeof ad1843_source / sizeof ad1843_source[1]; for (i = 0; i < di->un.e.num_mem; i++) { strlcpy(di->un.e.member[i].label.name, ad1843_source[i], sizeof di->un.e.member[0].label.name); di->un.e.member[i].ord = i; } break; case AD1843_ADC_GAIN: di->type = AUDIO_MIXER_VALUE; di->mixer_class = AD1843_RECORD_CLASS; di->prev = AD1843_ADC_SOURCE; strlcpy(di->label.name, AudioNvolume, sizeof di->label.name); di->un.v.num_channels = 2; strlcpy(di->un.v.units.name, AudioNvolume, sizeof di->un.v.units.name); break; case AD1843_INPUT_CLASS: di->type = AUDIO_MIXER_CLASS; di->mixer_class = AD1843_INPUT_CLASS; strlcpy(di->label.name, AudioCinputs, sizeof di->label.name); break; case AD1843_DAC1_GAIN: di->type = AUDIO_MIXER_VALUE; di->mixer_class = AD1843_OUTPUT_CLASS; di->next = AD1843_DAC1_MUTE; strlcpy(di->label.name, AudioNmaster, sizeof di->label.name); di->un.v.num_channels = 2; strlcpy(di->un.v.units.name, AudioNvolume, sizeof di->un.v.units.name); break; case AD1843_DAC1_MUTE: di->type = AUDIO_MIXER_ENUM; di->mixer_class = AD1843_OUTPUT_CLASS; di->prev = AD1843_DAC1_GAIN; strlcpy(di->label.name, AudioNmute, sizeof di->label.name); di->un.e.num_mem = 2; strlcpy(di->un.e.member[0].label.name, AudioNoff, sizeof di->un.e.member[0].label.name); di->un.e.member[0].ord = 0; strlcpy(di->un.e.member[1].label.name, AudioNon, sizeof di->un.e.member[1].label.name); di->un.e.member[1].ord = 1; break; case AD1843_DAC2_GAIN: case AD1843_AUX1_GAIN: case AD1843_AUX2_GAIN: case AD1843_AUX3_GAIN: case AD1843_MIC_GAIN: case AD1843_MONO_GAIN: di->type = AUDIO_MIXER_VALUE; di->mixer_class = AD1843_INPUT_CLASS; di->next = di->index + AD1843_DAC2_MUTE - AD1843_DAC2_GAIN; strlcpy(di->label.name, ad1843_input[di->index - AD1843_DAC2_GAIN], sizeof di->label.name); if (di->index == AD1843_MONO_GAIN) di->un.v.num_channels = 1; else di->un.v.num_channels = 2; strlcpy(di->un.v.units.name, AudioNvolume, sizeof di->un.v.units.name); break; case AD1843_DAC2_MUTE: case AD1843_AUX1_MUTE: case AD1843_AUX2_MUTE: case AD1843_AUX3_MUTE: case AD1843_MIC_MUTE: case AD1843_MONO_MUTE: di->type = AUDIO_MIXER_ENUM; di->mixer_class = AD1843_INPUT_CLASS; di->prev = di->index + AD1843_DAC2_GAIN - AD1843_DAC2_MUTE; strlcpy(di->label.name, AudioNmute, sizeof di->label.name); di->un.e.num_mem = 2; strlcpy(di->un.e.member[0].label.name, AudioNoff, sizeof di->un.e.member[0].label.name); di->un.e.member[0].ord = 0; strlcpy(di->un.e.member[1].label.name, AudioNon, sizeof di->un.e.member[1].label.name); di->un.e.member[1].ord = 1; break; case AD1843_SUM_MUTE: di->type = AUDIO_MIXER_ENUM; di->mixer_class = AD1843_INPUT_CLASS; strlcpy(di->label.name, "sum." AudioNmute, sizeof di->label.name); di->un.e.num_mem = 2; strlcpy(di->un.e.member[0].label.name, AudioNoff, sizeof di->un.e.member[0].label.name); di->un.e.member[0].ord = 0; strlcpy(di->un.e.member[1].label.name, AudioNon, sizeof di->un.e.member[1].label.name); di->un.e.member[1].ord = 1; break; case AD1843_OUTPUT_CLASS: di->type = AUDIO_MIXER_CLASS; di->mixer_class = AD1843_OUTPUT_CLASS; strlcpy(di->label.name, AudioCoutputs, sizeof di->label.name); break; case AD1843_MNO_MUTE: di->type = AUDIO_MIXER_ENUM; di->mixer_class = AD1843_OUTPUT_CLASS; strlcpy(di->label.name, AudioNmono "." AudioNmute, sizeof di->label.name); di->un.e.num_mem = 2; strlcpy(di->un.e.member[0].label.name, AudioNoff, sizeof di->un.e.member[0].label.name); di->un.e.member[0].ord = 0; strlcpy(di->un.e.member[1].label.name, AudioNon, sizeof di->un.e.member[1].label.name); di->un.e.member[1].ord = 1; break; case AD1843_HPO_MUTE: di->type = AUDIO_MIXER_ENUM; di->mixer_class = AD1843_OUTPUT_CLASS; strlcpy(di->label.name, AudioNheadphone "." AudioNmute, sizeof di->label.name); di->un.e.num_mem = 2; strlcpy(di->un.e.member[0].label.name, AudioNoff, sizeof di->un.e.member[0].label.name); di->un.e.member[0].ord = 0; strlcpy(di->un.e.member[1].label.name, AudioNon, sizeof di->un.e.member[1].label.name); di->un.e.member[1].ord = 1; break; default: return EINVAL; } return 0; } size_t mavb_round_buffersize(void *hdl, int dir, size_t bufsize) { return bufsize; } int mavb_get_props(void *hdl) { return AUDIO_PROP_FULLDUPLEX | AUDIO_PROP_INDEPENDENT; } static void mavb_dma_output(struct mavb_softc *sc) { bus_space_tag_t st = sc->sc_st; bus_space_handle_t sh = sc->sc_sh; uint64_t write_ptr; uint64_t depth; uint8_t *src, *dst; int count; KASSERT(mutex_owned(&sc->sc_intr_lock)); write_ptr = bus_space_read_8(st, sh, MAVB_CHANNEL2_WRITE_PTR); depth = bus_space_read_8(st, sh, MAVB_CHANNEL2_DEPTH); dst = sc->sc_ring + write_ptr; src = sc->sc_get; count = (MAVB_ISA_RING_SIZE - depth - 32); while (--count >= 0) { *dst++ = *src++; if (dst >= sc->sc_ring + MAVB_ISA_RING_SIZE) dst = sc->sc_ring; if (src >= sc->sc_end) src = sc->sc_start; if (++sc->sc_count >= sc->sc_blksize) { if (sc->sc_intr) sc->sc_intr(sc->sc_intrarg); sc->sc_count = 0; } } write_ptr = dst - sc->sc_ring; bus_space_write_8(st, sh, MAVB_CHANNEL2_WRITE_PTR, write_ptr); sc->sc_get = src; } int mavb_trigger_output(void *hdl, void *start, void *end, int blksize, void (*intr)(void *), void *intrarg, const audio_params_t *param) { struct mavb_softc *sc = (struct mavb_softc *)hdl; DPRINTF(1, ("%s: mavb_trigger_output: start=%p end=%p " "blksize=%d intr=%p(%p)\n", device_xname(sc->sc_dev), start, end, blksize, intr, intrarg)); sc->sc_blksize = blksize; sc->sc_intr = intr; sc->sc_intrarg = intrarg; sc->sc_start = sc->sc_get = start; sc->sc_end = end; sc->sc_count = 0; bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CHANNEL2_CONTROL, MAVB_CHANNEL_RESET); delay(1000); bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CHANNEL2_CONTROL, 0); mavb_dma_output(sc); bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CHANNEL2_CONTROL, MAVB_CHANNEL_DMA_ENABLE | MAVB_CHANNEL_INT_50); return 0; } int mavb_trigger_input(void *hdl, void *start, void *end, int blksize, void (*intr)(void *), void *intrarg, const audio_params_t *param) { return 0; } void mavb_get_locks(void *hdl, kmutex_t **intr, kmutex_t **thread) { struct mavb_softc *sc = (struct mavb_softc *)hdl; *intr = &sc->sc_intr_lock; *thread = &sc->sc_lock; } static void mavb_button_repeat(void *hdl) { struct mavb_softc *sc = (struct mavb_softc *)hdl; uint64_t intmask, control; uint16_t value, left, right; DPRINTF(1, ("%s: mavb_repeat called\n", device_xname(sc->sc_dev))); #define MAVB_CONTROL_VOLUME_BUTTONS \ (MAVB_CONTROL_VOLUME_BUTTON_UP | MAVB_CONTROL_VOLUME_BUTTON_DOWN) control = bus_space_read_8(sc->sc_st, sc->sc_sh, MAVB_CONTROL); if (control & MAVB_CONTROL_VOLUME_BUTTONS) { value = ad1843_reg_read(sc, AD1843_DAC1_ANALOG_GAIN); left = (value & AD1843_LDA1G_MASK) >> AD1843_LDA1G_SHIFT; right = (value & AD1843_RDA1G_MASK) >> AD1843_RDA1G_SHIFT; if (control & MAVB_CONTROL_VOLUME_BUTTON_UP) { control &= ~MAVB_CONTROL_VOLUME_BUTTON_UP; if (left > 0) left--; /* attenuation! */ if (right > 0) right--; } if (control & MAVB_CONTROL_VOLUME_BUTTON_DOWN) { control &= ~MAVB_CONTROL_VOLUME_BUTTON_DOWN; if (left < 63) left++; if (right < 63) right++; } bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CONTROL, control); value &= ~(AD1843_LDA1G_MASK | AD1843_RDA1G_MASK); value |= (left << AD1843_LDA1G_SHIFT); value |= (right << AD1843_RDA1G_SHIFT); ad1843_reg_write(sc, AD1843_DAC1_ANALOG_GAIN, value); callout_reset(&sc->sc_volume_button_ch, (hz * MAVB_VOLUME_BUTTON_REPEAT_DELN) / 1000, mavb_button_repeat, sc); } else { /* Enable volume button interrupts again. */ intmask = bus_space_read_8(sc->sc_st, sc->sc_isash, MACE_ISA_INT_MASK); bus_space_write_8(sc->sc_st, sc->sc_isash, MACE_ISA_INT_MASK, intmask | MACE_ISA_INT_AUDIO_SC); } } static int mavb_intr(void *arg) { struct mavb_softc *sc = arg; uint64_t stat, intmask; mutex_spin_enter(&sc->sc_intr_lock); stat = bus_space_read_8(sc->sc_st, sc->sc_isash, MACE_ISA_INT_STATUS); DPRINTF(MAVB_DEBUG_INTR, ("%s: mavb_intr: stat = 0x%llx\n", device_xname(sc->sc_dev), stat)); if (stat & MACE_ISA_INT_AUDIO_SC) { /* Disable volume button interrupts. */ intmask = bus_space_read_8(sc->sc_st, sc->sc_isash, MACE_ISA_INT_MASK); bus_space_write_8(sc->sc_st, sc->sc_isash, MACE_ISA_INT_MASK, intmask & ~MACE_ISA_INT_AUDIO_SC); callout_reset(&sc->sc_volume_button_ch, (hz * MAVB_VOLUME_BUTTON_REPEAT_DEL1) / 1000, mavb_button_repeat, sc); } if (stat & MACE_ISA_INT_AUDIO_DMA2) mavb_dma_output(sc); mutex_spin_exit(&sc->sc_intr_lock); return 1; } int mavb_match(device_t parent, cfdata_t match, void *aux) { return 1; } void mavb_attach(device_t parent, device_t self, void *aux) { struct mavb_softc *sc = device_private(self); struct mace_attach_args *maa = aux; bus_dma_segment_t seg; uint64_t control; uint16_t value; int rseg, err; sc->sc_dev = self; mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED); sc->sc_st = maa->maa_st; if (bus_space_subregion(sc->sc_st, maa->maa_sh, maa->maa_offset, 0, &sc->sc_sh) != 0) { printf(": can't map i/o space\n"); return; } /* XXX We need access to some of the MACE ISA registers. */ if (bus_space_subregion(sc->sc_st, maa->maa_sh, 0, 0, &sc->sc_isash) != 0) { printf(": can't map isa i/o space\n"); return; } /* Set up DMA structures. */ sc->sc_dmat = maa->maa_dmat; if (bus_dmamap_create(sc->sc_dmat, 4 * MAVB_ISA_RING_SIZE, 1, 4 * MAVB_ISA_RING_SIZE, 0, 0, &sc->sc_dmamap)) { printf(": can't create MACE ISA DMA map\n"); return; } if (bus_dmamem_alloc(sc->sc_dmat, 4 * MAVB_ISA_RING_SIZE, MACE_ISA_RING_ALIGN, 0, &seg, 1, &rseg, BUS_DMA_NOWAIT)) { printf(": can't allocate ring buffer\n"); return; } if (bus_dmamem_map(sc->sc_dmat, &seg, rseg, 4 * MAVB_ISA_RING_SIZE, (void *)&sc->sc_ring, BUS_DMA_COHERENT)) { printf(": can't map ring buffer\n"); return; } if (bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_ring, 4 * MAVB_ISA_RING_SIZE, NULL, BUS_DMA_NOWAIT)) { printf(": can't load MACE ISA DMA map\n"); return; } sc->sc_ring += MAVB_ISA_RING_SIZE; /* XXX */ bus_space_write_8(sc->sc_st, sc->sc_isash, MACE_ISA_RINGBASE, sc->sc_dmamap->dm_segs[0].ds_addr); /* Establish interrupt. */ cpu_intr_establish(maa->maa_intr, maa->maa_intrmask, mavb_intr, sc); control = bus_space_read_8(sc->sc_st, sc->sc_sh, MAVB_CONTROL); if (!(control & MAVB_CONTROL_CODEC_PRESENT)) { printf(": no codec present\n"); return; } /* 2. Assert the RESET signal. */ bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CONTROL, MAVB_CONTROL_RESET); delay(1); /* at least 100 ns */ /* 3. Deassert the RESET signal and enter a wait period to allow the AD1843 internal clocks and the external crystal oscillator to stabilize. */ bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CONTROL, 0); delay(800); /* typically 400 us to 800 us */ if (ad1843_reg_read(sc, AD1843_CODEC_STATUS) & AD1843_INIT) { printf(": codec not ready\n"); return; } /* 4. Put the conversion sources into standby. */ value = ad1843_reg_read(sc, AD1843_FUNDAMENTAL_SETTINGS); ad1843_reg_write(sc, AD1843_FUNDAMENTAL_SETTINGS, value & ~AD1843_PDNI); delay (500000); /* approximately 474 ms */ if (ad1843_reg_read(sc, AD1843_CODEC_STATUS) & AD1843_PDNO) { printf(": can't power up conversion resources\n"); return; } /* 5. Power up the clock generators and enable clock output pins. */ value = ad1843_reg_read(sc, AD1843_FUNDAMENTAL_SETTINGS); ad1843_reg_write(sc, AD1843_FUNDAMENTAL_SETTINGS, value | AD1843_C2EN); /* 6. Configure conversion resources while they are in standby. */ value = ad1843_reg_read(sc, AD1843_CHANNEL_SAMPLE_RATE); ad1843_reg_write(sc, AD1843_CHANNEL_SAMPLE_RATE, value | (2 << AD1843_DA1C_SHIFT)); /* 7. Enable conversion resources. */ value = ad1843_reg_read(sc, AD1843_CHANNEL_POWER_DOWN); ad1843_reg_write(sc, AD1843_CHANNEL_POWER_DOWN, value | (AD1843_DA1EN | AD1843_AAMEN)); /* 8. Configure conversion resources while they are enabled. */ value = ad1843_reg_read(sc, AD1843_DAC1_ANALOG_GAIN); ad1843_reg_write(sc, AD1843_DAC1_ANALOG_GAIN, value & ~(AD1843_LDA1GM | AD1843_RDA1GM)); value = ad1843_reg_read(sc, AD1843_DAC1_DIGITAL_GAIN); ad1843_reg_write(sc, AD1843_DAC1_DIGITAL_GAIN, value & ~(AD1843_LDA1AM | AD1843_RDA1AM)); value = ad1843_reg_read(sc, AD1843_MISC_SETTINGS); ad1843_reg_write(sc, AD1843_MISC_SETTINGS, value & ~(AD1843_HPOM | AD1843_MNOM)); value = ad1843_reg_read(sc, AD1843_CODEC_STATUS); printf(": AD1843 rev %d\n", (u_int)value & AD1843_REVISION_MASK); sc->sc_play_rate = 48000; sc->sc_play_format = AD1843_PCM8; memcpy(sc->sc_formats, mavb_formats, sizeof(mavb_formats)); err = auconv_create_encodings(sc->sc_formats, MAVB_NFORMATS, &sc->sc_encodings); if (err) { printf("%s: couldn't create encodings: %d\n", device_xname(self), err); return; } callout_init(&sc->sc_volume_button_ch, 0); audio_attach_mi(&mavb_sa_hw_if, sc, self); return; } uint16_t ad1843_reg_read(struct mavb_softc *sc, ad1843_addr_t addr) { bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CODEC_CONTROL, (addr & MAVB_CODEC_ADDRESS_MASK) << MAVB_CODEC_ADDRESS_SHIFT | MAVB_CODEC_READ); delay(200); return bus_space_read_8(sc->sc_st, sc->sc_sh, MAVB_CODEC_STATUS); } uint16_t ad1843_reg_write(struct mavb_softc *sc, ad1843_addr_t addr, uint16_t value) { bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CODEC_CONTROL, (addr & MAVB_CODEC_ADDRESS_MASK) << MAVB_CODEC_ADDRESS_SHIFT | (value & MAVB_CODEC_WORD_MASK) << MAVB_CODEC_WORD_SHIFT); delay(200); return bus_space_read_8(sc->sc_st, sc->sc_sh, MAVB_CODEC_STATUS); } void ad1843_dump_regs(struct mavb_softc *sc) { uint16_t addr; for (addr = 0; addr < AD1843_NREGS; addr++) printf("%d: 0x%04x\n", addr, ad1843_reg_read(sc, addr)); }