Last active
August 29, 2015 13:56
-
-
Save steleto/9211944 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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