Skip to content

Instantly share code, notes, and snippets.

@steleto
Last active August 29, 2015 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save steleto/9211944 to your computer and use it in GitHub Desktop.
Save steleto/9211944 to your computer and use it in GitHub Desktop.
Index: sys/arch/zaurus/conf/GENERIC
==================================================================
--- sys/arch/zaurus/conf/GENERIC
+++ sys/arch/zaurus/conf/GENERIC
@@ -231,10 +231,14 @@
# WM8750 Audio
zaudio0 at iic? addr 0x1b
audio* at zaudio?
#options ZAUDIO_VOLUME_STRIDE=8
+# WM8731 Audio
+zaud0 at iic? addr 0x1b
+audio* at zaud?
+
# Audio support
audio* at audiobus?
# integrated 16550 UARTs
options COM_PXA2X0
Index: sys/arch/zaurus/conf/files.zaurus
==================================================================
--- sys/arch/zaurus/conf/files.zaurus
+++ sys/arch/zaurus/conf/files.zaurus
@@ -100,10 +100,16 @@
device zaudio: audiobus, auconv, mulaw, aurateconv, pxaiis
attach zaudio at iic
file arch/zaurus/dev/zaudio.c zaudio
defparam opt_zaudio.h ZAUDIO_VOLUME_STRIDE
+# WM8731 audio
+device zaud: audiobus, auconv, mulaw, aurateconv, pxaiis
+attach zaud at iic
+file arch/zaurus/dev/wm8731.c zaud
+defparam opt_wm8731.h WM8731_VOLUME_STRIDE
+
# Zaurus remote control
device zrc: wskbddev
attach zrc at pxaip
file arch/zaurus/dev/zrc.c zrc
Index: sys/arch/zaurus/dev/scoop.c
==================================================================
--- sys/arch/zaurus/dev/scoop.c
+++ sys/arch/zaurus/dev/scoop.c
@@ -242,21 +242,49 @@
scoop_gpio_pin_write(sc, SCOOP0_MUTE_L, GPIO_PIN_LOW);
scoop_gpio_pin_write(sc, SCOOP0_MUTE_R, GPIO_PIN_LOW);
}
}
+/*
+ * Enable or disable the speaker output connection.
+ */
+void
+scoop_set_speaker(int on)
+{
+ struct scoop_softc *sc;
+
+ if (!ZAURUS_ISC860)
+ return;
+
+ sc = device_lookup_private(&scoop_cd, 0);
+ if (sc == NULL)
+ return;
+
+ scoop_gpio_pin_ctl(sc, SCOOP0_AMP_ON, GPIO_PIN_OUTPUT);
+
+ if (on) {
+ scoop_gpio_pin_write(sc, SCOOP0_AMP_ON, GPIO_PIN_HIGH);
+ } else {
+ scoop_gpio_pin_write(sc, SCOOP0_AMP_ON, GPIO_PIN_LOW);
+ }
+}
/*
* Enable or disable the mic bias
*/
void
scoop_set_mic_bias(int onoff)
{
+ struct scoop_softc *sc0;
struct scoop_softc *sc1;
+ sc0 = device_lookup_private(&scoop_cd, 0);
sc1 = device_lookup_private(&scoop_cd, 1);
+
if (sc1 != NULL)
scoop_gpio_pin_write(sc1, SCOOP1_MIC_BIAS, onoff);
+ else if (sc0 != NULL)
+ scoop_gpio_pin_write(sc0, SCOOP0_MIC_BIAS, onoff);
}
/*
* Turn on pullup resistor while not reading the remote control.
*/
Index: sys/arch/zaurus/dev/scoopreg.h
==================================================================
--- sys/arch/zaurus/dev/scoopreg.h
+++ sys/arch/zaurus/dev/scoopreg.h
@@ -66,11 +66,11 @@
#define SCOOP0_SWB_C860 3
#define SCOOP0_MUTE_L 4
#define SCOOP0_MUTE_R 5
#define SCOOP0_AKIN_PULLUP 6
#define SCOOP0_CF_POWER_C3000 6
-#define SCOOP0_APM_ON 7
+#define SCOOP0_AMP_ON 7
#define SCOOP0_LED_ORANGE_C3000 7
#define SCOOP0_BACKLIGHT_CONT 8
#define SCOOP0_JK_A_C3000 8
#define SCOOP0_MIC_BIAS 9
#define SCOOP0_ADC_TEMP_ON_C3000 9
Index: sys/arch/zaurus/dev/scoopvar.h
==================================================================
--- sys/arch/zaurus/dev/scoopvar.h
+++ sys/arch/zaurus/dev/scoopvar.h
@@ -30,11 +30,12 @@
void scoop_charge_battery(int, int);
void scoop_discharge_battery(int);
void scoop_set_sdmmc_power(int);
void scoop_check_mcr(void);
void scoop_set_headphone(int);
+void scoop_set_speaker(int);
void scoop_set_mic_bias(int);
void scoop_akin_pullup(int);
void scoop_suspend(void);
void scoop_resume(void);
#endif /* _ZAURUS_DEV_SCOOP_H_ */
ADDED sys/arch/zaurus/dev/wm8731.c
Index: sys/arch/zaurus/dev/wm8731.c
==================================================================
--- sys/arch/zaurus/dev/wm8731.c
+++ sys/arch/zaurus/dev/wm8731.c
@@ -0,0 +1,1207 @@
+/* $NetBSD: zaudio.c,v 1.19 2012/01/29 10:12:41 tsutsui Exp $ */
+/* $OpenBSD: zaurus_audio.c,v 1.8 2005/08/18 13:23:02 robert Exp $ */
+
+/*
+ * Copyright (c) 2005 Christopher Pascoe <pascoe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*-
+ * Copyright (C) 2009 NONAKA Kimihiro <nonaka@netbsd.org>
+ * 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 AUTHOR ``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 AUTHOR 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)
+ */
+
+#include "opt_wm8731.h"
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: zaudio.c,v 1.19 2012/01/29 10:12:41 tsutsui Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/callout.h>
+#include <sys/device.h>
+#include <sys/kmem.h>
+#include <sys/kernel.h>
+#include <sys/audioio.h>
+#include <sys/mutex.h>
+#include <sys/intr.h>
+#include <sys/bus.h>
+
+#include <dev/audio_if.h>
+#include <dev/mulaw.h>
+#include <dev/auconv.h>
+
+#include <dev/i2c/i2cvar.h>
+
+#include <arm/xscale/pxa2x0reg.h>
+#include <arm/xscale/pxa2x0var.h>
+#include <arm/xscale/pxa2x0_i2c.h>
+#include <arm/xscale/pxa2x0_i2s.h>
+#include <arm/xscale/pxa2x0_dmac.h>
+#include <arm/xscale/pxa2x0_gpio.h>
+
+#include <zaurus/zaurus/zaurus_var.h>
+#include <zaurus/dev/wm8731reg.h>
+#include <zaurus/dev/scoopvar.h>
+
+#define WM8731_ADDRESS 0x1B
+
+/* GPIO pins */
+#define GPIO_HP_IN_C860 4
+
+#define WM8731_OP_SPKR 0
+#define WM8731_OP_MIC 1
+#define WM8731_OP_NUM 2
+
+#define WM8731_JACK_STATE_OUT 0
+#define WM8731_JACK_STATE_IN 1
+#define WM8731_JACK_STATE_INS 2
+#define WM8731_JACK_STATE_REM 3
+
+struct wm8731_volume {
+ uint8_t left;
+ uint8_t right;
+};
+
+struct wm8731_softc {
+ device_t sc_dev;
+ kmutex_t sc_lock;
+ kmutex_t sc_intr_lock;
+
+ /* i2s device softc */
+ /* NB: pxa2x0_i2s requires this to be the second struct member */
+ struct pxa2x0_i2s_softc sc_i2s;
+
+ i2c_tag_t sc_i2c;
+
+ int sc_playing;
+ int sc_recording;
+
+ struct wm8731_volume sc_volume[WM8731_OP_NUM];
+ uint8_t sc_unmute[WM8731_OP_NUM];
+ uint8_t sc_unmute_toggle[WM8731_OP_NUM];
+
+ int sc_jack;
+ int sc_state;
+ int sc_icount;
+ struct callout sc_to;
+};
+
+#define UNMUTE(sc,op,val) sc->sc_unmute[op] = sc->sc_unmute_toggle[op] = val
+
+static int wm8731_match(device_t, cfdata_t, void *);
+static void wm8731_attach(device_t, device_t, void *);
+
+CFATTACH_DECL_NEW(zaud, sizeof(struct wm8731_softc),
+ wm8731_match, wm8731_attach, NULL, NULL);
+
+static int wm8731_finalize(device_t);
+static bool wm8731_suspend(device_t, const pmf_qual_t *);
+static bool wm8731_resume(device_t, const pmf_qual_t *);
+static void wm8731_volume_up(device_t);
+static void wm8731_volume_down(device_t);
+static void wm8731_volume_toggle(device_t);
+
+static struct audio_device wm8731_device = {
+ "WM8731",
+ "1.0",
+ "wm"
+};
+
+static const struct audio_format wm8731_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 }
+ }
+};
+static const int wm8731_nformats = (int)__arraycount(wm8731_formats);
+
+static void wm8731_init(struct wm8731_softc *);
+static int wm8731_jack_intr(void *);
+static void wm8731_jack(void *);
+static void wm8731_standby(struct wm8731_softc *);
+static void wm8731_update_volume(struct wm8731_softc *, int);
+static void wm8731_update_mutes(struct wm8731_softc *, int);
+static void wm8731_play_setup(struct wm8731_softc *);
+/*static*/ void wm8731_record_setup(struct wm8731_softc *);
+static int wm8731_open(void *, int);
+static void wm8731_close(void *);
+static int wm8731_query_encoding(void *, struct audio_encoding *);
+static int wm8731_set_params(void *, int, int, audio_params_t *,
+ audio_params_t *, stream_filter_list_t *, stream_filter_list_t *);
+static int wm8731_round_blocksize(void *, int, int, const audio_params_t *);
+static int wm8731_start_output(void *, void *, int, void (*)(void *), void *);
+static int wm8731_start_input(void *, void *, int, void (*)(void *), void *);
+static int wm8731_halt_output(void *);
+static int wm8731_halt_input(void *);
+static int wm8731_getdev(void *, struct audio_device *);
+static int wm8731_set_port(void *, struct mixer_ctrl *);
+static int wm8731_get_port(void *, struct mixer_ctrl *);
+static int wm8731_query_devinfo(void *, struct mixer_devinfo *);
+static void *wm8731_allocm(void *, int, size_t);
+static void wm8731_freem(void *, void *, size_t);
+static size_t wm8731_round_buffersize(void *, int, size_t);
+static paddr_t wm8731_mappage(void *, void *, off_t, int);
+static int wm8731_get_props(void *);
+static void wm8731_get_locks(void *, kmutex_t **, kmutex_t **);
+
+struct audio_hw_if wm8731_hw_if = {
+ .open = wm8731_open,
+ .close = wm8731_close,
+ .drain = NULL,
+ .query_encoding = wm8731_query_encoding,
+ .set_params = wm8731_set_params,
+ .round_blocksize = wm8731_round_blocksize,
+ .commit_settings = NULL,
+ .init_output = NULL,
+ .init_input = NULL,
+ .start_output = wm8731_start_output,
+ .start_input = wm8731_start_input,
+ .halt_output = wm8731_halt_output,
+ .halt_input = wm8731_halt_input,
+ .speaker_ctl = NULL,
+ .getdev = wm8731_getdev,
+ .setfd = NULL,
+ .set_port = wm8731_set_port,
+ .get_port = wm8731_get_port,
+ .query_devinfo = wm8731_query_devinfo,
+ .allocm = wm8731_allocm,
+ .freem = wm8731_freem,
+ .round_buffersize = wm8731_round_buffersize,
+ .mappage = wm8731_mappage,
+ .get_props = wm8731_get_props,
+ .trigger_output = NULL,
+ .trigger_input = NULL,
+ .dev_ioctl = NULL,
+ .get_locks = wm8731_get_locks,
+};
+
+static const uint16_t playback_regs[][2] = {
+ /* Power Down Control */
+ { WM8731_PD_REG, WM8731_CLKOUTPD | WM8731_OSCPD | WM8731_OUTPD
+ | WM8731_ADCPD | WM8731_MICPD | WM8731_LINEINPD },
+
+ /* Digital Audio Path Control */
+ { WM8731_DAP_REG, 0 },
+
+ /* Analogue Audio Path Control */
+ { WM8731_AAP_REG, WM8731_DACSEL | WM8731_MUTEMIC },
+
+ /* Activating DSP and DAI */
+ { WM8731_AC_REG, WM8731_ACTIVE },
+
+ /* End of list */
+ { 0xffff, 0xffff }
+};
+
+static const uint16_t record_regs[][2] = {
+ /* Power Down Control */
+ { WM8731_PD_REG, WM8731_CLKOUTPD | WM8731_OSCPD | WM8731_DACPD
+ | WM8731_LINEINPD },
+
+ /* Digital Audio Path Control */
+ { WM8731_DAP_REG, 0 },
+
+ /* Analogue Audio Path Control */
+ { WM8731_AAP_REG, WM8731_INSEL | WM8731_MICBOOST },
+
+ /* Activating DSP and DAI */
+ { WM8731_AC_REG, WM8731_ACTIVE },
+
+ /* End of list */
+ { 0xffff, 0xffff }
+};
+
+static __inline int
+wm8731_write(struct wm8731_softc *sc, int reg, int val)
+{
+ uint16_t tmp;
+ uint8_t cmd;
+ uint8_t data;
+
+ tmp = (reg << 9) | (val & 0x1ff);
+ cmd = tmp >> 8;
+ data = tmp;
+ return iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP, WM8731_ADDRESS,
+ &cmd, 1, &data, 1, 0);
+}
+
+static int
+wm8731_match(device_t parent, cfdata_t cf, void *aux)
+{
+ struct i2c_attach_args *ia = aux;
+
+ if (ZAURUS_ISC1000 || ZAURUS_ISC3000)
+ return 0; /* XXX for now */
+
+ if (ia->ia_name) {
+ /* direct config - check name */
+ if (strcmp(ia->ia_name, "wm8731") == 0)
+ return 1;
+ } else {
+ /* indirect config - check typical address */
+ if (ia->ia_addr == WM8731_ADDRESS)
+ return 1;
+ }
+ return 0;
+}
+
+static void
+wm8731_attach(device_t parent, device_t self, void *aux)
+{
+ struct wm8731_softc *sc = device_private(self);
+ struct i2c_attach_args *ia = aux;
+ int error;
+
+ sc->sc_dev = self;
+ sc->sc_i2c = ia->ia_tag;
+ mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
+ mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
+
+ aprint_normal(": I2S, WM8731 Audio\n");
+ aprint_naive("\n");
+
+ sc->sc_i2s.sc_iot = &pxa2x0_bs_tag;
+ sc->sc_i2s.sc_dmat = &pxa2x0_bus_dma_tag;
+ sc->sc_i2s.sc_size = PXA2X0_I2S_SIZE;
+ sc->sc_i2s.sc_intr_lock = &sc->sc_intr_lock;
+ if (pxa2x0_i2s_attach_sub(&sc->sc_i2s)) {
+ aprint_error_dev(self, "unable to attach I2S\n");
+ goto fail_i2s;
+ }
+
+ /* Check for an I2C response from the wm8731 */
+ iic_acquire_bus(sc->sc_i2c, 0);
+ error = wm8731_write(sc, WM8731_RESET_REG, 0);
+ iic_release_bus(sc->sc_i2c, 0);
+ if (error) {
+ aprint_error_dev(self, "codec failed to respond\n");
+ goto fail_i2c;
+ }
+ delay(100);
+
+ /* Speaker On by default. */
+ sc->sc_volume[WM8731_OP_SPKR].left = 180;
+ sc->sc_volume[WM8731_OP_SPKR].right = 180;
+ sc->sc_jack = FALSE;
+ UNMUTE(sc, WM8731_OP_SPKR, 1);
+ sc->sc_volume[WM8731_OP_MIC].left = 180;
+ UNMUTE(sc, WM8731_OP_MIC, 0);
+
+ /* Configure headphone jack state change handling. */
+ callout_init(&sc->sc_to, 0);
+ callout_setfunc(&sc->sc_to, wm8731_jack, sc);
+ pxa2x0_gpio_set_function(GPIO_HP_IN_C860, GPIO_IN);
+ (void) pxa2x0_gpio_intr_establish(GPIO_HP_IN_C860, IST_EDGE_BOTH,
+ IPL_BIO, wm8731_jack_intr, sc);
+
+ /* wm8731_init() implicitly depends on ioexp or scoop */
+ config_finalize_register(self, wm8731_finalize);
+
+ audio_attach_mi(&wm8731_hw_if, sc, self);
+
+ if (!pmf_device_register(self, wm8731_suspend, wm8731_resume))
+ aprint_error_dev(self, "couldn't establish power handler\n");
+ if (!pmf_event_register(self, PMFE_AUDIO_VOLUME_UP,
+ wm8731_volume_up, true))
+ aprint_error_dev(self, "couldn't register event handler\n");
+ if (!pmf_event_register(self, PMFE_AUDIO_VOLUME_DOWN,
+ wm8731_volume_down, true))
+ aprint_error_dev(self, "couldn't register event handler\n");
+ if (!pmf_event_register(self, PMFE_AUDIO_VOLUME_TOGGLE,
+ wm8731_volume_toggle, true))
+ aprint_error_dev(self, "couldn't register event handler\n");
+
+ return;
+
+fail_i2c:
+ pxa2x0_i2s_detach_sub(&sc->sc_i2s);
+fail_i2s:
+ pmf_device_deregister(self);
+}
+
+static int
+wm8731_finalize(device_t dv)
+{
+ struct wm8731_softc *sc = device_private(dv);
+
+ wm8731_init(sc);
+ return 0;
+}
+
+static bool
+wm8731_suspend(device_t dv, const pmf_qual_t *qual)
+{
+ struct wm8731_softc *sc = device_private(dv);
+
+ callout_stop(&sc->sc_to);
+ wm8731_standby(sc);
+
+ return true;
+}
+
+static bool
+wm8731_resume(device_t dv, const pmf_qual_t *qual)
+{
+ struct wm8731_softc *sc = device_private(dv);
+
+ pxa2x0_i2s_init(&sc->sc_i2s);
+ wm8731_init(sc);
+
+ return true;
+}
+
+static __inline uint8_t
+vol_sadd(int vol, int stride)
+{
+
+ vol += stride;
+ if (vol > 255)
+ return 255;
+ return (uint8_t)vol;
+}
+
+#ifndef WM8731_VOLUME_STRIDE
+#define WM8731_VOLUME_STRIDE 8
+#endif
+
+static void
+wm8731_volume_up(device_t dv)
+{
+ struct wm8731_softc *sc = device_private(dv);
+ int s;
+
+ s = splbio();
+ iic_acquire_bus(sc->sc_i2c, 0);
+
+ sc->sc_volume[WM8731_OP_SPKR].left =
+ vol_sadd(sc->sc_volume[WM8731_OP_SPKR].left, WM8731_VOLUME_STRIDE);
+ sc->sc_volume[WM8731_OP_SPKR].right =
+ vol_sadd(sc->sc_volume[WM8731_OP_SPKR].right, WM8731_VOLUME_STRIDE);
+
+ wm8731_update_volume(sc, WM8731_OP_SPKR);
+
+ iic_release_bus(sc->sc_i2c, 0);
+ splx(s);
+}
+
+static __inline uint8_t
+vol_ssub(int vol, int stride)
+{
+
+ vol -= stride;
+ if (vol < 0)
+ return 0;
+ return (uint8_t)vol;
+}
+
+static void
+wm8731_volume_down(device_t dv)
+{
+ struct wm8731_softc *sc = device_private(dv);
+ int s;
+
+ s = splbio();
+ iic_acquire_bus(sc->sc_i2c, 0);
+
+ sc->sc_volume[WM8731_OP_SPKR].left =
+ vol_ssub(sc->sc_volume[WM8731_OP_SPKR].left, WM8731_VOLUME_STRIDE);
+ sc->sc_volume[WM8731_OP_SPKR].right =
+ vol_ssub(sc->sc_volume[WM8731_OP_SPKR].right, WM8731_VOLUME_STRIDE);
+
+ wm8731_update_volume(sc, WM8731_OP_SPKR);
+
+ iic_release_bus(sc->sc_i2c, 0);
+ splx(s);
+}
+
+static void
+wm8731_volume_toggle(device_t dv)
+{
+ struct wm8731_softc *sc = device_private(dv);
+ int s;
+
+ s = splbio();
+ iic_acquire_bus(sc->sc_i2c, 0);
+
+ if (!sc->sc_unmute[WM8731_OP_SPKR]) {
+ sc->sc_unmute[WM8731_OP_SPKR] =
+ sc->sc_unmute_toggle[WM8731_OP_SPKR];
+ } else {
+ sc->sc_unmute[WM8731_OP_SPKR] = 0;
+ }
+ wm8731_update_mutes(sc, 1);
+
+ iic_release_bus(sc->sc_i2c, 0);
+ splx(s);
+}
+
+static void
+wm8731_init(struct wm8731_softc *sc)
+{
+
+ iic_acquire_bus(sc->sc_i2c, 0);
+
+ /* Reset the codec */
+ wm8731_write(sc, WM8731_RESET_REG, 0);
+ delay(100);
+
+ /* Switch to standby power only */
+ wm8731_write(sc, WM8731_PD_REG, WM8731_CLKOUTPD | WM8731_OSCPD |
+ WM8731_OUTPD | WM8731_DACPD | WM8731_ADCPD | WM8731_MICPD |
+ WM8731_LINEINPD);
+
+ /* Configure digital interface for I2S */
+ wm8731_write(sc, WM8731_DAI_REG, WM8731_SET_IWL(2) | WM8731_SET_FORMAT(2));
+
+ /* Initialise volume levels */
+ wm8731_update_volume(sc, WM8731_OP_SPKR);
+ wm8731_update_volume(sc, WM8731_OP_MIC);
+
+ scoop_set_headphone(0);
+ scoop_set_speaker(0);
+ scoop_set_mic_bias(0);
+
+ iic_release_bus(sc->sc_i2c, 0);
+
+ /* Assume that the jack state has changed. */
+ wm8731_jack(sc);
+}
+
+static int
+wm8731_jack_intr(void *v)
+{
+ struct wm8731_softc *sc = v;
+
+ if (!callout_active(&sc->sc_to))
+ wm8731_jack(sc);
+
+ return 1;
+}
+
+static void
+wm8731_jack(void *v)
+{
+ struct wm8731_softc *sc = v;
+
+ switch (sc->sc_state) {
+ case WM8731_JACK_STATE_OUT:
+ if (pxa2x0_gpio_get_bit(GPIO_HP_IN_C860)) {
+ sc->sc_state = WM8731_JACK_STATE_INS;
+ sc->sc_icount = 0;
+ }
+ break;
+
+ case WM8731_JACK_STATE_INS:
+ if (sc->sc_icount++ > 2) {
+ if (pxa2x0_gpio_get_bit(GPIO_HP_IN_C860)) {
+ sc->sc_state = WM8731_JACK_STATE_IN;
+ sc->sc_jack = TRUE;
+ UNMUTE(sc, WM8731_OP_MIC, 1);
+ goto update_mutes;
+ } else
+ sc->sc_state = WM8731_JACK_STATE_OUT;
+ }
+ break;
+
+ case WM8731_JACK_STATE_IN:
+ if (!pxa2x0_gpio_get_bit(GPIO_HP_IN_C860)) {
+ sc->sc_state = WM8731_JACK_STATE_REM;
+ sc->sc_icount = 0;
+ }
+ break;
+
+ case WM8731_JACK_STATE_REM:
+ if (sc->sc_icount++ > 2) {
+ if (!pxa2x0_gpio_get_bit(GPIO_HP_IN_C860)) {
+ sc->sc_state = WM8731_JACK_STATE_OUT;
+ sc->sc_jack = FALSE;
+ UNMUTE(sc, WM8731_OP_MIC, 0);
+ goto update_mutes;
+ } else
+ sc->sc_state = WM8731_JACK_STATE_IN;
+ }
+ break;
+ }
+
+ callout_schedule(&sc->sc_to, hz/4);
+
+ return;
+
+update_mutes:
+ callout_stop(&sc->sc_to);
+
+ if (sc->sc_playing || sc->sc_recording) {
+ iic_acquire_bus(sc->sc_i2c, 0);
+ if (sc->sc_playing)
+ wm8731_update_mutes(sc, 1);
+ if (sc->sc_recording)
+ wm8731_update_mutes(sc, 2);
+ iic_release_bus(sc->sc_i2c, 0);
+ }
+}
+
+static void
+wm8731_standby(struct wm8731_softc *sc)
+{
+
+ iic_acquire_bus(sc->sc_i2c, 0);
+
+ /* Switch to standby power only */
+ wm8731_write(sc, WM8731_PD_REG, WM8731_CLKOUTPD | WM8731_OSCPD |
+ WM8731_OUTPD | WM8731_DACPD | WM8731_ADCPD | WM8731_MICPD |
+ WM8731_LINEINPD);
+
+ scoop_set_headphone(0);
+ scoop_set_speaker(0);
+ scoop_set_mic_bias(0);
+
+ /* Activating DSP and DAI */
+ wm8731_write(sc, WM8731_AC_REG, 0);
+
+ iic_release_bus(sc->sc_i2c, 0);
+}
+
+static void
+wm8731_update_volume(struct wm8731_softc *sc, int output)
+{
+ struct wm8731_volume *volume;
+
+ switch (output) {
+ case WM8731_OP_SPKR:
+ volume = &sc->sc_volume[WM8731_OP_SPKR];
+ wm8731_write(sc, WM8731_LHP_REG,
+ WM8731_SET_LHPVOL(volume->left >> 1));
+ wm8731_write(sc, WM8731_RHP_REG,
+ WM8731_SET_RHPVOL(volume->right >> 1));
+ break;
+
+ case WM8731_OP_MIC:
+ volume = &sc->sc_volume[WM8731_OP_MIC];
+ wm8731_write(sc, WM8731_LIN_REG, WM8731_LRINBOTH |
+ WM8731_SET_LINVOL(volume->left >> 3));
+ break;
+ }
+}
+
+static void
+wm8731_update_mutes(struct wm8731_softc *sc, int mask)
+{
+ uint16_t val = WM8731_CLKOUTPD | WM8731_OSCPD | WM8731_LINEINPD;
+
+ /* playback */
+ if (mask & 1) {
+ val |= WM8731_ADCPD | WM8731_MICPD;
+ if (!sc->sc_unmute[WM8731_OP_SPKR]) {
+ val |= WM8731_OUTPD | WM8731_DACPD;
+ }
+ wm8731_write(sc, WM8731_PD_REG, val);
+ scoop_set_headphone(sc->sc_unmute[WM8731_OP_SPKR] & sc->sc_jack);
+ scoop_set_speaker(sc->sc_unmute[WM8731_OP_SPKR] & !sc->sc_jack);
+ }
+
+ /* record */
+ if (mask & 2) {
+ val = WM8731_OUTPD | WM8731_DACPD;
+ if (!sc->sc_unmute[WM8731_OP_MIC]) {
+ val |= WM8731_ADCPD | WM8731_MICPD;
+ }
+ wm8731_write(sc, WM8731_PD_REG, val);
+ scoop_set_mic_bias(sc->sc_unmute[WM8731_OP_MIC]);
+ }
+}
+
+static void
+wm8731_play_setup(struct wm8731_softc *sc)
+{
+ int i;
+
+ iic_acquire_bus(sc->sc_i2c, 0);
+
+ /* Program the codec with playback settings */
+ for (i = 0; playback_regs[i][0] != 0xffff; i++) {
+ wm8731_write(sc, playback_regs[i][0], playback_regs[i][1]);
+ }
+ wm8731_update_mutes(sc, 1);
+
+ iic_release_bus(sc->sc_i2c, 0);
+}
+
+/*static*/ void
+wm8731_record_setup(struct wm8731_softc *sc)
+{
+ int i;
+
+ iic_acquire_bus(sc->sc_i2c, 0);
+
+ /* Program the codec with playback settings */
+ for (i = 0; record_regs[i][0] != 0xffff; i++) {
+ wm8731_write(sc, record_regs[i][0], record_regs[i][1]);
+ }
+ wm8731_update_mutes(sc, 2);
+
+ iic_release_bus(sc->sc_i2c, 0);
+}
+
+/*
+ * audio operation functions.
+ */
+static int
+wm8731_open(void *hdl, int flags)
+{
+ struct wm8731_softc *sc = hdl;
+
+ /* Power on the I2S bus and codec */
+ pxa2x0_i2s_open(&sc->sc_i2s);
+
+ return 0;
+}
+
+static void
+wm8731_close(void *hdl)
+{
+ struct wm8731_softc *sc = hdl;
+
+ wm8731_standby(sc);
+
+ /* Power off the I2S bus and codec */
+ pxa2x0_i2s_close(&sc->sc_i2s);
+}
+
+static int
+wm8731_query_encoding(void *hdl, struct audio_encoding *aep)
+{
+
+ switch (aep->index) {
+ case 0:
+ strlcpy(aep->name, AudioEulinear, sizeof(aep->name));
+ aep->encoding = AUDIO_ENCODING_ULINEAR;
+ aep->precision = 8;
+ aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ break;
+
+ case 1:
+ strlcpy(aep->name, AudioEmulaw, sizeof(aep->name));
+ aep->encoding = AUDIO_ENCODING_ULAW;
+ aep->precision = 8;
+ aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ break;
+
+ case 2:
+ strlcpy(aep->name, AudioEalaw, sizeof(aep->name));
+ aep->encoding = AUDIO_ENCODING_ALAW;
+ aep->precision = 8;
+ aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ break;
+
+ case 3:
+ strlcpy(aep->name, AudioEslinear, sizeof(aep->name));
+ aep->encoding = AUDIO_ENCODING_SLINEAR;
+ aep->precision = 8;
+ aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ break;
+
+ case 4:
+ strlcpy(aep->name, AudioEslinear_le, sizeof(aep->name));
+ aep->encoding = AUDIO_ENCODING_SLINEAR_LE;
+ aep->precision = 16;
+ aep->flags = 0;
+ break;
+
+ case 5:
+ strlcpy(aep->name, AudioEulinear_le, sizeof(aep->name));
+ aep->encoding = AUDIO_ENCODING_ULINEAR_LE;
+ aep->precision = 16;
+ aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ break;
+
+ case 6:
+ strlcpy(aep->name, AudioEslinear_be, sizeof(aep->name));
+ aep->encoding = AUDIO_ENCODING_SLINEAR_BE;
+ aep->precision = 16;
+ aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ break;
+
+ case 7:
+ strlcpy(aep->name, AudioEulinear_be, sizeof(aep->name));
+ aep->encoding = AUDIO_ENCODING_ULINEAR_BE;
+ aep->precision = 16;
+ aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+wm8731_set_params(void *hdl, int setmode, int usemode, audio_params_t *play,
+ audio_params_t *rec, stream_filter_list_t *pfil, stream_filter_list_t *rfil)
+{
+ struct wm8731_softc *sc = hdl;
+ struct audio_params *p;
+ stream_filter_list_t *fil;
+ int mode, i;
+
+ if (play->sample_rate != rec->sample_rate &&
+ usemode == (AUMODE_PLAY | AUMODE_RECORD)) {
+ if (setmode == AUMODE_PLAY) {
+ rec->sample_rate = play->sample_rate;
+ setmode |= AUMODE_RECORD;
+ } else if (setmode == AUMODE_RECORD) {
+ play->sample_rate = rec->sample_rate;
+ setmode |= AUMODE_PLAY;
+ } else
+ return EINVAL;
+ }
+
+ for (mode = AUMODE_RECORD; mode != -1;
+ mode = (mode == AUMODE_RECORD) ? AUMODE_PLAY : -1) {
+ if ((setmode & mode) == 0)
+ continue;
+
+ p = (mode == AUMODE_PLAY) ? play : rec;
+
+ if (p->sample_rate < 4000 || p->sample_rate > 48000 ||
+ (p->precision != 8 && p->precision != 16) ||
+ (p->channels != 1 && p->channels != 2))
+ return EINVAL;
+
+ fil = (mode == AUMODE_PLAY) ? pfil : rfil;
+ i = auconv_set_converter(wm8731_formats, wm8731_nformats,
+ mode, p, true, fil);
+ if (i < 0)
+ return EINVAL;
+ }
+
+ if (setmode == AUMODE_RECORD)
+ pxa2x0_i2s_setspeed(&sc->sc_i2s, &rec->sample_rate);
+ else
+ pxa2x0_i2s_setspeed(&sc->sc_i2s, &play->sample_rate);
+
+ return 0;
+}
+
+static int
+wm8731_round_blocksize(void *hdl, int bs, int mode, const audio_params_t *param)
+{
+ struct wm8731_softc *sc = hdl;
+
+ return pxa2x0_i2s_round_blocksize(&sc->sc_i2s, bs, mode, param);
+}
+
+static int
+wm8731_halt_output(void *hdl)
+{
+ struct wm8731_softc *sc = hdl;
+ int rv;
+
+ rv = pxa2x0_i2s_halt_output(&sc->sc_i2s);
+ if (!sc->sc_recording)
+ wm8731_standby(sc);
+ sc->sc_playing = 0;
+
+ return rv;
+}
+
+static int
+wm8731_halt_input(void *hdl)
+{
+ struct wm8731_softc *sc = hdl;
+ int rv;
+
+ rv = pxa2x0_i2s_halt_input(&sc->sc_i2s);
+ if (!sc->sc_playing)
+ wm8731_standby(sc);
+ sc->sc_recording = 0;
+
+ return rv;
+}
+
+static int
+wm8731_getdev(void *hdl, struct audio_device *ret)
+{
+
+ *ret = wm8731_device;
+ return 0;
+}
+
+#define WM8731_SPKR_LVL 0
+#define WM8731_SPKR_MUTE 1
+#define WM8731_MIC_LVL 2
+#define WM8731_MIC_MUTE 3
+#define WM8731_RECORD_SOURCE 4
+#define WM8731_OUTPUT_CLASS 5
+#define WM8731_INPUT_CLASS 6
+#define WM8731_RECORD_CLASS 7
+
+static int
+wm8731_set_port(void *hdl, struct mixer_ctrl *mc)
+{
+ struct wm8731_softc *sc = hdl;
+ int error = EINVAL;
+ int s;
+
+ s = splbio();
+ iic_acquire_bus(sc->sc_i2c, 0);
+
+ switch (mc->dev) {
+ case WM8731_SPKR_LVL:
+ if (mc->type != AUDIO_MIXER_VALUE)
+ break;
+ if (mc->un.value.num_channels == 1) {
+ sc->sc_volume[WM8731_OP_SPKR].left =
+ mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
+ sc->sc_volume[WM8731_OP_SPKR].right =
+ mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
+ } else if (mc->un.value.num_channels == 2) {
+ sc->sc_volume[WM8731_OP_SPKR].left =
+ mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
+ sc->sc_volume[WM8731_OP_SPKR].right =
+ mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
+ }
+ else
+ break;
+ wm8731_update_volume(sc, WM8731_OP_SPKR);
+ error = 0;
+ break;
+
+ case WM8731_SPKR_MUTE:
+ if (mc->type != AUDIO_MIXER_ENUM)
+ break;
+ UNMUTE(sc, WM8731_OP_SPKR, mc->un.ord ? 1 : 0);
+ wm8731_update_mutes(sc, 1);
+ error = 0;
+ break;
+
+ case WM8731_MIC_LVL:
+ if (mc->type != AUDIO_MIXER_VALUE)
+ break;
+ if (mc->un.value.num_channels == 1)
+ sc->sc_volume[WM8731_OP_MIC].left =
+ mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
+ else
+ break;
+ wm8731_update_volume(sc, WM8731_OP_MIC);
+ error = 0;
+ break;
+
+ case WM8731_MIC_MUTE:
+ if (mc->type != AUDIO_MIXER_ENUM)
+ break;
+ UNMUTE(sc, WM8731_OP_MIC, mc->un.ord ? 1 : 0);
+ wm8731_update_mutes(sc, 2);
+ error = 0;
+ break;
+
+ case WM8731_RECORD_SOURCE:
+ if (mc->type != AUDIO_MIXER_ENUM)
+ break;
+ if (mc->un.ord != 0)
+ break;
+ /* MIC only */
+ error = 0;
+ break;
+ }
+
+ iic_release_bus(sc->sc_i2c, 0);
+ splx(s);
+
+ return error;
+}
+
+static int
+wm8731_get_port(void *hdl, struct mixer_ctrl *mc)
+{
+ struct wm8731_softc *sc = hdl;
+ int error = EINVAL;
+
+ switch (mc->dev) {
+ case WM8731_SPKR_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[WM8731_OP_SPKR].left;
+ else if (mc->un.value.num_channels == 2) {
+ mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
+ sc->sc_volume[WM8731_OP_SPKR].left;
+ mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
+ sc->sc_volume[WM8731_OP_SPKR].right;
+ }
+ else
+ break;
+ error = 0;
+ break;
+
+ case WM8731_SPKR_MUTE:
+ if (mc->type != AUDIO_MIXER_ENUM)
+ break;
+ mc->un.ord = sc->sc_unmute[WM8731_OP_SPKR] ? 1 : 0;
+ error = 0;
+ break;
+
+ case WM8731_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[WM8731_OP_MIC].left;
+ else
+ break;
+ error = 0;
+ break;
+
+ case WM8731_MIC_MUTE:
+ if (mc->type != AUDIO_MIXER_ENUM)
+ break;
+ mc->un.ord = sc->sc_unmute[WM8731_OP_MIC] ? 1 : 0;
+ error = 0;
+ break;
+
+ case WM8731_RECORD_SOURCE:
+ if (mc->type != AUDIO_MIXER_ENUM)
+ break;
+ mc->un.ord = 0; /* MIC only */
+ error = 0;
+ break;
+ }
+
+ return error;
+}
+
+/*ARGSUSED*/
+static int
+wm8731_query_devinfo(void *hdl, struct mixer_devinfo *di)
+{
+
+ switch (di->index) {
+ case WM8731_SPKR_LVL:
+ di->type = AUDIO_MIXER_VALUE;
+ di->mixer_class = WM8731_OUTPUT_CLASS;
+ di->prev = AUDIO_MIXER_LAST;
+ di->next = WM8731_SPKR_MUTE;
+ strlcpy(di->label.name, AudioNspeaker,
+ sizeof(di->label.name));
+ di->un.v.num_channels = 1;
+ strlcpy(di->un.v.units.name, AudioNvolume,
+ sizeof(di->un.v.units.name));
+ break;
+
+ case WM8731_SPKR_MUTE:
+ di->type = AUDIO_MIXER_ENUM;
+ di->mixer_class = WM8731_OUTPUT_CLASS;
+ di->prev = WM8731_SPKR_LVL;
+ di->next = AUDIO_MIXER_LAST;
+mute:
+ strlcpy(di->label.name, AudioNmute, sizeof(di->label.name));
+ di->un.e.num_mem = 2;
+ strlcpy(di->un.e.member[0].label.name, AudioNon,
+ sizeof(di->un.e.member[0].label.name));
+ di->un.e.member[0].ord = 0;
+ strlcpy(di->un.e.member[1].label.name, AudioNoff,
+ sizeof(di->un.e.member[1].label.name));
+ di->un.e.member[1].ord = 1;
+ break;
+
+ case WM8731_MIC_LVL:
+ di->type = AUDIO_MIXER_VALUE;
+ di->mixer_class = WM8731_INPUT_CLASS;
+ di->prev = AUDIO_MIXER_LAST;
+ di->next = WM8731_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 WM8731_MIC_MUTE:
+ di->type = AUDIO_MIXER_ENUM;
+ di->mixer_class = WM8731_INPUT_CLASS;
+ di->prev = WM8731_MIC_LVL;
+ di->next = AUDIO_MIXER_LAST;
+ goto mute;
+
+ case WM8731_RECORD_SOURCE:
+ di->type = AUDIO_MIXER_ENUM;
+ di->mixer_class = WM8731_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 WM8731_OUTPUT_CLASS:
+ di->type = AUDIO_MIXER_CLASS;
+ di->mixer_class = WM8731_OUTPUT_CLASS;
+ di->prev = AUDIO_MIXER_LAST;
+ di->next = AUDIO_MIXER_LAST;
+ strlcpy(di->label.name, AudioCoutputs, sizeof(di->label.name));
+ break;
+
+ case WM8731_INPUT_CLASS:
+ di->type = AUDIO_MIXER_CLASS;
+ di->mixer_class = WM8731_INPUT_CLASS;
+ di->prev = AUDIO_MIXER_LAST;
+ di->next = AUDIO_MIXER_LAST;
+ strlcpy(di->label.name, AudioCinputs, sizeof(di->label.name));
+ break;
+
+ case WM8731_RECORD_CLASS:
+ di->type = AUDIO_MIXER_CLASS;
+ di->mixer_class = WM8731_RECORD_CLASS;
+ di->prev = AUDIO_MIXER_LAST;
+ di->next = AUDIO_MIXER_LAST;
+ strlcpy(di->label.name, AudioCinputs, sizeof(di->label.name));
+ break;
+
+ default:
+ return ENXIO;
+ }
+
+ return 0;
+}
+
+static void *
+wm8731_allocm(void *hdl, int direction, size_t size)
+{
+ struct wm8731_softc *sc = hdl;
+
+ return pxa2x0_i2s_allocm(&sc->sc_i2s, direction, size);
+}
+
+static void
+wm8731_freem(void *hdl, void *ptr, size_t size)
+{
+ struct wm8731_softc *sc = hdl;
+
+ return pxa2x0_i2s_freem(&sc->sc_i2s, ptr, size);
+}
+
+static size_t
+wm8731_round_buffersize(void *hdl, int direction, size_t bufsize)
+{
+ struct wm8731_softc *sc = hdl;
+
+ return pxa2x0_i2s_round_buffersize(&sc->sc_i2s, direction, bufsize);
+}
+
+static paddr_t
+wm8731_mappage(void *hdl, void *mem, off_t off, int prot)
+{
+ struct wm8731_softc *sc = hdl;
+
+ return pxa2x0_i2s_mappage(&sc->sc_i2s, mem, off, prot);
+}
+
+static int
+wm8731_get_props(void *hdl)
+{
+
+ return AUDIO_PROP_MMAP|AUDIO_PROP_INDEPENDENT;
+}
+
+static int
+wm8731_start_output(void *hdl, void *block, int bsize, void (*intr)(void *),
+ void *intrarg)
+{
+ struct wm8731_softc *sc = hdl;
+ int rv;
+
+ /* Power up codec if we are not already playing. */
+ if (!sc->sc_playing) {
+ sc->sc_playing = 1;
+ wm8731_play_setup(sc);
+ }
+
+ /* Start DMA via I2S */
+ rv = pxa2x0_i2s_start_output(&sc->sc_i2s, block, bsize, intr, intrarg);
+ if (rv) {
+ if (!sc->sc_recording)
+ wm8731_standby(sc);
+ sc->sc_playing = 0;
+ }
+
+ return rv;
+}
+
+static int
+wm8731_start_input(void *hdl, void *block, int bsize, void (*intr)(void *),
+ void *intrarg)
+{
+ struct wm8731_softc *sc = hdl;
+ int rv;
+
+ /* Power up codec if we are not already recording. */
+ if (!sc->sc_recording) {
+ sc->sc_recording = 1;
+ wm8731_record_setup(sc);
+ }
+
+ /* Start DMA via I2S */
+ rv = pxa2x0_i2s_start_input(&sc->sc_i2s, block, bsize, intr, intrarg);
+ if (rv) {
+ if (!sc->sc_playing)
+ wm8731_standby(sc);
+ sc->sc_recording = 0;
+ }
+ return rv;
+}
+
+static void
+wm8731_get_locks(void *hdl, kmutex_t **intr, kmutex_t **thread)
+{
+ struct wm8731_softc *sc = hdl;
+
+ *intr = &sc->sc_intr_lock;
+ *thread = &sc->sc_lock;
+}
ADDED sys/arch/zaurus/dev/wm8731reg.h
Index: sys/arch/zaurus/dev/wm8731reg.h
==================================================================
--- sys/arch/zaurus/dev/wm8731reg.h
+++ sys/arch/zaurus/dev/wm8731reg.h
@@ -0,0 +1,125 @@
+/* $NetBSD$ */
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by TOYOKURA Atsushi.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef _DEV_IC_WM8731REG_H_
+#define _DEV_IC_WM8731REG_H_
+
+/*
+ * Wolfson Microelectronics' WM8731/WM8731L I2C/I2S audio codec:
+ * - I2C register definitions. Used in the Sharp Zaurus SL-C7x0/860.
+ */
+
+#define WM8731_LIN_REG 0x00 /* Left Line In */
+#define WM8731_LRINBOTH 0x100
+#define WM8731_LINMUTE 0x080
+#define WM8731_LINVOL_MASK 0x01F
+#define WM8731_GET_LINVOL(x) ((x) & WM8731_LINVOL_MASK)
+#define WM8731_SET_LINVOL(x) ((x) & WM8731_LINVOL_MASK)
+
+#define WM8731_RIN_REG 0x01 /* Right Line In */
+#define WM8731_RLINBOTH 0x100
+#define WM8731_RINMUTE 0x080
+#define WM8731_RINVOL_MASK 0x01F
+#define WM8731_GET_RINVOL(x) ((x) & WM8731_RINVOL_MASK)
+#define WM8731_SET_RINVOL(x) ((x) & WM8731_RINVOL_MASK)
+
+#define WM8731_LHP_REG 0x02 /* Left Headphone Out */
+#define WM8731_LRHPBOTH 0x100
+#define WM8731_LZCEN 0x080
+#define WM8731_LHPVOL_MASK 0x07F
+#define WM8731_GET_LHPVOL(x) ((x) & WM8731_LHPVOL_MASK)
+#define WM8731_SET_LHPVOL(x) ((x) & WM8731_LHPVOL_MASK)
+
+#define WM8731_RHP_REG 0x03 /* Right Headphone Out */
+#define WM8731_RLHPBOTH 0x100
+#define WM8731_RZCEN 0x080
+#define WM8731_RHPVOL_MASK 0x07F
+#define WM8731_GET_RHPVOL(x) ((x) & WM8731_RHPVOL_MASK)
+#define WM8731_SET_RHPVOL(x) ((x) & WM8731_RHPVOL_MASK)
+
+#define WM8731_AAP_REG 0x04 /* Analogue Audio Path Control */
+#define WM8731_SIDEATT_MASK 0x0C0
+#define WM8731_SIDETONE 0x020
+#define WM8731_DACSEL 0x010
+#define WM8731_BYPASS 0x008
+#define WM8731_INSEL 0x004
+#define WM8731_MUTEMIC 0x002
+#define WM8731_MICBOOST 0x001
+#define WM8731_GET_SIDEATT(x) (((x) & WM8731_SIDEATT_MASK) >> 6)
+#define WM8731_SET_SIDEATT(x) (((x) << 6) & WM8731_SIDEATT_MASK)
+
+#define WM8731_DAP_REG 0x05 /* Digital Audio Path Control */
+#define WM8731_HPOR 0x010
+#define WM8731_DACMU 0x008
+#define WM8731_DEEMP_MASK 0x006
+#define WM8731_ADCHP 0x001
+#define WM8731_GET_DEEMP(x) (((x) & WM8731_DEEMP_MASK) >> 1)
+#define WM8731_SET_DEEMP(x) (((x) << 1) & WM8731_DEEMP_MASK)
+
+#define WM8731_PD_REG 0x06 /* Power Down Control */
+#define WM8731_POWEROFF 0x080
+#define WM8731_CLKOUTPD 0x040
+#define WM8731_OSCPD 0x020
+#define WM8731_OUTPD 0x010
+#define WM8731_DACPD 0x008
+#define WM8731_ADCPD 0x004
+#define WM8731_MICPD 0x002
+#define WM8731_LINEINPD 0x001
+
+#define WM8731_DAI_REG 0x07 /* Digital Audio Interface Format */
+#define WM8731_BCLKINV 0x080
+#define WM8731_MS 0x040
+#define WM8731_LRSWAP 0x020
+#define WM8731_LRP 0x010
+#define WM8731_IWL_MASK 0x00C
+#define WM8731_FORMAT_MASK 0x003
+#define WM8731_GET_IWL(x) (((x) & WM8731_IWL_MASK) >> 2)
+#define WM8731_SET_IWL(x) (((x) << 2) & WM8731_IWL_MASK)
+#define WM8731_GET_FORMAT(x) ((x) & WM8731_FORMAT_MASK)
+#define WM8731_SET_FORMAT(x) ((x) & WM8731_FORMAT_MASK)
+
+#define WM8731_SP_REG 0x08 /* Sampling Control */
+#define WM8731_CLKODIV2 0x080
+#define WM8731_CLKIDIV2 0x040
+#define WM8731_SR_MASK 0x03C
+#define WM8731_BOSR 0x002
+#define WM8731_USB 0x001
+#define WM8731_GET_SR(x) (((x) & WM8731_SR_MASK) >> 2)
+#define WM8731_SET_SR(x) (((x) << 2) & WM8731_SR_MASK)
+
+#define WM8731_AC_REG 0x09 /* Active Control */
+#define WM8731_ACTIVE 0x001
+
+#define WM8731_RESET_REG 0x0F /* Reset Register */
+#define WM8731_RESET_MASK 0x1FF
+#define WM8731_GET_RESET(x) ((x) & WM8731_RESET_MASK)
+#define WM8731_SET_RESET(x) ((x) & WM8731_RESET_MASK)
+
+#endif /* _DEV_IC_WM8731REG_H_ */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment