xref: /netbsd-src/sys/arch/zaurus/dev/wm8731_zaudio.c (revision e622eac459adf11c2e710d7a4de0f05510bbbe61)
1*e622eac4Sisaki /*	$NetBSD: wm8731_zaudio.c,v 1.3 2019/05/08 13:40:17 isaki Exp $	*/
294ff29c8Snonaka 
394ff29c8Snonaka /*-
494ff29c8Snonaka  * Copyright (c) 2013 The NetBSD Foundation, Inc.
594ff29c8Snonaka  * All rights reserved.
694ff29c8Snonaka  *
794ff29c8Snonaka  * This code is derived from software contributed to The NetBSD Foundation
894ff29c8Snonaka  * by TOYOKURA Atsushi.
994ff29c8Snonaka  *
1094ff29c8Snonaka  * Redistribution and use in source and binary forms, with or without
1194ff29c8Snonaka  * modification, are permitted provided that the following conditions
1294ff29c8Snonaka  * are met:
1394ff29c8Snonaka  * 1. Redistributions of source code must retain the above copyright
1494ff29c8Snonaka  *    notice, this list of conditions and the following disclaimer.
1594ff29c8Snonaka  * 2. Redistributions in binary form must reproduce the above copyright
1694ff29c8Snonaka  *    notice, this list of conditions and the following disclaimer in the
1794ff29c8Snonaka  *    documentation and/or other materials provided with the distribution.
1894ff29c8Snonaka  *
1994ff29c8Snonaka  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2094ff29c8Snonaka  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2194ff29c8Snonaka  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2294ff29c8Snonaka  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2394ff29c8Snonaka  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2494ff29c8Snonaka  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2594ff29c8Snonaka  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2694ff29c8Snonaka  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2794ff29c8Snonaka  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2894ff29c8Snonaka  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2994ff29c8Snonaka  * POSSIBILITY OF SUCH DAMAGE.
3094ff29c8Snonaka  */
3194ff29c8Snonaka 
3294ff29c8Snonaka /*
3394ff29c8Snonaka  * TODO:
3494ff29c8Snonaka  *	- powerhooks (currently only works until first suspend)
3594ff29c8Snonaka  */
3694ff29c8Snonaka 
3794ff29c8Snonaka #include "opt_cputypes.h"
3894ff29c8Snonaka #include "opt_zaudio.h"
3994ff29c8Snonaka 
4094ff29c8Snonaka #include <sys/cdefs.h>
41*e622eac4Sisaki __KERNEL_RCSID(0, "$NetBSD: wm8731_zaudio.c,v 1.3 2019/05/08 13:40:17 isaki Exp $");
4294ff29c8Snonaka 
4394ff29c8Snonaka #include <sys/param.h>
4494ff29c8Snonaka #include <sys/systm.h>
4594ff29c8Snonaka #include <sys/callout.h>
4694ff29c8Snonaka #include <sys/device.h>
4794ff29c8Snonaka #include <sys/kmem.h>
4894ff29c8Snonaka #include <sys/kernel.h>
4994ff29c8Snonaka #include <sys/audioio.h>
5094ff29c8Snonaka #include <sys/mutex.h>
5194ff29c8Snonaka #include <sys/intr.h>
5294ff29c8Snonaka #include <sys/bus.h>
5394ff29c8Snonaka 
54*e622eac4Sisaki #include <dev/audio/audio_if.h>
5594ff29c8Snonaka 
5694ff29c8Snonaka #include <dev/i2c/i2cvar.h>
5794ff29c8Snonaka 
5894ff29c8Snonaka #include <arm/xscale/pxa2x0reg.h>
5994ff29c8Snonaka #include <arm/xscale/pxa2x0var.h>
6094ff29c8Snonaka #include <arm/xscale/pxa2x0_i2c.h>
6194ff29c8Snonaka #include <arm/xscale/pxa2x0_i2s.h>
6294ff29c8Snonaka #include <arm/xscale/pxa2x0_dmac.h>
6394ff29c8Snonaka #include <arm/xscale/pxa2x0_gpio.h>
6494ff29c8Snonaka 
6594ff29c8Snonaka #include <zaurus/zaurus/zaurus_var.h>
6694ff29c8Snonaka #include <zaurus/dev/zaudiovar.h>
6794ff29c8Snonaka #include <zaurus/dev/wm8731reg.h>
6894ff29c8Snonaka #include <zaurus/dev/wm8731var.h>
6994ff29c8Snonaka #include <zaurus/dev/scoopvar.h>
7094ff29c8Snonaka 
7194ff29c8Snonaka #define WM8731_ADDRESS  0x1B
7294ff29c8Snonaka 
7394ff29c8Snonaka /* GPIO pins */
7494ff29c8Snonaka #define GPIO_HP_IN_C860	4
7594ff29c8Snonaka 
7694ff29c8Snonaka #define WM8731_OP_SPKR	0
7794ff29c8Snonaka #define WM8731_OP_MIC	1
7894ff29c8Snonaka #define WM8731_OP_NUM	2
7994ff29c8Snonaka 
8094ff29c8Snonaka static int	wm8731_finalize(device_t);
8194ff29c8Snonaka static bool	wm8731_suspend(device_t, const pmf_qual_t *);
8294ff29c8Snonaka static bool	wm8731_resume(device_t, const pmf_qual_t *);
8394ff29c8Snonaka static void	wm8731_volume_up(device_t);
8494ff29c8Snonaka static void	wm8731_volume_down(device_t);
8594ff29c8Snonaka static void	wm8731_volume_toggle(device_t);
8694ff29c8Snonaka 
8794ff29c8Snonaka static struct audio_device wm8731_device = {
8894ff29c8Snonaka 	"WM8731",
8994ff29c8Snonaka 	"1.0",
9094ff29c8Snonaka 	"wm"
9194ff29c8Snonaka };
9294ff29c8Snonaka 
9394ff29c8Snonaka static void wm8731_init(struct zaudio_softc *);
9494ff29c8Snonaka static int wm8731_jack_intr(void *);
9594ff29c8Snonaka static void wm8731_jack(void *);
9694ff29c8Snonaka static void wm8731_standby(struct zaudio_softc *);
9794ff29c8Snonaka static void wm8731_update_volume(struct zaudio_softc *, int);
9894ff29c8Snonaka static void wm8731_update_mutes(struct zaudio_softc *, int);
9994ff29c8Snonaka static void wm8731_play_setup(struct zaudio_softc *);
10094ff29c8Snonaka /*static*/ void wm8731_record_setup(struct zaudio_softc *);
10194ff29c8Snonaka static int wm8731_start_output(void *, void *, int, void (*)(void *), void *);
10294ff29c8Snonaka static int wm8731_start_input(void *, void *, int, void (*)(void *), void *);
10394ff29c8Snonaka static int wm8731_halt_output(void *);
10494ff29c8Snonaka static int wm8731_halt_input(void *);
10594ff29c8Snonaka static int wm8731_getdev(void *, struct audio_device *);
10694ff29c8Snonaka static int wm8731_set_port(void *, struct mixer_ctrl *);
10794ff29c8Snonaka static int wm8731_get_port(void *, struct mixer_ctrl *);
10894ff29c8Snonaka static int wm8731_query_devinfo(void *, struct mixer_devinfo *);
10994ff29c8Snonaka 
11094ff29c8Snonaka static struct audio_hw_if wm8731_hw_if = {
11194ff29c8Snonaka 	.open			= zaudio_open,
11294ff29c8Snonaka 	.close			= zaudio_close,
113*e622eac4Sisaki 	.query_format		= zaudio_query_format,
114*e622eac4Sisaki 	.set_format		= zaudio_set_format,
11594ff29c8Snonaka 	.round_blocksize	= zaudio_round_blocksize,
11694ff29c8Snonaka 	.commit_settings	= NULL,
11794ff29c8Snonaka 	.init_output		= NULL,
11894ff29c8Snonaka 	.init_input		= NULL,
11994ff29c8Snonaka 	.start_output		= wm8731_start_output,
12094ff29c8Snonaka 	.start_input		= wm8731_start_input,
12194ff29c8Snonaka 	.halt_output		= wm8731_halt_output,
12294ff29c8Snonaka 	.halt_input		= wm8731_halt_input,
12394ff29c8Snonaka 	.speaker_ctl		= NULL,
12494ff29c8Snonaka 	.getdev			= wm8731_getdev,
12594ff29c8Snonaka 	.set_port		= wm8731_set_port,
12694ff29c8Snonaka 	.get_port		= wm8731_get_port,
12794ff29c8Snonaka 	.query_devinfo		= wm8731_query_devinfo,
12894ff29c8Snonaka 	.allocm			= zaudio_allocm,
12994ff29c8Snonaka 	.freem			= zaudio_freem,
13094ff29c8Snonaka 	.round_buffersize	= zaudio_round_buffersize,
13194ff29c8Snonaka 	.get_props		= zaudio_get_props,
13294ff29c8Snonaka 	.trigger_output		= NULL,
13394ff29c8Snonaka 	.trigger_input		= NULL,
13494ff29c8Snonaka 	.dev_ioctl		= NULL,
13594ff29c8Snonaka 	.get_locks		= zaudio_get_locks,
13694ff29c8Snonaka };
13794ff29c8Snonaka 
13894ff29c8Snonaka static const uint16_t playback_regs[][2] = {
13994ff29c8Snonaka 	/* Power Down Control */
14094ff29c8Snonaka 	{ WM8731_PD_REG, WM8731_CLKOUTPD | WM8731_OSCPD | WM8731_OUTPD
14194ff29c8Snonaka 	    | WM8731_ADCPD | WM8731_MICPD | WM8731_LINEINPD },
14294ff29c8Snonaka 
14394ff29c8Snonaka 	/* Digital Audio Path Control */
14494ff29c8Snonaka 	{ WM8731_DAP_REG, 0 },
14594ff29c8Snonaka 
14694ff29c8Snonaka 	/* Analogue Audio Path Control */
14794ff29c8Snonaka 	{ WM8731_AAP_REG, WM8731_DACSEL | WM8731_MUTEMIC },
14894ff29c8Snonaka 
14994ff29c8Snonaka 	/* Activating DSP and DAI */
15094ff29c8Snonaka 	{ WM8731_AC_REG, WM8731_ACTIVE },
15194ff29c8Snonaka 
15294ff29c8Snonaka 	/* End of list */
15394ff29c8Snonaka 	{ 0xffff, 0xffff }
15494ff29c8Snonaka };
15594ff29c8Snonaka 
15694ff29c8Snonaka static const uint16_t record_regs[][2] = {
15794ff29c8Snonaka 	/* Power Down Control */
15894ff29c8Snonaka 	{ WM8731_PD_REG, WM8731_CLKOUTPD | WM8731_OSCPD | WM8731_DACPD
15994ff29c8Snonaka 	    | WM8731_LINEINPD },
16094ff29c8Snonaka 
16194ff29c8Snonaka 	/* Digital Audio Path Control */
16294ff29c8Snonaka 	{ WM8731_DAP_REG, 0 },
16394ff29c8Snonaka 
16494ff29c8Snonaka 	/* Analogue Audio Path Control */
16594ff29c8Snonaka 	{ WM8731_AAP_REG, WM8731_INSEL | WM8731_MICBOOST },
16694ff29c8Snonaka 
16794ff29c8Snonaka 	/* Activating DSP and DAI */
16894ff29c8Snonaka 	{ WM8731_AC_REG, WM8731_ACTIVE },
16994ff29c8Snonaka 
17094ff29c8Snonaka 	/* End of list */
17194ff29c8Snonaka 	{ 0xffff, 0xffff }
17294ff29c8Snonaka };
17394ff29c8Snonaka 
17494ff29c8Snonaka static __inline int
wm8731_write(struct zaudio_softc * sc,int reg,int val)17594ff29c8Snonaka wm8731_write(struct zaudio_softc *sc, int reg, int val)
17694ff29c8Snonaka {
17794ff29c8Snonaka 	uint16_t tmp;
17894ff29c8Snonaka 	uint8_t cmd;
17994ff29c8Snonaka 	uint8_t data;
18094ff29c8Snonaka 
18194ff29c8Snonaka 	tmp = (reg << 9) | (val & 0x1ff);
18294ff29c8Snonaka 	cmd = tmp >> 8;
18394ff29c8Snonaka 	data = tmp;
18494ff29c8Snonaka 	return iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP, WM8731_ADDRESS,
18594ff29c8Snonaka 	    &cmd, 1, &data, 1, 0);
18694ff29c8Snonaka }
18794ff29c8Snonaka 
18894ff29c8Snonaka int
wm8731_match(device_t parent,cfdata_t cf,struct i2c_attach_args * ia)18994ff29c8Snonaka wm8731_match(device_t parent, cfdata_t cf, struct i2c_attach_args *ia)
19094ff29c8Snonaka {
191aa41e992Sthorpej 	int match_result;
19294ff29c8Snonaka 
19394ff29c8Snonaka 	if (ZAURUS_ISC1000 || ZAURUS_ISC3000)
19494ff29c8Snonaka 		return 0;
19594ff29c8Snonaka 
196aa41e992Sthorpej 	if (iic_use_direct_match(ia, cf, NULL, &match_result))
197aa41e992Sthorpej 		return match_result;
198aa41e992Sthorpej 
19994ff29c8Snonaka 	/* indirect config - check typical address */
20094ff29c8Snonaka 	if (ia->ia_addr == WM8731_ADDRESS)
201aa41e992Sthorpej 		return I2C_MATCH_ADDRESS_ONLY;
202aa41e992Sthorpej 
20394ff29c8Snonaka 	return 0;
20494ff29c8Snonaka }
20594ff29c8Snonaka 
20694ff29c8Snonaka void
wm8731_attach(device_t parent,device_t self,struct i2c_attach_args * ia)20794ff29c8Snonaka wm8731_attach(device_t parent, device_t self, struct i2c_attach_args *ia)
20894ff29c8Snonaka {
20994ff29c8Snonaka 	struct zaudio_softc *sc = device_private(self);
21094ff29c8Snonaka 	int error;
21194ff29c8Snonaka 
21294ff29c8Snonaka 	aprint_normal(": I2S, WM8731 Audio\n");
21394ff29c8Snonaka 	aprint_naive("\n");
21494ff29c8Snonaka 
21594ff29c8Snonaka 	/* Check for an I2C response from the wm8731 */
21694ff29c8Snonaka 	iic_acquire_bus(sc->sc_i2c, 0);
21794ff29c8Snonaka 	error = wm8731_write(sc, WM8731_RESET_REG, 0);
21894ff29c8Snonaka 	iic_release_bus(sc->sc_i2c, 0);
21994ff29c8Snonaka 	if (error) {
22094ff29c8Snonaka 		aprint_error_dev(self, "codec failed to respond\n");
22194ff29c8Snonaka 		goto fail_i2c;
22294ff29c8Snonaka 	}
22394ff29c8Snonaka 	delay(100);
22494ff29c8Snonaka 
22594ff29c8Snonaka 	/* Allocate memory for volume & mute operations */
22694ff29c8Snonaka 	sc->sc_volume = kmem_zalloc(sizeof(*sc->sc_volume) * WM8731_OP_NUM,
22794ff29c8Snonaka 	    KM_SLEEP);
22894ff29c8Snonaka 	sc->sc_unmute = kmem_zalloc(sizeof(*sc->sc_unmute) * WM8731_OP_NUM,
22994ff29c8Snonaka 	    KM_SLEEP);
23094ff29c8Snonaka 	sc->sc_unmute_toggle = kmem_zalloc(
23194ff29c8Snonaka 	    sizeof(*sc->sc_unmute_toggle) * WM8731_OP_NUM, KM_SLEEP);
23294ff29c8Snonaka 
23394ff29c8Snonaka 	/* Speaker On by default. */
23494ff29c8Snonaka 	sc->sc_volume[WM8731_OP_SPKR].left = 180;
23594ff29c8Snonaka 	sc->sc_volume[WM8731_OP_SPKR].right = 180;
23694ff29c8Snonaka 	sc->sc_jack = FALSE;
23794ff29c8Snonaka 	UNMUTE(sc, WM8731_OP_SPKR, 1);
23894ff29c8Snonaka 	sc->sc_volume[WM8731_OP_MIC].left = 180;
23994ff29c8Snonaka 	UNMUTE(sc, WM8731_OP_MIC, 0);
24094ff29c8Snonaka 
24194ff29c8Snonaka 	/* Configure headphone jack state change handling. */
24294ff29c8Snonaka 	callout_setfunc(&sc->sc_to, wm8731_jack, sc);
24394ff29c8Snonaka 	pxa2x0_gpio_set_function(GPIO_HP_IN_C860, GPIO_IN);
24494ff29c8Snonaka 	(void) pxa2x0_gpio_intr_establish(GPIO_HP_IN_C860, IST_EDGE_BOTH,
24594ff29c8Snonaka 	    IPL_BIO, wm8731_jack_intr, sc);
24694ff29c8Snonaka 
24794ff29c8Snonaka 	/* wm8731_init() implicitly depends on ioexp or scoop */
24894ff29c8Snonaka 	config_finalize_register(self, wm8731_finalize);
24994ff29c8Snonaka 
25094ff29c8Snonaka 	audio_attach_mi(&wm8731_hw_if, sc, self);
25194ff29c8Snonaka 
25294ff29c8Snonaka 	if (!pmf_device_register(self, wm8731_suspend, wm8731_resume))
25394ff29c8Snonaka 		aprint_error_dev(self, "couldn't establish power handler\n");
25494ff29c8Snonaka 	if (!pmf_event_register(self, PMFE_AUDIO_VOLUME_UP,
25594ff29c8Snonaka 	    wm8731_volume_up, true))
25694ff29c8Snonaka 		aprint_error_dev(self, "couldn't register event handler\n");
25794ff29c8Snonaka 	if (!pmf_event_register(self, PMFE_AUDIO_VOLUME_DOWN,
25894ff29c8Snonaka 	    wm8731_volume_down, true))
25994ff29c8Snonaka 		aprint_error_dev(self, "couldn't register event handler\n");
26094ff29c8Snonaka 	if (!pmf_event_register(self, PMFE_AUDIO_VOLUME_TOGGLE,
26194ff29c8Snonaka 	    wm8731_volume_toggle, true))
26294ff29c8Snonaka 		aprint_error_dev(self, "couldn't register event handler\n");
26394ff29c8Snonaka 
26494ff29c8Snonaka 	return;
26594ff29c8Snonaka 
26694ff29c8Snonaka fail_i2c:
26794ff29c8Snonaka 	pxa2x0_i2s_detach_sub(&sc->sc_i2s);
26894ff29c8Snonaka }
26994ff29c8Snonaka 
27094ff29c8Snonaka static int
wm8731_finalize(device_t dv)27194ff29c8Snonaka wm8731_finalize(device_t dv)
27294ff29c8Snonaka {
27394ff29c8Snonaka 	struct zaudio_softc *sc = device_private(dv);
27494ff29c8Snonaka 
27594ff29c8Snonaka 	wm8731_init(sc);
27694ff29c8Snonaka 	return 0;
27794ff29c8Snonaka }
27894ff29c8Snonaka 
27994ff29c8Snonaka static bool
wm8731_suspend(device_t dv,const pmf_qual_t * qual)28094ff29c8Snonaka wm8731_suspend(device_t dv, const pmf_qual_t *qual)
28194ff29c8Snonaka {
28294ff29c8Snonaka 	struct zaudio_softc *sc = device_private(dv);
28394ff29c8Snonaka 
28494ff29c8Snonaka 	callout_stop(&sc->sc_to);
28594ff29c8Snonaka 	wm8731_standby(sc);
28694ff29c8Snonaka 
28794ff29c8Snonaka 	return true;
28894ff29c8Snonaka }
28994ff29c8Snonaka 
29094ff29c8Snonaka static bool
wm8731_resume(device_t dv,const pmf_qual_t * qual)29194ff29c8Snonaka wm8731_resume(device_t dv, const pmf_qual_t *qual)
29294ff29c8Snonaka {
29394ff29c8Snonaka 	struct zaudio_softc *sc = device_private(dv);
29494ff29c8Snonaka 
29594ff29c8Snonaka 	pxa2x0_i2s_init(&sc->sc_i2s);
29694ff29c8Snonaka 	wm8731_init(sc);
29794ff29c8Snonaka 
29894ff29c8Snonaka 	return true;
29994ff29c8Snonaka }
30094ff29c8Snonaka 
30194ff29c8Snonaka static __inline uint8_t
vol_sadd(int vol,int stride)30294ff29c8Snonaka vol_sadd(int vol, int stride)
30394ff29c8Snonaka {
30494ff29c8Snonaka 
30594ff29c8Snonaka 	vol += stride;
30694ff29c8Snonaka 	if (vol > 255)
30794ff29c8Snonaka 		return 255;
30894ff29c8Snonaka 	return (uint8_t)vol;
30994ff29c8Snonaka }
31094ff29c8Snonaka 
31194ff29c8Snonaka #ifndef	ZAUDIO_VOLUME_STRIDE
31294ff29c8Snonaka #define	ZAUDIO_VOLUME_STRIDE	8
31394ff29c8Snonaka #endif
31494ff29c8Snonaka 
31594ff29c8Snonaka static void
wm8731_volume_up(device_t dv)31694ff29c8Snonaka wm8731_volume_up(device_t dv)
31794ff29c8Snonaka {
31894ff29c8Snonaka 	struct zaudio_softc *sc = device_private(dv);
31994ff29c8Snonaka 	int s;
32094ff29c8Snonaka 
32194ff29c8Snonaka 	s = splbio();
32294ff29c8Snonaka 	iic_acquire_bus(sc->sc_i2c, 0);
32394ff29c8Snonaka 
32494ff29c8Snonaka 	sc->sc_volume[WM8731_OP_SPKR].left =
32594ff29c8Snonaka 	    vol_sadd(sc->sc_volume[WM8731_OP_SPKR].left, ZAUDIO_VOLUME_STRIDE);
32694ff29c8Snonaka 	sc->sc_volume[WM8731_OP_SPKR].right =
32794ff29c8Snonaka 	    vol_sadd(sc->sc_volume[WM8731_OP_SPKR].right, ZAUDIO_VOLUME_STRIDE);
32894ff29c8Snonaka 
32994ff29c8Snonaka 	wm8731_update_volume(sc, WM8731_OP_SPKR);
33094ff29c8Snonaka 
33194ff29c8Snonaka 	iic_release_bus(sc->sc_i2c, 0);
33294ff29c8Snonaka 	splx(s);
33394ff29c8Snonaka }
33494ff29c8Snonaka 
33594ff29c8Snonaka static __inline uint8_t
vol_ssub(int vol,int stride)33694ff29c8Snonaka vol_ssub(int vol, int stride)
33794ff29c8Snonaka {
33894ff29c8Snonaka 
33994ff29c8Snonaka 	vol -= stride;
34094ff29c8Snonaka 	if (vol < 0)
34194ff29c8Snonaka 		return 0;
34294ff29c8Snonaka 	return (uint8_t)vol;
34394ff29c8Snonaka }
34494ff29c8Snonaka 
34594ff29c8Snonaka static void
wm8731_volume_down(device_t dv)34694ff29c8Snonaka wm8731_volume_down(device_t dv)
34794ff29c8Snonaka {
34894ff29c8Snonaka 	struct zaudio_softc *sc = device_private(dv);
34994ff29c8Snonaka 	int s;
35094ff29c8Snonaka 
35194ff29c8Snonaka 	s = splbio();
35294ff29c8Snonaka 	iic_acquire_bus(sc->sc_i2c, 0);
35394ff29c8Snonaka 
35494ff29c8Snonaka 	sc->sc_volume[WM8731_OP_SPKR].left =
35594ff29c8Snonaka 	    vol_ssub(sc->sc_volume[WM8731_OP_SPKR].left, ZAUDIO_VOLUME_STRIDE);
35694ff29c8Snonaka 	sc->sc_volume[WM8731_OP_SPKR].right =
35794ff29c8Snonaka 	    vol_ssub(sc->sc_volume[WM8731_OP_SPKR].right, ZAUDIO_VOLUME_STRIDE);
35894ff29c8Snonaka 
35994ff29c8Snonaka 	wm8731_update_volume(sc, WM8731_OP_SPKR);
36094ff29c8Snonaka 
36194ff29c8Snonaka 	iic_release_bus(sc->sc_i2c, 0);
36294ff29c8Snonaka 	splx(s);
36394ff29c8Snonaka }
36494ff29c8Snonaka 
36594ff29c8Snonaka static void
wm8731_volume_toggle(device_t dv)36694ff29c8Snonaka wm8731_volume_toggle(device_t dv)
36794ff29c8Snonaka {
36894ff29c8Snonaka 	struct zaudio_softc *sc = device_private(dv);
36994ff29c8Snonaka 	int s;
37094ff29c8Snonaka 
37194ff29c8Snonaka 	s = splbio();
37294ff29c8Snonaka 	iic_acquire_bus(sc->sc_i2c, 0);
37394ff29c8Snonaka 
37494ff29c8Snonaka 	if (!sc->sc_unmute[WM8731_OP_SPKR]) {
37594ff29c8Snonaka 		sc->sc_unmute[WM8731_OP_SPKR] =
37694ff29c8Snonaka 		    sc->sc_unmute_toggle[WM8731_OP_SPKR];
37794ff29c8Snonaka 	} else {
37894ff29c8Snonaka 		sc->sc_unmute[WM8731_OP_SPKR] = 0;
37994ff29c8Snonaka 	}
38094ff29c8Snonaka 	wm8731_update_mutes(sc, 1);
38194ff29c8Snonaka 
38294ff29c8Snonaka 	iic_release_bus(sc->sc_i2c, 0);
38394ff29c8Snonaka 	splx(s);
38494ff29c8Snonaka }
38594ff29c8Snonaka 
38694ff29c8Snonaka static void
wm8731_init(struct zaudio_softc * sc)38794ff29c8Snonaka wm8731_init(struct zaudio_softc *sc)
38894ff29c8Snonaka {
38994ff29c8Snonaka 
39094ff29c8Snonaka 	iic_acquire_bus(sc->sc_i2c, 0);
39194ff29c8Snonaka 
39294ff29c8Snonaka 	/* Reset the codec */
39394ff29c8Snonaka 	wm8731_write(sc, WM8731_RESET_REG, 0);
39494ff29c8Snonaka 	delay(100);
39594ff29c8Snonaka 
39694ff29c8Snonaka 	/* Switch to standby power only */
39794ff29c8Snonaka 	wm8731_write(sc, WM8731_PD_REG, WM8731_CLKOUTPD | WM8731_OSCPD |
39894ff29c8Snonaka 	      WM8731_OUTPD | WM8731_DACPD | WM8731_ADCPD | WM8731_MICPD |
39994ff29c8Snonaka 	      WM8731_LINEINPD);
40094ff29c8Snonaka 
40194ff29c8Snonaka 	/* Configure digital interface for I2S */
40294ff29c8Snonaka 	wm8731_write(sc, WM8731_DAI_REG, WM8731_SET_IWL(2) | WM8731_SET_FORMAT(2));
40394ff29c8Snonaka 
40494ff29c8Snonaka 	/* Initialise volume levels */
40594ff29c8Snonaka 	wm8731_update_volume(sc, WM8731_OP_SPKR);
40694ff29c8Snonaka 	wm8731_update_volume(sc, WM8731_OP_MIC);
40794ff29c8Snonaka 
40894ff29c8Snonaka 	scoop_set_headphone(0);
40994ff29c8Snonaka 	scoop_set_speaker(0);
41094ff29c8Snonaka 	scoop_set_mic_bias(0);
41194ff29c8Snonaka 
41294ff29c8Snonaka 	iic_release_bus(sc->sc_i2c, 0);
41394ff29c8Snonaka 
41494ff29c8Snonaka 	/* Assume that the jack state has changed. */
41594ff29c8Snonaka 	wm8731_jack(sc);
41694ff29c8Snonaka }
41794ff29c8Snonaka 
41894ff29c8Snonaka static int
wm8731_jack_intr(void * v)41994ff29c8Snonaka wm8731_jack_intr(void *v)
42094ff29c8Snonaka {
42194ff29c8Snonaka 	struct zaudio_softc *sc = v;
42294ff29c8Snonaka 
42394ff29c8Snonaka 	if (!callout_active(&sc->sc_to))
42494ff29c8Snonaka 		wm8731_jack(sc);
42594ff29c8Snonaka 
42694ff29c8Snonaka 	return 1;
42794ff29c8Snonaka }
42894ff29c8Snonaka 
42994ff29c8Snonaka static void
wm8731_jack(void * v)43094ff29c8Snonaka wm8731_jack(void *v)
43194ff29c8Snonaka {
43294ff29c8Snonaka 	struct zaudio_softc *sc = v;
43394ff29c8Snonaka 
43494ff29c8Snonaka 	switch (sc->sc_state) {
43594ff29c8Snonaka 	case ZAUDIO_JACK_STATE_OUT:
43694ff29c8Snonaka 		if (pxa2x0_gpio_get_bit(GPIO_HP_IN_C860)) {
43794ff29c8Snonaka 			sc->sc_state = ZAUDIO_JACK_STATE_INS;
43894ff29c8Snonaka 			sc->sc_icount = 0;
43994ff29c8Snonaka 		}
44094ff29c8Snonaka 		break;
44194ff29c8Snonaka 
44294ff29c8Snonaka 	case ZAUDIO_JACK_STATE_INS:
44394ff29c8Snonaka 		if (sc->sc_icount++ > 2) {
44494ff29c8Snonaka 			if (pxa2x0_gpio_get_bit(GPIO_HP_IN_C860)) {
44594ff29c8Snonaka 				sc->sc_state = ZAUDIO_JACK_STATE_IN;
44694ff29c8Snonaka 				sc->sc_jack = TRUE;
44794ff29c8Snonaka 				UNMUTE(sc, WM8731_OP_MIC, 1);
44894ff29c8Snonaka 				goto update_mutes;
44994ff29c8Snonaka 			} else
45094ff29c8Snonaka 				sc->sc_state = ZAUDIO_JACK_STATE_OUT;
45194ff29c8Snonaka 		}
45294ff29c8Snonaka 		break;
45394ff29c8Snonaka 
45494ff29c8Snonaka 	case ZAUDIO_JACK_STATE_IN:
45594ff29c8Snonaka 		if (!pxa2x0_gpio_get_bit(GPIO_HP_IN_C860)) {
45694ff29c8Snonaka 			sc->sc_state = ZAUDIO_JACK_STATE_REM;
45794ff29c8Snonaka 			sc->sc_icount = 0;
45894ff29c8Snonaka 		}
45994ff29c8Snonaka 		break;
46094ff29c8Snonaka 
46194ff29c8Snonaka 	case ZAUDIO_JACK_STATE_REM:
46294ff29c8Snonaka 		if (sc->sc_icount++ > 2) {
46394ff29c8Snonaka 			if (!pxa2x0_gpio_get_bit(GPIO_HP_IN_C860)) {
46494ff29c8Snonaka 				sc->sc_state = ZAUDIO_JACK_STATE_OUT;
46594ff29c8Snonaka 				sc->sc_jack = FALSE;
46694ff29c8Snonaka 				UNMUTE(sc, WM8731_OP_MIC, 0);
46794ff29c8Snonaka 				goto update_mutes;
46894ff29c8Snonaka 			} else
46994ff29c8Snonaka 				sc->sc_state = ZAUDIO_JACK_STATE_IN;
47094ff29c8Snonaka 		}
47194ff29c8Snonaka 		break;
47294ff29c8Snonaka 	}
47394ff29c8Snonaka 
47494ff29c8Snonaka 	callout_schedule(&sc->sc_to, hz/4);
47594ff29c8Snonaka 
47694ff29c8Snonaka 	return;
47794ff29c8Snonaka 
47894ff29c8Snonaka update_mutes:
47994ff29c8Snonaka 	callout_stop(&sc->sc_to);
48094ff29c8Snonaka 
48194ff29c8Snonaka 	if (sc->sc_playing || sc->sc_recording) {
48294ff29c8Snonaka 		iic_acquire_bus(sc->sc_i2c, 0);
48394ff29c8Snonaka 		if (sc->sc_playing)
48494ff29c8Snonaka 			wm8731_update_mutes(sc, 1);
48594ff29c8Snonaka 		if (sc->sc_recording)
48694ff29c8Snonaka 			wm8731_update_mutes(sc, 2);
48794ff29c8Snonaka 		iic_release_bus(sc->sc_i2c, 0);
48894ff29c8Snonaka 	}
48994ff29c8Snonaka }
49094ff29c8Snonaka 
49194ff29c8Snonaka static void
wm8731_standby(struct zaudio_softc * sc)49294ff29c8Snonaka wm8731_standby(struct zaudio_softc *sc)
49394ff29c8Snonaka {
49494ff29c8Snonaka 
49594ff29c8Snonaka 	iic_acquire_bus(sc->sc_i2c, 0);
49694ff29c8Snonaka 
49794ff29c8Snonaka 	/* Switch to standby power only */
49894ff29c8Snonaka 	wm8731_write(sc, WM8731_PD_REG, WM8731_CLKOUTPD | WM8731_OSCPD |
49994ff29c8Snonaka 	      WM8731_OUTPD | WM8731_DACPD | WM8731_ADCPD | WM8731_MICPD |
50094ff29c8Snonaka 	      WM8731_LINEINPD);
50194ff29c8Snonaka 
50294ff29c8Snonaka 	scoop_set_headphone(0);
50394ff29c8Snonaka 	scoop_set_speaker(0);
50494ff29c8Snonaka 	scoop_set_mic_bias(0);
50594ff29c8Snonaka 
50694ff29c8Snonaka 	/* Activating DSP and DAI */
50794ff29c8Snonaka 	wm8731_write(sc, WM8731_AC_REG, 0);
50894ff29c8Snonaka 
50994ff29c8Snonaka 	iic_release_bus(sc->sc_i2c, 0);
51094ff29c8Snonaka }
51194ff29c8Snonaka 
51294ff29c8Snonaka static void
wm8731_update_volume(struct zaudio_softc * sc,int output)51394ff29c8Snonaka wm8731_update_volume(struct zaudio_softc *sc, int output)
51494ff29c8Snonaka {
51594ff29c8Snonaka 	struct zaudio_volume *volume;
51694ff29c8Snonaka 
51794ff29c8Snonaka 	switch (output) {
51894ff29c8Snonaka 	case WM8731_OP_SPKR:
51994ff29c8Snonaka 		volume = &sc->sc_volume[WM8731_OP_SPKR];
52094ff29c8Snonaka 		wm8731_write(sc, WM8731_LHP_REG,
52194ff29c8Snonaka 		       WM8731_SET_LHPVOL(volume->left >> 1));
52294ff29c8Snonaka 		wm8731_write(sc, WM8731_RHP_REG,
52394ff29c8Snonaka 		       WM8731_SET_RHPVOL(volume->right >> 1));
52494ff29c8Snonaka 		break;
52594ff29c8Snonaka 
52694ff29c8Snonaka 	case WM8731_OP_MIC:
52794ff29c8Snonaka 		volume = &sc->sc_volume[WM8731_OP_MIC];
52894ff29c8Snonaka 		wm8731_write(sc, WM8731_LIN_REG, WM8731_LRINBOTH |
52994ff29c8Snonaka 		    WM8731_SET_LINVOL(volume->left >> 3));
53094ff29c8Snonaka 		break;
53194ff29c8Snonaka 	}
53294ff29c8Snonaka }
53394ff29c8Snonaka 
53494ff29c8Snonaka static void
wm8731_update_mutes(struct zaudio_softc * sc,int mask)53594ff29c8Snonaka wm8731_update_mutes(struct zaudio_softc *sc, int mask)
53694ff29c8Snonaka {
53794ff29c8Snonaka 	uint16_t val = WM8731_CLKOUTPD | WM8731_OSCPD | WM8731_LINEINPD;
53894ff29c8Snonaka 
53994ff29c8Snonaka 	/* playback */
54094ff29c8Snonaka 	if (mask & 1) {
54194ff29c8Snonaka 		val |= WM8731_ADCPD | WM8731_MICPD;
54294ff29c8Snonaka 		if (!sc->sc_unmute[WM8731_OP_SPKR]) {
54394ff29c8Snonaka 			val |= WM8731_OUTPD | WM8731_DACPD;
54494ff29c8Snonaka 		}
54594ff29c8Snonaka 		wm8731_write(sc, WM8731_PD_REG, val);
54694ff29c8Snonaka 		scoop_set_headphone(sc->sc_unmute[WM8731_OP_SPKR] & sc->sc_jack);
54794ff29c8Snonaka 		scoop_set_speaker(sc->sc_unmute[WM8731_OP_SPKR] & !sc->sc_jack);
54894ff29c8Snonaka 	}
54994ff29c8Snonaka 
55094ff29c8Snonaka 	/* record */
55194ff29c8Snonaka 	if (mask & 2) {
55294ff29c8Snonaka 		val = WM8731_OUTPD | WM8731_DACPD;
55394ff29c8Snonaka 		if (!sc->sc_unmute[WM8731_OP_MIC]) {
55494ff29c8Snonaka 			val |= WM8731_ADCPD | WM8731_MICPD;
55594ff29c8Snonaka 		}
55694ff29c8Snonaka 		wm8731_write(sc, WM8731_PD_REG, val);
55794ff29c8Snonaka 		scoop_set_mic_bias(sc->sc_unmute[WM8731_OP_MIC]);
55894ff29c8Snonaka 	}
55994ff29c8Snonaka }
56094ff29c8Snonaka 
56194ff29c8Snonaka static void
wm8731_play_setup(struct zaudio_softc * sc)56294ff29c8Snonaka wm8731_play_setup(struct zaudio_softc *sc)
56394ff29c8Snonaka {
56494ff29c8Snonaka 	int i;
56594ff29c8Snonaka 
56694ff29c8Snonaka 	iic_acquire_bus(sc->sc_i2c, 0);
56794ff29c8Snonaka 
56894ff29c8Snonaka 	/* Program the codec with playback settings */
56994ff29c8Snonaka 	for (i = 0; playback_regs[i][0] != 0xffff; i++) {
57094ff29c8Snonaka 		wm8731_write(sc, playback_regs[i][0], playback_regs[i][1]);
57194ff29c8Snonaka 	}
57294ff29c8Snonaka 	wm8731_update_mutes(sc, 1);
57394ff29c8Snonaka 
57494ff29c8Snonaka 	iic_release_bus(sc->sc_i2c, 0);
57594ff29c8Snonaka }
57694ff29c8Snonaka 
57794ff29c8Snonaka /*static*/ void
wm8731_record_setup(struct zaudio_softc * sc)57894ff29c8Snonaka wm8731_record_setup(struct zaudio_softc *sc)
57994ff29c8Snonaka {
58094ff29c8Snonaka 	int i;
58194ff29c8Snonaka 
58294ff29c8Snonaka 	iic_acquire_bus(sc->sc_i2c, 0);
58394ff29c8Snonaka 
58494ff29c8Snonaka 	/* Program the codec with playback settings */
58594ff29c8Snonaka 	for (i = 0; record_regs[i][0] != 0xffff; i++) {
58694ff29c8Snonaka 		wm8731_write(sc, record_regs[i][0], record_regs[i][1]);
58794ff29c8Snonaka 	}
58894ff29c8Snonaka 	wm8731_update_mutes(sc, 2);
58994ff29c8Snonaka 
59094ff29c8Snonaka 	iic_release_bus(sc->sc_i2c, 0);
59194ff29c8Snonaka }
59294ff29c8Snonaka 
59394ff29c8Snonaka static int
wm8731_halt_output(void * hdl)59494ff29c8Snonaka wm8731_halt_output(void *hdl)
59594ff29c8Snonaka {
59694ff29c8Snonaka 	struct zaudio_softc *sc = hdl;
59794ff29c8Snonaka 	int rv;
59894ff29c8Snonaka 
59994ff29c8Snonaka 	rv = pxa2x0_i2s_halt_output(&sc->sc_i2s);
60094ff29c8Snonaka 	if (!sc->sc_recording)
60194ff29c8Snonaka 		wm8731_standby(sc);
60294ff29c8Snonaka 	sc->sc_playing = 0;
60394ff29c8Snonaka 
60494ff29c8Snonaka 	return rv;
60594ff29c8Snonaka }
60694ff29c8Snonaka 
60794ff29c8Snonaka static int
wm8731_halt_input(void * hdl)60894ff29c8Snonaka wm8731_halt_input(void *hdl)
60994ff29c8Snonaka {
61094ff29c8Snonaka 	struct zaudio_softc *sc = hdl;
61194ff29c8Snonaka 	int rv;
61294ff29c8Snonaka 
61394ff29c8Snonaka 	rv = pxa2x0_i2s_halt_input(&sc->sc_i2s);
61494ff29c8Snonaka 	if (!sc->sc_playing)
61594ff29c8Snonaka 		wm8731_standby(sc);
61694ff29c8Snonaka 	sc->sc_recording = 0;
61794ff29c8Snonaka 
61894ff29c8Snonaka 	return rv;
61994ff29c8Snonaka }
62094ff29c8Snonaka 
62194ff29c8Snonaka static int
wm8731_getdev(void * hdl,struct audio_device * ret)62294ff29c8Snonaka wm8731_getdev(void *hdl, struct audio_device *ret)
62394ff29c8Snonaka {
62494ff29c8Snonaka 
62594ff29c8Snonaka 	*ret = wm8731_device;
62694ff29c8Snonaka 	return 0;
62794ff29c8Snonaka }
62894ff29c8Snonaka 
62994ff29c8Snonaka #define WM8731_SPKR_LVL		0
63094ff29c8Snonaka #define WM8731_SPKR_MUTE	1
63194ff29c8Snonaka #define WM8731_MIC_LVL		2
63294ff29c8Snonaka #define WM8731_MIC_MUTE		3
63394ff29c8Snonaka #define WM8731_RECORD_SOURCE	4
63494ff29c8Snonaka #define WM8731_OUTPUT_CLASS	5
63594ff29c8Snonaka #define WM8731_INPUT_CLASS	6
63694ff29c8Snonaka #define WM8731_RECORD_CLASS	7
63794ff29c8Snonaka 
63894ff29c8Snonaka static int
wm8731_set_port(void * hdl,struct mixer_ctrl * mc)63994ff29c8Snonaka wm8731_set_port(void *hdl, struct mixer_ctrl *mc)
64094ff29c8Snonaka {
64194ff29c8Snonaka 	struct zaudio_softc *sc = hdl;
64294ff29c8Snonaka 	int error = EINVAL;
64394ff29c8Snonaka 	int s;
64494ff29c8Snonaka 
64594ff29c8Snonaka 	s = splbio();
64694ff29c8Snonaka 	iic_acquire_bus(sc->sc_i2c, 0);
64794ff29c8Snonaka 
64894ff29c8Snonaka 	switch (mc->dev) {
64994ff29c8Snonaka 	case WM8731_SPKR_LVL:
65094ff29c8Snonaka 		if (mc->type != AUDIO_MIXER_VALUE)
65194ff29c8Snonaka 			break;
65294ff29c8Snonaka 		if (mc->un.value.num_channels == 1) {
65394ff29c8Snonaka 			sc->sc_volume[WM8731_OP_SPKR].left =
65494ff29c8Snonaka 			    mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
65594ff29c8Snonaka 			sc->sc_volume[WM8731_OP_SPKR].right =
65694ff29c8Snonaka 			    mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
65794ff29c8Snonaka 		} else if (mc->un.value.num_channels == 2) {
65894ff29c8Snonaka 			sc->sc_volume[WM8731_OP_SPKR].left =
65994ff29c8Snonaka 			    mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
66094ff29c8Snonaka 			sc->sc_volume[WM8731_OP_SPKR].right =
66194ff29c8Snonaka 			    mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
66294ff29c8Snonaka 		}
66394ff29c8Snonaka 		else
66494ff29c8Snonaka 			break;
66594ff29c8Snonaka 		wm8731_update_volume(sc, WM8731_OP_SPKR);
66694ff29c8Snonaka 		error = 0;
66794ff29c8Snonaka 		break;
66894ff29c8Snonaka 
66994ff29c8Snonaka 	case WM8731_SPKR_MUTE:
67094ff29c8Snonaka 		if (mc->type != AUDIO_MIXER_ENUM)
67194ff29c8Snonaka 			break;
67294ff29c8Snonaka 		UNMUTE(sc, WM8731_OP_SPKR, mc->un.ord ? 1 : 0);
67394ff29c8Snonaka 		wm8731_update_mutes(sc, 1);
67494ff29c8Snonaka 		error = 0;
67594ff29c8Snonaka 		break;
67694ff29c8Snonaka 
67794ff29c8Snonaka 	case WM8731_MIC_LVL:
67894ff29c8Snonaka 		if (mc->type != AUDIO_MIXER_VALUE)
67994ff29c8Snonaka 			break;
68094ff29c8Snonaka 		if (mc->un.value.num_channels == 1)
68194ff29c8Snonaka 			sc->sc_volume[WM8731_OP_MIC].left =
68294ff29c8Snonaka 			    mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
68394ff29c8Snonaka 		else
68494ff29c8Snonaka 			break;
68594ff29c8Snonaka 		wm8731_update_volume(sc, WM8731_OP_MIC);
68694ff29c8Snonaka 		error = 0;
68794ff29c8Snonaka 		break;
68894ff29c8Snonaka 
68994ff29c8Snonaka 	case WM8731_MIC_MUTE:
69094ff29c8Snonaka 		if (mc->type != AUDIO_MIXER_ENUM)
69194ff29c8Snonaka 			break;
69294ff29c8Snonaka 		UNMUTE(sc, WM8731_OP_MIC, mc->un.ord ? 1 : 0);
69394ff29c8Snonaka 		wm8731_update_mutes(sc, 2);
69494ff29c8Snonaka 		error = 0;
69594ff29c8Snonaka 		break;
69694ff29c8Snonaka 
69794ff29c8Snonaka 	case WM8731_RECORD_SOURCE:
69894ff29c8Snonaka 		if (mc->type != AUDIO_MIXER_ENUM)
69994ff29c8Snonaka 			break;
70094ff29c8Snonaka 		if (mc->un.ord != 0)
70194ff29c8Snonaka 			break;
70294ff29c8Snonaka 		/* MIC only */
70394ff29c8Snonaka 		error = 0;
70494ff29c8Snonaka 		break;
70594ff29c8Snonaka 	}
70694ff29c8Snonaka 
70794ff29c8Snonaka 	iic_release_bus(sc->sc_i2c, 0);
70894ff29c8Snonaka 	splx(s);
70994ff29c8Snonaka 
71094ff29c8Snonaka 	return error;
71194ff29c8Snonaka }
71294ff29c8Snonaka 
71394ff29c8Snonaka static int
wm8731_get_port(void * hdl,struct mixer_ctrl * mc)71494ff29c8Snonaka wm8731_get_port(void *hdl, struct mixer_ctrl *mc)
71594ff29c8Snonaka {
71694ff29c8Snonaka 	struct zaudio_softc *sc = hdl;
71794ff29c8Snonaka 	int error = EINVAL;
71894ff29c8Snonaka 
71994ff29c8Snonaka 	switch (mc->dev) {
72094ff29c8Snonaka 	case WM8731_SPKR_LVL:
72194ff29c8Snonaka 		if (mc->type != AUDIO_MIXER_VALUE)
72294ff29c8Snonaka 			break;
72394ff29c8Snonaka 		if (mc->un.value.num_channels == 1)
72494ff29c8Snonaka 			mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
72594ff29c8Snonaka 			    sc->sc_volume[WM8731_OP_SPKR].left;
72694ff29c8Snonaka 		else if (mc->un.value.num_channels == 2) {
72794ff29c8Snonaka 			mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
72894ff29c8Snonaka 			    sc->sc_volume[WM8731_OP_SPKR].left;
72994ff29c8Snonaka 			mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
73094ff29c8Snonaka 			    sc->sc_volume[WM8731_OP_SPKR].right;
73194ff29c8Snonaka 		}
73294ff29c8Snonaka 		else
73394ff29c8Snonaka 			break;
73494ff29c8Snonaka 		error = 0;
73594ff29c8Snonaka 		break;
73694ff29c8Snonaka 
73794ff29c8Snonaka 	case WM8731_SPKR_MUTE:
73894ff29c8Snonaka 		if (mc->type != AUDIO_MIXER_ENUM)
73994ff29c8Snonaka 			break;
74094ff29c8Snonaka 		mc->un.ord = sc->sc_unmute[WM8731_OP_SPKR] ? 1 : 0;
74194ff29c8Snonaka 		error = 0;
74294ff29c8Snonaka 		break;
74394ff29c8Snonaka 
74494ff29c8Snonaka 	case WM8731_MIC_LVL:
74594ff29c8Snonaka 		if (mc->type != AUDIO_MIXER_VALUE)
74694ff29c8Snonaka 			break;
74794ff29c8Snonaka 		if (mc->un.value.num_channels == 1)
74894ff29c8Snonaka 			mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
74994ff29c8Snonaka 			    sc->sc_volume[WM8731_OP_MIC].left;
75094ff29c8Snonaka 		else
75194ff29c8Snonaka 			break;
75294ff29c8Snonaka 		error = 0;
75394ff29c8Snonaka 		break;
75494ff29c8Snonaka 
75594ff29c8Snonaka 	case WM8731_MIC_MUTE:
75694ff29c8Snonaka 		if (mc->type != AUDIO_MIXER_ENUM)
75794ff29c8Snonaka 			break;
75894ff29c8Snonaka 		mc->un.ord = sc->sc_unmute[WM8731_OP_MIC] ? 1 : 0;
75994ff29c8Snonaka 		error = 0;
76094ff29c8Snonaka 		break;
76194ff29c8Snonaka 
76294ff29c8Snonaka 	case WM8731_RECORD_SOURCE:
76394ff29c8Snonaka 		if (mc->type != AUDIO_MIXER_ENUM)
76494ff29c8Snonaka 			break;
76594ff29c8Snonaka 		mc->un.ord = 0; /* MIC only */
76694ff29c8Snonaka 		error = 0;
76794ff29c8Snonaka 		break;
76894ff29c8Snonaka 	}
76994ff29c8Snonaka 
77094ff29c8Snonaka 	return error;
77194ff29c8Snonaka }
77294ff29c8Snonaka 
77394ff29c8Snonaka /*ARGSUSED*/
77494ff29c8Snonaka static int
wm8731_query_devinfo(void * hdl,struct mixer_devinfo * di)77594ff29c8Snonaka wm8731_query_devinfo(void *hdl, struct mixer_devinfo *di)
77694ff29c8Snonaka {
77794ff29c8Snonaka 
77894ff29c8Snonaka 	switch (di->index) {
77994ff29c8Snonaka 	case WM8731_SPKR_LVL:
78094ff29c8Snonaka 		di->type = AUDIO_MIXER_VALUE;
78194ff29c8Snonaka 		di->mixer_class = WM8731_OUTPUT_CLASS;
78294ff29c8Snonaka 		di->prev = AUDIO_MIXER_LAST;
78394ff29c8Snonaka 		di->next = WM8731_SPKR_MUTE;
78494ff29c8Snonaka 		strlcpy(di->label.name, AudioNspeaker,
78594ff29c8Snonaka 		    sizeof(di->label.name));
78694ff29c8Snonaka 		di->un.v.num_channels = 1;
78794ff29c8Snonaka 		strlcpy(di->un.v.units.name, AudioNvolume,
78894ff29c8Snonaka 		    sizeof(di->un.v.units.name));
78994ff29c8Snonaka 		break;
79094ff29c8Snonaka 
79194ff29c8Snonaka 	case WM8731_SPKR_MUTE:
79294ff29c8Snonaka 		di->type = AUDIO_MIXER_ENUM;
79394ff29c8Snonaka 		di->mixer_class = WM8731_OUTPUT_CLASS;
79494ff29c8Snonaka 		di->prev = WM8731_SPKR_LVL;
79594ff29c8Snonaka 		di->next = AUDIO_MIXER_LAST;
79694ff29c8Snonaka mute:
79794ff29c8Snonaka 		strlcpy(di->label.name, AudioNmute, sizeof(di->label.name));
79894ff29c8Snonaka 		di->un.e.num_mem = 2;
79994ff29c8Snonaka 		strlcpy(di->un.e.member[0].label.name, AudioNon,
80094ff29c8Snonaka 		    sizeof(di->un.e.member[0].label.name));
80194ff29c8Snonaka 		di->un.e.member[0].ord = 0;
80294ff29c8Snonaka 		strlcpy(di->un.e.member[1].label.name, AudioNoff,
80394ff29c8Snonaka 		    sizeof(di->un.e.member[1].label.name));
80494ff29c8Snonaka 		di->un.e.member[1].ord = 1;
80594ff29c8Snonaka 		break;
80694ff29c8Snonaka 
80794ff29c8Snonaka 	case WM8731_MIC_LVL:
80894ff29c8Snonaka 		di->type = AUDIO_MIXER_VALUE;
80994ff29c8Snonaka 		di->mixer_class = WM8731_INPUT_CLASS;
81094ff29c8Snonaka 		di->prev = AUDIO_MIXER_LAST;
81194ff29c8Snonaka 		di->next = WM8731_MIC_MUTE;
81294ff29c8Snonaka 		strlcpy(di->label.name, AudioNmicrophone,
81394ff29c8Snonaka 		    sizeof(di->label.name));
81494ff29c8Snonaka 		strlcpy(di->un.v.units.name, AudioNvolume,
81594ff29c8Snonaka 		    sizeof(di->un.v.units.name));
81694ff29c8Snonaka 		di->un.v.num_channels = 1;
81794ff29c8Snonaka 		break;
81894ff29c8Snonaka 
81994ff29c8Snonaka 	case WM8731_MIC_MUTE:
82094ff29c8Snonaka 		di->type = AUDIO_MIXER_ENUM;
82194ff29c8Snonaka 		di->mixer_class = WM8731_INPUT_CLASS;
82294ff29c8Snonaka 		di->prev = WM8731_MIC_LVL;
82394ff29c8Snonaka 		di->next = AUDIO_MIXER_LAST;
82494ff29c8Snonaka 		goto mute;
82594ff29c8Snonaka 
82694ff29c8Snonaka 	case WM8731_RECORD_SOURCE:
82794ff29c8Snonaka 		di->type = AUDIO_MIXER_ENUM;
82894ff29c8Snonaka 		di->mixer_class = WM8731_RECORD_CLASS;
82994ff29c8Snonaka 		di->prev = AUDIO_MIXER_LAST;
83094ff29c8Snonaka 		di->next = AUDIO_MIXER_LAST;
83194ff29c8Snonaka 		strlcpy(di->label.name, AudioNsource, sizeof(di->label.name));
83294ff29c8Snonaka 		di->un.e.num_mem = 1;
83394ff29c8Snonaka 		strlcpy(di->un.e.member[0].label.name, AudioNmicrophone,
83494ff29c8Snonaka 		    sizeof(di->un.e.member[0].label.name));
83594ff29c8Snonaka 		di->un.e.member[0].ord = 0;
83694ff29c8Snonaka 		break;
83794ff29c8Snonaka 
83894ff29c8Snonaka 	case WM8731_OUTPUT_CLASS:
83994ff29c8Snonaka 		di->type = AUDIO_MIXER_CLASS;
84094ff29c8Snonaka 		di->mixer_class = WM8731_OUTPUT_CLASS;
84194ff29c8Snonaka 		di->prev = AUDIO_MIXER_LAST;
84294ff29c8Snonaka 		di->next = AUDIO_MIXER_LAST;
84394ff29c8Snonaka 		strlcpy(di->label.name, AudioCoutputs, sizeof(di->label.name));
84494ff29c8Snonaka 		break;
84594ff29c8Snonaka 
84694ff29c8Snonaka 	case WM8731_INPUT_CLASS:
84794ff29c8Snonaka 		di->type = AUDIO_MIXER_CLASS;
84894ff29c8Snonaka 		di->mixer_class = WM8731_INPUT_CLASS;
84994ff29c8Snonaka 		di->prev = AUDIO_MIXER_LAST;
85094ff29c8Snonaka 		di->next = AUDIO_MIXER_LAST;
85194ff29c8Snonaka 		strlcpy(di->label.name, AudioCinputs, sizeof(di->label.name));
85294ff29c8Snonaka 		break;
85394ff29c8Snonaka 
85494ff29c8Snonaka 	case WM8731_RECORD_CLASS:
85594ff29c8Snonaka 		di->type = AUDIO_MIXER_CLASS;
85694ff29c8Snonaka 		di->mixer_class = WM8731_RECORD_CLASS;
85794ff29c8Snonaka 		di->prev = AUDIO_MIXER_LAST;
85894ff29c8Snonaka 		di->next = AUDIO_MIXER_LAST;
85994ff29c8Snonaka 		strlcpy(di->label.name, AudioCinputs, sizeof(di->label.name));
86094ff29c8Snonaka 		break;
86194ff29c8Snonaka 
86294ff29c8Snonaka 	default:
86394ff29c8Snonaka 		return ENXIO;
86494ff29c8Snonaka 	}
86594ff29c8Snonaka 
86694ff29c8Snonaka 	return 0;
86794ff29c8Snonaka }
86894ff29c8Snonaka 
86994ff29c8Snonaka static int
wm8731_start_output(void * hdl,void * block,int bsize,void (* intr)(void *),void * intrarg)87094ff29c8Snonaka wm8731_start_output(void *hdl, void *block, int bsize, void (*intr)(void *),
87194ff29c8Snonaka     void *intrarg)
87294ff29c8Snonaka {
87394ff29c8Snonaka 	struct zaudio_softc *sc = hdl;
87494ff29c8Snonaka 	int rv;
87594ff29c8Snonaka 
87694ff29c8Snonaka 	/* Power up codec if we are not already playing. */
87794ff29c8Snonaka 	if (!sc->sc_playing) {
87894ff29c8Snonaka 		sc->sc_playing = 1;
87994ff29c8Snonaka 		wm8731_play_setup(sc);
88094ff29c8Snonaka 	}
88194ff29c8Snonaka 
88294ff29c8Snonaka 	/* Start DMA via I2S */
88394ff29c8Snonaka 	rv = pxa2x0_i2s_start_output(&sc->sc_i2s, block, bsize, intr, intrarg);
88494ff29c8Snonaka 	if (rv) {
88594ff29c8Snonaka 		if (!sc->sc_recording)
88694ff29c8Snonaka 			wm8731_standby(sc);
88794ff29c8Snonaka 		sc->sc_playing = 0;
88894ff29c8Snonaka 	}
88994ff29c8Snonaka 
89094ff29c8Snonaka 	return rv;
89194ff29c8Snonaka }
89294ff29c8Snonaka 
89394ff29c8Snonaka static int
wm8731_start_input(void * hdl,void * block,int bsize,void (* intr)(void *),void * intrarg)89494ff29c8Snonaka wm8731_start_input(void *hdl, void *block, int bsize, void (*intr)(void *),
89594ff29c8Snonaka     void *intrarg)
89694ff29c8Snonaka {
89794ff29c8Snonaka 	struct zaudio_softc *sc = hdl;
89894ff29c8Snonaka 	int rv;
89994ff29c8Snonaka 
90094ff29c8Snonaka 	/* Power up codec if we are not already recording. */
90194ff29c8Snonaka 	if (!sc->sc_recording) {
90294ff29c8Snonaka 		sc->sc_recording = 1;
90394ff29c8Snonaka 		wm8731_record_setup(sc);
90494ff29c8Snonaka 	}
90594ff29c8Snonaka 
90694ff29c8Snonaka 	/* Start DMA via I2S */
90794ff29c8Snonaka 	rv = pxa2x0_i2s_start_input(&sc->sc_i2s, block, bsize, intr, intrarg);
90894ff29c8Snonaka 	if (rv) {
90994ff29c8Snonaka 		if (!sc->sc_playing)
91094ff29c8Snonaka 			wm8731_standby(sc);
91194ff29c8Snonaka 		sc->sc_recording = 0;
91294ff29c8Snonaka 	}
91394ff29c8Snonaka 	return rv;
91494ff29c8Snonaka }
915