Module Name: src Committed By: nonaka Date: Tue Sep 23 14:49:46 UTC 2014
Modified Files: src/sys/arch/zaurus/conf: files.zaurus src/sys/arch/zaurus/dev: scoop.c scoopreg.h scoopvar.h zaudio.c Added Files: src/sys/arch/zaurus/dev: wm8731_zaudio.c wm8731reg.h wm8731var.h wm8750_zaudio.c wm8750var.h zaudiovar.h Log Message: Apply WM8731 support patch from TOYOKURA Atsushi, arranged by me. - SL-C700: work fine - SL-C750: attach failed - SL-C860: not tested To generate a diff of this commit: cvs rdiff -u -r1.11 -r1.12 src/sys/arch/zaurus/conf/files.zaurus cvs rdiff -u -r1.12 -r1.13 src/sys/arch/zaurus/dev/scoop.c cvs rdiff -u -r1.5 -r1.6 src/sys/arch/zaurus/dev/scoopreg.h \ src/sys/arch/zaurus/dev/scoopvar.h cvs rdiff -u -r0 -r1.1 src/sys/arch/zaurus/dev/wm8731_zaudio.c \ src/sys/arch/zaurus/dev/wm8731reg.h src/sys/arch/zaurus/dev/wm8731var.h \ src/sys/arch/zaurus/dev/wm8750_zaudio.c \ src/sys/arch/zaurus/dev/wm8750var.h src/sys/arch/zaurus/dev/zaudiovar.h cvs rdiff -u -r1.20 -r1.21 src/sys/arch/zaurus/dev/zaudio.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/zaurus/conf/files.zaurus diff -u src/sys/arch/zaurus/conf/files.zaurus:1.11 src/sys/arch/zaurus/conf/files.zaurus:1.12 --- src/sys/arch/zaurus/conf/files.zaurus:1.11 Sun Jan 29 10:12:41 2012 +++ src/sys/arch/zaurus/conf/files.zaurus Tue Sep 23 14:49:46 2014 @@ -1,4 +1,4 @@ -# $NetBSD: files.zaurus,v 1.11 2012/01/29 10:12:41 tsutsui Exp $ +# $NetBSD: files.zaurus,v 1.12 2014/09/23 14:49:46 nonaka Exp $ # # Sharp Zaurus specific configuration info # @@ -100,6 +100,8 @@ file arch/zaurus/dev/ztp.c ztp device zaudio: audiobus, auconv, mulaw, aurateconv, pxaiis attach zaudio at iic file arch/zaurus/dev/zaudio.c zaudio +file arch/zaurus/dev/wm8731_zaudio.c zaudio & cpu_xscale_pxa250 +file arch/zaurus/dev/wm8750_zaudio.c zaudio & cpu_xscale_pxa270 defparam opt_zaudio.h ZAUDIO_VOLUME_STRIDE # Zaurus remote control Index: src/sys/arch/zaurus/dev/scoop.c diff -u src/sys/arch/zaurus/dev/scoop.c:1.12 src/sys/arch/zaurus/dev/scoop.c:1.13 --- src/sys/arch/zaurus/dev/scoop.c:1.12 Sat Oct 27 17:18:14 2012 +++ src/sys/arch/zaurus/dev/scoop.c Tue Sep 23 14:49:46 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: scoop.c,v 1.12 2012/10/27 17:18:14 chs Exp $ */ +/* $NetBSD: scoop.c,v 1.13 2014/09/23 14:49:46 nonaka Exp $ */ /* $OpenBSD: zaurus_scoop.c,v 1.12 2005/11/17 05:26:31 uwe Exp $ */ /* @@ -18,7 +18,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: scoop.c,v 1.12 2012/10/27 17:18:14 chs Exp $"); +__KERNEL_RCSID(0, "$NetBSD: scoop.c,v 1.13 2014/09/23 14:49:46 nonaka Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -245,16 +245,45 @@ scoop_set_headphone(int on) } /* + * Enable or disable the speaker output connection. + */ +void +scoop_set_speaker(int onoff) +{ + 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 (onoff) { + 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) + + 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); + } } /* Index: src/sys/arch/zaurus/dev/scoopreg.h diff -u src/sys/arch/zaurus/dev/scoopreg.h:1.5 src/sys/arch/zaurus/dev/scoopreg.h:1.6 --- src/sys/arch/zaurus/dev/scoopreg.h:1.5 Sun Jan 29 10:12:41 2012 +++ src/sys/arch/zaurus/dev/scoopreg.h Tue Sep 23 14:49:46 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: scoopreg.h,v 1.5 2012/01/29 10:12:41 tsutsui Exp $ */ +/* $NetBSD: scoopreg.h,v 1.6 2014/09/23 14:49:46 nonaka Exp $ */ /* $OpenBSD: zaurus_scoopreg.h,v 1.7 2005/07/01 23:51:55 uwe Exp $ */ /* @@ -68,7 +68,7 @@ #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 Index: src/sys/arch/zaurus/dev/scoopvar.h diff -u src/sys/arch/zaurus/dev/scoopvar.h:1.5 src/sys/arch/zaurus/dev/scoopvar.h:1.6 --- src/sys/arch/zaurus/dev/scoopvar.h:1.5 Sat Apr 18 05:20:21 2009 +++ src/sys/arch/zaurus/dev/scoopvar.h Tue Sep 23 14:49:46 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: scoopvar.h,v 1.5 2009/04/18 05:20:21 nonaka Exp $ */ +/* $NetBSD: scoopvar.h,v 1.6 2014/09/23 14:49:46 nonaka Exp $ */ /* $OpenBSD: zaurus_scoopvar.h,v 1.10 2005/11/17 05:26:31 uwe Exp $ */ /* @@ -32,6 +32,7 @@ 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); Index: src/sys/arch/zaurus/dev/zaudio.c diff -u src/sys/arch/zaurus/dev/zaudio.c:1.20 src/sys/arch/zaurus/dev/zaudio.c:1.21 --- src/sys/arch/zaurus/dev/zaudio.c:1.20 Sat Oct 27 17:18:14 2012 +++ src/sys/arch/zaurus/dev/zaudio.c Tue Sep 23 14:49:46 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: zaudio.c,v 1.20 2012/10/27 17:18:14 chs Exp $ */ +/* $NetBSD: zaudio.c,v 1.21 2014/09/23 14:49:46 nonaka Exp $ */ /* $OpenBSD: zaurus_audio.c,v 1.8 2005/08/18 13:23:02 robert Exp $ */ /* @@ -47,83 +47,34 @@ * - powerhooks (currently only works until first suspend) */ -#include "opt_zaudio.h" +#include "opt_cputypes.h" #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: zaudio.c,v 1.20 2012/10/27 17:18:14 chs Exp $"); +__KERNEL_RCSID(0, "$NetBSD: zaudio.c,v 1.21 2014/09/23 14:49:46 nonaka Exp $"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/audioio.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/wm8750reg.h> -#include <zaurus/dev/scoopvar.h> -#include <zaurus/dev/ioexpvar.h> - -#define WM8750_ADDRESS 0x1B - -/* GPIO pins */ -#define GPIO_HP_IN_C3000 116 - -#define ZAUDIO_OP_SPKR 0 -#define ZAUDIO_OP_HP 1 -#define ZAUDIO_OP_MIC 2 -#define ZAUDIO_OP_NUM 3 - -#define ZAUDIO_JACK_STATE_OUT 0 -#define ZAUDIO_JACK_STATE_IN 1 -#define ZAUDIO_JACK_STATE_INS 2 -#define ZAUDIO_JACK_STATE_REM 3 - -struct zaudio_volume { - uint8_t left; - uint8_t right; -}; - -struct zaudio_softc { - 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 zaudio_volume sc_volume[ZAUDIO_OP_NUM]; - uint8_t sc_unmute[ZAUDIO_OP_NUM]; - uint8_t sc_unmute_toggle[ZAUDIO_OP_NUM]; - - 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 +#include <zaurus/dev/zaudiovar.h> +#if defined(CPU_XSCALE_PXA270) +#include <zaurus/dev/wm8750var.h> +#endif +#if defined(CPU_XSCALE_PXA250) +#include <zaurus/dev/wm8731var.h> +#endif static int zaudio_match(device_t, cfdata_t, void *); static void zaudio_attach(device_t, device_t, void *); @@ -131,205 +82,19 @@ static void zaudio_attach(device_t, devi CFATTACH_DECL_NEW(zaudio, sizeof(struct zaudio_softc), zaudio_match, zaudio_attach, NULL, NULL); -static int zaudio_finalize(device_t); -static bool zaudio_suspend(device_t, const pmf_qual_t *); -static bool zaudio_resume(device_t, const pmf_qual_t *); -static void zaudio_volume_up(device_t); -static void zaudio_volume_down(device_t); -static void zaudio_volume_toggle(device_t); - -static struct audio_device wm8750_device = { - "WM8750", - "1.0", - "wm" -}; - -static const struct audio_format zaudio_formats[] = { - { - .driver_data = NULL, - .mode = AUMODE_PLAY | AUMODE_RECORD, - .encoding = AUDIO_ENCODING_SLINEAR_LE, - .validbits = 16, - .precision = 16, - .channels = 2, - .channel_mask = AUFMT_STEREO, - .frequency_type = 0, - .frequency = { 4000, 48000 } - }, - { - .driver_data = NULL, - .mode = AUMODE_PLAY | AUMODE_RECORD, - .encoding = AUDIO_ENCODING_SLINEAR_LE, - .validbits = 16, - .precision = 16, - .channels = 1, - .channel_mask = AUFMT_MONAURAL, - .frequency_type = 0, - .frequency = { 4000, 48000 } - }, - { - .driver_data = NULL, - .mode = AUMODE_PLAY | AUMODE_RECORD, - .encoding = AUDIO_ENCODING_ULINEAR_LE, - .validbits = 8, - .precision = 8, - .channels = 2, - .channel_mask = AUFMT_STEREO, - .frequency_type = 0, - .frequency = { 4000, 48000 } - }, - { - .driver_data = NULL, - .mode = AUMODE_PLAY | AUMODE_RECORD, - .encoding = AUDIO_ENCODING_ULINEAR_LE, - .validbits = 8, - .precision = 8, - .channels = 1, - .channel_mask = AUFMT_MONAURAL, - .frequency_type = 0, - .frequency = { 4000, 48000 } - }, -}; -static const int zaudio_nformats = (int)__arraycount(zaudio_formats); - -static void zaudio_init(struct zaudio_softc *); -static int zaudio_jack_intr(void *); -static void zaudio_jack(void *); -static void zaudio_standby(struct zaudio_softc *); -static void zaudio_update_volume(struct zaudio_softc *, int); -static void zaudio_update_mutes(struct zaudio_softc *, int); -static void zaudio_play_setup(struct zaudio_softc *); -/*static*/ void zaudio_record_setup(struct zaudio_softc *); -static int zaudio_open(void *, int); -static void zaudio_close(void *); -static int zaudio_query_encoding(void *, struct audio_encoding *); -static int zaudio_set_params(void *, int, int, audio_params_t *, - audio_params_t *, stream_filter_list_t *, stream_filter_list_t *); -static int zaudio_round_blocksize(void *, int, int, const audio_params_t *); -static int zaudio_start_output(void *, void *, int, void (*)(void *), void *); -static int zaudio_start_input(void *, void *, int, void (*)(void *), void *); -static int zaudio_halt_output(void *); -static int zaudio_halt_input(void *); -static int zaudio_getdev(void *, struct audio_device *); -static int zaudio_set_port(void *, struct mixer_ctrl *); -static int zaudio_get_port(void *, struct mixer_ctrl *); -static int zaudio_query_devinfo(void *, struct mixer_devinfo *); -static void *zaudio_allocm(void *, int, size_t); -static void zaudio_freem(void *, void *, size_t); -static size_t zaudio_round_buffersize(void *, int, size_t); -static paddr_t zaudio_mappage(void *, void *, off_t, int); -static int zaudio_get_props(void *); -static void zaudio_get_locks(void *, kmutex_t **, kmutex_t **); - -struct audio_hw_if wm8750_hw_if = { - .open = zaudio_open, - .close = zaudio_close, - .drain = NULL, - .query_encoding = zaudio_query_encoding, - .set_params = zaudio_set_params, - .round_blocksize = zaudio_round_blocksize, - .commit_settings = NULL, - .init_output = NULL, - .init_input = NULL, - .start_output = zaudio_start_output, - .start_input = zaudio_start_input, - .halt_output = zaudio_halt_output, - .halt_input = zaudio_halt_input, - .speaker_ctl = NULL, - .getdev = zaudio_getdev, - .setfd = NULL, - .set_port = zaudio_set_port, - .get_port = zaudio_get_port, - .query_devinfo = zaudio_query_devinfo, - .allocm = zaudio_allocm, - .freem = zaudio_freem, - .round_buffersize = zaudio_round_buffersize, - .mappage = zaudio_mappage, - .get_props = zaudio_get_props, - .trigger_output = NULL, - .trigger_input = NULL, - .dev_ioctl = NULL, - .get_locks = zaudio_get_locks, -}; - -static const uint16_t playback_regs[][2] = { - /* Unmute DAC */ - { ADCDACCTL_REG, 0x000 }, - - /* 16 bit audio words */ - { AUDINT_REG, AUDINT_SET_FORMAT(2) }, - - /* Enable thermal protection, power */ - { ADCTL1_REG, ADCTL1_TSDEN | ADCTL1_SET_VSEL(3) }, - - /* Enable speaker driver, DAC oversampling */ - { ADCTL2_REG, ADCTL2_ROUT2INV | ADCTL2_DACOSR }, - - /* Set DAC voltage references */ - { PWRMGMT1_REG, PWRMGMT1_SET_VMIDSEL(1) | PWRMGMT1_VREF }, - - /* Direct DACs to output mixers */ - { LOUTMIX1_REG, LOUTMIX1_LD2LO }, - { LOUTMIX2_REG, 0x000 }, - { ROUTMIX1_REG, 0x000 }, - { ROUTMIX2_REG, ROUTMIX2_RD2RO }, - - /* End of list */ - { 0xffff, 0xffff } -}; - -static const uint16_t record_regs[][2] = { - /* Unmute DAC */ - { ADCDACCTL_REG, 0x000 }, - - /* 16 bit audio words */ - { AUDINT_REG, AUDINT_SET_FORMAT(2) }, - - /* Enable thermal protection, power, left DAC for both channel */ - { ADCTL1_REG, ADCTL1_TSDEN | ADCTL1_SET_VSEL(3) - | ADCTL1_SET_DATSEL(1) }, - - /* Diffrential input select: LINPUT1-RINPUT1, stereo */ - { ADCINPMODE_REG, 0x000 }, - - /* L-R differential, micboost 20dB */ - { ADCLSPATH_REG, ADCLSPATH_SET_LINSEL(3) | ADCLSPATH_SET_LMICBOOST(2) }, - { ADCRSPATH_REG, ADCRSPATH_SET_RINSEL(3) | ADCRSPATH_SET_RMICBOOST(2) }, - - /* End of list */ - { 0xffff, 0xffff } -}; - -static __inline int -wm8750_write(struct zaudio_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, WM8750_ADDRESS, - &cmd, 1, &data, 1, 0); -} - static int zaudio_match(device_t parent, cfdata_t cf, void *aux) { struct i2c_attach_args *ia = aux; - if (ZAURUS_ISC860) - return 0; /* XXX for now */ - - if (ia->ia_name) { - /* direct config - check name */ - if (strcmp(ia->ia_name, "zaudio") == 0) - return 1; - } else { - /* indirect config - check typical address */ - if (ia->ia_addr == WM8750_ADDRESS) - return 1; + if (ZAURUS_ISC1000 || ZAURUS_ISC3000) { +#if defined(CPU_XSCALE_PXA270) + return wm8750_match(parent, cf, ia); +#endif + } else if (ZAURUS_ISC860) { +#if defined(CPU_XSCALE_PXA250) + return wm8731_match(parent, cf, ia); +#endif } return 0; } @@ -339,14 +104,13 @@ zaudio_attach(device_t parent, device_t { struct zaudio_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, WM8750 Audio\n"); - aprint_naive("\n"); + callout_init(&sc->sc_to, 0); sc->sc_i2s.sc_iot = &pxa2x0_bs_tag; sc->sc_i2s.sc_dmat = &pxa2x0_bus_dma_tag; @@ -354,408 +118,26 @@ zaudio_attach(device_t parent, device_t 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; + return; } - /* Check for an I2C response from the wm8750 */ - iic_acquire_bus(sc->sc_i2c, 0); - error = wm8750_write(sc, 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, headphones off by default. */ - sc->sc_volume[ZAUDIO_OP_SPKR].left = 180; - UNMUTE(sc, ZAUDIO_OP_SPKR, 1); - sc->sc_volume[ZAUDIO_OP_HP].left = 180; - sc->sc_volume[ZAUDIO_OP_HP].right = 180; - UNMUTE(sc, ZAUDIO_OP_HP, 0); - sc->sc_volume[ZAUDIO_OP_MIC].left = 180; - UNMUTE(sc, ZAUDIO_OP_MIC, 0); - - /* Configure headphone jack state change handling. */ - callout_init(&sc->sc_to, 0); - callout_setfunc(&sc->sc_to, zaudio_jack, sc); - pxa2x0_gpio_set_function(GPIO_HP_IN_C3000, GPIO_IN); - (void) pxa2x0_gpio_intr_establish(GPIO_HP_IN_C3000, IST_EDGE_BOTH, - IPL_BIO, zaudio_jack_intr, sc); - - /* zaudio_init() implicitly depends on ioexp or scoop */ - config_finalize_register(self, zaudio_finalize); - - audio_attach_mi(&wm8750_hw_if, sc, self); - - if (!pmf_device_register(self, zaudio_suspend, zaudio_resume)) - aprint_error_dev(self, "couldn't establish power handler\n"); - if (!pmf_event_register(self, PMFE_AUDIO_VOLUME_UP, - zaudio_volume_up, true)) - aprint_error_dev(self, "couldn't register event handler\n"); - if (!pmf_event_register(self, PMFE_AUDIO_VOLUME_DOWN, - zaudio_volume_down, true)) - aprint_error_dev(self, "couldn't register event handler\n"); - if (!pmf_event_register(self, PMFE_AUDIO_VOLUME_TOGGLE, - zaudio_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 -zaudio_finalize(device_t dv) -{ - struct zaudio_softc *sc = device_private(dv); - - zaudio_init(sc); - return 0; -} - -static bool -zaudio_suspend(device_t dv, const pmf_qual_t *qual) -{ - struct zaudio_softc *sc = device_private(dv); - - callout_stop(&sc->sc_to); - zaudio_standby(sc); - - return true; -} - -static bool -zaudio_resume(device_t dv, const pmf_qual_t *qual) -{ - struct zaudio_softc *sc = device_private(dv); - - pxa2x0_i2s_init(&sc->sc_i2s); - zaudio_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 ZAUDIO_VOLUME_STRIDE -#define ZAUDIO_VOLUME_STRIDE 8 + if (ZAURUS_ISC1000 || ZAURUS_ISC3000) { +#if defined(CPU_XSCALE_PXA270) + wm8750_attach(parent, self, ia); +#endif + } else if (ZAURUS_ISC860) { +#if defined(CPU_XSCALE_PXA250) + wm8731_attach(parent, self, ia); #endif - -static void -zaudio_volume_up(device_t dv) -{ - struct zaudio_softc *sc = device_private(dv); - int s; - - s = splbio(); - iic_acquire_bus(sc->sc_i2c, 0); - - sc->sc_volume[ZAUDIO_OP_SPKR].left = - vol_sadd(sc->sc_volume[ZAUDIO_OP_SPKR].left, ZAUDIO_VOLUME_STRIDE); - sc->sc_volume[ZAUDIO_OP_HP].left = - vol_sadd(sc->sc_volume[ZAUDIO_OP_HP].left, ZAUDIO_VOLUME_STRIDE); - sc->sc_volume[ZAUDIO_OP_HP].right = - vol_sadd(sc->sc_volume[ZAUDIO_OP_HP].right, ZAUDIO_VOLUME_STRIDE); - - zaudio_update_volume(sc, ZAUDIO_OP_SPKR); - zaudio_update_volume(sc, ZAUDIO_OP_HP); - - 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 -zaudio_volume_down(device_t dv) -{ - struct zaudio_softc *sc = device_private(dv); - int s; - - s = splbio(); - iic_acquire_bus(sc->sc_i2c, 0); - - sc->sc_volume[ZAUDIO_OP_SPKR].left = - vol_ssub(sc->sc_volume[ZAUDIO_OP_SPKR].left, ZAUDIO_VOLUME_STRIDE); - sc->sc_volume[ZAUDIO_OP_HP].left = - vol_ssub(sc->sc_volume[ZAUDIO_OP_HP].left, ZAUDIO_VOLUME_STRIDE); - sc->sc_volume[ZAUDIO_OP_HP].right = - vol_ssub(sc->sc_volume[ZAUDIO_OP_HP].right, ZAUDIO_VOLUME_STRIDE); - - zaudio_update_volume(sc, ZAUDIO_OP_SPKR); - zaudio_update_volume(sc, ZAUDIO_OP_HP); - - iic_release_bus(sc->sc_i2c, 0); - splx(s); -} - -static void -zaudio_volume_toggle(device_t dv) -{ - struct zaudio_softc *sc = device_private(dv); - int s; - - s = splbio(); - iic_acquire_bus(sc->sc_i2c, 0); - - if (!sc->sc_unmute[ZAUDIO_OP_SPKR] && !sc->sc_unmute[ZAUDIO_OP_HP]) { - sc->sc_unmute[ZAUDIO_OP_SPKR] = - sc->sc_unmute_toggle[ZAUDIO_OP_SPKR]; - sc->sc_unmute[ZAUDIO_OP_HP] = - sc->sc_unmute_toggle[ZAUDIO_OP_HP]; - } else { - sc->sc_unmute[ZAUDIO_OP_SPKR] = 0; - sc->sc_unmute[ZAUDIO_OP_HP] = 0; - } - zaudio_update_mutes(sc, 1); - - iic_release_bus(sc->sc_i2c, 0); - splx(s); -} - -static void -zaudio_init(struct zaudio_softc *sc) -{ - - iic_acquire_bus(sc->sc_i2c, 0); - - /* Reset the codec */ - wm8750_write(sc, RESET_REG, 0); - delay(100); - - /* Switch to standby power only */ - wm8750_write(sc, PWRMGMT1_REG, PWRMGMT1_SET_VMIDSEL(2)); - wm8750_write(sc, PWRMGMT2_REG, 0); - - /* Configure digital interface for I2S */ - wm8750_write(sc, AUDINT_REG, AUDINT_SET_FORMAT(2)); - - /* Initialise volume levels */ - zaudio_update_volume(sc, ZAUDIO_OP_SPKR); - zaudio_update_volume(sc, ZAUDIO_OP_HP); - zaudio_update_volume(sc, ZAUDIO_OP_MIC); - - scoop_set_headphone(0); - if (ZAURUS_ISC1000) - ioexp_set_mic_bias(0); - else - scoop_set_mic_bias(0); - - iic_release_bus(sc->sc_i2c, 0); - - /* Assume that the jack state has changed. */ - zaudio_jack(sc); -} - -static int -zaudio_jack_intr(void *v) -{ - struct zaudio_softc *sc = v; - - if (!callout_active(&sc->sc_to)) - zaudio_jack(sc); - - return 1; -} - -static void -zaudio_jack(void *v) -{ - struct zaudio_softc *sc = v; - - switch (sc->sc_state) { - case ZAUDIO_JACK_STATE_OUT: - if (pxa2x0_gpio_get_bit(GPIO_HP_IN_C3000)) { - sc->sc_state = ZAUDIO_JACK_STATE_INS; - sc->sc_icount = 0; - } - break; - - case ZAUDIO_JACK_STATE_INS: - if (sc->sc_icount++ > 2) { - if (pxa2x0_gpio_get_bit(GPIO_HP_IN_C3000)) { - sc->sc_state = ZAUDIO_JACK_STATE_IN; - UNMUTE(sc, ZAUDIO_OP_SPKR, 0); - UNMUTE(sc, ZAUDIO_OP_HP, 1); - UNMUTE(sc, ZAUDIO_OP_MIC, 1); - goto update_mutes; - } else - sc->sc_state = ZAUDIO_JACK_STATE_OUT; - } - break; - - case ZAUDIO_JACK_STATE_IN: - if (!pxa2x0_gpio_get_bit(GPIO_HP_IN_C3000)) { - sc->sc_state = ZAUDIO_JACK_STATE_REM; - sc->sc_icount = 0; - } - break; - - case ZAUDIO_JACK_STATE_REM: - if (sc->sc_icount++ > 2) { - if (!pxa2x0_gpio_get_bit(GPIO_HP_IN_C3000)) { - sc->sc_state = ZAUDIO_JACK_STATE_OUT; - UNMUTE(sc, ZAUDIO_OP_SPKR, 1); - UNMUTE(sc, ZAUDIO_OP_HP, 0); - UNMUTE(sc, ZAUDIO_OP_MIC, 0); - goto update_mutes; - } else - sc->sc_state = ZAUDIO_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) - zaudio_update_mutes(sc, 1); - if (sc->sc_recording) - zaudio_update_mutes(sc, 2); - iic_release_bus(sc->sc_i2c, 0); - } -} - -static void -zaudio_standby(struct zaudio_softc *sc) -{ - - iic_acquire_bus(sc->sc_i2c, 0); - - /* Switch codec to standby power only */ - wm8750_write(sc, PWRMGMT1_REG, PWRMGMT1_SET_VMIDSEL(2)); - wm8750_write(sc, PWRMGMT2_REG, 0); - - scoop_set_headphone(0); - if (ZAURUS_ISC1000) - ioexp_set_mic_bias(0); - else - scoop_set_mic_bias(0); - - iic_release_bus(sc->sc_i2c, 0); -} - -static void -zaudio_update_volume(struct zaudio_softc *sc, int output) -{ - - switch (output) { - case ZAUDIO_OP_SPKR: - wm8750_write(sc, LOUT2VOL_REG, LOUT2VOL_LO2VU | LOUT2VOL_LO2ZC | - LOUT2VOL_SET_LOUT2VOL(sc->sc_volume[ZAUDIO_OP_SPKR].left >> 1)); - wm8750_write(sc, ROUT2VOL_REG, ROUT2VOL_RO2VU | ROUT2VOL_RO2ZC | - ROUT2VOL_SET_ROUT2VOL(sc->sc_volume[ZAUDIO_OP_SPKR].left >> 1)); - break; - - case ZAUDIO_OP_HP: - wm8750_write(sc, LOUT1VOL_REG, LOUT1VOL_LO1VU | LOUT1VOL_LO1ZC | - LOUT1VOL_SET_LOUT1VOL(sc->sc_volume[ZAUDIO_OP_HP].left >> 1)); - wm8750_write(sc, ROUT1VOL_REG, ROUT1VOL_RO1VU | ROUT1VOL_RO1ZC | - ROUT1VOL_SET_ROUT1VOL(sc->sc_volume[ZAUDIO_OP_HP].right >> 1)); - break; - - case ZAUDIO_OP_MIC: - wm8750_write(sc, LINVOL_REG, LINVOL_LIVU | - LINVOL_SET_LINVOL(sc->sc_volume[ZAUDIO_OP_MIC].left >> 2)); - wm8750_write(sc, RINVOL_REG, RINVOL_RIVU | - RINVOL_SET_RINVOL(sc->sc_volume[ZAUDIO_OP_MIC].left >> 2)); - break; - } -} - -static void -zaudio_update_mutes(struct zaudio_softc *sc, int mask) -{ - uint16_t val; - - /* playback */ - if (mask & 1) { - val = PWRMGMT2_DACL | PWRMGMT2_DACR; - if (sc->sc_unmute[ZAUDIO_OP_SPKR]) - val |= PWRMGMT2_LOUT2 | PWRMGMT2_ROUT2; - if (sc->sc_unmute[ZAUDIO_OP_HP]) - val |= PWRMGMT2_LOUT1 | PWRMGMT2_ROUT1; - wm8750_write(sc, PWRMGMT2_REG, val); - scoop_set_headphone(sc->sc_unmute[ZAUDIO_OP_HP]); - } - - /* record */ - if (mask & 2) { - val = PWRMGMT1_SET_VMIDSEL(1) | PWRMGMT1_VREF; - if (sc->sc_unmute[ZAUDIO_OP_MIC]) { - val |= PWRMGMT1_AINL | PWRMGMT1_AINR - | PWRMGMT1_ADCL | PWRMGMT1_ADCR | PWRMGMT1_MICB; - } - wm8750_write(sc, PWRMGMT1_REG, val); - if (ZAURUS_ISC1000) - ioexp_set_mic_bias(sc->sc_unmute[ZAUDIO_OP_MIC]); - else - scoop_set_mic_bias(sc->sc_unmute[ZAUDIO_OP_MIC]); - } -} - -static void -zaudio_play_setup(struct zaudio_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++) { - wm8750_write(sc, playback_regs[i][0], playback_regs[i][1]); - } - zaudio_update_mutes(sc, 1); - - iic_release_bus(sc->sc_i2c, 0); -} - -/*static*/ void -zaudio_record_setup(struct zaudio_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++) { - wm8750_write(sc, record_regs[i][0], record_regs[i][1]); - } - zaudio_update_mutes(sc, 2); - - iic_release_bus(sc->sc_i2c, 0); } /* * audio operation functions. */ -static int +int zaudio_open(void *hdl, int flags) { struct zaudio_softc *sc = hdl; @@ -766,7 +148,7 @@ zaudio_open(void *hdl, int flags) return 0; } -static void +void zaudio_close(void *hdl) { struct zaudio_softc *sc = hdl; @@ -775,123 +157,7 @@ zaudio_close(void *hdl) pxa2x0_i2s_close(&sc->sc_i2s); } -static int -zaudio_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 = 0; - 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 -zaudio_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 zaudio_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(zaudio_formats, zaudio_nformats, - mode, p, false, 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 +int zaudio_round_blocksize(void *hdl, int bs, int mode, const audio_params_t *param) { struct zaudio_softc *sc = hdl; @@ -899,341 +165,7 @@ zaudio_round_blocksize(void *hdl, int bs return pxa2x0_i2s_round_blocksize(&sc->sc_i2s, bs, mode, param); } -static int -zaudio_halt_output(void *hdl) -{ - struct zaudio_softc *sc = hdl; - int rv; - - rv = pxa2x0_i2s_halt_output(&sc->sc_i2s); - if (!sc->sc_recording) - zaudio_standby(sc); - sc->sc_playing = 0; - - return rv; -} - -static int -zaudio_halt_input(void *hdl) -{ - struct zaudio_softc *sc = hdl; - int rv; - - rv = pxa2x0_i2s_halt_input(&sc->sc_i2s); - if (!sc->sc_playing) - zaudio_standby(sc); - sc->sc_recording = 0; - - return rv; -} - -static int -zaudio_getdev(void *hdl, struct audio_device *ret) -{ - - *ret = wm8750_device; - return 0; -} - -#define ZAUDIO_SPKR_LVL 0 -#define ZAUDIO_SPKR_MUTE 1 -#define ZAUDIO_HP_LVL 2 -#define ZAUDIO_HP_MUTE 3 -#define ZAUDIO_MIC_LVL 4 -#define ZAUDIO_MIC_MUTE 5 -#define ZAUDIO_RECORD_SOURCE 6 -#define ZAUDIO_OUTPUT_CLASS 7 -#define ZAUDIO_INPUT_CLASS 8 -#define ZAUDIO_RECORD_CLASS 9 - -static int -zaudio_set_port(void *hdl, struct mixer_ctrl *mc) -{ - struct zaudio_softc *sc = hdl; - int error = EINVAL; - int s; - - s = splbio(); - iic_acquire_bus(sc->sc_i2c, 0); - - switch (mc->dev) { - case ZAUDIO_SPKR_LVL: - if (mc->type != AUDIO_MIXER_VALUE) - break; - if (mc->un.value.num_channels == 1) - sc->sc_volume[ZAUDIO_OP_SPKR].left = - mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]; - else - break; - zaudio_update_volume(sc, ZAUDIO_OP_SPKR); - error = 0; - break; - - case ZAUDIO_SPKR_MUTE: - if (mc->type != AUDIO_MIXER_ENUM) - break; - UNMUTE(sc, ZAUDIO_OP_SPKR, mc->un.ord ? 1 : 0); - zaudio_update_mutes(sc, 1); - error = 0; - break; - - case ZAUDIO_HP_LVL: - if (mc->type != AUDIO_MIXER_VALUE) - break; - if (mc->un.value.num_channels == 1) { - sc->sc_volume[ZAUDIO_OP_HP].left = - mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]; - sc->sc_volume[ZAUDIO_OP_HP].right = - mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]; - } else if (mc->un.value.num_channels == 2) { - sc->sc_volume[ZAUDIO_OP_HP].left = - mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; - sc->sc_volume[ZAUDIO_OP_HP].right = - mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; - } - else - break; - zaudio_update_volume(sc, ZAUDIO_OP_HP); - error = 0; - break; - - case ZAUDIO_HP_MUTE: - if (mc->type != AUDIO_MIXER_ENUM) - break; - UNMUTE(sc, ZAUDIO_OP_HP, mc->un.ord ? 1 : 0); - zaudio_update_mutes(sc, 1); - error = 0; - break; - - case ZAUDIO_MIC_LVL: - if (mc->type != AUDIO_MIXER_VALUE) - break; - if (mc->un.value.num_channels == 1) - sc->sc_volume[ZAUDIO_OP_MIC].left = - mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]; - else - break; - zaudio_update_volume(sc, ZAUDIO_OP_MIC); - error = 0; - break; - - case ZAUDIO_MIC_MUTE: - if (mc->type != AUDIO_MIXER_ENUM) - break; - UNMUTE(sc, ZAUDIO_OP_MIC, mc->un.ord ? 1 : 0); - zaudio_update_mutes(sc, 2); - error = 0; - break; - - case ZAUDIO_RECORD_SOURCE: - if (mc->type != AUDIO_MIXER_ENUM) - break; - if (mc->un.ord != 0) - break; - /* MIC only */ - error = 0; - break; - } - - iic_release_bus(sc->sc_i2c, 0); - splx(s); - - return error; -} - -static int -zaudio_get_port(void *hdl, struct mixer_ctrl *mc) -{ - struct zaudio_softc *sc = hdl; - int error = EINVAL; - - switch (mc->dev) { - case ZAUDIO_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[ZAUDIO_OP_SPKR].left; - else - break; - error = 0; - break; - - case ZAUDIO_SPKR_MUTE: - if (mc->type != AUDIO_MIXER_ENUM) - break; - mc->un.ord = sc->sc_unmute[ZAUDIO_OP_SPKR] ? 1 : 0; - error = 0; - break; - - case ZAUDIO_HP_LVL: - if (mc->type != AUDIO_MIXER_VALUE) - break; - if (mc->un.value.num_channels == 1) - mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = - sc->sc_volume[ZAUDIO_OP_HP].left; - else if (mc->un.value.num_channels == 2) { - mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = - sc->sc_volume[ZAUDIO_OP_HP].left; - mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = - sc->sc_volume[ZAUDIO_OP_HP].right; - } - else - break; - error = 0; - break; - - case ZAUDIO_HP_MUTE: - if (mc->type != AUDIO_MIXER_ENUM) - break; - mc->un.ord = sc->sc_unmute[ZAUDIO_OP_HP] ? 1 : 0; - error = 0; - break; - - case ZAUDIO_MIC_LVL: - if (mc->type != AUDIO_MIXER_VALUE) - break; - if (mc->un.value.num_channels == 1) - mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = - sc->sc_volume[ZAUDIO_OP_MIC].left; - else - break; - error = 0; - break; - - case ZAUDIO_MIC_MUTE: - if (mc->type != AUDIO_MIXER_ENUM) - break; - mc->un.ord = sc->sc_unmute[ZAUDIO_OP_MIC] ? 1 : 0; - error = 0; - break; - - case ZAUDIO_RECORD_SOURCE: - if (mc->type != AUDIO_MIXER_ENUM) - break; - mc->un.ord = 0; /* MIC only */ - error = 0; - break; - } - - return error; -} - -/*ARGSUSED*/ -static int -zaudio_query_devinfo(void *hdl, struct mixer_devinfo *di) -{ - - switch (di->index) { - case ZAUDIO_SPKR_LVL: - di->type = AUDIO_MIXER_VALUE; - di->mixer_class = ZAUDIO_OUTPUT_CLASS; - di->prev = AUDIO_MIXER_LAST; - di->next = ZAUDIO_SPKR_MUTE; - strlcpy(di->label.name, AudioNspeaker, sizeof(di->label.name)); - strlcpy(di->un.v.units.name, AudioNvolume, - sizeof(di->un.v.units.name)); - di->un.v.num_channels = 1; - break; - - case ZAUDIO_SPKR_MUTE: - di->type = AUDIO_MIXER_ENUM; - di->mixer_class = ZAUDIO_OUTPUT_CLASS; - di->prev = ZAUDIO_SPKR_LVL; - di->next = AUDIO_MIXER_LAST; - goto mute; - - case ZAUDIO_HP_LVL: - di->type = AUDIO_MIXER_VALUE; - di->mixer_class = ZAUDIO_OUTPUT_CLASS; - di->prev = AUDIO_MIXER_LAST; - di->next = ZAUDIO_HP_MUTE; - strlcpy(di->label.name, AudioNheadphone, - 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 ZAUDIO_HP_MUTE: - di->type = AUDIO_MIXER_ENUM; - di->mixer_class = ZAUDIO_OUTPUT_CLASS; - di->prev = ZAUDIO_HP_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 ZAUDIO_MIC_LVL: - di->type = AUDIO_MIXER_VALUE; - di->mixer_class = ZAUDIO_INPUT_CLASS; - di->prev = AUDIO_MIXER_LAST; - di->next = ZAUDIO_MIC_MUTE; - strlcpy(di->label.name, AudioNmicrophone, - sizeof(di->label.name)); - strlcpy(di->un.v.units.name, AudioNvolume, - sizeof(di->un.v.units.name)); - di->un.v.num_channels = 1; - break; - - case ZAUDIO_MIC_MUTE: - di->type = AUDIO_MIXER_ENUM; - di->mixer_class = ZAUDIO_INPUT_CLASS; - di->prev = ZAUDIO_MIC_LVL; - di->next = AUDIO_MIXER_LAST; - goto mute; - - case ZAUDIO_RECORD_SOURCE: - di->type = AUDIO_MIXER_ENUM; - di->mixer_class = ZAUDIO_RECORD_CLASS; - di->prev = AUDIO_MIXER_LAST; - di->next = AUDIO_MIXER_LAST; - strlcpy(di->label.name, AudioNsource, sizeof(di->label.name)); - di->un.e.num_mem = 1; - strlcpy(di->un.e.member[0].label.name, AudioNmicrophone, - sizeof(di->un.e.member[0].label.name)); - di->un.e.member[0].ord = 0; - break; - - case ZAUDIO_OUTPUT_CLASS: - di->type = AUDIO_MIXER_CLASS; - di->mixer_class = ZAUDIO_OUTPUT_CLASS; - di->prev = AUDIO_MIXER_LAST; - di->next = AUDIO_MIXER_LAST; - strlcpy(di->label.name, AudioCoutputs, sizeof(di->label.name)); - break; - - case ZAUDIO_INPUT_CLASS: - di->type = AUDIO_MIXER_CLASS; - di->mixer_class = ZAUDIO_INPUT_CLASS; - di->prev = AUDIO_MIXER_LAST; - di->next = AUDIO_MIXER_LAST; - strlcpy(di->label.name, AudioCinputs, sizeof(di->label.name)); - break; - - case ZAUDIO_RECORD_CLASS: - di->type = AUDIO_MIXER_CLASS; - di->mixer_class = ZAUDIO_RECORD_CLASS; - di->prev = AUDIO_MIXER_LAST; - di->next = AUDIO_MIXER_LAST; - strlcpy(di->label.name, AudioCinputs, sizeof(di->label.name)); - break; - - default: - return ENXIO; - } - - return 0; -} - -static void * +void * zaudio_allocm(void *hdl, int direction, size_t size) { struct zaudio_softc *sc = hdl; @@ -1241,7 +173,7 @@ zaudio_allocm(void *hdl, int direction, return pxa2x0_i2s_allocm(&sc->sc_i2s, direction, size); } -static void +void zaudio_freem(void *hdl, void *ptr, size_t size) { struct zaudio_softc *sc = hdl; @@ -1249,7 +181,7 @@ zaudio_freem(void *hdl, void *ptr, size_ return pxa2x0_i2s_freem(&sc->sc_i2s, ptr, size); } -static size_t +size_t zaudio_round_buffersize(void *hdl, int direction, size_t bufsize) { struct zaudio_softc *sc = hdl; @@ -1257,7 +189,7 @@ zaudio_round_buffersize(void *hdl, int d return pxa2x0_i2s_round_buffersize(&sc->sc_i2s, direction, bufsize); } -static paddr_t +paddr_t zaudio_mappage(void *hdl, void *mem, off_t off, int prot) { struct zaudio_softc *sc = hdl; @@ -1265,61 +197,14 @@ zaudio_mappage(void *hdl, void *mem, off return pxa2x0_i2s_mappage(&sc->sc_i2s, mem, off, prot); } -static int +int zaudio_get_props(void *hdl) { return AUDIO_PROP_MMAP|AUDIO_PROP_INDEPENDENT; } -static int -zaudio_start_output(void *hdl, void *block, int bsize, void (*intr)(void *), - void *intrarg) -{ - struct zaudio_softc *sc = hdl; - int rv; - - /* Power up codec if we are not already playing. */ - if (!sc->sc_playing) { - sc->sc_playing = 1; - zaudio_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) - zaudio_standby(sc); - sc->sc_playing = 0; - } - - return rv; -} - -static int -zaudio_start_input(void *hdl, void *block, int bsize, void (*intr)(void *), - void *intrarg) -{ - struct zaudio_softc *sc = hdl; - int rv; - - /* Power up codec if we are not already recording. */ - if (!sc->sc_recording) { - sc->sc_recording = 1; - zaudio_record_setup(sc); - } - - /* Start DMA via I2S */ - rv = pxa2x0_i2s_start_input(&sc->sc_i2s, block, bsize, intr, intrarg); - if (rv) { - if (!sc->sc_playing) - zaudio_standby(sc); - sc->sc_recording = 0; - } - return rv; -} - -static void +void zaudio_get_locks(void *hdl, kmutex_t **intr, kmutex_t **thread) { struct zaudio_softc *sc = hdl; Added files: Index: src/sys/arch/zaurus/dev/wm8731_zaudio.c diff -u /dev/null src/sys/arch/zaurus/dev/wm8731_zaudio.c:1.1 --- /dev/null Tue Sep 23 14:49:46 2014 +++ src/sys/arch/zaurus/dev/wm8731_zaudio.c Tue Sep 23 14:49:46 2014 @@ -0,0 +1,1054 @@ +/* $NetBSD: wm8731_zaudio.c,v 1.1 2014/09/23 14:49:46 nonaka Exp $ */ + +/*- + * 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. + */ + +/* + * TODO: + * - powerhooks (currently only works until first suspend) + */ + +#include "opt_cputypes.h" +#include "opt_zaudio.h" + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: wm8731_zaudio.c,v 1.1 2014/09/23 14:49:46 nonaka 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/zaudiovar.h> +#include <zaurus/dev/wm8731reg.h> +#include <zaurus/dev/wm8731var.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 + +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 zaudio_softc *); +static int wm8731_jack_intr(void *); +static void wm8731_jack(void *); +static void wm8731_standby(struct zaudio_softc *); +static void wm8731_update_volume(struct zaudio_softc *, int); +static void wm8731_update_mutes(struct zaudio_softc *, int); +static void wm8731_play_setup(struct zaudio_softc *); +/*static*/ void wm8731_record_setup(struct zaudio_softc *); +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_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 struct audio_hw_if wm8731_hw_if = { + .open = zaudio_open, + .close = zaudio_close, + .drain = NULL, + .query_encoding = wm8731_query_encoding, + .set_params = wm8731_set_params, + .round_blocksize = zaudio_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 = zaudio_allocm, + .freem = zaudio_freem, + .round_buffersize = zaudio_round_buffersize, + .mappage = zaudio_mappage, + .get_props = zaudio_get_props, + .trigger_output = NULL, + .trigger_input = NULL, + .dev_ioctl = NULL, + .get_locks = zaudio_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 zaudio_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); +} + +int +wm8731_match(device_t parent, cfdata_t cf, struct i2c_attach_args *ia) +{ + + if (ZAURUS_ISC1000 || ZAURUS_ISC3000) + return 0; + + if (ia->ia_name) { + /* direct config - check name */ + if (strcmp(ia->ia_name, "zaudio") == 0) + return 1; + } else { + /* indirect config - check typical address */ + if (ia->ia_addr == WM8731_ADDRESS) + return 1; + } + return 0; +} + +void +wm8731_attach(device_t parent, device_t self, struct i2c_attach_args *ia) +{ + struct zaudio_softc *sc = device_private(self); + int error; + + aprint_normal(": I2S, WM8731 Audio\n"); + aprint_naive("\n"); + + /* 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); + + /* Allocate memory for volume & mute operations */ + sc->sc_volume = kmem_zalloc(sizeof(*sc->sc_volume) * WM8731_OP_NUM, + KM_SLEEP); + sc->sc_unmute = kmem_zalloc(sizeof(*sc->sc_unmute) * WM8731_OP_NUM, + KM_SLEEP); + sc->sc_unmute_toggle = kmem_zalloc( + sizeof(*sc->sc_unmute_toggle) * WM8731_OP_NUM, KM_SLEEP); + + /* 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_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); +} + +static int +wm8731_finalize(device_t dv) +{ + struct zaudio_softc *sc = device_private(dv); + + wm8731_init(sc); + return 0; +} + +static bool +wm8731_suspend(device_t dv, const pmf_qual_t *qual) +{ + struct zaudio_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 zaudio_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 ZAUDIO_VOLUME_STRIDE +#define ZAUDIO_VOLUME_STRIDE 8 +#endif + +static void +wm8731_volume_up(device_t dv) +{ + struct zaudio_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, ZAUDIO_VOLUME_STRIDE); + sc->sc_volume[WM8731_OP_SPKR].right = + vol_sadd(sc->sc_volume[WM8731_OP_SPKR].right, ZAUDIO_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 zaudio_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, ZAUDIO_VOLUME_STRIDE); + sc->sc_volume[WM8731_OP_SPKR].right = + vol_ssub(sc->sc_volume[WM8731_OP_SPKR].right, ZAUDIO_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 zaudio_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 zaudio_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 zaudio_softc *sc = v; + + if (!callout_active(&sc->sc_to)) + wm8731_jack(sc); + + return 1; +} + +static void +wm8731_jack(void *v) +{ + struct zaudio_softc *sc = v; + + switch (sc->sc_state) { + case ZAUDIO_JACK_STATE_OUT: + if (pxa2x0_gpio_get_bit(GPIO_HP_IN_C860)) { + sc->sc_state = ZAUDIO_JACK_STATE_INS; + sc->sc_icount = 0; + } + break; + + case ZAUDIO_JACK_STATE_INS: + if (sc->sc_icount++ > 2) { + if (pxa2x0_gpio_get_bit(GPIO_HP_IN_C860)) { + sc->sc_state = ZAUDIO_JACK_STATE_IN; + sc->sc_jack = TRUE; + UNMUTE(sc, WM8731_OP_MIC, 1); + goto update_mutes; + } else + sc->sc_state = ZAUDIO_JACK_STATE_OUT; + } + break; + + case ZAUDIO_JACK_STATE_IN: + if (!pxa2x0_gpio_get_bit(GPIO_HP_IN_C860)) { + sc->sc_state = ZAUDIO_JACK_STATE_REM; + sc->sc_icount = 0; + } + break; + + case ZAUDIO_JACK_STATE_REM: + if (sc->sc_icount++ > 2) { + if (!pxa2x0_gpio_get_bit(GPIO_HP_IN_C860)) { + sc->sc_state = ZAUDIO_JACK_STATE_OUT; + sc->sc_jack = FALSE; + UNMUTE(sc, WM8731_OP_MIC, 0); + goto update_mutes; + } else + sc->sc_state = ZAUDIO_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 zaudio_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 zaudio_softc *sc, int output) +{ + struct zaudio_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 zaudio_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 zaudio_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 zaudio_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); +} + +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 zaudio_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_halt_output(void *hdl) +{ + struct zaudio_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 zaudio_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 zaudio_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 zaudio_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 int +wm8731_start_output(void *hdl, void *block, int bsize, void (*intr)(void *), + void *intrarg) +{ + struct zaudio_softc *sc = hdl; + int rv; + + /* Power up codec if we are not already 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 zaudio_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; +} Index: src/sys/arch/zaurus/dev/wm8731reg.h diff -u /dev/null src/sys/arch/zaurus/dev/wm8731reg.h:1.1 --- /dev/null Tue Sep 23 14:49:46 2014 +++ src/sys/arch/zaurus/dev/wm8731reg.h Tue Sep 23 14:49:46 2014 @@ -0,0 +1,125 @@ +/* $NetBSD: wm8731reg.h,v 1.1 2014/09/23 14:49:46 nonaka Exp $ */ +/*- + * 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_ */ Index: src/sys/arch/zaurus/dev/wm8731var.h diff -u /dev/null src/sys/arch/zaurus/dev/wm8731var.h:1.1 --- /dev/null Tue Sep 23 14:49:46 2014 +++ src/sys/arch/zaurus/dev/wm8731var.h Tue Sep 23 14:49:46 2014 @@ -0,0 +1,35 @@ +/* $NetBSD: wm8731var.h,v 1.1 2014/09/23 14:49:46 nonaka Exp $ */ + +/*- + * Copyright (C) 2014 NONAKA Kimihiro <non...@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. + */ + +#ifndef _ZAURUS_DEV_WM8731VAR_H_ +#define _ZAURUS_DEV_WM8731VAR_H_ + +int wm8731_match(device_t, cfdata_t, struct i2c_attach_args *); +void wm8731_attach(device_t, device_t, struct i2c_attach_args *); + +#endif /* _ZAURUS_DEV_WM8731VAR_H_ */ + Index: src/sys/arch/zaurus/dev/wm8750_zaudio.c diff -u /dev/null src/sys/arch/zaurus/dev/wm8750_zaudio.c:1.1 --- /dev/null Tue Sep 23 14:49:46 2014 +++ src/sys/arch/zaurus/dev/wm8750_zaudio.c Tue Sep 23 14:49:46 2014 @@ -0,0 +1,1202 @@ +/* $NetBSD: wm8750_zaudio.c,v 1.1 2014/09/23 14:49:46 nonaka Exp $ */ +/* $OpenBSD: zaurus_audio.c,v 1.8 2005/08/18 13:23:02 robert Exp $ */ + +/* + * Copyright (c) 2005 Christopher Pascoe <pas...@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 <non...@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_cputypes.h" +#include "opt_zaudio.h" + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: wm8750_zaudio.c,v 1.1 2014/09/23 14:49:46 nonaka 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/zaudiovar.h> +#include <zaurus/dev/wm8750reg.h> +#include <zaurus/dev/wm8750var.h> +#include <zaurus/dev/scoopvar.h> +#include <zaurus/dev/ioexpvar.h> + +#define WM8750_ADDRESS 0x1B + +/* GPIO pins */ +#define GPIO_HP_IN_C3000 116 + +#define WM8750_OP_SPKR 0 +#define WM8750_OP_HP 1 +#define WM8750_OP_MIC 2 +#define WM8750_OP_NUM 3 + +#define ZAUDIO_JACK_STATE_OUT 0 +#define ZAUDIO_JACK_STATE_IN 1 +#define ZAUDIO_JACK_STATE_INS 2 +#define ZAUDIO_JACK_STATE_REM 3 + +static int wm8750_finalize(device_t); +static bool wm8750_suspend(device_t, const pmf_qual_t *); +static bool wm8750_resume(device_t, const pmf_qual_t *); +static void wm8750_volume_up(device_t); +static void wm8750_volume_down(device_t); +static void wm8750_volume_toggle(device_t); + +static struct audio_device wm8750_device = { + "WM8750", + "1.0", + "wm" +}; + +static const struct audio_format wm8750_formats[] = { + { + .driver_data = NULL, + .mode = AUMODE_PLAY | AUMODE_RECORD, + .encoding = AUDIO_ENCODING_SLINEAR_LE, + .validbits = 16, + .precision = 16, + .channels = 2, + .channel_mask = AUFMT_STEREO, + .frequency_type = 0, + .frequency = { 4000, 48000 } + }, + { + .driver_data = NULL, + .mode = AUMODE_PLAY | AUMODE_RECORD, + .encoding = AUDIO_ENCODING_SLINEAR_LE, + .validbits = 16, + .precision = 16, + .channels = 1, + .channel_mask = AUFMT_MONAURAL, + .frequency_type = 0, + .frequency = { 4000, 48000 } + }, + { + .driver_data = NULL, + .mode = AUMODE_PLAY | AUMODE_RECORD, + .encoding = AUDIO_ENCODING_ULINEAR_LE, + .validbits = 8, + .precision = 8, + .channels = 2, + .channel_mask = AUFMT_STEREO, + .frequency_type = 0, + .frequency = { 4000, 48000 } + }, + { + .driver_data = NULL, + .mode = AUMODE_PLAY | AUMODE_RECORD, + .encoding = AUDIO_ENCODING_ULINEAR_LE, + .validbits = 8, + .precision = 8, + .channels = 1, + .channel_mask = AUFMT_MONAURAL, + .frequency_type = 0, + .frequency = { 4000, 48000 } + }, +}; +static const int wm8750_nformats = (int)__arraycount(wm8750_formats); + +static void wm8750_init(struct zaudio_softc *); +static int wm8750_jack_intr(void *); +static void wm8750_jack(void *); +static void wm8750_standby(struct zaudio_softc *); +static void wm8750_update_volume(struct zaudio_softc *, int); +static void wm8750_update_mutes(struct zaudio_softc *, int); +static void wm8750_play_setup(struct zaudio_softc *); +/*static*/ void wm8750_record_setup(struct zaudio_softc *); +static int wm8750_query_encoding(void *, struct audio_encoding *); +static int wm8750_set_params(void *, int, int, audio_params_t *, + audio_params_t *, stream_filter_list_t *, stream_filter_list_t *); +static int wm8750_start_output(void *, void *, int, void (*)(void *), void *); +static int wm8750_start_input(void *, void *, int, void (*)(void *), void *); +static int wm8750_halt_output(void *); +static int wm8750_halt_input(void *); +static int wm8750_getdev(void *, struct audio_device *); +static int wm8750_set_port(void *, struct mixer_ctrl *); +static int wm8750_get_port(void *, struct mixer_ctrl *); +static int wm8750_query_devinfo(void *, struct mixer_devinfo *); + +static struct audio_hw_if wm8750_hw_if = { + .open = zaudio_open, + .close = zaudio_close, + .drain = NULL, + .query_encoding = wm8750_query_encoding, + .set_params = wm8750_set_params, + .round_blocksize = zaudio_round_blocksize, + .commit_settings = NULL, + .init_output = NULL, + .init_input = NULL, + .start_output = wm8750_start_output, + .start_input = wm8750_start_input, + .halt_output = wm8750_halt_output, + .halt_input = wm8750_halt_input, + .speaker_ctl = NULL, + .getdev = wm8750_getdev, + .setfd = NULL, + .set_port = wm8750_set_port, + .get_port = wm8750_get_port, + .query_devinfo = wm8750_query_devinfo, + .allocm = zaudio_allocm, + .freem = zaudio_freem, + .round_buffersize = zaudio_round_buffersize, + .mappage = zaudio_mappage, + .get_props = zaudio_get_props, + .trigger_output = NULL, + .trigger_input = NULL, + .dev_ioctl = NULL, + .get_locks = zaudio_get_locks, +}; + +static const uint16_t playback_regs[][2] = { + /* Unmute DAC */ + { ADCDACCTL_REG, 0x000 }, + + /* 16 bit audio words */ + { AUDINT_REG, AUDINT_SET_FORMAT(2) }, + + /* Enable thermal protection, power */ + { ADCTL1_REG, ADCTL1_TSDEN | ADCTL1_SET_VSEL(3) }, + + /* Enable speaker driver, DAC oversampling */ + { ADCTL2_REG, ADCTL2_ROUT2INV | ADCTL2_DACOSR }, + + /* Set DAC voltage references */ + { PWRMGMT1_REG, PWRMGMT1_SET_VMIDSEL(1) | PWRMGMT1_VREF }, + + /* Direct DACs to output mixers */ + { LOUTMIX1_REG, LOUTMIX1_LD2LO }, + { LOUTMIX2_REG, 0x000 }, + { ROUTMIX1_REG, 0x000 }, + { ROUTMIX2_REG, ROUTMIX2_RD2RO }, + + /* End of list */ + { 0xffff, 0xffff } +}; + +static const uint16_t record_regs[][2] = { + /* Unmute DAC */ + { ADCDACCTL_REG, 0x000 }, + + /* 16 bit audio words */ + { AUDINT_REG, AUDINT_SET_FORMAT(2) }, + + /* Enable thermal protection, power, left DAC for both channel */ + { ADCTL1_REG, ADCTL1_TSDEN | ADCTL1_SET_VSEL(3) + | ADCTL1_SET_DATSEL(1) }, + + /* Diffrential input select: LINPUT1-RINPUT1, stereo */ + { ADCINPMODE_REG, 0x000 }, + + /* L-R differential, micboost 20dB */ + { ADCLSPATH_REG, ADCLSPATH_SET_LINSEL(3) | ADCLSPATH_SET_LMICBOOST(2) }, + { ADCRSPATH_REG, ADCRSPATH_SET_RINSEL(3) | ADCRSPATH_SET_RMICBOOST(2) }, + + /* End of list */ + { 0xffff, 0xffff } +}; + +static __inline int +wm8750_write(struct zaudio_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, WM8750_ADDRESS, + &cmd, 1, &data, 1, 0); +} + +int +wm8750_match(device_t parent, cfdata_t cf, struct i2c_attach_args *ia) +{ + + if (ZAURUS_ISC860) + return 0; + + if (ia->ia_name) { + /* direct config - check name */ + if (strcmp(ia->ia_name, "zaudio") == 0) + return 1; + } else { + /* indirect config - check typical address */ + if (ia->ia_addr == WM8750_ADDRESS) + return 1; + } + return 0; +} + +void +wm8750_attach(device_t parent, device_t self, struct i2c_attach_args *ia) +{ + struct zaudio_softc *sc = device_private(self); + int error; + + aprint_normal(": I2S, WM8750 Audio\n"); + aprint_naive("\n"); + + /* Check for an I2C response from the wm8750 */ + iic_acquire_bus(sc->sc_i2c, 0); + error = wm8750_write(sc, 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); + + /* Allocate memory for volume & mute operations */ + sc->sc_volume = kmem_zalloc(sizeof(*sc->sc_volume) * WM8750_OP_NUM, + KM_SLEEP); + sc->sc_unmute = kmem_zalloc(sizeof(*sc->sc_unmute) * WM8750_OP_NUM, + KM_SLEEP); + sc->sc_unmute_toggle = kmem_zalloc( + sizeof(*sc->sc_unmute_toggle) * WM8750_OP_NUM, KM_SLEEP); + + /* Speaker on, headphones off by default. */ + sc->sc_volume[WM8750_OP_SPKR].left = 180; + sc->sc_jack = FALSE; + UNMUTE(sc, WM8750_OP_SPKR, 1); + sc->sc_volume[WM8750_OP_HP].left = 180; + sc->sc_volume[WM8750_OP_HP].right = 180; + UNMUTE(sc, WM8750_OP_HP, 0); + sc->sc_volume[WM8750_OP_MIC].left = 180; + UNMUTE(sc, WM8750_OP_MIC, 0); + + /* Configure headphone jack state change handling. */ + callout_setfunc(&sc->sc_to, wm8750_jack, sc); + pxa2x0_gpio_set_function(GPIO_HP_IN_C3000, GPIO_IN); + (void) pxa2x0_gpio_intr_establish(GPIO_HP_IN_C3000, IST_EDGE_BOTH, + IPL_BIO, wm8750_jack_intr, sc); + + /* wm8750_init() implicitly depends on ioexp or scoop */ + config_finalize_register(self, wm8750_finalize); + + audio_attach_mi(&wm8750_hw_if, sc, self); + + if (!pmf_device_register(self, wm8750_suspend, wm8750_resume)) + aprint_error_dev(self, "couldn't establish power handler\n"); + if (!pmf_event_register(self, PMFE_AUDIO_VOLUME_UP, + wm8750_volume_up, true)) + aprint_error_dev(self, "couldn't register event handler\n"); + if (!pmf_event_register(self, PMFE_AUDIO_VOLUME_DOWN, + wm8750_volume_down, true)) + aprint_error_dev(self, "couldn't register event handler\n"); + if (!pmf_event_register(self, PMFE_AUDIO_VOLUME_TOGGLE, + wm8750_volume_toggle, true)) + aprint_error_dev(self, "couldn't register event handler\n"); + + return; + +fail_i2c: + pxa2x0_i2s_detach_sub(&sc->sc_i2s); +} + +static int +wm8750_finalize(device_t dv) +{ + struct zaudio_softc *sc = device_private(dv); + + wm8750_init(sc); + return 0; +} + +static bool +wm8750_suspend(device_t dv, const pmf_qual_t *qual) +{ + struct zaudio_softc *sc = device_private(dv); + + callout_stop(&sc->sc_to); + wm8750_standby(sc); + + return true; +} + +static bool +wm8750_resume(device_t dv, const pmf_qual_t *qual) +{ + struct zaudio_softc *sc = device_private(dv); + + pxa2x0_i2s_init(&sc->sc_i2s); + wm8750_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 ZAUDIO_VOLUME_STRIDE +#define ZAUDIO_VOLUME_STRIDE 8 +#endif + +static void +wm8750_volume_up(device_t dv) +{ + struct zaudio_softc *sc = device_private(dv); + int s; + + s = splbio(); + iic_acquire_bus(sc->sc_i2c, 0); + + sc->sc_volume[WM8750_OP_SPKR].left = + vol_sadd(sc->sc_volume[WM8750_OP_SPKR].left, ZAUDIO_VOLUME_STRIDE); + sc->sc_volume[WM8750_OP_HP].left = + vol_sadd(sc->sc_volume[WM8750_OP_HP].left, ZAUDIO_VOLUME_STRIDE); + sc->sc_volume[WM8750_OP_HP].right = + vol_sadd(sc->sc_volume[WM8750_OP_HP].right, ZAUDIO_VOLUME_STRIDE); + + wm8750_update_volume(sc, WM8750_OP_SPKR); + wm8750_update_volume(sc, WM8750_OP_HP); + + 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 +wm8750_volume_down(device_t dv) +{ + struct zaudio_softc *sc = device_private(dv); + int s; + + s = splbio(); + iic_acquire_bus(sc->sc_i2c, 0); + + sc->sc_volume[WM8750_OP_SPKR].left = + vol_ssub(sc->sc_volume[WM8750_OP_SPKR].left, ZAUDIO_VOLUME_STRIDE); + sc->sc_volume[WM8750_OP_HP].left = + vol_ssub(sc->sc_volume[WM8750_OP_HP].left, ZAUDIO_VOLUME_STRIDE); + sc->sc_volume[WM8750_OP_HP].right = + vol_ssub(sc->sc_volume[WM8750_OP_HP].right, ZAUDIO_VOLUME_STRIDE); + + wm8750_update_volume(sc, WM8750_OP_SPKR); + wm8750_update_volume(sc, WM8750_OP_HP); + + iic_release_bus(sc->sc_i2c, 0); + splx(s); +} + +static void +wm8750_volume_toggle(device_t dv) +{ + struct zaudio_softc *sc = device_private(dv); + int s; + + s = splbio(); + iic_acquire_bus(sc->sc_i2c, 0); + + if (!sc->sc_unmute[WM8750_OP_SPKR] && !sc->sc_unmute[WM8750_OP_HP]) { + sc->sc_unmute[WM8750_OP_SPKR] = + sc->sc_unmute_toggle[WM8750_OP_SPKR]; + sc->sc_unmute[WM8750_OP_HP] = + sc->sc_unmute_toggle[WM8750_OP_HP]; + } else { + sc->sc_unmute[WM8750_OP_SPKR] = 0; + sc->sc_unmute[WM8750_OP_HP] = 0; + } + wm8750_update_mutes(sc, 1); + + iic_release_bus(sc->sc_i2c, 0); + splx(s); +} + +static void +wm8750_init(struct zaudio_softc *sc) +{ + + iic_acquire_bus(sc->sc_i2c, 0); + + /* Reset the codec */ + wm8750_write(sc, RESET_REG, 0); + delay(100); + + /* Switch to standby power only */ + wm8750_write(sc, PWRMGMT1_REG, PWRMGMT1_SET_VMIDSEL(2)); + wm8750_write(sc, PWRMGMT2_REG, 0); + + /* Configure digital interface for I2S */ + wm8750_write(sc, AUDINT_REG, AUDINT_SET_FORMAT(2)); + + /* Initialise volume levels */ + wm8750_update_volume(sc, WM8750_OP_SPKR); + wm8750_update_volume(sc, WM8750_OP_HP); + wm8750_update_volume(sc, WM8750_OP_MIC); + + scoop_set_headphone(0); + if (ZAURUS_ISC1000) + ioexp_set_mic_bias(0); + else + scoop_set_mic_bias(0); + + iic_release_bus(sc->sc_i2c, 0); + + /* Assume that the jack state has changed. */ + wm8750_jack(sc); +} + +static int +wm8750_jack_intr(void *v) +{ + struct zaudio_softc *sc = v; + + if (!callout_active(&sc->sc_to)) + wm8750_jack(sc); + + return 1; +} + +static void +wm8750_jack(void *v) +{ + struct zaudio_softc *sc = v; + + switch (sc->sc_state) { + case ZAUDIO_JACK_STATE_OUT: + if (pxa2x0_gpio_get_bit(GPIO_HP_IN_C3000)) { + sc->sc_state = ZAUDIO_JACK_STATE_INS; + sc->sc_icount = 0; + } + break; + + case ZAUDIO_JACK_STATE_INS: + if (sc->sc_icount++ > 2) { + if (pxa2x0_gpio_get_bit(GPIO_HP_IN_C3000)) { + sc->sc_state = ZAUDIO_JACK_STATE_IN; + sc->sc_jack = TRUE; + UNMUTE(sc, WM8750_OP_SPKR, 0); + UNMUTE(sc, WM8750_OP_HP, 1); + UNMUTE(sc, WM8750_OP_MIC, 1); + goto update_mutes; + } else + sc->sc_state = ZAUDIO_JACK_STATE_OUT; + } + break; + + case ZAUDIO_JACK_STATE_IN: + if (!pxa2x0_gpio_get_bit(GPIO_HP_IN_C3000)) { + sc->sc_state = ZAUDIO_JACK_STATE_REM; + sc->sc_icount = 0; + } + break; + + case ZAUDIO_JACK_STATE_REM: + if (sc->sc_icount++ > 2) { + if (!pxa2x0_gpio_get_bit(GPIO_HP_IN_C3000)) { + sc->sc_state = ZAUDIO_JACK_STATE_OUT; + sc->sc_jack = FALSE; + UNMUTE(sc, WM8750_OP_SPKR, 1); + UNMUTE(sc, WM8750_OP_HP, 0); + UNMUTE(sc, WM8750_OP_MIC, 0); + goto update_mutes; + } else + sc->sc_state = ZAUDIO_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) + wm8750_update_mutes(sc, 1); + if (sc->sc_recording) + wm8750_update_mutes(sc, 2); + iic_release_bus(sc->sc_i2c, 0); + } +} + +static void +wm8750_standby(struct zaudio_softc *sc) +{ + + iic_acquire_bus(sc->sc_i2c, 0); + + /* Switch codec to standby power only */ + wm8750_write(sc, PWRMGMT1_REG, PWRMGMT1_SET_VMIDSEL(2)); + wm8750_write(sc, PWRMGMT2_REG, 0); + + scoop_set_headphone(0); + if (ZAURUS_ISC1000) + ioexp_set_mic_bias(0); + else + scoop_set_mic_bias(0); + + iic_release_bus(sc->sc_i2c, 0); +} + +static void +wm8750_update_volume(struct zaudio_softc *sc, int output) +{ + + switch (output) { + case WM8750_OP_SPKR: + wm8750_write(sc, LOUT2VOL_REG, LOUT2VOL_LO2VU | LOUT2VOL_LO2ZC | + LOUT2VOL_SET_LOUT2VOL(sc->sc_volume[WM8750_OP_SPKR].left >> 1)); + wm8750_write(sc, ROUT2VOL_REG, ROUT2VOL_RO2VU | ROUT2VOL_RO2ZC | + ROUT2VOL_SET_ROUT2VOL(sc->sc_volume[WM8750_OP_SPKR].left >> 1)); + break; + + case WM8750_OP_HP: + wm8750_write(sc, LOUT1VOL_REG, LOUT1VOL_LO1VU | LOUT1VOL_LO1ZC | + LOUT1VOL_SET_LOUT1VOL(sc->sc_volume[WM8750_OP_HP].left >> 1)); + wm8750_write(sc, ROUT1VOL_REG, ROUT1VOL_RO1VU | ROUT1VOL_RO1ZC | + ROUT1VOL_SET_ROUT1VOL(sc->sc_volume[WM8750_OP_HP].right >> 1)); + break; + + case WM8750_OP_MIC: + wm8750_write(sc, LINVOL_REG, LINVOL_LIVU | + LINVOL_SET_LINVOL(sc->sc_volume[WM8750_OP_MIC].left >> 2)); + wm8750_write(sc, RINVOL_REG, RINVOL_RIVU | + RINVOL_SET_RINVOL(sc->sc_volume[WM8750_OP_MIC].left >> 2)); + break; + } +} + +static void +wm8750_update_mutes(struct zaudio_softc *sc, int mask) +{ + uint16_t val; + + /* playback */ + if (mask & 1) { + val = PWRMGMT2_DACL | PWRMGMT2_DACR; + if (sc->sc_unmute[WM8750_OP_SPKR]) + val |= PWRMGMT2_LOUT2 | PWRMGMT2_ROUT2; + if (sc->sc_unmute[WM8750_OP_HP]) + val |= PWRMGMT2_LOUT1 | PWRMGMT2_ROUT1; + wm8750_write(sc, PWRMGMT2_REG, val); + scoop_set_headphone(sc->sc_unmute[WM8750_OP_HP]); + } + + /* record */ + if (mask & 2) { + val = PWRMGMT1_SET_VMIDSEL(1) | PWRMGMT1_VREF; + if (sc->sc_unmute[WM8750_OP_MIC]) { + val |= PWRMGMT1_AINL | PWRMGMT1_AINR + | PWRMGMT1_ADCL | PWRMGMT1_ADCR | PWRMGMT1_MICB; + } + wm8750_write(sc, PWRMGMT1_REG, val); + if (ZAURUS_ISC1000) + ioexp_set_mic_bias(sc->sc_unmute[WM8750_OP_MIC]); + else + scoop_set_mic_bias(sc->sc_unmute[WM8750_OP_MIC]); + } +} + +static void +wm8750_play_setup(struct zaudio_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++) { + wm8750_write(sc, playback_regs[i][0], playback_regs[i][1]); + } + wm8750_update_mutes(sc, 1); + + iic_release_bus(sc->sc_i2c, 0); +} + +/*static*/ void +wm8750_record_setup(struct zaudio_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++) { + wm8750_write(sc, record_regs[i][0], record_regs[i][1]); + } + wm8750_update_mutes(sc, 2); + + iic_release_bus(sc->sc_i2c, 0); +} + +static int +wm8750_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 = 0; + 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 +wm8750_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 zaudio_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(wm8750_formats, wm8750_nformats, + mode, p, false, 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 +wm8750_halt_output(void *hdl) +{ + struct zaudio_softc *sc = hdl; + int rv; + + rv = pxa2x0_i2s_halt_output(&sc->sc_i2s); + if (!sc->sc_recording) + wm8750_standby(sc); + sc->sc_playing = 0; + + return rv; +} + +static int +wm8750_halt_input(void *hdl) +{ + struct zaudio_softc *sc = hdl; + int rv; + + rv = pxa2x0_i2s_halt_input(&sc->sc_i2s); + if (!sc->sc_playing) + wm8750_standby(sc); + sc->sc_recording = 0; + + return rv; +} + +static int +wm8750_getdev(void *hdl, struct audio_device *ret) +{ + + *ret = wm8750_device; + return 0; +} + +#define WM8750_SPKR_LVL 0 +#define WM8750_SPKR_MUTE 1 +#define WM8750_HP_LVL 2 +#define WM8750_HP_MUTE 3 +#define WM8750_MIC_LVL 4 +#define WM8750_MIC_MUTE 5 +#define WM8750_RECORD_SOURCE 6 +#define WM8750_OUTPUT_CLASS 7 +#define WM8750_INPUT_CLASS 8 +#define WM8750_RECORD_CLASS 9 + +static int +wm8750_set_port(void *hdl, struct mixer_ctrl *mc) +{ + struct zaudio_softc *sc = hdl; + int error = EINVAL; + int s; + + s = splbio(); + iic_acquire_bus(sc->sc_i2c, 0); + + switch (mc->dev) { + case WM8750_SPKR_LVL: + if (mc->type != AUDIO_MIXER_VALUE) + break; + if (mc->un.value.num_channels == 1) + sc->sc_volume[WM8750_OP_SPKR].left = + mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]; + else + break; + wm8750_update_volume(sc, WM8750_OP_SPKR); + error = 0; + break; + + case WM8750_SPKR_MUTE: + if (mc->type != AUDIO_MIXER_ENUM) + break; + UNMUTE(sc, WM8750_OP_SPKR, mc->un.ord ? 1 : 0); + wm8750_update_mutes(sc, 1); + error = 0; + break; + + case WM8750_HP_LVL: + if (mc->type != AUDIO_MIXER_VALUE) + break; + if (mc->un.value.num_channels == 1) { + sc->sc_volume[WM8750_OP_HP].left = + mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]; + sc->sc_volume[WM8750_OP_HP].right = + mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]; + } else if (mc->un.value.num_channels == 2) { + sc->sc_volume[WM8750_OP_HP].left = + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; + sc->sc_volume[WM8750_OP_HP].right = + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; + } + else + break; + wm8750_update_volume(sc, WM8750_OP_HP); + error = 0; + break; + + case WM8750_HP_MUTE: + if (mc->type != AUDIO_MIXER_ENUM) + break; + UNMUTE(sc, WM8750_OP_HP, mc->un.ord ? 1 : 0); + wm8750_update_mutes(sc, 1); + error = 0; + break; + + case WM8750_MIC_LVL: + if (mc->type != AUDIO_MIXER_VALUE) + break; + if (mc->un.value.num_channels == 1) + sc->sc_volume[WM8750_OP_MIC].left = + mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]; + else + break; + wm8750_update_volume(sc, WM8750_OP_MIC); + error = 0; + break; + + case WM8750_MIC_MUTE: + if (mc->type != AUDIO_MIXER_ENUM) + break; + UNMUTE(sc, WM8750_OP_MIC, mc->un.ord ? 1 : 0); + wm8750_update_mutes(sc, 2); + error = 0; + break; + + case WM8750_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 +wm8750_get_port(void *hdl, struct mixer_ctrl *mc) +{ + struct zaudio_softc *sc = hdl; + int error = EINVAL; + + switch (mc->dev) { + case WM8750_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[WM8750_OP_SPKR].left; + else + break; + error = 0; + break; + + case WM8750_SPKR_MUTE: + if (mc->type != AUDIO_MIXER_ENUM) + break; + mc->un.ord = sc->sc_unmute[WM8750_OP_SPKR] ? 1 : 0; + error = 0; + break; + + case WM8750_HP_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[WM8750_OP_HP].left; + else if (mc->un.value.num_channels == 2) { + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = + sc->sc_volume[WM8750_OP_HP].left; + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = + sc->sc_volume[WM8750_OP_HP].right; + } + else + break; + error = 0; + break; + + case WM8750_HP_MUTE: + if (mc->type != AUDIO_MIXER_ENUM) + break; + mc->un.ord = sc->sc_unmute[WM8750_OP_HP] ? 1 : 0; + error = 0; + break; + + case WM8750_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[WM8750_OP_MIC].left; + else + break; + error = 0; + break; + + case WM8750_MIC_MUTE: + if (mc->type != AUDIO_MIXER_ENUM) + break; + mc->un.ord = sc->sc_unmute[WM8750_OP_MIC] ? 1 : 0; + error = 0; + break; + + case WM8750_RECORD_SOURCE: + if (mc->type != AUDIO_MIXER_ENUM) + break; + mc->un.ord = 0; /* MIC only */ + error = 0; + break; + } + + return error; +} + +/*ARGSUSED*/ +static int +wm8750_query_devinfo(void *hdl, struct mixer_devinfo *di) +{ + + switch (di->index) { + case WM8750_SPKR_LVL: + di->type = AUDIO_MIXER_VALUE; + di->mixer_class = WM8750_OUTPUT_CLASS; + di->prev = AUDIO_MIXER_LAST; + di->next = WM8750_SPKR_MUTE; + strlcpy(di->label.name, AudioNspeaker, sizeof(di->label.name)); + strlcpy(di->un.v.units.name, AudioNvolume, + sizeof(di->un.v.units.name)); + di->un.v.num_channels = 1; + break; + + case WM8750_SPKR_MUTE: + di->type = AUDIO_MIXER_ENUM; + di->mixer_class = WM8750_OUTPUT_CLASS; + di->prev = WM8750_SPKR_LVL; + di->next = AUDIO_MIXER_LAST; + goto mute; + + case WM8750_HP_LVL: + di->type = AUDIO_MIXER_VALUE; + di->mixer_class = WM8750_OUTPUT_CLASS; + di->prev = AUDIO_MIXER_LAST; + di->next = WM8750_HP_MUTE; + strlcpy(di->label.name, AudioNheadphone, + 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 WM8750_HP_MUTE: + di->type = AUDIO_MIXER_ENUM; + di->mixer_class = WM8750_OUTPUT_CLASS; + di->prev = WM8750_HP_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 WM8750_MIC_LVL: + di->type = AUDIO_MIXER_VALUE; + di->mixer_class = WM8750_INPUT_CLASS; + di->prev = AUDIO_MIXER_LAST; + di->next = WM8750_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 WM8750_MIC_MUTE: + di->type = AUDIO_MIXER_ENUM; + di->mixer_class = WM8750_INPUT_CLASS; + di->prev = WM8750_MIC_LVL; + di->next = AUDIO_MIXER_LAST; + goto mute; + + case WM8750_RECORD_SOURCE: + di->type = AUDIO_MIXER_ENUM; + di->mixer_class = WM8750_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 WM8750_OUTPUT_CLASS: + di->type = AUDIO_MIXER_CLASS; + di->mixer_class = WM8750_OUTPUT_CLASS; + di->prev = AUDIO_MIXER_LAST; + di->next = AUDIO_MIXER_LAST; + strlcpy(di->label.name, AudioCoutputs, sizeof(di->label.name)); + break; + + case WM8750_INPUT_CLASS: + di->type = AUDIO_MIXER_CLASS; + di->mixer_class = WM8750_INPUT_CLASS; + di->prev = AUDIO_MIXER_LAST; + di->next = AUDIO_MIXER_LAST; + strlcpy(di->label.name, AudioCinputs, sizeof(di->label.name)); + break; + + case WM8750_RECORD_CLASS: + di->type = AUDIO_MIXER_CLASS; + di->mixer_class = WM8750_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 int +wm8750_start_output(void *hdl, void *block, int bsize, void (*intr)(void *), + void *intrarg) +{ + struct zaudio_softc *sc = hdl; + int rv; + + /* Power up codec if we are not already playing. */ + if (!sc->sc_playing) { + sc->sc_playing = 1; + wm8750_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) + wm8750_standby(sc); + sc->sc_playing = 0; + } + + return rv; +} + +static int +wm8750_start_input(void *hdl, void *block, int bsize, void (*intr)(void *), + void *intrarg) +{ + struct zaudio_softc *sc = hdl; + int rv; + + /* Power up codec if we are not already recording. */ + if (!sc->sc_recording) { + sc->sc_recording = 1; + wm8750_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) + wm8750_standby(sc); + sc->sc_recording = 0; + } + return rv; +} Index: src/sys/arch/zaurus/dev/wm8750var.h diff -u /dev/null src/sys/arch/zaurus/dev/wm8750var.h:1.1 --- /dev/null Tue Sep 23 14:49:46 2014 +++ src/sys/arch/zaurus/dev/wm8750var.h Tue Sep 23 14:49:46 2014 @@ -0,0 +1,34 @@ +/* $NetBSD: wm8750var.h,v 1.1 2014/09/23 14:49:46 nonaka Exp $ */ + +/*- + * Copyright (C) 2014 NONAKA Kimihiro <non...@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. + */ + +#ifndef _ZAURUS_DEV_WM8750VAR_H_ +#define _ZAURUS_DEV_WM8750VAR_H_ + +int wm8750_match(device_t, cfdata_t, struct i2c_attach_args *); +void wm8750_attach(device_t, device_t, struct i2c_attach_args *); + +#endif /* _ZAURUS_DEV_WM8750VAR_H_ */ Index: src/sys/arch/zaurus/dev/zaudiovar.h diff -u /dev/null src/sys/arch/zaurus/dev/zaudiovar.h:1.1 --- /dev/null Tue Sep 23 14:49:46 2014 +++ src/sys/arch/zaurus/dev/zaudiovar.h Tue Sep 23 14:49:46 2014 @@ -0,0 +1,94 @@ +/* $NetBSD: zaudiovar.h,v 1.1 2014/09/23 14:49:46 nonaka Exp $ */ +/* $OpenBSD: zaurus_audio.c,v 1.8 2005/08/18 13:23:02 robert Exp $ */ + +/* + * Copyright (c) 2005 Christopher Pascoe <pas...@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 <non...@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. + */ + +#ifndef _ZAURUS_DEV_ZAUDIOVAR_H_ +#define _ZAURUS_DEV_ZAUDIOVAR_H_ + +struct zaudio_volume { + uint8_t left; + uint8_t right; +}; + +struct zaudio_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 zaudio_volume *sc_volume; + uint8_t *sc_unmute; + uint8_t *sc_unmute_toggle; + + int sc_jack; + int sc_state; + int sc_icount; + callout_t sc_to; +}; + +#define UNMUTE(sc,op,val) sc->sc_unmute[op] = sc->sc_unmute_toggle[op] = val + +#define ZAUDIO_JACK_STATE_OUT 0 +#define ZAUDIO_JACK_STATE_IN 1 +#define ZAUDIO_JACK_STATE_INS 2 +#define ZAUDIO_JACK_STATE_REM 3 + +int zaudio_open(void *, int); +void zaudio_close(void *); +int zaudio_round_blocksize(void *, int, int, const audio_params_t *); +void * zaudio_allocm(void *, int, size_t); +void zaudio_freem(void *, void *, size_t); +size_t zaudio_round_buffersize(void *, int, size_t); +paddr_t zaudio_mappage(void *, void *, off_t, int); +int zaudio_get_props(void *); +void zaudio_get_locks(void *, kmutex_t **, kmutex_t **); + +#endif /* _ZAURUS_DEV_ZAUDIOVAR_H_ */