Module Name:    src
Committed By:   nonaka
Date:           Sat Apr 18 05:20:21 UTC 2009

Modified Files:
        src/sys/arch/zaurus/dev: scoop.c scoopvar.h zaudio.c

Log Message:
zaudio(4): Support recording.


To generate a diff of this commit:
cvs rdiff -u -r1.6 -r1.7 src/sys/arch/zaurus/dev/scoop.c
cvs rdiff -u -r1.4 -r1.5 src/sys/arch/zaurus/dev/scoopvar.h
cvs rdiff -u -r1.9 -r1.10 src/sys/arch/zaurus/dev/zaudio.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/zaurus/dev/scoop.c
diff -u src/sys/arch/zaurus/dev/scoop.c:1.6 src/sys/arch/zaurus/dev/scoop.c:1.7
--- src/sys/arch/zaurus/dev/scoop.c:1.6	Thu Jan 29 12:28:15 2009
+++ src/sys/arch/zaurus/dev/scoop.c	Sat Apr 18 05:20:21 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: scoop.c,v 1.6 2009/01/29 12:28:15 nonaka Exp $	*/
+/*	$NetBSD: scoop.c,v 1.7 2009/04/18 05:20:21 nonaka Exp $	*/
 /*	$OpenBSD: zaurus_scoop.c,v 1.12 2005/11/17 05:26:31 uwe Exp $	*/
 
 /*
@@ -18,7 +18,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: scoop.c,v 1.6 2009/01/29 12:28:15 nonaka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: scoop.c,v 1.7 2009/04/18 05:20:21 nonaka Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -256,6 +256,19 @@
 }
 
 /*
+ * Enable or disable the mic bias
+ */
+void
+scoop_set_mic_bias(int onoff)
+{
+	struct scoop_softc *sc1;
+
+	sc1 = device_lookup_private(&scoop_cd, 1);
+	if (sc1 != NULL)
+		scoop_gpio_pin_write(sc1, SCOOP1_MIC_BIAS, onoff);
+}
+
+/*
  * Turn on pullup resistor while not reading the remote control.
  */
 void

Index: src/sys/arch/zaurus/dev/scoopvar.h
diff -u src/sys/arch/zaurus/dev/scoopvar.h:1.4 src/sys/arch/zaurus/dev/scoopvar.h:1.5
--- src/sys/arch/zaurus/dev/scoopvar.h:1.4	Wed Oct 17 19:58:34 2007
+++ src/sys/arch/zaurus/dev/scoopvar.h	Sat Apr 18 05:20:21 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: scoopvar.h,v 1.4 2007/10/17 19:58:34 garbled Exp $	*/
+/*	$NetBSD: scoopvar.h,v 1.5 2009/04/18 05:20:21 nonaka Exp $	*/
 /*	$OpenBSD: zaurus_scoopvar.h,v 1.10 2005/11/17 05:26:31 uwe Exp $	*/
 
 /*
@@ -32,6 +32,7 @@
 void	scoop_set_sdmmc_power(int);
 void	scoop_check_mcr(void);
 void	scoop_set_headphone(int);
+void	scoop_set_mic_bias(int);
 void	scoop_akin_pullup(int);
 void	scoop_suspend(void);
 void	scoop_resume(void);

Index: src/sys/arch/zaurus/dev/zaudio.c
diff -u src/sys/arch/zaurus/dev/zaudio.c:1.9 src/sys/arch/zaurus/dev/zaudio.c:1.10
--- src/sys/arch/zaurus/dev/zaudio.c:1.9	Fri Mar 13 13:55:18 2009
+++ src/sys/arch/zaurus/dev/zaudio.c	Sat Apr 18 05:20:21 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: zaudio.c,v 1.9 2009/03/13 13:55:18 nonaka Exp $	*/
+/*	$NetBSD: zaudio.c,v 1.10 2009/04/18 05:20:21 nonaka Exp $	*/
 /*	$OpenBSD: zaurus_audio.c,v 1.8 2005/08/18 13:23:02 robert Exp $	*/
 
 /*
@@ -17,14 +17,39 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+/*-
+ * Copyright (c) 2009 NONAKA Kimihiro <[email protected]>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
+
 /*
  * TODO:
  *	- powerhooks (currently only works until first suspend)
- *	- record support
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: zaudio.c,v 1.9 2009/03/13 13:55:18 nonaka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: zaudio.c,v 1.10 2009/04/18 05:20:21 nonaka Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -52,7 +77,6 @@
 #include <zaurus/dev/scoopvar.h>
 
 #define WM8750_ADDRESS  0x1B
-#define SPKR_VOLUME	112
 
 #define wm8750_write(sc, reg, val) \
 	pxa2x0_i2c_write_2(&sc->sc_i2c, WM8750_ADDRESS, \
@@ -65,6 +89,8 @@
 
 #define ZAUDIO_OP_SPKR	0
 #define ZAUDIO_OP_HP	1
+#define ZAUDIO_OP_MIC	2
+#define ZAUDIO_OP_NUM	3
 
 #define ZAUDIO_JACK_STATE_OUT	0
 #define ZAUDIO_JACK_STATE_IN	1
@@ -90,9 +116,10 @@
 	struct pxa2x0_i2c_softc	sc_i2c;
 
 	int			sc_playing;
+	int			sc_recording;
 
-	struct zaudio_volume	sc_volume[2];
-	char			sc_unmute[2];
+	struct zaudio_volume	sc_volume[ZAUDIO_OP_NUM];
+	char			sc_unmute[ZAUDIO_OP_NUM];
 
 	int			sc_state;
 	int			sc_icount;
@@ -108,25 +135,62 @@
 	"wm"
 };
 
-#define ZAUDIO_NFORMATS	4
-static const struct audio_format zaudio_formats[ZAUDIO_NFORMATS] = {
-	{NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16,
-	 2, AUFMT_STEREO, 0, {4000, 48000}},
-	{NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16,
-	 1, AUFMT_MONAURAL, 0, {4000, 48000}},
-	{NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_ULINEAR_LE, 8, 8,
-	 2, AUFMT_STEREO, 0, {4000, 48000}},
-	{NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_ULINEAR_LE, 8, 8,
-	 1, AUFMT_MONAURAL, 0, {4000, 48000}},
+static const struct audio_format zaudio_formats[] = {
+	{
+		.driver_data	= NULL,
+		.mode		= AUMODE_PLAY | AUMODE_RECORD,
+		.encoding	= AUDIO_ENCODING_SLINEAR_LE,
+		.validbits	= 16,
+		.precision	= 16,
+		.channels	= 2,
+		.channel_mask	= AUFMT_STEREO,
+		.frequency_type	= 0,
+		.frequency	= { 4000, 48000 }
+	},
+	{
+		.driver_data	= NULL,
+		.mode		= AUMODE_PLAY | AUMODE_RECORD,
+		.encoding	= AUDIO_ENCODING_SLINEAR_LE,
+		.validbits	= 16,
+		.precision	= 16,
+		.channels	= 1,
+		.channel_mask	= AUFMT_MONAURAL,
+		.frequency_type	= 0,
+		.frequency	= { 4000, 48000 }
+	},
+	{
+		.driver_data	= NULL,
+		.mode		= AUMODE_PLAY | AUMODE_RECORD,
+		.encoding	= AUDIO_ENCODING_ULINEAR_LE,
+		.validbits	= 8,
+		.precision	= 8,
+		.channels	= 2,
+		.channel_mask	= AUFMT_STEREO,
+		.frequency_type	= 0,
+		.frequency	= { 4000, 48000 }
+	},
+	{
+		.driver_data	= NULL,
+		.mode		= AUMODE_PLAY | AUMODE_RECORD,
+		.encoding	= AUDIO_ENCODING_ULINEAR_LE,
+		.validbits	= 8,
+		.precision	= 8,
+		.channels	= 1,
+		.channel_mask	= AUFMT_MONAURAL,
+		.frequency_type	= 0,
+		.frequency	= { 4000, 48000 }
+	},
 };
+static const int zaudio_nformats = (int)__arraycount(zaudio_formats);
 
-void zaudio_init(struct zaudio_softc *);
+static void zaudio_init(struct zaudio_softc *);
 static int zaudio_jack_intr(void *);
-void zaudio_jack(void *);
-void zaudio_standby(struct zaudio_softc *);
-void zaudio_update_volume(struct zaudio_softc *, int);
-void zaudio_update_mutes(struct zaudio_softc *);
-void zaudio_play_setup(struct zaudio_softc *);
+static void zaudio_jack(void *);
+static void zaudio_standby(struct zaudio_softc *);
+static void zaudio_update_volume(struct zaudio_softc *, int);
+static void zaudio_update_mutes(struct zaudio_softc *, int);
+static void zaudio_play_setup(struct zaudio_softc *);
+/*static*/ void zaudio_record_setup(struct zaudio_softc *);
 static int zaudio_open(void *, int);
 static void zaudio_close(void *);
 static int zaudio_query_encoding(void *, struct audio_encoding *);
@@ -178,7 +242,7 @@
 	.powerstate		= NULL,
 };
 
-static const uint16_t playback_registers[][2] = {
+static const uint16_t playback_regs[][2] = {
 	/* Unmute DAC */
 	{ ADCDACCTL_REG, 0x000 },
 
@@ -196,12 +260,36 @@
 
 	/* Direct DACs to output mixers */
 	{ LOUTMIX1_REG, LOUTMIX1_LD2LO },
+	{ LOUTMIX2_REG, 0x000 },
+	{ ROUTMIX1_REG, 0x000 },
 	{ ROUTMIX2_REG, ROUTMIX2_RD2RO },
 
 	/* End of list */
 	{ 0xffff, 0xffff }
 };
 
+static const uint16_t record_regs[][2] = {
+	/* Unmute DAC */
+	{ ADCDACCTL_REG, 0x000 },
+
+	/* 16 bit audio words */
+	{ AUDINT_REG, AUDINT_SET_FORMAT(2) },
+
+	/* Enable thermal protection, power, left DAC for both channel */
+	{ ADCTL1_REG, ADCTL1_TSDEN | ADCTL1_SET_VSEL(3)
+	              | ADCTL1_SET_DATSEL(1) },
+
+	/* Diffrential input select: LINPUT1-RINPUT1, stereo */
+	{ ADCINPMODE_REG, 0x000 },
+
+	/* L-R differential, micboost 20dB */
+	{ ADCLSPATH_REG, ADCLSPATH_SET_LINSEL(3) | ADCLSPATH_SET_LMICBOOST(2) },
+	{ ADCRSPATH_REG, ADCRSPATH_SET_RINSEL(3) | ADCRSPATH_SET_RMICBOOST(2) },
+
+	/* End of list */
+	{ 0xffff, 0xffff }
+};
+
 static int
 zaudio_match(device_t parent, cfdata_t cf, void *aux)
 {
@@ -256,13 +344,15 @@
 	sc->sc_volume[ZAUDIO_OP_HP].left = 180;
 	sc->sc_volume[ZAUDIO_OP_HP].right = 180;
 	sc->sc_unmute[ZAUDIO_OP_HP] = 0;
+	sc->sc_volume[ZAUDIO_OP_MIC].left = 240;
+	sc->sc_unmute[ZAUDIO_OP_MIC] = 0;
 
 	/* Configure headphone jack state change handling. */
 	callout_init(&sc->sc_to, 0);
 	callout_setfunc(&sc->sc_to, zaudio_jack, sc);
 	pxa2x0_gpio_set_function(GPIO_HP_IN_C3000, GPIO_IN);
-	(void)pxa2x0_gpio_intr_establish(GPIO_HP_IN_C3000,
-	    IST_EDGE_BOTH, IPL_BIO, zaudio_jack_intr, sc);
+	(void) pxa2x0_gpio_intr_establish(GPIO_HP_IN_C3000, IST_EDGE_BOTH,
+	    IPL_BIO, zaudio_jack_intr, sc);
 
 	zaudio_init(sc);
 
@@ -301,7 +391,7 @@
 	return true;
 }
 
-void
+static void
 zaudio_init(struct zaudio_softc *sc)
 {
 
@@ -321,10 +411,12 @@
 	/* Initialise volume levels */
 	zaudio_update_volume(sc, ZAUDIO_OP_SPKR);
 	zaudio_update_volume(sc, ZAUDIO_OP_HP);
+	zaudio_update_volume(sc, ZAUDIO_OP_MIC);
 
 	pxa2x0_i2c_close(&sc->sc_i2c);
 
 	scoop_set_headphone(0);
+	scoop_set_mic_bias(0);
 
 	/* Assume that the jack state has changed. */ 
 	zaudio_jack(sc);
@@ -341,7 +433,7 @@
 	return 1;
 }
 
-void
+static void
 zaudio_jack(void *v)
 {
 	struct zaudio_softc *sc = v;
@@ -360,6 +452,7 @@
 				sc->sc_state = ZAUDIO_JACK_STATE_IN;
 				sc->sc_unmute[ZAUDIO_OP_SPKR] = 0;
 				sc->sc_unmute[ZAUDIO_OP_HP] = 1;
+				sc->sc_unmute[ZAUDIO_OP_MIC] = 1;
 				goto update_mutes;
 			} else 
 				sc->sc_state = ZAUDIO_JACK_STATE_OUT;
@@ -379,6 +472,7 @@
 				sc->sc_state = ZAUDIO_JACK_STATE_OUT;
 				sc->sc_unmute[ZAUDIO_OP_SPKR] = 1;
 				sc->sc_unmute[ZAUDIO_OP_HP] = 0;
+				sc->sc_unmute[ZAUDIO_OP_MIC] = 0;
 				goto update_mutes;
 			} else
 				sc->sc_state = ZAUDIO_JACK_STATE_IN;
@@ -393,14 +487,17 @@
 update_mutes:
 	callout_stop(&sc->sc_to);
 
-	if (sc->sc_playing) {
+	if (sc->sc_playing || sc->sc_recording) {
 		pxa2x0_i2c_open(&sc->sc_i2c);
-		zaudio_update_mutes(sc);
+		if (sc->sc_playing)
+			zaudio_update_mutes(sc, 1);
+		if (sc->sc_recording)
+			zaudio_update_mutes(sc, 2);
 		pxa2x0_i2c_close(&sc->sc_i2c);
 	}
 }
 
-void
+static void
 zaudio_standby(struct zaudio_softc *sc)
 {
 
@@ -413,64 +510,94 @@
 	pxa2x0_i2c_close(&sc->sc_i2c);
 
 	scoop_set_headphone(0);
+	scoop_set_mic_bias(0);
 }
 
-void
+static void
 zaudio_update_volume(struct zaudio_softc *sc, int output)
 {
 
 	switch (output) {
 	case ZAUDIO_OP_SPKR:
 		wm8750_write(sc, LOUT2VOL_REG, LOUT2VOL_LO2VU | LOUT2VOL_LO2ZC |
-		    LOUT2VOL_SET_LOUT2VOL(sc->sc_volume[ZAUDIO_OP_SPKR
-		    ].left >> 1));
+		    LOUT2VOL_SET_LOUT2VOL(sc->sc_volume[ZAUDIO_OP_SPKR].left >> 1));
 		wm8750_write(sc, ROUT2VOL_REG, ROUT2VOL_RO2VU | ROUT2VOL_RO2ZC |
-		    ROUT2VOL_SET_ROUT2VOL(sc->sc_volume[ZAUDIO_OP_SPKR
-		    ].left >> 1));
+		    ROUT2VOL_SET_ROUT2VOL(sc->sc_volume[ZAUDIO_OP_SPKR].left >> 1));
 		break;
 
 	case ZAUDIO_OP_HP:
 		wm8750_write(sc, LOUT1VOL_REG, LOUT1VOL_LO1VU | LOUT1VOL_LO1ZC |
-		    LOUT1VOL_SET_LOUT1VOL(sc->sc_volume[ZAUDIO_OP_HP
-		    ].left >> 1));
+		    LOUT1VOL_SET_LOUT1VOL(sc->sc_volume[ZAUDIO_OP_HP].left >> 1));
 		wm8750_write(sc, ROUT1VOL_REG, ROUT1VOL_RO1VU | ROUT1VOL_RO1ZC |
-		    ROUT1VOL_SET_ROUT1VOL(sc->sc_volume[ZAUDIO_OP_HP
-		    ].right >> 1));
+		    ROUT1VOL_SET_ROUT1VOL(sc->sc_volume[ZAUDIO_OP_HP].right >> 1));
+		break;
+
+	case ZAUDIO_OP_MIC:
+		wm8750_write(sc, LINVOL_REG, LINVOL_LIVU |
+		    LINVOL_SET_LINVOL(sc->sc_volume[ZAUDIO_OP_MIC].left >> 2));
+		wm8750_write(sc, RINVOL_REG, RINVOL_RIVU |
+		    RINVOL_SET_RINVOL(sc->sc_volume[ZAUDIO_OP_MIC].left >> 2));
 		break;
 	}
 }
 
-void
-zaudio_update_mutes(struct zaudio_softc *sc)
+static void
+zaudio_update_mutes(struct zaudio_softc *sc, int mask)
 {
 	uint16_t val;
 
-	val = PWRMGMT2_DACL | PWRMGMT2_DACR;
+	/* playback */
+	if (mask & 1) {
+		val = PWRMGMT2_DACL | PWRMGMT2_DACR;
+		if (sc->sc_unmute[ZAUDIO_OP_SPKR])
+			val |= PWRMGMT2_LOUT2 | PWRMGMT2_ROUT2;
+		if (sc->sc_unmute[ZAUDIO_OP_HP])
+			val |= PWRMGMT2_LOUT1 | PWRMGMT2_ROUT1;
+		wm8750_write(sc, PWRMGMT2_REG, val);
+		scoop_set_headphone(sc->sc_unmute[ZAUDIO_OP_HP]);
+	}
 
-	if (sc->sc_unmute[ZAUDIO_OP_SPKR])
-		val |= PWRMGMT2_LOUT2 | PWRMGMT2_ROUT2;
+	/* record */
+	if (mask & 2) {
+		val = PWRMGMT1_SET_VMIDSEL(1) | PWRMGMT1_VREF;
+		if (sc->sc_unmute[ZAUDIO_OP_MIC]) {
+			val |= PWRMGMT1_AINL | PWRMGMT1_AINR
+			       | PWRMGMT1_ADCL | PWRMGMT1_ADCR | PWRMGMT1_MICB;
+		}
+		wm8750_write(sc, PWRMGMT1_REG, val);
+		scoop_set_mic_bias(sc->sc_unmute[ZAUDIO_OP_MIC]);
+	}
+}
+
+static void
+zaudio_play_setup(struct zaudio_softc *sc)
+{
+	int i;
 
-	if (sc->sc_unmute[ZAUDIO_OP_HP])
-		val |= PWRMGMT2_LOUT1 | PWRMGMT2_ROUT1;
+	pxa2x0_i2c_open(&sc->sc_i2c);
 
-	wm8750_write(sc, PWRMGMT2_REG, val);
+	/* Program the codec with playback settings */
+	for (i = 0; playback_regs[i][0] != 0xffff; i++) {
+		wm8750_write(sc, playback_regs[i][0], playback_regs[i][1]);
+	}
+	zaudio_update_mutes(sc, 1);
 
-	scoop_set_headphone(sc->sc_unmute[ZAUDIO_OP_HP]);
+	pxa2x0_i2c_close(&sc->sc_i2c);
 }
 
-void
-zaudio_play_setup(struct zaudio_softc *sc)
+/*static*/ void
+zaudio_record_setup(struct zaudio_softc *sc)
 {
 	int i;
 
 	pxa2x0_i2c_open(&sc->sc_i2c);
 
 	/* Program the codec with playback settings */
-	for (i = 0; playback_registers[i][0] != 0xffff; i++) {
-		wm8750_write(sc, playback_registers[i][0],
-		    playback_registers[i][1]);
+	for (i = 0; record_regs[i][0] != 0xffff; i++) {
+		wm8750_write(sc, record_regs[i][0], record_regs[i][1]);
 	}
-	zaudio_update_mutes(sc);
+
+	zaudio_update_mutes(sc, 2);
 
 	pxa2x0_i2c_close(&sc->sc_i2c);
 }
@@ -601,7 +728,7 @@
 			return EINVAL;
 
 		fil = (mode == AUMODE_PLAY) ? pfil : rfil;
-		i = auconv_set_converter(zaudio_formats, ZAUDIO_NFORMATS,
+		i = auconv_set_converter(zaudio_formats, zaudio_nformats,
 					 mode, p, false, fil);
 		if (i < 0)
 			return EINVAL;
@@ -631,7 +758,8 @@
 	int rv;
 
 	rv = pxa2x0_i2s_halt_output(&sc->sc_i2s);
-	zaudio_standby(sc);
+	if (!sc->sc_recording)
+		zaudio_standby(sc);
 	sc->sc_playing = 0;
 
 	return rv;
@@ -644,6 +772,9 @@
 	int rv;
 
 	rv = pxa2x0_i2s_halt_input(&sc->sc_i2s);
+	if (!sc->sc_playing)
+		zaudio_standby(sc);
+	sc->sc_recording = 0;
 
 	return rv;
 }
@@ -651,7 +782,6 @@
 static int
 zaudio_getdev(void *hdl, struct audio_device *ret)
 {
-	/* struct zaudio_softc *sc = hdl; */
 
 	*ret = wm8750_device;
 	return 0;
@@ -661,7 +791,12 @@
 #define ZAUDIO_SPKR_MUTE	1
 #define ZAUDIO_HP_LVL		2
 #define ZAUDIO_HP_MUTE		3
-#define ZAUDIO_OUTPUT_CLASS	4
+#define ZAUDIO_MIC_LVL		4
+#define ZAUDIO_MIC_MUTE		5
+#define ZAUDIO_RECORD_SOURCE	6
+#define ZAUDIO_OUTPUT_CLASS	7
+#define ZAUDIO_INPUT_CLASS	8
+#define ZAUDIO_RECORD_CLASS	9
 
 static int
 zaudio_set_port(void *hdl, struct mixer_ctrl *mc)
@@ -690,7 +825,7 @@
 		if (mc->type != AUDIO_MIXER_ENUM)
 			break;
 		sc->sc_unmute[ZAUDIO_OP_SPKR] = mc->un.ord ? 1 : 0;
-		zaudio_update_mutes(sc);
+		zaudio_update_mutes(sc, 1);
 		error = 0;
 		break;
 
@@ -718,7 +853,36 @@
 		if (mc->type != AUDIO_MIXER_ENUM)
 			break;
 		sc->sc_unmute[ZAUDIO_OP_HP] = mc->un.ord ? 1 : 0;
-		zaudio_update_mutes(sc);
+		zaudio_update_mutes(sc, 1);
+		error = 0;
+		break;
+
+	case ZAUDIO_MIC_LVL:
+		if (mc->type != AUDIO_MIXER_VALUE)
+			break;
+		if (mc->un.value.num_channels == 1)
+			sc->sc_volume[ZAUDIO_OP_MIC].left =
+			    mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
+		else
+			break;
+		zaudio_update_volume(sc, ZAUDIO_OP_MIC);
+		error = 0;
+		break;
+
+	case ZAUDIO_MIC_MUTE:
+		if (mc->type != AUDIO_MIXER_ENUM)
+			break;
+		sc->sc_unmute[ZAUDIO_OP_MIC] = mc->un.ord ? 1 : 0;
+		zaudio_update_mutes(sc, 2);
+		error = 0;
+		break;
+
+	case ZAUDIO_RECORD_SOURCE:
+		if (mc->type != AUDIO_MIXER_ENUM)
+			break;
+		if (mc->un.ord != 0)
+			break;
+		/* MIC only */
 		error = 0;
 		break;
 	}
@@ -777,15 +941,40 @@
 		mc->un.ord = sc->sc_unmute[ZAUDIO_OP_HP] ? 1 : 0;
 		error = 0;
 		break;
+
+	case ZAUDIO_MIC_LVL:
+		if (mc->type != AUDIO_MIXER_VALUE)
+			break;
+		if (mc->un.value.num_channels == 1)
+			mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
+			    sc->sc_volume[ZAUDIO_OP_MIC].left;
+		else
+			break;
+		error = 0;
+		break;
+
+	case ZAUDIO_MIC_MUTE:
+		if (mc->type != AUDIO_MIXER_ENUM)
+			break;
+		mc->un.ord = sc->sc_unmute[ZAUDIO_OP_MIC] ? 1 : 0;
+		error = 0;
+		break;
+
+	case ZAUDIO_RECORD_SOURCE:
+		if (mc->type != AUDIO_MIXER_ENUM)
+			break;
+		mc->un.ord = 0; /* MIC only */
+		error = 0;
+		break;
 	}
 
 	return error;
 }
 
+/*ARGSUSED*/
 static int
 zaudio_query_devinfo(void *hdl, struct mixer_devinfo *di)
 {
-	/* struct zaudio_softc *sc = hdl; */
 
 	switch (di->index) {
 	case ZAUDIO_SPKR_LVL:
@@ -793,8 +982,7 @@
 		di->mixer_class = ZAUDIO_OUTPUT_CLASS;
 		di->prev = AUDIO_MIXER_LAST;
 		di->next = ZAUDIO_SPKR_MUTE;
-		strlcpy(di->label.name, AudioNspeaker,
-		    sizeof(di->label.name));
+		strlcpy(di->label.name, AudioNspeaker, sizeof(di->label.name));
 		strlcpy(di->un.v.units.name, AudioNvolume,
 		    sizeof(di->un.v.units.name));
 		di->un.v.num_channels = 1;
@@ -835,13 +1023,59 @@
 		di->un.e.member[1].ord = 1;
 		break;
 
+	case ZAUDIO_MIC_LVL:
+		di->type = AUDIO_MIXER_VALUE;
+		di->mixer_class = ZAUDIO_INPUT_CLASS;
+		di->prev = AUDIO_MIXER_LAST;
+		di->next = ZAUDIO_MIC_MUTE;
+		strlcpy(di->label.name, AudioNmicrophone,
+		    sizeof(di->label.name));
+		strlcpy(di->un.v.units.name, AudioNvolume,
+		    sizeof(di->un.v.units.name));
+		di->un.v.num_channels = 1;
+		break;
+
+	case ZAUDIO_MIC_MUTE:
+		di->type = AUDIO_MIXER_ENUM;
+		di->mixer_class = ZAUDIO_INPUT_CLASS;
+		di->prev = ZAUDIO_MIC_LVL;
+		di->next = AUDIO_MIXER_LAST;
+		goto mute;
+
+	case ZAUDIO_RECORD_SOURCE:
+		di->type = AUDIO_MIXER_ENUM;
+		di->mixer_class = ZAUDIO_RECORD_CLASS;
+		di->prev = AUDIO_MIXER_LAST;
+		di->next = AUDIO_MIXER_LAST;
+		strlcpy(di->label.name, AudioNsource, sizeof(di->label.name));
+		di->un.e.num_mem = 1;
+		strlcpy(di->un.e.member[0].label.name, AudioNmicrophone,
+		    sizeof(di->un.e.member[0].label.name));
+		di->un.e.member[0].ord = 0;
+		break;
+
 	case ZAUDIO_OUTPUT_CLASS:
 		di->type = AUDIO_MIXER_CLASS;
 		di->mixer_class = ZAUDIO_OUTPUT_CLASS;
 		di->prev = AUDIO_MIXER_LAST;
 		di->next = AUDIO_MIXER_LAST;
-		strlcpy(di->label.name, AudioCoutputs,
-		    sizeof(di->label.name));
+		strlcpy(di->label.name, AudioCoutputs, sizeof(di->label.name));
+		break;
+
+	case ZAUDIO_INPUT_CLASS:
+		di->type = AUDIO_MIXER_CLASS;
+		di->mixer_class = ZAUDIO_INPUT_CLASS;
+		di->prev = AUDIO_MIXER_LAST;
+		di->next = AUDIO_MIXER_LAST;
+		strlcpy(di->label.name, AudioCinputs, sizeof(di->label.name));
+		break;
+
+	case ZAUDIO_RECORD_CLASS:
+		di->type = AUDIO_MIXER_CLASS;
+		di->mixer_class = ZAUDIO_RECORD_CLASS;
+		di->prev = AUDIO_MIXER_LAST;
+		di->next = AUDIO_MIXER_LAST;
+		strlcpy(di->label.name, AudioCinputs, sizeof(di->label.name));
 		break;
 
 	default:
@@ -852,8 +1086,8 @@
 }
 
 static void *
-zaudio_allocm(void *hdl, int direction, size_t size,
-    struct malloc_type *type, int flags)
+zaudio_allocm(void *hdl, int direction, size_t size, struct malloc_type *type,
+    int flags)
 {
 	struct zaudio_softc *sc = hdl;
 
@@ -888,7 +1122,7 @@
 zaudio_get_props(void *hdl)
 {
 
-	return AUDIO_PROP_MMAP|AUDIO_PROP_INDEPENDENT|AUDIO_PROP_FULLDUPLEX;
+	return AUDIO_PROP_MMAP|AUDIO_PROP_INDEPENDENT;
 }
 
 static int
@@ -907,7 +1141,8 @@
 	/* Start DMA via I2S */
 	rv = pxa2x0_i2s_start_output(&sc->sc_i2s, block, bsize, intr, intrarg);
 	if (rv) {
-		zaudio_standby(sc);
+		if (!sc->sc_recording)
+			zaudio_standby(sc);
 		sc->sc_playing = 0;
 	}
 	return rv;
@@ -917,6 +1152,21 @@
 zaudio_start_input(void *hdl, void *block, int bsize, void (*intr)(void *),
     void *intrarg)
 {
+	struct zaudio_softc *sc = hdl;
+	int rv;
+
+	/* Power up codec if we are not already recording. */
+	if (!sc->sc_recording) {
+		sc->sc_recording = 1;
+		zaudio_record_setup(sc);
+	}
 
-	return ENXIO;
+	/* Start DMA via I2S */
+	rv = pxa2x0_i2s_start_input(&sc->sc_i2s, block, bsize, intr, intrarg);
+	if (rv) {
+		if (!sc->sc_playing)
+			zaudio_standby(sc);
+		sc->sc_recording = 0;
+	}
+	return rv;
 }

Reply via email to