Module Name: src Committed By: snj Date: Sun Mar 15 22:55:47 UTC 2015
Modified Files: src/sys/arch/arm/broadcom [netbsd-7]: bcm2835_vcaudio.c files.bcm2835 Log Message: Pull up following revision(s) (requested by jmcneill in ticket #594): sys/arch/arm/broadcom/bcm2835_vcaudio.c: revision 1.8, 1.9 sys/arch/arm/broadcom/files.bcm2835: revision 1.25 port-arm/49057: Raspberry Pi Audio volume control does not work - Use software volume control for outputs.master/inputs.dac - Previous volume control (for analog output only) is available on new outputs.headphones mixer control. - Add an outputs.select enum to choose between "auto", "headphones", and "hdmi" outputs. -- port-arm/49057: Raspberry Pi Audio volume control does not work AUDS server messages expect volume to be expressed in units of 1/256 dB, where we previously (incorrectly) treated it as as percentage. Map the NetBSD audio level (0-255) to 20 steps (levels from FreeBSD bcm2835_audio.c) and provide independent volume knobs for outputs.auto, outputs.hdmi, and outputs.headphones. To generate a diff of this commit: cvs rdiff -u -r1.3.4.1 -r1.3.4.2 src/sys/arch/arm/broadcom/bcm2835_vcaudio.c cvs rdiff -u -r1.21.2.3 -r1.21.2.4 src/sys/arch/arm/broadcom/files.bcm2835 Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/broadcom/bcm2835_vcaudio.c diff -u src/sys/arch/arm/broadcom/bcm2835_vcaudio.c:1.3.4.1 src/sys/arch/arm/broadcom/bcm2835_vcaudio.c:1.3.4.2 --- src/sys/arch/arm/broadcom/bcm2835_vcaudio.c:1.3.4.1 Sun Oct 19 15:22:00 2014 +++ src/sys/arch/arm/broadcom/bcm2835_vcaudio.c Sun Mar 15 22:55:47 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: bcm2835_vcaudio.c,v 1.3.4.1 2014/10/19 15:22:00 martin Exp $ */ +/* $NetBSD: bcm2835_vcaudio.c,v 1.3.4.2 2015/03/15 22:55:47 snj Exp $ */ /*- * Copyright (c) 2013 Jared D. McNeill <jmcne...@invisible.ca> @@ -31,7 +31,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: bcm2835_vcaudio.c,v 1.3.4.1 2014/10/19 15:22:00 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: bcm2835_vcaudio.c,v 1.3.4.2 2015/03/15 22:55:47 snj Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -44,6 +44,7 @@ __KERNEL_RCSID(0, "$NetBSD: bcm2835_vcau #include <sys/audioio.h> #include <dev/audio_if.h> #include <dev/auconv.h> +#include <dev/auvolconv.h> #include <interface/compat/vchi_bsd.h> #include <interface/vchiq_arm/vchiq_netbsd.h> @@ -51,13 +52,26 @@ __KERNEL_RCSID(0, "$NetBSD: bcm2835_vcau #include "bcm2835_vcaudioreg.h" -#define vol2pct(vol) (((vol) * 100) / 255) +/* levels with 5% volume step */ +static int vcaudio_levels[] = { + -10239, -4605, -3794, -3218, -2772, + -2407, -2099, -1832, -1597, -1386, + -1195, -1021, -861, -713, -575, + -446, -325, -210, -102, 0, +}; + +#define vol2db(vol) vcaudio_levels[((vol) * 20) >> 8] +#define vol2vc(vol) ((uint32_t)(-(vol2db((vol)) << 8) / 100)) enum { VCAUDIO_OUTPUT_CLASS, VCAUDIO_INPUT_CLASS, VCAUDIO_OUTPUT_MASTER_VOLUME, VCAUDIO_INPUT_DAC_VOLUME, + VCAUDIO_OUTPUT_AUTO_VOLUME, + VCAUDIO_OUTPUT_HEADPHONE_VOLUME, + VCAUDIO_OUTPUT_HDMI_VOLUME, + VCAUDIO_OUTPUT_SELECT, VCAUDIO_ENUM_LAST, }; @@ -122,8 +136,10 @@ struct vcaudio_softc { short sc_peer_version; - int sc_volume; + int sc_hwvol[3]; enum vcaudio_dest sc_dest; + + uint8_t sc_swvol; }; static int vcaudio_match(device_t, cfdata_t, void *); @@ -163,6 +179,10 @@ static int vcaudio_trigger_input(void *, static void vcaudio_get_locks(void *, kmutex_t **, kmutex_t **); +static stream_filter_t *vcaudio_swvol_filter(struct audio_softc *, + const audio_params_t *, const audio_params_t *); +static void vcaudio_swvol_dtor(stream_filter_t *); + static const struct audio_hw_if vcaudio_hw_if = { .open = vcaudio_open, .close = vcaudio_close, @@ -257,7 +277,10 @@ vcaudio_init(struct vcaudio_softc *sc) VC_AUDIO_MSG_T msg; int error; - sc->sc_volume = 128; + sc->sc_swvol = 255; + sc->sc_hwvol[VCAUDIO_DEST_AUTO] = 255; + sc->sc_hwvol[VCAUDIO_DEST_HP] = 255; + sc->sc_hwvol[VCAUDIO_DEST_HDMI] = 255; sc->sc_dest = VCAUDIO_DEST_AUTO; sc->sc_format.mode = AUMODE_PLAY|AUMODE_RECORD; @@ -342,8 +365,8 @@ vcaudio_init(struct vcaudio_softc *sc) memset(&msg, 0, sizeof(msg)); msg.type = VC_AUDIO_MSG_TYPE_CONTROL; - msg.u.control.volume = vol2pct(sc->sc_volume); - msg.u.control.dest = VCAUDIO_DEST_AUTO; + msg.u.control.volume = vol2vc(sc->sc_hwvol[sc->sc_dest]); + msg.u.control.dest = sc->sc_dest; error = vcaudio_msg_sync(sc, &msg, sizeof(msg)); if (error) { aprint_error_dev(sc->sc_dev, @@ -585,6 +608,9 @@ vcaudio_set_params(void *priv, int setmo AUMODE_PLAY, play, true, pfil); if (index < 0) return EINVAL; + if (pfil->req_size > 0) + play = &pfil->filters[0].param; + pfil->prepend(pfil, vcaudio_swvol_filter, play); } return 0; @@ -629,33 +655,59 @@ vcaudio_halt_input(void *priv) } static int -vcaudio_set_port(void *priv, mixer_ctrl_t *mc) +vcaudio_set_volume(struct vcaudio_softc *sc, enum vcaudio_dest dest, + int hwvol) { - struct vcaudio_softc *sc = priv; VC_AUDIO_MSG_T msg; int error; - switch (mc->dev) { - case VCAUDIO_OUTPUT_MASTER_VOLUME: - case VCAUDIO_INPUT_DAC_VOLUME: - sc->sc_volume = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; + sc->sc_hwvol[dest] = hwvol; + if (dest != sc->sc_dest) + return 0; - vchi_service_use(sc->sc_service); + vchi_service_use(sc->sc_service); - memset(&msg, 0, sizeof(msg)); - msg.type = VC_AUDIO_MSG_TYPE_CONTROL; - msg.u.control.volume = vol2pct(sc->sc_volume); - msg.u.control.dest = VCAUDIO_DEST_AUTO; + memset(&msg, 0, sizeof(msg)); + msg.type = VC_AUDIO_MSG_TYPE_CONTROL; + msg.u.control.volume = vol2vc(hwvol); + msg.u.control.dest = dest; - error = vcaudio_msg_sync(sc, &msg, sizeof(msg)); - if (error) { - device_printf(sc->sc_dev, - "couldn't send CONTROL message (%d)\n", error); - } + error = vcaudio_msg_sync(sc, &msg, sizeof(msg)); + if (error) { + device_printf(sc->sc_dev, + "couldn't send CONTROL message (%d)\n", error); + } - vchi_service_release(sc->sc_service); + vchi_service_release(sc->sc_service); - return error; + return error; +} + +static int +vcaudio_set_port(void *priv, mixer_ctrl_t *mc) +{ + struct vcaudio_softc *sc = priv; + + switch (mc->dev) { + case VCAUDIO_OUTPUT_MASTER_VOLUME: + case VCAUDIO_INPUT_DAC_VOLUME: + sc->sc_swvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; + return 0; + case VCAUDIO_OUTPUT_AUTO_VOLUME: + return vcaudio_set_volume(sc, VCAUDIO_DEST_AUTO, + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]); + case VCAUDIO_OUTPUT_HEADPHONE_VOLUME: + return vcaudio_set_volume(sc, VCAUDIO_DEST_HP, + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]); + case VCAUDIO_OUTPUT_HDMI_VOLUME: + return vcaudio_set_volume(sc, VCAUDIO_DEST_HDMI, + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]); + case VCAUDIO_OUTPUT_SELECT: + if (mc->un.ord < 0 || mc->un.ord > 2) + return EINVAL; + sc->sc_dest = mc->un.ord; + return vcaudio_set_volume(sc, mc->un.ord, + sc->sc_hwvol[mc->un.ord]); } return ENXIO; } @@ -670,7 +722,25 @@ vcaudio_get_port(void *priv, mixer_ctrl_ case VCAUDIO_INPUT_DAC_VOLUME: mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = - sc->sc_volume; + sc->sc_swvol; + return 0; + case VCAUDIO_OUTPUT_AUTO_VOLUME: + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = + sc->sc_hwvol[VCAUDIO_DEST_AUTO]; + return 0; + case VCAUDIO_OUTPUT_HEADPHONE_VOLUME: + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = + sc->sc_hwvol[VCAUDIO_DEST_HP]; + return 0; + case VCAUDIO_OUTPUT_HDMI_VOLUME: + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = + sc->sc_hwvol[VCAUDIO_DEST_HDMI]; + return 0; + case VCAUDIO_OUTPUT_SELECT: + mc->un.ord = sc->sc_dest; return 0; } return ENXIO; @@ -700,6 +770,33 @@ vcaudio_query_devinfo(void *priv, mixer_ di->un.v.num_channels = 2; strcpy(di->un.v.units.name, AudioNvolume); return 0; + case VCAUDIO_OUTPUT_AUTO_VOLUME: + di->mixer_class = VCAUDIO_OUTPUT_CLASS; + strcpy(di->label.name, "auto"); + di->type = AUDIO_MIXER_VALUE; + di->next = di->prev = AUDIO_MIXER_LAST; + di->un.v.num_channels = 2; + di->un.v.delta = 13; + strcpy(di->un.v.units.name, AudioNvolume); + return 0; + case VCAUDIO_OUTPUT_HEADPHONE_VOLUME: + di->mixer_class = VCAUDIO_OUTPUT_CLASS; + strcpy(di->label.name, AudioNheadphone); + di->type = AUDIO_MIXER_VALUE; + di->next = di->prev = AUDIO_MIXER_LAST; + di->un.v.num_channels = 2; + di->un.v.delta = 13; + strcpy(di->un.v.units.name, AudioNvolume); + return 0; + case VCAUDIO_OUTPUT_HDMI_VOLUME: + di->mixer_class = VCAUDIO_OUTPUT_CLASS; + strcpy(di->label.name, "hdmi"); + di->type = AUDIO_MIXER_VALUE; + di->next = di->prev = AUDIO_MIXER_LAST; + di->un.v.num_channels = 2; + di->un.v.delta = 13; + strcpy(di->un.v.units.name, AudioNvolume); + return 0; case VCAUDIO_INPUT_DAC_VOLUME: di->mixer_class = VCAUDIO_INPUT_CLASS; strcpy(di->label.name, AudioNdac); @@ -708,6 +805,19 @@ vcaudio_query_devinfo(void *priv, mixer_ di->un.v.num_channels = 2; strcpy(di->un.v.units.name, AudioNvolume); return 0; + case VCAUDIO_OUTPUT_SELECT: + di->mixer_class = VCAUDIO_OUTPUT_CLASS; + strcpy(di->label.name, AudioNselect); + di->type = AUDIO_MIXER_ENUM; + di->next = di->prev = AUDIO_MIXER_LAST; + di->un.e.num_mem = 3; + di->un.e.member[0].ord = 0; + strcpy(di->un.e.member[0].label.name, "auto"); + di->un.e.member[1].ord = 1; + strcpy(di->un.e.member[1].label.name, AudioNheadphone); + di->un.e.member[2].ord = 2; + strcpy(di->un.e.member[2].label.name, "hdmi"); + return 0; } return ENXIO; @@ -786,3 +896,28 @@ vcaudio_get_locks(void *priv, kmutex_t * *intr = &sc->sc_intr_lock; *thread = &sc->sc_lock; } + +static stream_filter_t * +vcaudio_swvol_filter(struct audio_softc *asc, + const audio_params_t *from, const audio_params_t *to) +{ + auvolconv_filter_t *this; + device_t dev = audio_get_device(asc); + struct vcaudio_softc *sc = device_private(dev); + + this = kmem_alloc(sizeof(auvolconv_filter_t), KM_SLEEP); + this->base.base.fetch_to = auvolconv_slinear16_le_fetch_to; + this->base.dtor = vcaudio_swvol_dtor; + this->base.set_fetcher = stream_filter_set_fetcher; + this->base.set_inputbuffer = stream_filter_set_inputbuffer; + this->vol = &sc->sc_swvol; + + return (stream_filter_t *)this; +} + +static void +vcaudio_swvol_dtor(stream_filter_t *this) +{ + if (this) + kmem_free(this, sizeof(auvolconv_filter_t)); +} Index: src/sys/arch/arm/broadcom/files.bcm2835 diff -u src/sys/arch/arm/broadcom/files.bcm2835:1.21.2.3 src/sys/arch/arm/broadcom/files.bcm2835:1.21.2.4 --- src/sys/arch/arm/broadcom/files.bcm2835:1.21.2.3 Wed Mar 11 20:22:55 2015 +++ src/sys/arch/arm/broadcom/files.bcm2835 Sun Mar 15 22:55:47 2015 @@ -1,4 +1,4 @@ -# $NetBSD: files.bcm2835,v 1.21.2.3 2015/03/11 20:22:55 snj Exp $ +# $NetBSD: files.bcm2835,v 1.21.2.4 2015/03/15 22:55:47 snj Exp $ # # Configuration info for Broadcom BCM2835 ARM Peripherals # @@ -92,7 +92,7 @@ file arch/arm/broadcom/bcm2835_genfb.c b include "external/bsd/vchiq/conf/files.vchiq" # VC audio -device vcaudio: audiobus, auconv, mulaw, aurateconv +device vcaudio: audiobus, auconv, mulaw, aurateconv, auvolconv attach vcaudio at vchiqbus file arch/arm/broadcom/bcm2835_vcaudio.c vcaudio