Module Name: src Committed By: jmcneill Date: Sun Mar 15 18:31:29 UTC 2015
Modified Files: src/sys/arch/arm/broadcom: bcm2835_vcaudio.c Log Message: 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.8 -r1.9 src/sys/arch/arm/broadcom/bcm2835_vcaudio.c 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.8 src/sys/arch/arm/broadcom/bcm2835_vcaudio.c:1.9 --- src/sys/arch/arm/broadcom/bcm2835_vcaudio.c:1.8 Fri Mar 13 22:48:41 2015 +++ src/sys/arch/arm/broadcom/bcm2835_vcaudio.c Sun Mar 15 18:31:29 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: bcm2835_vcaudio.c,v 1.8 2015/03/13 22:48:41 jmcneill Exp $ */ +/* $NetBSD: bcm2835_vcaudio.c,v 1.9 2015/03/15 18:31:29 jmcneill 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.8 2015/03/13 22:48:41 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: bcm2835_vcaudio.c,v 1.9 2015/03/15 18:31:29 jmcneill Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -52,14 +52,25 @@ __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, }; @@ -125,7 +136,7 @@ struct vcaudio_softc { short sc_peer_version; - int sc_hpvol; + int sc_hwvol[3]; enum vcaudio_dest sc_dest; uint8_t sc_swvol; @@ -267,7 +278,9 @@ vcaudio_init(struct vcaudio_softc *sc) int error; sc->sc_swvol = 255; - sc->sc_hpvol = 128; + 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; @@ -352,7 +365,7 @@ 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_hpvol); + 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) { @@ -642,58 +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_swvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; + sc->sc_hwvol[dest] = hwvol; + if (dest != sc->sc_dest) return 0; - case VCAUDIO_OUTPUT_HEADPHONE_VOLUME: - sc->sc_hpvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; - 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_hpvol); - msg.u.control.dest = sc->sc_dest; + 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; - - 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_hpvol); - msg.u.control.dest = sc->sc_dest; - - 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); - - return error; + return vcaudio_set_volume(sc, mc->un.ord, + sc->sc_hwvol[mc->un.ord]); } return ENXIO; } @@ -702,18 +716,28 @@ static int vcaudio_get_port(void *priv, mixer_ctrl_t *mc) { struct vcaudio_softc *sc = priv; - uint8_t vol = sc->sc_swvol; switch (mc->dev) { case VCAUDIO_OUTPUT_MASTER_VOLUME: case VCAUDIO_INPUT_DAC_VOLUME: - mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = vol; - mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = vol; + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = + 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_hpvol; + 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; @@ -746,12 +770,31 @@ 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: