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