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
 

Reply via email to