diff options
author | Aaron Campbell <aaron@cvs.openbsd.org> | 2001-03-29 14:20:48 +0000 |
---|---|---|
committer | Aaron Campbell <aaron@cvs.openbsd.org> | 2001-03-29 14:20:48 +0000 |
commit | b5f6b9aba903ddbfac37c0871e6e619415a83bdc (patch) | |
tree | f6f9f86e4cbd3bca28128be70c722140e008f019 | |
parent | b9095add3f784e133dab31923db2bd5205a6cc0c (diff) |
Add support for Yamaha DS-XG based audio chips, as found in machines such as
newer Sony Vaio notebooks; written by Kazuki Sakamoto and Minoura Makoto.
This is a slightly updated version of the driver that was previously tested
by jcs@rt.fm and niklas@.
XXX - lacks MPU MIDI support for now, first we must move the mpu401 driver
from sys/dev/isa to sys/dev/ic.
-rw-r--r-- | sys/arch/i386/conf/GENERIC | 5 | ||||
-rw-r--r-- | sys/dev/pci/files.pci | 10 | ||||
-rw-r--r-- | sys/dev/pci/opl_yds.c | 109 | ||||
-rw-r--r-- | sys/dev/pci/yds.c | 1784 | ||||
-rw-r--r-- | sys/dev/pci/ydsreg.h | 314 | ||||
-rw-r--r-- | sys/dev/pci/ydsvar.h | 115 |
6 files changed, 2335 insertions, 2 deletions
diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC index 2051b480c22..516489bb4ef 100644 --- a/sys/arch/i386/conf/GENERIC +++ b/sys/arch/i386/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.249 2001/03/28 20:52:40 deraadt Exp $ +# $OpenBSD: GENERIC,v 1.250 2001/03/29 14:20:45 aaron Exp $ # $NetBSD: GENERIC,v 1.48 1996/05/20 18:17:23 mrg Exp $ # # GENERIC -- everything that's currently supported @@ -368,6 +368,7 @@ clct* at pci? dev ? function ? # CS4281 CrystalClear audio fms* at pci? dev ? function ? # Forte Media FM801 auvia* at pci? dev ? function ? # VIA VT82C686A maestro* at pci? dev ? function ? # ESS Maestro PCI +yds* at pci? dev ? function ? # Yamaha YMF Audio sb0 at isa? port 0x220 irq 5 drq 1 # SoundBlaster sb* at isapnp? ess* at isapnp? # ESS Tech ES188[78], ES888 @@ -383,6 +384,7 @@ mpu* at isapnp? opl* at eso? opl* at sb? opl* at ess? +opl* at yds? # MIDI support midi* at pcppi? # MIDI interface to the PC speaker @@ -414,6 +416,7 @@ audio* at fms? audio* at auvia? audio* at uaudio? audio* at maestro? +audio* at yds? bktr0 at pci? dev ? function ? diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 0ccb52ec9e2..7a7255143c9 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.99 2001/03/28 03:28:33 fgsch Exp $ +# $OpenBSD: files.pci,v 1.100 2001/03/29 14:20:46 aaron Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -104,6 +104,14 @@ device maestro: audio, auconv, mulaw, ac97 attach maestro at pci file dev/pci/maestro.c maestro +# Yamaha YMF Audio +device yds { }: audio, auconv, mulaw, ac97 +attach yds at pci +file dev/pci/yds.c yds + +attach opl at yds with opl_yds +file dev/pci/opl_yds.c opl_yds + # Forte Media FM801 device fms: audio, auconv, mulaw, ac97, midibus attach fms at pci diff --git a/sys/dev/pci/opl_yds.c b/sys/dev/pci/opl_yds.c new file mode 100644 index 00000000000..a2734f2d685 --- /dev/null +++ b/sys/dev/pci/opl_yds.c @@ -0,0 +1,109 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (augustss@netbsd.org). + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/errno.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/conf.h> +#include <sys/select.h> +#include <sys/audioio.h> +#include <sys/midiio.h> + +#include <machine/bus.h> + +#include <dev/audio_if.h> +#include <dev/midi_if.h> +#include <dev/ic/oplreg.h> +#include <dev/ic/oplvar.h> +#include <dev/ic/ac97.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +#include <dev/pci/ydsreg.h> +#include <dev/pci/ydsvar.h> + +int opl_yds_match __P((struct device *, void *, void *)); +void opl_yds_attach __P((struct device *, struct device *, void *)); + +struct cfdriver opl_yds_cd = { + NULL, "opl_yds", DV_DULL +}; + +struct cfattach opl_yds_ca = { + sizeof (struct opl_softc), opl_yds_match, opl_yds_attach +}; + +int +opl_yds_match(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct audio_attach_args *aa = (struct audio_attach_args *)aux; + struct yds_softc *ssc = (struct yds_softc *)parent; + struct opl_softc sc; + + if (aa->type != AUDIODEV_TYPE_OPL) + return (0); + memset(&sc, 0, sizeof sc); + sc.iot = ssc->sc_opl_iot; + sc.ioh = ssc->sc_opl_ioh; + return (opl_find(&sc)); +} + +void +opl_yds_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct yds_softc *ssc = (struct yds_softc *)parent; + struct opl_softc *sc = (struct opl_softc *)self; + + sc->ioh = ssc->sc_opl_ioh; + sc->iot = ssc->sc_opl_iot; + sc->offs = 0; + strcpy(sc->syn.name, "DS-1 integrated "); + + opl_attach(sc); +} diff --git a/sys/dev/pci/yds.c b/sys/dev/pci/yds.c new file mode 100644 index 00000000000..8d9c1c83245 --- /dev/null +++ b/sys/dev/pci/yds.c @@ -0,0 +1,1784 @@ +/* $OpenBSD: yds.c,v 1.1 2001/03/29 14:20:46 aaron Exp $ */ +/* $NetBSD$ */ + +/* + * Copyright (c) 2000, 2001 Kazuki Sakamoto and Minoura Makoto. + * 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. + */ + +/* + * Yamaha YMF724[B-F]/740[B-C]/744/754 + * + * Documentation links: + * - ftp://ftp.alsa-project.org/pub/manuals/yamaha/ + * - ftp://ftp.alsa-project.org/pub/manuals/yamaha/pci/ + * + * TODO: + * - Fill in yds_chip_capability_list + * - Digital in/out (SPDIF) support + * - Effect?? + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/proc.h> +#include <sys/queue.h> +#include <sys/fcntl.h> + +#include <dev/pci/pcidevs.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +#include <sys/audioio.h> +#include <dev/audio_if.h> +#include <dev/midi_if.h> +#include <dev/mulaw.h> +#include <dev/auconv.h> +#include <dev/ic/ac97.h> +#include <dev/ic/mpuvar.h> + +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/microcode/yds/yds_hwmcode.h> + +#include <dev/pci/ydsreg.h> +#include <dev/pci/ydsvar.h> + +/* Debug */ +#undef YDS_USE_REC_SLOT +#define YDS_USE_P44 + +#ifdef AUDIO_DEBUG +# define DPRINTF(x) if (ydsdebug) printf x +# define DPRINTFN(n,x) if (ydsdebug>(n)) printf x +int ydsdebug = 0; +#else +# define DPRINTF(x) +# define DPRINTFN(n,x) +#endif +#ifdef YDS_USE_REC_SLOT +# define YDS_INPUT_SLOT 0 /* REC slot = ADC + loopbacks */ +#else +# define YDS_INPUT_SLOT 1 /* ADC slot */ +#endif + +int yds_match __P((struct device *, void *, void *)); +void yds_attach __P((struct device *, struct device *, void *)); +int yds_intr __P((void *)); + +#define DMAADDR(p) ((p)->map->dm_segs[0].ds_addr) +#define KERNADDR(p) ((void *)((p)->addr)) + +int yds_allocmem __P((struct yds_softc *, size_t, size_t, struct yds_dma *)); +int yds_freemem __P((struct yds_softc *, struct yds_dma *)); + +#ifndef AUDIO_DEBUG +#define YWRITE1(sc, r, x) bus_space_write_1((sc)->memt, (sc)->memh, (r), (x)) +#define YWRITE2(sc, r, x) bus_space_write_2((sc)->memt, (sc)->memh, (r), (x)) +#define YWRITE4(sc, r, x) bus_space_write_4((sc)->memt, (sc)->memh, (r), (x)) +#define YREAD1(sc, r) bus_space_read_1((sc)->memt, (sc)->memh, (r)) +#define YREAD2(sc, r) bus_space_read_2((sc)->memt, (sc)->memh, (r)) +#define YREAD4(sc, r) bus_space_read_4((sc)->memt, (sc)->memh, (r)) +#else + +u_int16_t YREAD2(struct yds_softc *sc,bus_size_t r); +u_int32_t YREAD4(struct yds_softc *sc,bus_size_t r); +void YWRITE1(struct yds_softc *sc,bus_size_t r,u_int8_t x); +void YWRITE2(struct yds_softc *sc,bus_size_t r,u_int16_t x); +void YWRITE4(struct yds_softc *sc,bus_size_t r,u_int32_t x); + +u_int16_t YREAD2(struct yds_softc *sc,bus_size_t r) +{ + DPRINTFN(5, (" YREAD2(0x%lX)\n",(unsigned long)r)); + return bus_space_read_2(sc->memt,sc->memh,r); +} +u_int32_t YREAD4(struct yds_softc *sc,bus_size_t r) +{ + DPRINTFN(5, (" YREAD4(0x%lX)\n",(unsigned long)r)); + return bus_space_read_4(sc->memt,sc->memh,r); +} +void YWRITE1(struct yds_softc *sc,bus_size_t r,u_int8_t x) +{ + DPRINTFN(5, (" YWRITE1(0x%lX,0x%lX)\n",(unsigned long)r,(unsigned long)x)); + bus_space_write_1(sc->memt,sc->memh,r,x); +} +void YWRITE2(struct yds_softc *sc,bus_size_t r,u_int16_t x) +{ + DPRINTFN(5, (" YWRITE2(0x%lX,0x%lX)\n",(unsigned long)r,(unsigned long)x)); + bus_space_write_2(sc->memt,sc->memh,r,x); +} +void YWRITE4(struct yds_softc *sc,bus_size_t r,u_int32_t x) +{ + DPRINTFN(5, (" YWRITE4(0x%lX,0x%lX)\n",(unsigned long)r,(unsigned long)x)); + bus_space_write_4(sc->memt,sc->memh,r,x); +} +#endif + +#define YWRITEREGION4(sc, r, x, c) \ + bus_space_write_region_4((sc)->memt, (sc)->memh, (r), (x), (c) / 4) + +struct cfdriver yds_cd = { + NULL, "yds", DV_DULL +}; + +struct cfattach yds_ca = { + sizeof(struct yds_softc), yds_match, yds_attach +}; + +int yds_open __P((void *, int)); +void yds_close __P((void *)); +int yds_query_encoding __P((void *, struct audio_encoding *)); +int yds_set_params __P((void *, int, int, struct audio_params *, struct audio_params *)); +int yds_round_blocksize __P((void *, int)); +int yds_trigger_output __P((void *, void *, void *, int, void (*)(void *), + void *, struct audio_params *)); +int yds_trigger_input __P((void *, void *, void *, int, void (*)(void *), + void *, struct audio_params *)); +int yds_halt_output __P((void *)); +int yds_halt_input __P((void *)); +int yds_getdev __P((void *, struct audio_device *)); +int yds_mixer_set_port __P((void *, mixer_ctrl_t *)); +int yds_mixer_get_port __P((void *, mixer_ctrl_t *)); +void *yds_malloc __P((void *, u_long, int, int)); +void yds_free __P((void *, void *, int)); +u_long yds_round_buffersize __P((void *, u_long)); +int yds_mappage __P((void *, void *, int, int)); +int yds_get_props __P((void *)); +int yds_query_devinfo __P((void *addr, mixer_devinfo_t *dip)); + +int yds_attach_codec __P((void *sc, struct ac97_codec_if *)); +int yds_read_codec __P((void *sc, u_int8_t a, u_int16_t *d)); +int yds_write_codec __P((void *sc, u_int8_t a, u_int16_t d)); +void yds_reset_codec __P((void *sc)); +int yds_get_portnum_by_name __P((struct yds_softc *, char *, char *, + char *)); + +static u_int yds_get_dstype __P((int)); +static int yds_download_mcode __P((struct yds_softc *)); +static int yds_allocate_slots __P((struct yds_softc *)); +static void yds_configure_legacy __P((struct device *arg)); +static void yds_enable_dsp __P((struct yds_softc *)); +static int yds_disable_dsp __P((struct yds_softc *)); +static int yds_ready_codec __P((struct yds_codec_softc *)); +static int yds_halt __P((struct yds_softc *)); +static u_int32_t yds_get_lpfq __P((u_int)); +static u_int32_t yds_get_lpfk __P((u_int)); +static struct yds_dma *yds_find_dma __P((struct yds_softc *, void *)); + +#ifdef AUDIO_DEBUG +static void yds_dump_play_slot __P((struct yds_softc *, int)); +#define YDS_DUMP_PLAY_SLOT(n,sc,bank) \ + if (ydsdebug > (n)) yds_dump_play_slot(sc, bank) +#else +#define YDS_DUMP_PLAY_SLOT(n,sc,bank) +#endif /* AUDIO_DEBUG */ + +static struct audio_hw_if yds_hw_if = { + yds_open, + yds_close, + NULL, + yds_query_encoding, + yds_set_params, + yds_round_blocksize, + NULL, + NULL, + NULL, + NULL, + NULL, + yds_halt_output, + yds_halt_input, + NULL, + yds_getdev, + NULL, + yds_mixer_set_port, + yds_mixer_get_port, + yds_query_devinfo, + yds_malloc, + yds_free, + yds_round_buffersize, + yds_mappage, + yds_get_props, + yds_trigger_output, + yds_trigger_input, +}; + +struct audio_device yds_device = { + "Yamaha DS-1", + "", + "yds" +}; + +const static struct { + u_int id; + u_int flags; +#define YDS_CAP_MCODE_1 0x0001 +#define YDS_CAP_MCODE_1E 0x0002 +#define YDS_CAP_LEGACY_SELECTABLE 0x0004 +#define YDS_CAP_LEGACY_FLEXIBLE 0x0008 +#define YDS_CAP_HAS_P44 0x0010 +} yds_chip_capability_list[] = { + { PCI_PRODUCT_YAMAHA_YMF724, + YDS_CAP_MCODE_1|YDS_CAP_LEGACY_SELECTABLE }, + /* 740[C] has only 32 slots. But anyway we use only 2 */ + { PCI_PRODUCT_YAMAHA_YMF740, + YDS_CAP_MCODE_1|YDS_CAP_LEGACY_SELECTABLE }, /* XXX NOT TESTED */ + { PCI_PRODUCT_YAMAHA_YMF740C, + YDS_CAP_MCODE_1E|YDS_CAP_LEGACY_SELECTABLE }, + { PCI_PRODUCT_YAMAHA_YMF724F, + YDS_CAP_MCODE_1E|YDS_CAP_LEGACY_SELECTABLE }, + { PCI_PRODUCT_YAMAHA_YMF744, + YDS_CAP_MCODE_1E|YDS_CAP_LEGACY_FLEXIBLE }, + { PCI_PRODUCT_YAMAHA_YMF754, + YDS_CAP_MCODE_1E|YDS_CAP_LEGACY_FLEXIBLE|YDS_CAP_HAS_P44 }, + /* How about 734/737/738?? */ + { 0, 0 } +}; +#ifdef AUDIO_DEBUG +#define YDS_CAP_BITS "\020\005P44\004LEGFLEX\003LEGSEL\002MCODE1E\001MCODE1" +#endif + +#ifdef AUDIO_DEBUG +static void +yds_dump_play_slot(sc, bank) + struct yds_softc *sc; + int bank; +{ + int i, j; + u_int32_t *p; + u_int32_t num; + struct yds_dma *dma; + + for (i = 0; i < N_PLAY_SLOTS; i++) { + printf("pbankp[%d] = %p,", i*2, sc->pbankp[i*2]); + printf("pbankp[%d] = %p\n", i*2+1, sc->pbankp[i*2+1]); + } + + p = (u_int32_t*)sc->ptbl; + for (i = 0; i < N_PLAY_SLOTS+1; i++) { + printf("ptbl + %d:0x%x\n", i, *p); + p++; + } + + num = *(u_int32_t*)sc->ptbl; + printf("num = %d\n", num); + + for (i = 0; i < num; i++) { + + p = (u_int32_t *)sc->pbankp[i]; + + dma = yds_find_dma(sc,(void*)p); + + printf(" pbankp[%d] : %p(%p)\n", + i, p, (void*)vtophys((vaddr_t)p)); + for (j = 0; j < sizeof(struct play_slot_ctrl_bank) / + sizeof(u_int32_t); j++) { + printf(" 0x%02x: 0x%08x\n", + (unsigned) (j * sizeof(u_int32_t)), + (unsigned) *p++); + } + /* + p = (u_int32_t *)sc->pbankp[i*2 + 1]; + printf(" pbankp[%d] : %p\n", i*2 + 1, p); + for (j = 0; j < sizeof(struct play_slot_ctrl_bank) / + sizeof(u_int32_t); j++) { + printf(" 0x%02x: 0x%08x\n", + j * sizeof(u_int32_t), *p++); + delay(1); + } + */ + } +} +#endif /* AUDIO_DEBUG */ + +static u_int +yds_get_dstype(id) + int id; +{ + int i; + + for (i = 0; yds_chip_capability_list[i].id; i++) { + if (PCI_PRODUCT(id) == yds_chip_capability_list[i].id) + return yds_chip_capability_list[i].flags; + } + + return -1; +} + +static int +yds_download_mcode(sc) + struct yds_softc *sc; +{ + u_int ctrl; + const u_int32_t *p; + size_t size; + int dstype; + + static struct { + const u_int32_t *mcode; + size_t size; + } ctrls[] = { + {yds_ds1_ctrl_mcode, sizeof(yds_ds1_ctrl_mcode)}, + {yds_ds1e_ctrl_mcode, sizeof(yds_ds1e_ctrl_mcode)}, + }; + + if (sc->sc_flags & YDS_CAP_MCODE_1) + dstype = YDS_DS_1; + else if (sc->sc_flags & YDS_CAP_MCODE_1E) + dstype = YDS_DS_1E; + else + return 1; /* unknown */ + + if (yds_disable_dsp(sc)) + return 1; + + /* Software reset */ + YWRITE4(sc, YDS_MODE, YDS_MODE_RESET); + YWRITE4(sc, YDS_MODE, 0); + + YWRITE4(sc, YDS_MAPOF_REC, 0); + YWRITE4(sc, YDS_MAPOF_EFFECT, 0); + YWRITE4(sc, YDS_PLAY_CTRLBASE, 0); + YWRITE4(sc, YDS_REC_CTRLBASE, 0); + YWRITE4(sc, YDS_EFFECT_CTRLBASE, 0); + YWRITE4(sc, YDS_WORK_BASE, 0); + + ctrl = YREAD2(sc, YDS_GLOBAL_CONTROL); + YWRITE2(sc, YDS_GLOBAL_CONTROL, + ctrl & ~0x0007); + + /* Download DSP microcode. */ + p = yds_dsp_mcode; + size = sizeof(yds_dsp_mcode); + YWRITEREGION4(sc, YDS_DSP_INSTRAM, p, size); + + /* Download CONTROL microcode. */ + p = ctrls[dstype].mcode; + size = ctrls[dstype].size; + YWRITEREGION4(sc, YDS_CTRL_INSTRAM, p, size); + + yds_enable_dsp(sc); + delay(10*1000); /* nessesary on my 724F (??) */ + + return 0; +} + +static int +yds_allocate_slots(sc) + struct yds_softc *sc; +{ + size_t pcs, rcs, ecs, ws, memsize; + void *mp; + u_int32_t da; /* DMA address */ + char *va; /* KVA */ + off_t cb; + int i; + struct yds_dma *p; + + /* Alloc DSP Control Data */ + pcs = YREAD4(sc, YDS_PLAY_CTRLSIZE) * sizeof(u_int32_t); + rcs = YREAD4(sc, YDS_REC_CTRLSIZE) * sizeof(u_int32_t); + ecs = YREAD4(sc, YDS_EFFECT_CTRLSIZE) * sizeof(u_int32_t); + ws = WORK_SIZE; + YWRITE4(sc, YDS_WORK_SIZE, ws / sizeof(u_int32_t)); + + DPRINTF(("play control size : %d\n", (int) pcs)); + DPRINTF(("rec control size : %d\n", (int) rcs)); + DPRINTF(("eff control size : %d\n", (int) ecs)); + DPRINTF(("work size : %d\n", (int) ws)); + + memsize = N_PLAY_SLOTS*N_PLAY_SLOT_CTRL_BANK*pcs + + N_REC_SLOT_CTRL*N_REC_SLOT_CTRL_BANK*rcs + ws; + memsize += (N_PLAY_SLOTS+1)*sizeof(u_int32_t); + + p = &sc->sc_ctrldata; + i = yds_allocmem(sc, memsize, 16, p); + if (i) { + printf("%s: couldn't alloc/map DSP DMA buffer, reason %d\n", + sc->sc_dev.dv_xname, i); + free(p, M_DEVBUF); + return 1; + } + mp = KERNADDR(p); + da = DMAADDR(p); + + DPRINTF(("mp:%p, DMA addr:%p\n", + mp, (void*) sc->sc_ctrldata.map->dm_segs[0].ds_addr)); + + bzero(mp, memsize); + + /* Work space */ + cb = 0; + va = (u_int8_t*)mp; + YWRITE4(sc, YDS_WORK_BASE, da + cb); + cb += ws; + + /* Play control data table */ + sc->ptbl = (u_int32_t *)(va + cb); + sc->ptbloff = cb; + YWRITE4(sc, YDS_PLAY_CTRLBASE, da + cb); + cb += (N_PLAY_SLOT_CTRL + 1) * sizeof(u_int32_t); + + /* Record slot control data */ + sc->rbank = (struct rec_slot_ctrl_bank *)(va + cb); + YWRITE4(sc, YDS_REC_CTRLBASE, da + cb); + sc->rbankoff = cb; + cb += N_REC_SLOT_CTRL * N_REC_SLOT_CTRL_BANK * rcs; + +#if 0 + /* Effect slot control data -- unused */ + YWRITE4(sc, YDS_EFFECT_CTRLBASE, da + cb); + cb += N_EFFECT_SLOT_CTRL * N_EFFECT_SLOT_CTRL_BANK * ecs; +#endif + + /* Play slot control data */ + sc->pbankoff = da + cb; + for (i=0; i<N_PLAY_SLOT_CTRL; i++) { + sc->pbankp[i*2] = (struct play_slot_ctrl_bank *)(va + cb); + *(sc->ptbl + i+1) = da + cb; + cb += pcs; + + sc->pbankp[i*2+1] = (struct play_slot_ctrl_bank *)(va + cb); + cb += pcs; + } + /* Sync play control data table */ + bus_dmamap_sync(sc->sc_dmatag, p->map, BUS_DMASYNC_PREWRITE); + + return 0; +} + +static void +yds_enable_dsp(sc) + struct yds_softc *sc; +{ + YWRITE4(sc, YDS_CONFIG, YDS_DSP_SETUP); +} + +static int +yds_disable_dsp(sc) + struct yds_softc *sc; +{ + int to; + u_int32_t data; + + data = YREAD4(sc, YDS_CONFIG); + if (data) + YWRITE4(sc, YDS_CONFIG, YDS_DSP_DISABLE); + + for (to = 0; to < YDS_WORK_TIMEOUT; to++) { + if ((YREAD4(sc, YDS_STATUS) & YDS_STAT_WORK) == 0) + return 0; + delay(1); + } + + return 1; +} + +int +yds_match(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct pci_attach_args *pa = (struct pci_attach_args *) aux; + + switch (PCI_VENDOR(pa->pa_id)) { + case PCI_VENDOR_YAMAHA: + switch (PCI_PRODUCT(pa->pa_id)) { + case PCI_PRODUCT_YAMAHA_YMF724: + case PCI_PRODUCT_YAMAHA_YMF740: + case PCI_PRODUCT_YAMAHA_YMF740C: + case PCI_PRODUCT_YAMAHA_YMF724F: + case PCI_PRODUCT_YAMAHA_YMF744: + case PCI_PRODUCT_YAMAHA_YMF754: + /* 734, 737, 738?? */ + return (1); + } + break; + } + + return (0); +} + +/* + * This routine is called after all the ISA devices are configured, + * to avoid conflict. + */ +static void +yds_configure_legacy (arg) + struct device *arg; +#define FLEXIBLE (sc->sc_flags & YDS_CAP_LEGACY_FLEXIBLE) +#define SELECTABLE (sc->sc_flags & YDS_CAP_LEGACY_SELECTABLE) +{ + struct yds_softc *sc = (struct yds_softc*) arg; + pcireg_t reg; + struct device *dev; + int i; + bus_addr_t opl_addrs[] = {0x388, 0x398, 0x3A0, 0x3A8}; + bus_addr_t mpu_addrs[] = {0x330, 0x300, 0x332, 0x334}; + + if (!FLEXIBLE && !SELECTABLE) + return; + + reg = pci_conf_read(sc->sc_pc, sc->sc_pcitag, YDS_PCI_LEGACY); + reg &= ~0x8133c03f; /* these bits are out of interest */ + reg |= ((YDS_PCI_EX_LEGACY_IMOD) | + (YDS_PCI_LEGACY_FMEN | + YDS_PCI_LEGACY_MEN /*| YDS_PCI_LEGACY_MIEN*/)); + if (FLEXIBLE) { + pci_conf_write(sc->sc_pc, sc->sc_pcitag, YDS_PCI_LEGACY, reg); + delay(100*1000); + } + + /* Look for OPL */ + dev = 0; + for (i = 0; i < sizeof(opl_addrs) / sizeof (bus_addr_t); i++) { + if (SELECTABLE) { + pci_conf_write(sc->sc_pc, sc->sc_pcitag, + YDS_PCI_LEGACY, reg | (i << (0+16))); + delay(100*1000); /* wait 100ms */ + } else + pci_conf_write(sc->sc_pc, sc->sc_pcitag, + YDS_PCI_FM_BA, opl_addrs[i]); + if (bus_space_map(sc->sc_opl_iot, + opl_addrs[i], 4, 0, &sc->sc_opl_ioh) == 0) { + struct audio_attach_args aa; + + aa.type = AUDIODEV_TYPE_OPL; + aa.hwif = aa.hdl = NULL; + dev = config_found(&sc->sc_dev, &aa, audioprint); + if (dev == 0) + bus_space_unmap(sc->sc_opl_iot, + sc->sc_opl_ioh, 4); + else { + if (SELECTABLE) + reg |= (i << (0+16)); + break; + } + } + } + if (dev == 0) { + reg &= ~YDS_PCI_LEGACY_FMEN; + pci_conf_write(sc->sc_pc, sc->sc_pcitag, + YDS_PCI_LEGACY, reg); + } else { + /* Max. volume */ + YWRITE4(sc, YDS_LEGACY_OUT_VOLUME, 0x3fff3fff); + YWRITE4(sc, YDS_LEGACY_REC_VOLUME, 0x3fff3fff); + } + + /* Look for MPU */ + dev = 0; + for (i = 0; i < sizeof(mpu_addrs) / sizeof (bus_addr_t); i++) { + if (SELECTABLE) + pci_conf_write(sc->sc_pc, sc->sc_pcitag, + YDS_PCI_LEGACY, reg | (i << (4+16))); + else + pci_conf_write(sc->sc_pc, sc->sc_pcitag, + YDS_PCI_MPU_BA, mpu_addrs[i]); + if (bus_space_map(sc->sc_mpu_iot, + mpu_addrs[i], 2, 0, &sc->sc_mpu_ioh) == 0) { + struct audio_attach_args aa; + + aa.type = AUDIODEV_TYPE_MPU; + aa.hwif = aa.hdl = NULL; + dev = config_found(&sc->sc_dev, &aa, audioprint); + if (dev == 0) + bus_space_unmap(sc->sc_mpu_iot, + sc->sc_mpu_ioh, 2); + else { + if (SELECTABLE) + reg |= (i << (4+16)); + break; + } + } + } + if (dev == 0) { + reg &= ~(YDS_PCI_LEGACY_MEN | YDS_PCI_LEGACY_MIEN); + pci_conf_write(sc->sc_pc, sc->sc_pcitag, + YDS_PCI_LEGACY, reg); + } + sc->sc_mpu = dev; +} +#undef FLEXIBLE +#undef SELECTABLE + +void +yds_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct yds_softc *sc = (struct yds_softc *)self; + struct pci_attach_args *pa = (struct pci_attach_args *)aux; + pci_chipset_tag_t pc = pa->pa_pc; + char const *intrstr; + pci_intr_handle_t ih; + pcireg_t reg; + struct yds_codec_softc *codec; + mixer_ctrl_t ctl; + int i, r, to; + int ac97_id2; + + /* Map register to memory */ + if (pci_mapreg_map(pa, YDS_PCI_MBA, PCI_MAPREG_TYPE_MEM, 0, + &sc->memt, &sc->memh, NULL, NULL)) { + printf("%s: can't map memory space\n", sc->sc_dev.dv_xname); + return; + } + + /* Map and establish the interrupt. */ + if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin, pa->pa_intrline, + &ih)) { + printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname); + return; + } + intrstr = pci_intr_string(pc, ih); + sc->sc_ih = pci_intr_establish(pc, ih, IPL_AUDIO, yds_intr, sc, + self->dv_xname); + if (sc->sc_ih == NULL) { + printf("%s: couldn't establish interrupt", + sc->sc_dev.dv_xname); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + printf(": %s\n", intrstr); + + sc->sc_dmatag = pa->pa_dmat; + sc->sc_pc = pc; + sc->sc_pcitag = pa->pa_tag; + sc->sc_id = pa->pa_id; + sc->sc_flags = yds_get_dstype(sc->sc_id); +#ifdef AUDIO_DEBUG + if (ydsdebug) { + char bits[80]; + + printf("%s: chip has %s\n", sc->sc_dev.dv_xname, + bitmask_snprintf(sc->sc_flags, YDS_CAP_BITS, bits, + sizeof(bits))); + } +#endif + + /* Disable legacy mode */ + reg = pci_conf_read(pc, pa->pa_tag, YDS_PCI_LEGACY); + pci_conf_write(pc, pa->pa_tag, YDS_PCI_LEGACY, + reg & YDS_PCI_LEGACY_LAD); + + /* Enable the device. */ + reg = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + reg |= (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | + PCI_COMMAND_MASTER_ENABLE); + pci_conf_write(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, reg); + reg = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + + /* Mute all volumes */ + for (i = 0x80; i < 0xc0; i += 2) + YWRITE2(sc, i, 0); + + /* Download microcode */ + if (yds_download_mcode(sc)) { + printf("%s: download microcode failed\n", sc->sc_dev.dv_xname); + return; + } + /* Allocate DMA buffers */ + if (yds_allocate_slots(sc)) { + printf("%s: could not allocate slots\n", sc->sc_dev.dv_xname); + return; + } + + /* Warm reset */ + reg = pci_conf_read(pc, pa->pa_tag, YDS_PCI_DSCTRL); + pci_conf_write(pc, pa->pa_tag, YDS_PCI_DSCTRL, reg | YDS_DSCTRL_WRST); + delay(50000); + + /* + * Detect primary/secondary AC97 + * YMF754 Hardware Specification Rev 1.01 page 24 + */ + reg = pci_conf_read(pc, pa->pa_tag, YDS_PCI_DSCTRL); + pci_conf_write(pc, pa->pa_tag, YDS_PCI_DSCTRL, + reg & ~YDS_DSCTRL_CRST); + delay(400000); /* Needed for 740C. */ + + /* Primary */ + for (to = 0; to < AC97_TIMEOUT; to++) { + if ((YREAD2(sc, AC97_STAT_ADDR1) & AC97_BUSY) == 0) + break; + delay(1); + } + if (to == AC97_TIMEOUT) { + printf("%s: no AC97 avaliable\n", sc->sc_dev.dv_xname); + return; + } + + /* Secondary */ + /* Secondary AC97 is used for 4ch audio. Currently unused. */ + ac97_id2 = -1; + if ((YREAD2(sc, YDS_ACTIVITY) & YDS_ACTIVITY_DOCKA) == 0) + goto detected; +#if 0 /* reset secondary... */ + YWRITE2(sc, YDS_GPIO_OCTRL, + YREAD2(sc, YDS_GPIO_OCTRL) & ~YDS_GPIO_GPO2); + YWRITE2(sc, YDS_GPIO_FUNCE, + (YREAD2(sc, YDS_GPIO_FUNCE)&(~YDS_GPIO_GPC2))|YDS_GPIO_GPE2); +#endif + for (to = 0; to < AC97_TIMEOUT; to++) { + if ((YREAD2(sc, AC97_STAT_ADDR2) & AC97_BUSY) == 0) + break; + delay(1); + } + if (to < AC97_TIMEOUT) { + /* detect id */ + for (ac97_id2 = 1; ac97_id2 < 4; ac97_id2++) { + YWRITE2(sc, AC97_CMD_ADDR, + AC97_CMD_READ | AC97_ID(ac97_id2) | 0x28); + + for (to = 0; to < AC97_TIMEOUT; to++) { + if ((YREAD2(sc, AC97_STAT_ADDR2) & AC97_BUSY) + == 0) + goto detected; + delay(1); + } + } + if (ac97_id2 == 4) + ac97_id2 = -1; +detected: + } + + pci_conf_write(pc, pa->pa_tag, YDS_PCI_DSCTRL, + reg | YDS_DSCTRL_CRST); + delay (20); + pci_conf_write(pc, pa->pa_tag, YDS_PCI_DSCTRL, + reg & ~YDS_DSCTRL_CRST); + delay (400000); + for (to = 0; to < AC97_TIMEOUT; to++) { + if ((YREAD2(sc, AC97_STAT_ADDR1) & AC97_BUSY) == 0) + break; + delay(1); + } + + /* + * Attach ac97 codec + */ + for (i = 0; i < 2; i++) { + static struct { + int data; + int addr; + } statregs[] = { + {AC97_STAT_DATA1, AC97_STAT_ADDR1}, + {AC97_STAT_DATA2, AC97_STAT_ADDR2}, + }; + + if (i == 1 && ac97_id2 == -1) + break; /* secondary ac97 not available */ + + codec = &sc->sc_codec[i]; + memcpy(&codec->sc_dev, &sc->sc_dev, sizeof(codec->sc_dev)); + codec->sc = sc; + codec->id = i == 1 ? ac97_id2 : 0; + codec->status_data = statregs[i].data; + codec->status_addr = statregs[i].addr; + codec->host_if.arg = codec; + codec->host_if.attach = yds_attach_codec; + codec->host_if.read = yds_read_codec; + codec->host_if.write = yds_write_codec; + codec->host_if.reset = yds_reset_codec; + + if ((r = ac97_attach(&codec->host_if)) != 0) { + printf("%s: can't attach codec (error 0x%X)\n", + sc->sc_dev.dv_xname, r); + return; + } + } + + /* Just enable the DAC and master volumes by default */ + ctl.type = AUDIO_MIXER_ENUM; + ctl.un.ord = 0; /* off */ + ctl.dev = yds_get_portnum_by_name(sc, AudioCoutputs, + AudioNmaster, AudioNmute); + yds_mixer_set_port(sc, &ctl); + ctl.dev = yds_get_portnum_by_name(sc, AudioCinputs, + AudioNdac, AudioNmute); + yds_mixer_set_port(sc, &ctl); + ctl.dev = yds_get_portnum_by_name(sc, AudioCinputs, + AudioNcd, AudioNmute); + yds_mixer_set_port(sc, &ctl); + ctl.dev = yds_get_portnum_by_name(sc, AudioCrecord, + AudioNvolume, AudioNmute); + yds_mixer_set_port(sc, &ctl); + + ctl.dev = yds_get_portnum_by_name(sc, AudioCrecord, + AudioNsource, NULL); + ctl.type = AUDIO_MIXER_ENUM; + ctl.un.ord = 0; + yds_mixer_set_port(sc, &ctl); + + /* Set a reasonable default volume */ + ctl.type = AUDIO_MIXER_VALUE; + ctl.un.value.num_channels = 2; + ctl.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = + ctl.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 127; + + ctl.dev = sc->sc_codec[0].codec_if->vtbl->get_portnum_by_name( + sc->sc_codec[0].codec_if, AudioCoutputs, AudioNmaster, NULL); + yds_mixer_set_port(sc, &ctl); + + audio_attach_mi(&yds_hw_if, sc, &sc->sc_dev); + + sc->sc_legacy_iot = pa->pa_iot; + config_defer((struct device*) sc, yds_configure_legacy); +} + +int +yds_attach_codec(sc_, codec_if) + void *sc_; + struct ac97_codec_if *codec_if; +{ + struct yds_codec_softc *sc = sc_; + + sc->codec_if = codec_if; + return 0; +} + +static int +yds_ready_codec(sc) + struct yds_codec_softc *sc; +{ + int to; + + for (to = 0; to < AC97_TIMEOUT; to++) { + if ((YREAD2(sc->sc, sc->status_addr) & AC97_BUSY) == 0) + return 0; + delay(1); + } + + return 1; +} + +int +yds_read_codec(sc_, reg, data) + void *sc_; + u_int8_t reg; + u_int16_t *data; +{ + struct yds_codec_softc *sc = sc_; + + YWRITE2(sc->sc, AC97_CMD_ADDR, AC97_CMD_READ | AC97_ID(sc->id) | reg); + + if (yds_ready_codec(sc)) { + printf("%s: yds_read_codec timeout\n", + sc->sc->sc_dev.dv_xname); + return EIO; + } + + *data = YREAD2(sc->sc, sc->status_data); + + return 0; +} + +int +yds_write_codec(sc_, reg, data) + void *sc_; + u_int8_t reg; + u_int16_t data; +{ + struct yds_codec_softc *sc = sc_; + + YWRITE2(sc->sc, AC97_CMD_ADDR, AC97_CMD_WRITE | AC97_ID(sc->id) | reg); + YWRITE2(sc->sc, AC97_CMD_DATA, data); + + if (yds_ready_codec(sc)) { + printf("%s: yds_write_codec timeout\n", + sc->sc->sc_dev.dv_xname); + return EIO; + } + + return 0; +} + +/* + * XXX: Must handle the secondary differntly!! + */ +void +yds_reset_codec(sc_) + void *sc_; +{ + struct yds_codec_softc *codec = sc_; + struct yds_softc *sc = codec->sc; + pcireg_t reg; + + /* reset AC97 codec */ + reg = pci_conf_read(sc->sc_pc, sc->sc_pcitag, YDS_PCI_DSCTRL); + if (reg & 0x03) { + pci_conf_write(sc->sc_pc, sc->sc_pcitag, + YDS_PCI_DSCTRL, reg & ~0x03); + pci_conf_write(sc->sc_pc, sc->sc_pcitag, + YDS_PCI_DSCTRL, reg | 0x03); + pci_conf_write(sc->sc_pc, sc->sc_pcitag, + YDS_PCI_DSCTRL, reg & ~0x03); + delay(50000); + } + + yds_ready_codec(sc_); +} + +int +yds_intr(p) + void *p; +{ + struct yds_softc *sc = p; + u_int status; + + status = YREAD4(sc, YDS_STATUS); + DPRINTFN(1, ("yds_intr: status=%08x\n", status)); + if ((status & (YDS_STAT_INT|YDS_STAT_TINT)) == 0) { +#if NMPU > 0 + if (sc->sc_mpu) + return mpu_intr(sc->sc_mpu); +#endif + return 0; + } + + if (status & YDS_STAT_TINT) { + YWRITE4(sc, YDS_STATUS, YDS_STAT_TINT); + printf ("yds_intr: timeout!\n"); + } + + if (status & YDS_STAT_INT) { + int nbank = (YREAD4(sc, YDS_CONTROL_SELECT) == 0); + + /* Clear interrupt flag */ + YWRITE4(sc, YDS_STATUS, YDS_STAT_INT); + + /* Buffer for the next frame is always ready. */ + YWRITE4(sc, YDS_MODE, YREAD4(sc, YDS_MODE) | YDS_MODE_ACTV2); + + if (sc->sc_play.intr) { + u_int dma, cpu, blk, len; + + /* Sync play slot control data */ + bus_dmamap_sync(sc->sc_dmatag, sc->sc_ctrldata.map, + BUS_DMASYNC_POSTWRITE|BUS_DMASYNC_POSTREAD); + dma = sc->pbankp[nbank]->pgstart * sc->sc_play.factor; + cpu = sc->sc_play.offset; + blk = sc->sc_play.blksize; + len = sc->sc_play.length; + + if (((dma > cpu) && (dma - cpu > blk * 2)) || + ((cpu > dma) && (dma + len - cpu > blk * 2))) { + /* We can fill the next block */ + /* Sync ring buffer first for previous write */ + bus_dmamap_sync(sc->sc_dmatag, + sc->sc_play.dma->map, + BUS_DMASYNC_POSTWRITE); + sc->sc_play.intr(sc->sc_play.intr_arg); + sc->sc_play.offset += blk; + if (sc->sc_play.offset >= len) { + sc->sc_play.offset -= len; +#ifdef DIAGNOSTIC + if (sc->sc_play.offset != 0) + printf ("Audio ringbuffer botch\n"); +#endif + } + /* Sync ring buffer for next write */ + bus_dmamap_sync(sc->sc_dmatag, + sc->sc_play.dma->map, + BUS_DMASYNC_PREWRITE); + } + } + if (sc->sc_rec.intr) { + u_int dma, cpu, blk, len; + + /* Sync rec slot control data */ + bus_dmamap_sync(sc->sc_dmatag, sc->sc_ctrldata.map, + BUS_DMASYNC_POSTWRITE|BUS_DMASYNC_POSTREAD); + dma = sc->rbank[YDS_INPUT_SLOT*2 + nbank].pgstartadr; + cpu = sc->sc_rec.offset; + blk = sc->sc_rec.blksize; + len = sc->sc_rec.length; + + if (((dma > cpu) && (dma - cpu > blk * 2)) || + ((cpu > dma) && (dma + len - cpu > blk * 2))) { + /* We can drain the current block */ + /* Sync ring buffer first */ + bus_dmamap_sync(sc->sc_dmatag, + sc->sc_rec.dma->map, BUS_DMASYNC_POSTREAD); + sc->sc_rec.intr(sc->sc_rec.intr_arg); + sc->sc_rec.offset += blk; + if (sc->sc_rec.offset >= len) { + sc->sc_rec.offset -= len; +#ifdef DIAGNOSTIC + if (sc->sc_rec.offset != 0) + printf ("Audio ringbuffer botch\n"); +#endif + } + /* Sync ring buffer for next read */ + bus_dmamap_sync(sc->sc_dmatag, + sc->sc_rec.dma->map, BUS_DMASYNC_PREREAD); + } + } + } + + return 1; +} + +int +yds_allocmem(sc, size, align, p) + struct yds_softc *sc; + size_t size; + size_t align; + struct yds_dma *p; +{ + int error; + + p->size = size; + error = bus_dmamem_alloc(sc->sc_dmatag, p->size, align, 0, + p->segs, sizeof(p->segs)/sizeof(p->segs[0]), + &p->nsegs, BUS_DMA_NOWAIT); + if (error) + return (error); + + error = bus_dmamem_map(sc->sc_dmatag, p->segs, p->nsegs, p->size, + &p->addr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT); + if (error) + goto free; + + error = bus_dmamap_create(sc->sc_dmatag, p->size, 1, p->size, + 0, BUS_DMA_NOWAIT, &p->map); + if (error) + goto unmap; + + error = bus_dmamap_load(sc->sc_dmatag, p->map, p->addr, p->size, NULL, + BUS_DMA_NOWAIT); + if (error) + goto destroy; + return (0); + +destroy: + bus_dmamap_destroy(sc->sc_dmatag, p->map); +unmap: + bus_dmamem_unmap(sc->sc_dmatag, p->addr, p->size); +free: + bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs); + return (error); +} + +int +yds_freemem(sc, p) + struct yds_softc *sc; + struct yds_dma *p; +{ + bus_dmamap_unload(sc->sc_dmatag, p->map); + bus_dmamap_destroy(sc->sc_dmatag, p->map); + bus_dmamem_unmap(sc->sc_dmatag, p->addr, p->size); + bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs); + return 0; +} + +int +yds_open(addr, flags) + void *addr; + int flags; +{ + struct yds_softc *sc = addr; + int mode; + + /* Select bank 0. */ + YWRITE4(sc, YDS_CONTROL_SELECT, 0); + + /* Start the DSP operation. */ + mode = YREAD4(sc, YDS_MODE); + mode |= YDS_MODE_ACTV; + mode &= ~YDS_MODE_ACTV2; + YWRITE4(sc, YDS_MODE, mode); + + return 0; +} + +/* + * Close function is called at splaudio(). + */ +void +yds_close(addr) + void *addr; +{ + struct yds_softc *sc = addr; + + yds_halt_output(sc); + yds_halt_input(sc); + yds_halt(sc); +} + +int +yds_query_encoding(addr, fp) + void *addr; + struct audio_encoding *fp; +{ + switch (fp->index) { + case 0: + strcpy(fp->name, AudioEulinear); + fp->encoding = AUDIO_ENCODING_ULINEAR; + fp->precision = 8; + fp->flags = 0; + return (0); + case 1: + strcpy(fp->name, AudioEmulaw); + fp->encoding = AUDIO_ENCODING_ULAW; + fp->precision = 8; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 2: + strcpy(fp->name, AudioEalaw); + fp->encoding = AUDIO_ENCODING_ALAW; + fp->precision = 8; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 3: + strcpy(fp->name, AudioEslinear); + fp->encoding = AUDIO_ENCODING_SLINEAR; + fp->precision = 8; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 4: + strcpy(fp->name, AudioEslinear_le); + fp->encoding = AUDIO_ENCODING_SLINEAR_LE; + fp->precision = 16; + fp->flags = 0; + return (0); + case 5: + strcpy(fp->name, AudioEulinear_le); + fp->encoding = AUDIO_ENCODING_ULINEAR_LE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 6: + strcpy(fp->name, AudioEslinear_be); + fp->encoding = AUDIO_ENCODING_SLINEAR_BE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 7: + strcpy(fp->name, AudioEulinear_be); + fp->encoding = AUDIO_ENCODING_ULINEAR_BE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + default: + return (EINVAL); + } +} + +int +yds_set_params(addr, setmode, usemode, play, rec) + void *addr; + int setmode, usemode; + struct audio_params *play, *rec; +{ + struct audio_params *p; + int mode; + + 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); + + p->factor = 1; + p->sw_code = 0; + switch (p->encoding) { + case AUDIO_ENCODING_SLINEAR_BE: + if (p->precision == 16) + p->sw_code = swap_bytes; + else + p->sw_code = change_sign8; + break; + case AUDIO_ENCODING_SLINEAR_LE: + if (p->precision != 16) + p->sw_code = change_sign8; + break; + case AUDIO_ENCODING_ULINEAR_BE: + if (p->precision == 16) { + if (mode == AUMODE_PLAY) + p->sw_code = swap_bytes_change_sign16_le; + else + p->sw_code = change_sign16_swap_bytes_le; + } + break; + case AUDIO_ENCODING_ULINEAR_LE: + if (p->precision == 16) + p->sw_code = change_sign16_le; + break; + case AUDIO_ENCODING_ULAW: + if (mode == AUMODE_PLAY) { + p->factor = 2; + p->precision = 16; + p->sw_code = mulaw_to_slinear16_le; + } else + p->sw_code = ulinear8_to_mulaw; + break; + case AUDIO_ENCODING_ALAW: + if (mode == AUMODE_PLAY) { + p->factor = 2; + p->precision = 16; + p->sw_code = alaw_to_slinear16_le; + } else + p->sw_code = ulinear8_to_alaw; + break; + default: + return (EINVAL); + } + } + + return 0; +} + +int +yds_round_blocksize(addr, blk) + void *addr; + int blk; +{ + /* + * Block size must be bigger than a frame. + * That is 1024bytes at most, i.e. for 48000Hz, 16bit, 2ch. + */ + if (blk < 1024) + blk = 1024; + + return blk & ~4; +} + +static u_int32_t +yds_get_lpfq(sample_rate) + u_int sample_rate; +{ + int i; + static struct lpfqt { + u_int rate; + u_int32_t lpfq; + } lpfqt[] = { + {8000, 0x32020000}, + {11025, 0x31770000}, + {16000, 0x31390000}, + {22050, 0x31c90000}, + {32000, 0x33d00000}, + {48000, 0x40000000}, + {0, 0} + }; + + if (sample_rate == 44100) /* for P44 slot? */ + return 0x370A0000; + + for (i = 0; lpfqt[i].rate != 0; i++) + if (sample_rate <= lpfqt[i].rate) + break; + + return lpfqt[i].lpfq; +} + +static u_int32_t +yds_get_lpfk(sample_rate) + u_int sample_rate; +{ + int i; + static struct lpfkt { + u_int rate; + u_int32_t lpfk; + } lpfkt[] = { + {8000, 0x18b20000}, + {11025, 0x20930000}, + {16000, 0x2b9a0000}, + {22050, 0x35a10000}, + {32000, 0x3eaa0000}, + {48000, 0x40000000}, + {0, 0} + }; + + if (sample_rate == 44100) /* for P44 slot? */ + return 0x46460000; + + for (i = 0; lpfkt[i].rate != 0; i++) + if (sample_rate <= lpfkt[i].rate) + break; + + return lpfkt[i].lpfk; +} + +int +yds_trigger_output(addr, start, end, blksize, intr, arg, param) + void *addr; + void *start, *end; + int blksize; + void (*intr) __P((void *)); + void *arg; + struct audio_params *param; +#define P44 (sc->sc_flags & YDS_CAP_HAS_P44) +{ + struct yds_softc *sc = addr; + struct yds_dma *p; + struct play_slot_ctrl_bank *psb; + const u_int gain = 0x40000000; + bus_addr_t s; + size_t l; + int i; + int p44, channels; + +#ifdef DIAGNOSTIC + if (sc->sc_play.intr) + panic("yds_trigger_output: already running"); +#endif + + sc->sc_play.intr = intr; + sc->sc_play.intr_arg = arg; + sc->sc_play.offset = 0; + sc->sc_play.blksize = blksize; + + DPRINTFN(1, ("yds_trigger_output: sc=%p start=%p end=%p " + "blksize=%d intr=%p(%p)\n", addr, start, end, blksize, intr, arg)); + + p = yds_find_dma(sc, start); + if (!p) { + printf("yds_trigger_output: bad addr %p\n", start); + return (EINVAL); + } + sc->sc_play.dma = p; + +#ifdef DIAGNOSTIC + { + u_int32_t ctrlsize; + if ((ctrlsize = YREAD4(sc, YDS_PLAY_CTRLSIZE)) != + sizeof(struct play_slot_ctrl_bank) / sizeof(u_int32_t)) + panic("%s: invalid play slot ctrldata %d %d", + sc->sc_dev.dv_xname, ctrlsize, + sizeof(struct play_slot_ctrl_bank)); + } +#endif + +#ifdef YDS_USE_P44 + /* The document says the P44 SRC supports only stereo, 16bit PCM. */ + if (P44) + p44 = ((param->sample_rate == 44100) && + (param->channels == 2) && + (param->precision == 16)); + else +#endif + p44 = 0; + channels = p44 ? 1 : param->channels; + + s = DMAADDR(p); + l = ((char *)end - (char *)start); + sc->sc_play.length = l; + + *sc->ptbl = channels; /* Num of play */ + + sc->sc_play.factor = 1; + if (param->channels == 2) + sc->sc_play.factor *= 2; + if (param->precision != 8) + sc->sc_play.factor *= 2; + l /= sc->sc_play.factor; + + psb = sc->pbankp[0]; + memset(psb, 0, sizeof(*psb)); + psb->format = ((channels == 2 ? PSLT_FORMAT_STEREO : 0) | + (param->precision == 8 ? PSLT_FORMAT_8BIT : 0) | + (p44 ? PSLT_FORMAT_SRC441 : 0)); + psb->pgbase = s; + psb->pgloopend = l; + if (!p44) { + psb->pgdeltaend = (param->sample_rate * 65536 / 48000) << 12; + psb->lpfkend = yds_get_lpfk(param->sample_rate); + psb->eggainend = gain; + psb->lpfq = yds_get_lpfq(param->sample_rate); + psb->pgdelta = psb->pgdeltaend; + psb->lpfk = yds_get_lpfk(param->sample_rate); + psb->eggain = gain; + } + + for (i = 0; i < channels; i++) { + /* i == 0: left or mono, i == 1: right */ + psb = sc->pbankp[i*2]; + if (i) + /* copy from left */ + *psb = *(sc->pbankp[0]); + if (channels == 2) { + /* stereo */ + if (i == 0) { + psb->lchgain = psb->lchgainend = gain; + } else { + psb->rchgain = psb->rchgainend = gain; + psb->format |= PSLT_FORMAT_RCH; + } + } else if (!p44) { + /* mono */ + psb->lchgain = psb->rchgain = gain; + psb->lchgainend = psb->rchgainend = gain; + } + /* copy to the other bank */ + *(sc->pbankp[i*2+1]) = *psb; + } + + YDS_DUMP_PLAY_SLOT(5, sc, 0); + YDS_DUMP_PLAY_SLOT(5, sc, 1); + + if (p44) + YWRITE4(sc, YDS_P44_OUT_VOLUME, 0x3fff3fff); + else + YWRITE4(sc, YDS_DAC_OUT_VOLUME, 0x3fff3fff); + + /* Now the play slot for the next frame is set up!! */ + /* Sync play slot control data for both directions */ + bus_dmamap_sync(sc->sc_dmatag, sc->sc_ctrldata.map, + BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD); + /* Sync ring buffer */ + bus_dmamap_sync(sc->sc_dmatag, p->map, BUS_DMASYNC_PREWRITE); + /* HERE WE GO!! */ + YWRITE4(sc, YDS_MODE, + YREAD4(sc, YDS_MODE) | YDS_MODE_ACTV | YDS_MODE_ACTV2); + + return 0; +} +#undef P44 + +int +yds_trigger_input(addr, start, end, blksize, intr, arg, param) + void *addr; + void *start, *end; + int blksize; + void (*intr) __P((void *)); + void *arg; + struct audio_params *param; +{ + struct yds_softc *sc = addr; + struct yds_dma *p; + u_int srate, format; + struct rec_slot_ctrl_bank *rsb; + bus_addr_t s; + size_t l; + +#ifdef DIAGNOSTIC + if (sc->sc_rec.intr) + panic("yds_trigger_input: already running"); +#endif + sc->sc_rec.intr = intr; + sc->sc_rec.intr_arg = arg; + sc->sc_rec.offset = 0; + sc->sc_rec.blksize = blksize; + + DPRINTFN(1, ("yds_trigger_input: " + "sc=%p start=%p end=%p blksize=%d intr=%p(%p)\n", + addr, start, end, blksize, intr, arg)); + DPRINTFN(1, (" parameters: rate=%lu, precision=%u, channels=%u\n", + param->sample_rate, param->precision, param->channels)); + + p = yds_find_dma(sc, start); + if (!p) { + printf("yds_trigger_input: bad addr %p\n", start); + return (EINVAL); + } + sc->sc_rec.dma = p; + +#ifdef DIAGNOSTIC + { + u_int32_t ctrlsize; + if ((ctrlsize = YREAD4(sc, YDS_REC_CTRLSIZE)) != + sizeof(struct rec_slot) / sizeof(u_int32_t)) + panic("%s: invalid rec slot ctrldata %d", + sc->sc_dev.dv_xname, ctrlsize); + } +#endif + + s = DMAADDR(p); + l = ((char *)end - (char *)start); + sc->sc_rec.length = l; + + sc->sc_rec.factor = 1; + if (param->channels == 2) + sc->sc_rec.factor *= 2; + if (param->precision != 8) + sc->sc_rec.factor *= 2; + + rsb = &sc->rbank[0]; + memset(rsb, 0, sizeof(*rsb)); + rsb->pgbase = s; + rsb->pgloopendadr = l; + /* Seems all 4 banks must be set up... */ + sc->rbank[1] = *rsb; + sc->rbank[2] = *rsb; + sc->rbank[3] = *rsb; + + YWRITE4(sc, YDS_ADC_IN_VOLUME, 0x3fff3fff); + YWRITE4(sc, YDS_REC_IN_VOLUME, 0x3fff3fff); + srate = 48000 * 4096 / param->sample_rate - 1; + format = ((param->precision == 8 ? YDS_FORMAT_8BIT : 0) | + (param->channels == 2 ? YDS_FORMAT_STEREO : 0)); + DPRINTF(("srate=%d, format=%08x\n", srate, format)); +#ifdef YDS_USE_REC_SLOT + YWRITE4(sc, YDS_DAC_REC_VOLUME, 0x3fff3fff); + YWRITE4(sc, YDS_P44_REC_VOLUME, 0x3fff3fff); + YWRITE4(sc, YDS_MAPOF_REC, YDS_RECSLOT_VALID); + YWRITE4(sc, YDS_REC_SAMPLE_RATE, srate); + YWRITE4(sc, YDS_REC_FORMAT, format); +#else + YWRITE4(sc, YDS_MAPOF_REC, YDS_ADCSLOT_VALID); + YWRITE4(sc, YDS_ADC_SAMPLE_RATE, srate); + YWRITE4(sc, YDS_ADC_FORMAT, format); +#endif + /* Now the rec slot for the next frame is set up!! */ + /* Sync record slot control data */ + bus_dmamap_sync(sc->sc_dmatag, sc->sc_ctrldata.map, + BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD); + /* Sync ring buffer */ + bus_dmamap_sync(sc->sc_dmatag, p->map, BUS_DMASYNC_PREREAD); + /* HERE WE GO!! */ + YWRITE4(sc, YDS_MODE, + YREAD4(sc, YDS_MODE) | YDS_MODE_ACTV | YDS_MODE_ACTV2); + + return 0; +} + +static int +yds_halt(sc) + struct yds_softc *sc; +{ + u_int32_t mode; + + /* Stop the DSP operation. */ + mode = YREAD4(sc, YDS_MODE); + YWRITE4(sc, YDS_MODE, mode & ~(YDS_MODE_ACTV|YDS_MODE_ACTV2)); + + /* Paranoia... mute all */ + YWRITE4(sc, YDS_P44_OUT_VOLUME, 0); + YWRITE4(sc, YDS_DAC_OUT_VOLUME, 0); + YWRITE4(sc, YDS_ADC_IN_VOLUME, 0); + YWRITE4(sc, YDS_REC_IN_VOLUME, 0); + YWRITE4(sc, YDS_DAC_REC_VOLUME, 0); + YWRITE4(sc, YDS_P44_REC_VOLUME, 0); + + return 0; +} + +int +yds_halt_output(addr) + void *addr; +{ + struct yds_softc *sc = addr; + + DPRINTF(("yds: yds_halt_output\n")); + if (sc->sc_play.intr) { + sc->sc_play.intr = 0; + /* Sync play slot control data */ + bus_dmamap_sync(sc->sc_dmatag, sc->sc_ctrldata.map, + BUS_DMASYNC_POSTWRITE|BUS_DMASYNC_POSTREAD); + /* Stop the play slot operation */ + sc->pbankp[0]->status = + sc->pbankp[1]->status = + sc->pbankp[2]->status = + sc->pbankp[3]->status = 1; + /* Sync ring buffer */ + bus_dmamap_sync(sc->sc_dmatag, sc->sc_play.dma->map, + BUS_DMASYNC_POSTWRITE); + } + + return 0; +} + +int +yds_halt_input(addr) + void *addr; +{ + struct yds_softc *sc = addr; + + DPRINTF(("yds: yds_halt_input\n")); + sc->sc_rec.intr = NULL; + if (sc->sc_rec.intr) { + /* Stop the rec slot operation */ + YWRITE4(sc, YDS_MAPOF_REC, 0); + sc->sc_rec.intr = 0; + /* Sync rec slot control data */ + bus_dmamap_sync(sc->sc_dmatag, sc->sc_ctrldata.map, + BUS_DMASYNC_POSTWRITE|BUS_DMASYNC_POSTREAD); + /* Sync ring buffer */ + bus_dmamap_sync(sc->sc_dmatag, sc->sc_rec.dma->map, + BUS_DMASYNC_POSTREAD); + } + + return 0; +} + +int +yds_getdev(addr, retp) + void *addr; + struct audio_device *retp; +{ + *retp = yds_device; + + return 0; +} + +int +yds_mixer_set_port(addr, cp) + void *addr; + mixer_ctrl_t *cp; +{ + struct yds_softc *sc = addr; + + return (sc->sc_codec[0].codec_if->vtbl->mixer_set_port( + sc->sc_codec[0].codec_if, cp)); +} + +int +yds_mixer_get_port(addr, cp) + void *addr; + mixer_ctrl_t *cp; +{ + struct yds_softc *sc = addr; + + return (sc->sc_codec[0].codec_if->vtbl->mixer_get_port( + sc->sc_codec[0].codec_if, cp)); +} + +int +yds_query_devinfo(addr, dip) + void *addr; + mixer_devinfo_t *dip; +{ + struct yds_softc *sc = addr; + + return (sc->sc_codec[0].codec_if->vtbl->query_devinfo( + sc->sc_codec[0].codec_if, dip)); +} + +int +yds_get_portnum_by_name(sc, class, device, qualifier) + struct yds_softc *sc; + char *class, *device, *qualifier; +{ + return (sc->sc_codec[0].codec_if->vtbl->get_portnum_by_name( + sc->sc_codec[0].codec_if, class, device, qualifier)); +} + +void * +yds_malloc(addr, size, pool, flags) + void *addr; + u_long size; + int pool, flags; +{ + struct yds_softc *sc = addr; + struct yds_dma *p; + int error; + + p = malloc(sizeof(*p), pool, flags); + if (!p) + return (0); + error = yds_allocmem(sc, size, 16, p); + if (error) { + free(p, pool); + return (0); + } + p->next = sc->sc_dmas; + sc->sc_dmas = p; + return (KERNADDR(p)); +} + +void +yds_free(addr, ptr, pool) + void *addr; + void *ptr; + int pool; +{ + struct yds_softc *sc = addr; + struct yds_dma **pp, *p; + + for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &p->next) { + if (KERNADDR(p) == ptr) { + yds_freemem(sc, p); + *pp = p->next; + free(p, pool); + return; + } + } +} + +static struct yds_dma * +yds_find_dma(sc, addr) + struct yds_softc *sc; + void *addr; +{ + struct yds_dma *p; + + for (p = sc->sc_dmas; p && KERNADDR(p) != addr; p = p->next) + ; + + return p; +} + +u_long +yds_round_buffersize(addr, size) + void *addr; + u_long size; +{ + /* + * Buffer size should be at least twice as bigger as a frame. + */ + if (size < 1024 * 3) + size = 1024 * 3; + return (size); +} + +int +yds_mappage(addr, mem, off, prot) + void *addr; + void *mem; + int off; + int prot; +{ + struct yds_softc *sc = addr; + struct yds_dma *p; + + if (off < 0) + return (-1); + p = yds_find_dma(sc, mem); + if (!p) + return (-1); + return (bus_dmamem_mmap(sc->sc_dmatag, p->segs, p->nsegs, + off, prot, BUS_DMA_WAITOK)); +} + +int +yds_get_props(addr) + void *addr; +{ + return (AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT | + AUDIO_PROP_FULLDUPLEX); +} diff --git a/sys/dev/pci/ydsreg.h b/sys/dev/pci/ydsreg.h new file mode 100644 index 00000000000..1d8a256c18c --- /dev/null +++ b/sys/dev/pci/ydsreg.h @@ -0,0 +1,314 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 2000, 2001 Kazuki Sakamoto and Minoura Makoto. + * 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. + */ + +/* + * YMF724/740/744/754 registers + */ + +#ifndef _DEV_PCI_YDSREG_H_ +#define _DEV_PCI_YDSREG_H_ + +/* + * PCI Config Registers + */ +#define YDS_PCI_MBA 0x10 +#define YDS_PCI_LEGACY 0x40 +# define YDS_PCI_LEGACY_SBEN 0x0001 +# define YDS_PCI_LEGACY_FMEN 0x0002 +# define YDS_PCI_LEGACY_JPEN 0x0004 +# define YDS_PCI_LEGACY_MEN 0x0008 +# define YDS_PCI_LEGACY_MIEN 0x0010 +# define YDS_PCI_LEGACY_IO 0x0020 +# define YDS_PCI_LEGACY_SDMA0 0x0000 +# define YDS_PCI_LEGACY_SDMA1 0x0040 +# define YDS_PCI_LEGACY_SDMA3 0x00c0 +# define YDS_PCI_LEGACY_SBIRQ5 0x0000 +# define YDS_PCI_LEGACY_SBIRQ7 0x0100 +# define YDS_PCI_LEGACY_SBIRQ9 0x0200 +# define YDS_PCI_LEGACY_SBIRQ10 0x0300 +# define YDS_PCI_LEGACY_SBIRQ11 0x0400 +# define YDS_PCI_LEGACY_MPUIRQ5 0x0000 +# define YDS_PCI_LEGACY_MPUIRQ7 0x0800 +# define YDS_PCI_LEGACY_MPUIRQ9 0x1000 +# define YDS_PCI_LEGACY_MPUIRQ10 0x1800 +# define YDS_PCI_LEGACY_MPUIRQ11 0x2000 +# define YDS_PCI_LEGACY_SIEN 0x4000 +# define YDS_PCI_LEGACY_LAD 0x8000 + +# define YDS_PCI_EX_LEGACY_FMIO_388 (0x0000 << 16) +# define YDS_PCI_EX_LEGACY_FMIO_398 (0x0001 << 16) +# define YDS_PCI_EX_LEGACY_FMIO_3A0 (0x0002 << 16) +# define YDS_PCI_EX_LEGACY_FMIO_3A8 (0x0003 << 16) +# define YDS_PCI_EX_LEGACY_SBIO_220 (0x0000 << 16) +# define YDS_PCI_EX_LEGACY_SBIO_240 (0x0004 << 16) +# define YDS_PCI_EX_LEGACY_SBIO_260 (0x0008 << 16) +# define YDS_PCI_EX_LEGACY_SBIO_280 (0x000c << 16) +# define YDS_PCI_EX_LEGACY_MPUIO_330 (0x0000 << 16) +# define YDS_PCI_EX_LEGACY_MPUIO_300 (0x0010 << 16) +# define YDS_PCI_EX_LEGACY_MPUIO_332 (0x0020 << 16) +# define YDS_PCI_EX_LEGACY_MPUIO_334 (0x0030 << 16) +# define YDS_PCI_EX_LEGACY_JSIO_201 (0x0000 << 16) +# define YDS_PCI_EX_LEGACY_JSIO_202 (0x0040 << 16) +# define YDS_PCI_EX_LEGACY_JSIO_204 (0x0080 << 16) +# define YDS_PCI_EX_LEGACY_JSIO_205 (0x00c0 << 16) +# define YDS_PCI_EX_LEGACY_MAIM (0x0100 << 16) +# define YDS_PCI_EX_LEGACY_SBMOD_PCI (0x0000 << 16) +# define YDS_PCI_EX_LEGACY_SBMOD_DDMA (0x1000 << 16) +# define YDS_PCI_EX_LEGACY_SBVER_3 (0x0000 << 16) +# define YDS_PCI_EX_LEGACY_SBVER_2 (0x2000 << 16) +# define YDS_PCI_EX_LEGACY_SBVER_1 (0x4000 << 16) +# define YDS_PCI_EX_LEGACY_IMOD (0x8000 << 16) + +#define YDS_PCI_DSCTRL 0x48 +# define YDS_DSCTRL_CRST 0x00000001 +# define YDS_DSCTRL_WRST 0x00000004 + +#define YDS_PCI_FM_BA 0x60 +#define YDS_PCI_SB_BA 0x62 +#define YDS_PCI_MPU_BA 0x64 +#define YDS_PCI_JS_BA 0x66 + +/* + * DS-1 PCI Audio part registers + */ +#define YDS_INTERRUPT_FLAGS 0x0004 +#define YDS_INTERRUPT_FLAGS_TI 0x0001 +#define YDS_ACTIVITY 0x0006 +# define YDS_ACTIVITY_DOCKA 0x0010 +#define YDS_GLOBAL_CONTROL 0x0008 +# define YDS_GLCTRL_HVE 0x0001 +# define YDS_GLCTRL_HVIE 0x0002 + +#define YDS_GPIO_IIF 0x0050 +# define YDS_GPIO_GIO0 0x0001 +# define YDS_GPIO_GIO1 0x0002 +# define YDS_GPIO_GIO2 0x0004 +#define YDS_GPIO_IIE 0x0052 +# define YDS_GPIO_GIE0 0x0001 +# define YDS_GPIO_GIE1 0x0002 +# define YDS_GPIO_GIE2 0x0004 +#define YDS_GPIO_ISTAT 0x0054 +# define YDS_GPIO_GPI0 0x0001 +# define YDS_GPIO_GPI1 0x0002 +# define YDS_GPIO_GPI2 0x0004 +#define YDS_GPIO_OCTRL 0x0056 +# define YDS_GPIO_GPO0 0x0001 +# define YDS_GPIO_GPO1 0x0002 +# define YDS_GPIO_GPO2 0x0004 +#define YDS_GPIO_FUNCE 0x0058 +# define YDS_GPIO_GPC0 0x0001 +# define YDS_GPIO_GPC1 0x0002 +# define YDS_GPIO_GPC2 0x0004 +# define YDS_GPIO_GPE0 0x0010 +# define YDS_GPIO_GPE1 0x0020 +# define YDS_GPIO_GPE2 0x0040 +#define YDS_GPIO_ITYPE 0x005a +# define YDS_GPIO_GPT0_LEVEL 0x0000 +# define YDS_GPIO_GPT0_RISE 0x0001 +# define YDS_GPIO_GPT0_FALL 0x0002 +# define YDS_GPIO_GPT0_BOTH 0x0003 +# define YDS_GPIO_GPT0_MASK 0x0003 +# define YDS_GPIO_GPT1_LEVEL 0x0004 +# define YDS_GPIO_GPT1_RISE 0x0005 +# define YDS_GPIO_GPT1_FALL 0x0006 +# define YDS_GPIO_GPT1_BOTH 0x0007 +# define YDS_GPIO_GPT1_MASK 0x0007 +# define YDS_GPIO_GPT2_LEVEL 0x0000 +# define YDS_GPIO_GPT2_RISE 0x0010 +# define YDS_GPIO_GPT2_FALL 0x0020 +# define YDS_GPIO_GPT2_BOTH 0x0030 +# define YDS_GPIO_GPT2_MASK 0x0030 + +#define YDS_GLOBAL_CONTROL 0x0008 +# define YDS_GLCTRL_HVE 0x0001 +# define YDS_GLCTRL_HVIE 0x0002 + +#define AC97_CMD_DATA 0x0060 +#define AC97_CMD_ADDR 0x0062 +# define AC97_ID(id) ((id) << 8) +# define AC97_CMD_READ 0x8000 +# define AC97_CMD_WRITE 0x0000 +#define AC97_STAT_DATA1 0x0064 +#define AC97_STAT_ADDR1 0x0066 +#define AC97_STAT_DATA2 0x0068 +#define AC97_STAT_ADDR2 0x006a +# define AC97_BUSY 0x8000 + +#define YDS_LEGACY_OUT_VOLUME 0x0080 +#define YDS_DAC_OUT_VOLUME 0x0084 +#define YDS_DAC_OUT_VOL_L 0x0084 +#define YDS_DAC_OUT_VOL_R 0x0086 +#define YDS_ZV_OUT_VOLUME 0x0088 +#define YDS_2ND_OUT_VOLUME 0x008C +#define YDS_ADC_OUT_VOLUME 0x0090 +#define YDS_LEGACY_REC_VOLUME 0x0094 +#define YDS_DAC_REC_VOLUME 0x0098 +#define YDS_ZV_REC_VOLUME 0x009C +#define YDS_2ND_REC_VOLUME 0x00A0 +#define YDS_ADC_REC_VOLUME 0x00A4 +#define YDS_ADC_IN_VOLUME 0x00A8 +#define YDS_REC_IN_VOLUME 0x00AC +#define YDS_P44_OUT_VOLUME 0x00B0 +#define YDS_P44_REC_VOLUME 0x00B4 +#define YDS_SPDIFIN_OUT_VOLUME 0x00B8 +#define YDS_SPDIFIN_REC_VOLUME 0x00BC + +#define YDS_ADC_SAMPLE_RATE 0x00c0 +#define YDS_REC_SAMPLE_RATE 0x00c4 +#define YDS_ADC_FORMAT 0x00c8 +#define YDS_REC_FORMAT 0x00cc +# define YDS_FORMAT_8BIT 0x01 +# define YDS_FORMAT_STEREO 0x02 + +#define YDS_STATUS 0x0100 +# define YDS_STAT_ACT 0x00000001 +# define YDS_STAT_WORK 0x00000002 +# define YDS_STAT_TINT 0x00008000 +# define YDS_STAT_INT 0x80000000 +#define YDS_CONTROL_SELECT 0x0104 +# define YDS_CSEL 0x00000001 +#define YDS_MODE 0x0108 +# define YDS_MODE_ACTV 0x00000001 +# define YDS_MODE_ACTV2 0x00000002 +# define YDS_MODE_TOUT 0x00008000 +# define YDS_MODE_RESET 0x00010000 +# define YDS_MODE_AC3 0x40000000 +# define YDS_MODE_MUTE 0x80000000 + +#define YDS_CONFIG 0x0114 +# define YDS_DSP_DISABLE 0 +# define YDS_DSP_SETUP 0x00000001 + +#define YDS_PLAY_CTRLSIZE 0x0140 +#define YDS_REC_CTRLSIZE 0x0144 +#define YDS_EFFECT_CTRLSIZE 0x0148 +#define YDS_WORK_SIZE 0x014c +#define YDS_MAPOF_REC 0x0150 +# define YDS_RECSLOT_VALID 0x00000001 +# define YDS_ADCSLOT_VALID 0x00000002 +#define YDS_MAPOF_EFFECT 0x0154 +# define YDS_DL_VALID 0x00000001 +# define YDS_DR_VALID 0x00000002 +# define YDS_EFFECT1_VALID 0x00000004 +# define YDS_EFFECT2_VALID 0x00000008 +# define YDS_EFFECT3_VALID 0x00000010 + +#define YDS_PLAY_CTRLBASE 0x0158 +#define YDS_REC_CTRLBASE 0x015c +#define YDS_EFFECT_CTRLBASE 0x0160 +#define YDS_WORK_BASE 0x0164 + +#define YDS_DSP_INSTRAM 0x1000 +#define YDS_CTRL_INSTRAM 0x4000 + +typedef enum { + YDS_DS_1, + YDS_DS_1E +} yds_dstype_t; + +#define AC97_TIMEOUT 1000 +#define YDS_WORK_TIMEOUT 250000 + +/* slot control data structures */ +#define MAX_PLAY_SLOT_CTRL 64 +#define N_PLAY_SLOT_CTRL_BANK 2 +#define N_REC_SLOT_CTRL 2 +#define N_REC_SLOT_CTRL_BANK 2 + +/* + * play slot + */ +union play_slot_table { + u_int32_t numofplay; + u_int32_t slotbase; +}; + +struct play_slot_ctrl_bank { + u_int32_t format; +#define PSLT_FORMAT_STEREO 0x00010000 +#define PSLT_FORMAT_8BIT 0x80000000 +#define PSLT_FORMAT_SRC441 0x10000000 +#define PSLT_FORMAT_RCH 0x00000001 + u_int32_t loopdefault; + u_int32_t pgbase; + u_int32_t pgloop; + u_int32_t pgloopend; + u_int32_t pgloopfrac; + u_int32_t pgdeltaend; + u_int32_t lpfkend; + u_int32_t eggainend; + u_int32_t lchgainend; + u_int32_t rchgainend; + u_int32_t effect1gainend; + u_int32_t effect2gainend; + u_int32_t effect3gainend; + u_int32_t lpfq; + u_int32_t status; +#define PSLT_STATUS_DEND 0x00000001 + u_int32_t numofframes; + u_int32_t loopcount; + u_int32_t pgstart; + u_int32_t pgstartfrac; + u_int32_t pgdelta; + u_int32_t lpfk; + u_int32_t eggain; + u_int32_t lchgain; + u_int32_t rchgain; + u_int32_t effect1gain; + u_int32_t effect2gain; + u_int32_t effect3gain; + u_int32_t lpfd1; + u_int32_t lpfd2; +}; + +/* + * rec slot + */ +struct rec_slot_ctrl_bank { + u_int32_t pgbase; + u_int32_t pgloopendadr; + u_int32_t pgstartadr; + u_int32_t numofloops; +}; + +struct rec_slot { + struct rec_slot_ctrl { + struct rec_slot_ctrl_bank bank[N_REC_SLOT_CTRL_BANK]; + } ctrl[N_REC_SLOT_CTRL]; +}; + +/* + * effect slot + */ +struct effect_slot_ctrl_bank { + u_int32_t pgbase; + u_int32_t pgloopend; + u_int32_t pgstart; + u_int32_t temp; +}; + +#endif /* _DEV_PCI_YDSREG_H_ */ diff --git a/sys/dev/pci/ydsvar.h b/sys/dev/pci/ydsvar.h new file mode 100644 index 00000000000..a898ac5f1e4 --- /dev/null +++ b/sys/dev/pci/ydsvar.h @@ -0,0 +1,115 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 2000, 2001 Kazuki Sakamoto and Minoura Makoto. + * 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 _DEV_PCI_YDSVAR_H_ +#define _DEV_PCI_YDSVAR_H_ + +#define N_PLAY_SLOTS 2 /* We use only 2 (R and L) */ +#define N_PLAY_SLOT_CTRL 2 +#define WORK_SIZE 0x0400 + +/* + * softc + */ +struct yds_dma { + bus_dmamap_t map; + caddr_t addr; /* VA */ + bus_dma_segment_t segs[1]; + int nsegs; + size_t size; + struct yds_dma *next; +}; + +struct yds_codec_softc { + struct device sc_dev; /* base device */ + struct yds_softc *sc; + int id; + int status_data; + int status_addr; + struct ac97_host_if host_if; + struct ac97_codec_if *codec_if; +}; + +struct yds_softc { + struct device sc_dev; /* base device */ + pci_chipset_tag_t sc_pc; + pcitag_t sc_pcitag; + pcireg_t sc_id; + void *sc_ih; /* interrupt vectoring */ + bus_space_tag_t memt; + bus_space_handle_t memh; + bus_dma_tag_t sc_dmatag; /* DMA tag */ + u_int sc_flags; + + struct yds_codec_softc sc_codec[2]; /* Primary/Secondary AC97 */ + + struct yds_dma *sc_dmas; /* List of DMA handles */ + + /* + * Play/record status + */ + struct { + void (*intr)(void *); /* rint/pint */ + void *intr_arg; /* arg for intr */ + u_int offset; /* filled up to here */ + u_int blksize; + u_int factor; /* byte per sample */ + u_int length; /* ring buffer length */ + struct yds_dma *dma; /* DMA handle for ring buf */ + } sc_play, sc_rec; + + /* + * DSP control data + * + * Work space, play control data table, play slot control data, + * rec slot control data and effect slot control data are + * stored in a single memory segment in this order. + */ + struct yds_dma sc_ctrldata; + /* KVA and offset in buffer of play ctrl data tbl */ + u_int32_t *ptbl; + off_t ptbloff; + /* KVA and offset in buffer of rec slot ctrl data */ + struct rec_slot_ctrl_bank *rbank; + off_t rbankoff; + /* Array of KVA pointers and offset of play slot control data */ + struct play_slot_ctrl_bank *pbankp[N_PLAY_SLOT_CTRL_BANK + *N_PLAY_SLOTS]; + off_t pbankoff; + + /* + * Legacy support + */ + bus_space_tag_t sc_legacy_iot; + bus_space_handle_t sc_opl_ioh; + struct device *sc_mpu; + bus_space_handle_t sc_mpu_ioh; +}; +#define sc_opl_iot sc_legacy_iot +#define sc_mpu_iot sc_legacy_iot + +#endif /* _DEV_PCI_YDSVAR_H_ */ |